diff options
| author | benbaker76 <headkaze@gmail.com> | 2024-11-06 23:13:37 +0000 |
|---|---|---|
| committer | Gopher Robot <gobot@golang.org> | 2024-11-07 15:23:24 +0000 |
| commit | 2e97c30d8d9f5740a503428c09efae7bedb68efb (patch) | |
| tree | 05504990c5c1f74c8fc45ee39afaf0c84d12dd51 /src/debug/elf/file.go | |
| parent | fc5e8f2f6ba07f999a780848aa66da7d73083c1e (diff) | |
| download | go-2e97c30d8d9f5740a503428c09efae7bedb68efb.tar.xz | |
debug/elf: add SHT_GNU_VERDEF section parsing
Fixes #63952
Change-Id: Icf93e57e62243d9c3306d4e1c5dadb3f62747710
GitHub-Last-Rev: 5c2952760063474f3aac338fe5bdb65bde238ab6
GitHub-Pull-Request: golang/go#69850
Reviewed-on: https://go-review.googlesource.com/c/go/+/619077
Reviewed-by: Ian Lance Taylor <iant@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
Reviewed-by: David Chase <drchase@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Diffstat (limited to 'src/debug/elf/file.go')
| -rw-r--r-- | src/debug/elf/file.go | 241 |
1 files changed, 202 insertions, 39 deletions
diff --git a/src/debug/elf/file.go b/src/debug/elf/file.go index 398439dcce..05062f1433 100644 --- a/src/debug/elf/file.go +++ b/src/debug/elf/file.go @@ -52,11 +52,12 @@ type FileHeader struct { // A File represents an open ELF file. type File struct { FileHeader - Sections []*Section - Progs []*Prog - closer io.Closer - gnuNeed []verneed - gnuVersym []byte + Sections []*Section + Progs []*Prog + closer io.Closer + dynVers []DynamicVersion + dynVerNeeds []DynamicVersionNeed + gnuVersym []byte } // A SectionHeader represents a single ELF section header. @@ -207,11 +208,16 @@ func (p *Prog) Open() io.ReadSeeker { return io.NewSectionReader(p.sr, 0, 1<<63- type Symbol struct { Name string Info, Other byte + + // These fields are used for symbol versioning + // and are present only for the dynamic symbol table. + VersionIndex int16 + VersionFlags SymbolVersionFlag + Section SectionIndex Value, Size uint64 - // Version and Library are present only for the dynamic symbol - // table. + // These fields are present only for the dynamic symbol table. Version string Library string } @@ -657,6 +663,7 @@ func (f *File) getSymbols32(typ SectionType) ([]Symbol, []byte, error) { symbols[i].Name = str symbols[i].Info = sym.Info symbols[i].Other = sym.Other + symbols[i].VersionIndex = -1 symbols[i].Section = SectionIndex(sym.Shndx) symbols[i].Value = uint64(sym.Value) symbols[i].Size = uint64(sym.Size) @@ -704,6 +711,7 @@ func (f *File) getSymbols64(typ SectionType) ([]Symbol, []byte, error) { symbols[i].Name = str symbols[i].Info = sym.Info symbols[i].Other = sym.Other + symbols[i].VersionIndex = -1 symbols[i].Section = SectionIndex(sym.Shndx) symbols[i].Value = sym.Value symbols[i].Size = sym.Size @@ -1446,7 +1454,7 @@ func (f *File) DynamicSymbols() ([]Symbol, error) { } if f.gnuVersionInit(str) { for i := range sym { - sym[i].Library, sym[i].Version = f.gnuVersion(i) + sym[i].VersionIndex, sym[i].Version, sym[i].Library, sym[i].VersionFlags = f.gnuVersion(i) } } return sym, nil @@ -1473,22 +1481,121 @@ func (f *File) ImportedSymbols() ([]ImportedSymbol, error) { if ST_BIND(s.Info) == STB_GLOBAL && s.Section == SHN_UNDEF { all = append(all, ImportedSymbol{Name: s.Name}) sym := &all[len(all)-1] - sym.Library, sym.Version = f.gnuVersion(i) + _, sym.Version, sym.Library, _ = f.gnuVersion(i) } } return all, nil } -type verneed struct { - File string - Name string +type SymbolVersionFlag byte + +const ( + VerFlagNone SymbolVersionFlag = 0x0 // No flags. + VerFlagLocal SymbolVersionFlag = 0x1 // Symbol has local scope. + VerFlagGlobal SymbolVersionFlag = 0x2 // Symbol has global scope. + VerFlagHidden SymbolVersionFlag = 0x4 // Symbol is hidden. +) + +// DynamicVersion is a version defined by a dynamic object. +type DynamicVersion struct { + Version uint16 // Version of data structure. + Flags DynamicVersionFlag + Index uint16 // Version index. + Deps []string // Dependencies. } -// gnuVersionInit parses the GNU version tables -// for use by calls to gnuVersion. -func (f *File) gnuVersionInit(str []byte) bool { - if f.gnuNeed != nil { - // Already initialized +type DynamicVersionNeed struct { + Version uint16 // Version of data structure. + Name string // Shared library name. + Needs []DynamicVersionDep // Dependencies. +} + +type DynamicVersionDep struct { + Flags DynamicVersionFlag + Other uint16 // Version index. + Dep string // Name of required version. +} + +// dynamicVersions returns version information for a dynamic object. +func (f *File) dynamicVersions(str []byte) bool { + if f.dynVers != nil { + // Already initialized. + return true + } + + // Accumulate verdef information. + vd := f.SectionByType(SHT_GNU_VERDEF) + if vd == nil { + return false + } + d, _ := vd.Data() + + var dynVers []DynamicVersion + i := 0 + for { + if i+20 > len(d) { + break + } + version := f.ByteOrder.Uint16(d[i : i+2]) + flags := DynamicVersionFlag(f.ByteOrder.Uint16(d[i+2 : i+4])) + ndx := f.ByteOrder.Uint16(d[i+4 : i+6]) + cnt := f.ByteOrder.Uint16(d[i+6 : i+8]) + aux := f.ByteOrder.Uint32(d[i+12 : i+16]) + next := f.ByteOrder.Uint32(d[i+16 : i+20]) + + var depName string + var deps []string + j := i + int(aux) + for c := 0; c < int(cnt); c++ { + if j+8 > len(d) { + break + } + vname := f.ByteOrder.Uint32(d[j : j+4]) + vnext := f.ByteOrder.Uint32(d[j+4 : j+8]) + depName, _ = getString(str, int(vname)) + + deps = append(deps, depName) + + j += int(vnext) + } + + dynVers = append(dynVers, DynamicVersion{ + Version: version, + Flags: flags, + Index: ndx, + Deps: deps, + }) + + if next == 0 { + break + } + i += int(next) + } + + f.dynVers = dynVers + + return true +} + +// DynamicVersions returns version information for a dynamic object. +func (f *File) DynamicVersions() ([]DynamicVersion, error) { + if f.dynVers == nil { + _, str, err := f.getSymbols(SHT_DYNSYM) + if err != nil { + return nil, err + } + if !f.gnuVersionInit(str) { + return nil, errors.New("DynamicVersions: missing version table") + } + } + + return f.dynVers, nil +} + +// dynamicVersionNeeds returns version dependencies for a dynamic object. +func (f *File) dynamicVersionNeeds(str []byte) bool { + if f.dynVerNeeds != nil { + // Already initialized. return true } @@ -1499,7 +1606,7 @@ func (f *File) gnuVersionInit(str []byte) bool { } d, _ := vn.Data() - var need []verneed + var dynVerNeeds []DynamicVersionNeed i := 0 for { if i+16 > len(d) { @@ -1515,68 +1622,124 @@ func (f *File) gnuVersionInit(str []byte) bool { next := f.ByteOrder.Uint32(d[i+12 : i+16]) file, _ := getString(str, int(fileoff)) - var name string + var deps []DynamicVersionDep j := i + int(aux) for c := 0; c < int(cnt); c++ { if j+16 > len(d) { break } - // hash := f.ByteOrder.Uint32(d[j:j+4]) - // flags := f.ByteOrder.Uint16(d[j+4:j+6]) + flags := DynamicVersionFlag(f.ByteOrder.Uint16(d[j+4 : j+6])) other := f.ByteOrder.Uint16(d[j+6 : j+8]) nameoff := f.ByteOrder.Uint32(d[j+8 : j+12]) next := f.ByteOrder.Uint32(d[j+12 : j+16]) - name, _ = getString(str, int(nameoff)) - ndx := int(other) - if ndx >= len(need) { - a := make([]verneed, 2*(ndx+1)) - copy(a, need) - need = a - } + depName, _ := getString(str, int(nameoff)) + + deps = append(deps, DynamicVersionDep{ + Flags: flags, + Other: other, + Dep: depName, + }) - need[ndx] = verneed{file, name} if next == 0 { break } j += int(next) } + dynVerNeeds = append(dynVerNeeds, DynamicVersionNeed{ + Version: vers, + Name: file, + Needs: deps, + }) + if next == 0 { break } i += int(next) } + f.dynVerNeeds = dynVerNeeds + + return true +} + +// DynamicVersionNeeds returns version dependencies for a dynamic object. +func (f *File) DynamicVersionNeeds() ([]DynamicVersionNeed, error) { + if f.dynVerNeeds == nil { + _, str, err := f.getSymbols(SHT_DYNSYM) + if err != nil { + return nil, err + } + if !f.gnuVersionInit(str) { + return nil, errors.New("DynamicVersionNeeds: missing version table") + } + } + + return f.dynVerNeeds, nil +} + +// gnuVersionInit parses the GNU version tables +// for use by calls to gnuVersion. +func (f *File) gnuVersionInit(str []byte) bool { // Versym parallels symbol table, indexing into verneed. vs := f.SectionByType(SHT_GNU_VERSYM) if vs == nil { return false } - d, _ = vs.Data() + d, _ := vs.Data() - f.gnuNeed = need f.gnuVersym = d + f.dynamicVersions(str) + f.dynamicVersionNeeds(str) return true } // gnuVersion adds Library and Version information to sym, // which came from offset i of the symbol table. -func (f *File) gnuVersion(i int) (library string, version string) { +func (f *File) gnuVersion(i int) (versionIndex int16, version string, library string, versionFlags SymbolVersionFlag) { // Each entry is two bytes; skip undef entry at beginning. i = (i + 1) * 2 if i >= len(f.gnuVersym) { - return + return -1, "", "", VerFlagNone } s := f.gnuVersym[i:] if len(s) < 2 { - return + return -1, "", "", VerFlagNone + } + j := int32(f.ByteOrder.Uint16(s)) + var ndx = int16(j & 0x7fff) + + if ndx == 0 { + return ndx, "", "", VerFlagLocal + } else if ndx == 1 { + return ndx, "", "", VerFlagGlobal + } + + if ndx < 2 { + return 0, "", "", VerFlagNone + } + + for _, v := range f.dynVerNeeds { + for _, n := range v.Needs { + if uint16(ndx) == n.Other { + return ndx, n.Dep, v.Name, VerFlagHidden + } + } } - j := int(f.ByteOrder.Uint16(s)) - if j < 2 || j >= len(f.gnuNeed) { - return + + for _, v := range f.dynVers { + if uint16(ndx) == v.Index { + if len(v.Deps) > 0 { + flags := VerFlagNone + if j&0x8000 != 0 { + flags = VerFlagHidden + } + return ndx, v.Deps[0], "", flags + } + } } - n := &f.gnuNeed[j] - return n.File, n.Name + + return -1, "", "", VerFlagNone } // ImportedLibraries returns the names of all libraries |
