summaryrefslogtreecommitdiffstats
path: root/src/debug/pe/symbol.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/debug/pe/symbol.go')
-rw-r--r--src/debug/pe/symbol.go209
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
+}