| // 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" |
| "fmt" |
| "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) |
| } |
| syms := make([]COFFSymbol, fh.NumberOfSymbols) |
| naux := 0 |
| for k := range syms { |
| if naux == 0 { |
| // Read a primary symbol. |
| err = binary.Read(r, binary.LittleEndian, &syms[k]) |
| if err != nil { |
| return nil, fmt.Errorf("fail to read symbol table: %v", err) |
| } |
| // Record how many auxiliary symbols it has. |
| naux = int(syms[k].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(&syms[k])) |
| err = binary.Read(r, binary.LittleEndian, aux) |
| if err != nil { |
| return nil, fmt.Errorf("fail to read symbol table: %v", err) |
| } |
| } |
| } |
| 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 |
| } |