diff options
Diffstat (limited to 'src/debug/pe/symbol.go')
-rw-r--r-- | src/debug/pe/symbol.go | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/src/debug/pe/symbol.go b/src/debug/pe/symbol.go new file mode 100644 index 0000000..b1654f8 --- /dev/null +++ b/src/debug/pe/symbol.go @@ -0,0 +1,209 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pe + +import ( + "encoding/binary" + "errors" + "fmt" + "internal/saferio" + "io" + "unsafe" +) + +const COFFSymbolSize = 18 + +// COFFSymbol represents single COFF symbol table record. +type COFFSymbol struct { + Name [8]uint8 + Value uint32 + SectionNumber int16 + Type uint16 + StorageClass uint8 + NumberOfAuxSymbols uint8 +} + +// readCOFFSymbols reads in the symbol table for a PE file, returning +// a slice of COFFSymbol objects. The PE format includes both primary +// symbols (whose fields are described by COFFSymbol above) and +// auxiliary symbols; all symbols are 18 bytes in size. The auxiliary +// symbols for a given primary symbol are placed following it in the +// array, e.g. +// +// ... +// k+0: regular sym k +// k+1: 1st aux symbol for k +// k+2: 2nd aux symbol for k +// k+3: regular sym k+3 +// k+4: 1st aux symbol for k+3 +// k+5: regular sym k+5 +// k+6: regular sym k+6 +// +// The PE format allows for several possible aux symbol formats. For +// more info see: +// +// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#auxiliary-symbol-records +// +// At the moment this package only provides APIs for looking at +// aux symbols of format 5 (associated with section definition symbols). +func readCOFFSymbols(fh *FileHeader, r io.ReadSeeker) ([]COFFSymbol, error) { + if fh.PointerToSymbolTable == 0 { + return nil, nil + } + if fh.NumberOfSymbols <= 0 { + return nil, nil + } + _, err := r.Seek(int64(fh.PointerToSymbolTable), seekStart) + if err != nil { + return nil, fmt.Errorf("fail to seek to symbol table: %v", err) + } + c := saferio.SliceCap((*COFFSymbol)(nil), uint64(fh.NumberOfSymbols)) + if c < 0 { + return nil, errors.New("too many symbols; file may be corrupt") + } + syms := make([]COFFSymbol, 0, c) + naux := 0 + for k := uint32(0); k < fh.NumberOfSymbols; k++ { + var sym COFFSymbol + if naux == 0 { + // Read a primary symbol. + err = binary.Read(r, binary.LittleEndian, &sym) + if err != nil { + return nil, fmt.Errorf("fail to read symbol table: %v", err) + } + // Record how many auxiliary symbols it has. + naux = int(sym.NumberOfAuxSymbols) + } else { + // Read an aux symbol. At the moment we assume all + // aux symbols are format 5 (obviously this doesn't always + // hold; more cases will be needed below if more aux formats + // are supported in the future). + naux-- + aux := (*COFFSymbolAuxFormat5)(unsafe.Pointer(&sym)) + err = binary.Read(r, binary.LittleEndian, aux) + if err != nil { + return nil, fmt.Errorf("fail to read symbol table: %v", err) + } + } + syms = append(syms, sym) + } + if naux != 0 { + return nil, fmt.Errorf("fail to read symbol table: %d aux symbols unread", naux) + } + 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 + SectionNumber int16 + Type uint16 + StorageClass uint8 +} + +// COFFSymbolAuxFormat5 describes the expected form of an aux symbol +// attached to a section definition symbol. The PE format defines a +// number of different aux symbol formats: format 1 for function +// definitions, format 2 for .be and .ef symbols, and so on. Format 5 +// holds extra info associated with a section definition, including +// number of relocations + line numbers, as well as COMDAT info. See +// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#auxiliary-format-5-section-definitions +// for more on what's going on here. +type COFFSymbolAuxFormat5 struct { + Size uint32 + NumRelocs uint16 + NumLineNumbers uint16 + Checksum uint32 + SecNum uint16 + Selection uint8 + _ [3]uint8 // padding +} + +// These constants make up the possible values for the 'Selection' +// field in an AuxFormat5. +const ( + IMAGE_COMDAT_SELECT_NODUPLICATES = 1 + IMAGE_COMDAT_SELECT_ANY = 2 + IMAGE_COMDAT_SELECT_SAME_SIZE = 3 + IMAGE_COMDAT_SELECT_EXACT_MATCH = 4 + IMAGE_COMDAT_SELECT_ASSOCIATIVE = 5 + IMAGE_COMDAT_SELECT_LARGEST = 6 +) + +// COFFSymbolReadSectionDefAux returns a blob of axiliary information +// (including COMDAT info) for a section definition symbol. Here 'idx' +// is the index of a section symbol in the main COFFSymbol array for +// the File. Return value is a pointer to the appropriate aux symbol +// struct. For more info, see: +// +// auxiliary symbols: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#auxiliary-symbol-records +// COMDAT sections: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#comdat-sections-object-only +// auxiliary info for section definitions: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#auxiliary-format-5-section-definitions +func (f *File) COFFSymbolReadSectionDefAux(idx int) (*COFFSymbolAuxFormat5, error) { + var rv *COFFSymbolAuxFormat5 + if idx < 0 || idx >= len(f.COFFSymbols) { + return rv, fmt.Errorf("invalid symbol index") + } + pesym := &f.COFFSymbols[idx] + const IMAGE_SYM_CLASS_STATIC = 3 + if pesym.StorageClass != uint8(IMAGE_SYM_CLASS_STATIC) { + return rv, fmt.Errorf("incorrect symbol storage class") + } + if pesym.NumberOfAuxSymbols == 0 || idx+1 >= len(f.COFFSymbols) { + return rv, fmt.Errorf("aux symbol unavailable") + } + // Locate and return a pointer to the successor aux symbol. + pesymn := &f.COFFSymbols[idx+1] + rv = (*COFFSymbolAuxFormat5)(unsafe.Pointer(pesymn)) + return rv, nil +} |