From 687fe991e42f15fe1f491680c615ef96568f780a Mon Sep 17 00:00:00 2001 From: Alex Brainman Date: Thu, 21 Apr 2016 16:51:36 +1000 Subject: [PATCH] debug/pe: introduce File.COFFSymbols and (*COFFSymbol).FullName Reloc.SymbolTableIndex is an index into symbol table. But Reloc.SymbolTableIndex cannot be used as index into File.Symbols, because File.Symbols slice has Aux lines removed as it is built. We cannot change the way File.Symbols works, so I propose we introduce new File.COFFSymbols that does not have that limitation. Also unlike File.Symbols, File.COFFSymbols will consist of COFFSymbol. COFFSymbol matches PE COFF specification exactly, and it is simpler to use. Updates #15345 Change-Id: Icbc265853a472529cd6d64a76427b27e5459e373 Reviewed-on: https://go-review.googlesource.com/22336 Reviewed-by: David Crawshaw Run-TryBot: Alex Brainman TryBot-Result: Gobot Gobot --- src/debug/pe/file.go | 53 ++++++------------------------ src/debug/pe/section.go | 2 ++ src/debug/pe/symbol.go | 71 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 43 deletions(-) diff --git a/src/debug/pe/file.go b/src/debug/pe/file.go index cfd8e08a6384a..abc33dfea89a7 100644 --- a/src/debug/pe/file.go +++ b/src/debug/pe/file.go @@ -19,7 +19,8 @@ type File struct { FileHeader OptionalHeader interface{} // of type *OptionalHeader32 or *OptionalHeader64 Sections []*Section - Symbols []*Symbol + Symbols []*Symbol // COFF symbols with auxiliary symbol records removed + COFFSymbols []COFFSymbol // all COFF symbols (including auxiliary symbol records) StringTable StringTable closer io.Closer @@ -94,48 +95,14 @@ func NewFile(r io.ReaderAt) (*File, error) { return nil, err } - var ss []byte - if f.FileHeader.NumberOfSymbols > 0 { - // Get COFF string table, which is located at the end of the COFF symbol table. - sr.Seek(int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols), io.SeekStart) - var l uint32 - if err := binary.Read(sr, binary.LittleEndian, &l); err != nil { - return nil, err - } - ss = make([]byte, l) - if _, err := r.ReadAt(ss, int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols)); err != nil { - return nil, err - } - - // Process COFF symbol table. - sr.Seek(int64(f.FileHeader.PointerToSymbolTable), io.SeekStart) - aux := uint8(0) - for i := 0; i < int(f.FileHeader.NumberOfSymbols); i++ { - cs := new(COFFSymbol) - if err := binary.Read(sr, binary.LittleEndian, cs); err != nil { - return nil, err - } - if aux > 0 { - aux-- - continue - } - var name string - if cs.Name[0] == 0 && cs.Name[1] == 0 && cs.Name[2] == 0 && cs.Name[3] == 0 { - si := int(binary.LittleEndian.Uint32(cs.Name[4:])) - name, _ = getString(ss, si) - } else { - name = cstring(cs.Name[:]) - } - aux = cs.NumberOfAuxSymbols - s := &Symbol{ - Name: name, - Value: cs.Value, - SectionNumber: cs.SectionNumber, - Type: cs.Type, - StorageClass: cs.StorageClass, - } - f.Symbols = append(f.Symbols, s) - } + // Read symbol table. + f.COFFSymbols, err = readCOFFSymbols(&f.FileHeader, sr) + if err != nil { + return nil, err + } + f.Symbols, err = removeAuxSymbols(f.COFFSymbols, f.StringTable) + if err != nil { + return nil, err } // Read optional header. diff --git a/src/debug/pe/section.go b/src/debug/pe/section.go index 69fe41fd7a4d8..5d881577d3985 100644 --- a/src/debug/pe/section.go +++ b/src/debug/pe/section.go @@ -39,6 +39,8 @@ func (sh *SectionHeader32) fullName(st StringTable) (string, error) { return st.String(uint32(i)) } +// TODO(brainman): copy all IMAGE_REL_* consts from ldpe.go here + // Reloc represents a PE COFF relocation. // Each section contains its own relocation list. type Reloc struct { diff --git a/src/debug/pe/symbol.go b/src/debug/pe/symbol.go index 559174838cfbf..3cf5a101e7467 100644 --- a/src/debug/pe/symbol.go +++ b/src/debug/pe/symbol.go @@ -4,8 +4,15 @@ package pe +import ( + "encoding/binary" + "fmt" + "io" +) + const COFFSymbolSize = 18 +// COFFSymbol represents single COFF symbol table record. type COFFSymbol struct { Name [8]uint8 Value uint32 @@ -15,6 +22,70 @@ type COFFSymbol struct { NumberOfAuxSymbols uint8 } +func readCOFFSymbols(fh *FileHeader, r io.ReadSeeker) ([]COFFSymbol, error) { + if fh.NumberOfSymbols <= 0 { + return nil, nil + } + _, err := r.Seek(int64(fh.PointerToSymbolTable), io.SeekStart) + if err != nil { + return nil, fmt.Errorf("fail to seek to symbol table: %v", err) + } + syms := make([]COFFSymbol, fh.NumberOfSymbols) + err = binary.Read(r, binary.LittleEndian, syms) + if err != nil { + return nil, fmt.Errorf("fail to read symbol table: %v", err) + } + return syms, nil +} + +// isSymNameOffset checks symbol name if it is encoded as offset into string table. +func isSymNameOffset(name [8]byte) (bool, uint32) { + if name[0] == 0 && name[1] == 0 && name[2] == 0 && name[3] == 0 { + return true, binary.LittleEndian.Uint32(name[4:]) + } + return false, 0 +} + +// FullName finds real name of symbol sym. Normally name is stored +// in sym.Name, but if it is longer then 8 characters, it is stored +// in COFF string table st instead. +func (sym *COFFSymbol) FullName(st StringTable) (string, error) { + if ok, offset := isSymNameOffset(sym.Name); ok { + return st.String(offset) + } + return cstring(sym.Name[:]), nil +} + +func removeAuxSymbols(allsyms []COFFSymbol, st StringTable) ([]*Symbol, error) { + if len(allsyms) == 0 { + return nil, nil + } + syms := make([]*Symbol, 0) + aux := uint8(0) + for _, sym := range allsyms { + if aux > 0 { + aux-- + continue + } + name, err := sym.FullName(st) + if err != nil { + return nil, err + } + aux = sym.NumberOfAuxSymbols + s := &Symbol{ + Name: name, + Value: sym.Value, + SectionNumber: sym.SectionNumber, + Type: sym.Type, + StorageClass: sym.StorageClass, + } + syms = append(syms, s) + } + return syms, nil +} + +// Symbol is similar to COFFSymbol with Name field replaced +// by Go string. Symbol also does not have NumberOfAuxSymbols. type Symbol struct { Name string Value uint32