codekingpro/portable-devtools
114k
1// Copyright 2016 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.4 5package pe6 7import (8 "encoding/binary"9 "errors"10 "fmt"11 "internal/saferio"12 "io"13 "unsafe"14)15 16const COFFSymbolSize = 1817 18// COFFSymbol represents single COFF symbol table record.19type COFFSymbol struct {20 Name [8]uint821 Value uint3222 SectionNumber int1623 Type uint1624 StorageClass uint825 NumberOfAuxSymbols uint826}27 28// readCOFFSymbols reads in the symbol table for a PE file, returning29// a slice of COFFSymbol objects. The PE format includes both primary30// symbols (whose fields are described by COFFSymbol above) and31// auxiliary symbols; all symbols are 18 bytes in size. The auxiliary32// symbols for a given primary symbol are placed following it in the33// array, e.g.34//35// ...36// k+0: regular sym k37// k+1: 1st aux symbol for k38// k+2: 2nd aux symbol for k39// k+3: regular sym k+340// k+4: 1st aux symbol for k+341// k+5: regular sym k+542// k+6: regular sym k+643//44// The PE format allows for several possible aux symbol formats. For45// more info see:46//47// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#auxiliary-symbol-records48//49// At the moment this package only provides APIs for looking at50// aux symbols of format 5 (associated with section definition symbols).51func readCOFFSymbols(fh *FileHeader, r io.ReadSeeker) ([]COFFSymbol, error) {52 if fh.PointerToSymbolTable == 0 {53 return nil, nil54 }55 if fh.NumberOfSymbols <= 0 {56 return nil, nil57 }58 _, err := r.Seek(int64(fh.PointerToSymbolTable), io.SeekStart)59 if err != nil {60 return nil, fmt.Errorf("fail to seek to symbol table: %v", err)61 }62 c := saferio.SliceCap[COFFSymbol](uint64(fh.NumberOfSymbols))63 if c < 0 {64 return nil, errors.New("too many symbols; file may be corrupt")65 }66 syms := make([]COFFSymbol, 0, c)67 naux := 068 for k := uint32(0); k < fh.NumberOfSymbols; k++ {69 var sym COFFSymbol70 if naux == 0 {71 // Read a primary symbol.72 err = binary.Read(r, binary.LittleEndian, &sym)73 if err != nil {74 return nil, fmt.Errorf("fail to read symbol table: %v", err)75 }76 // Record how many auxiliary symbols it has.77 naux = int(sym.NumberOfAuxSymbols)78 } else {79 // Read an aux symbol. At the moment we assume all80 // aux symbols are format 5 (obviously this doesn't always81 // hold; more cases will be needed below if more aux formats82 // are supported in the future).83 naux--84 aux := (*COFFSymbolAuxFormat5)(unsafe.Pointer(&sym))85 err = binary.Read(r, binary.LittleEndian, aux)86 if err != nil {87 return nil, fmt.Errorf("fail to read symbol table: %v", err)88 }89 }90 syms = append(syms, sym)91 }92 if naux != 0 {93 return nil, fmt.Errorf("fail to read symbol table: %d aux symbols unread", naux)94 }95 return syms, nil96}97 98// isSymNameOffset checks symbol name if it is encoded as offset into string table.99func isSymNameOffset(name [8]byte) (bool, uint32) {100 if name[0] == 0 && name[1] == 0 && name[2] == 0 && name[3] == 0 {101 offset := binary.LittleEndian.Uint32(name[4:])102 if offset == 0 {103 // symbol has no name104 return false, 0105 }106 return true, offset107 }108 return false, 0109}110 111// FullName finds real name of symbol sym. Normally name is stored112// in sym.Name, but if it is longer then 8 characters, it is stored113// in COFF string table st instead.114func (sym *COFFSymbol) FullName(st StringTable) (string, error) {115 if ok, offset := isSymNameOffset(sym.Name); ok {116 return st.String(offset)117 }118 return cstring(sym.Name[:]), nil119}120 121func removeAuxSymbols(allsyms []COFFSymbol, st StringTable) ([]*Symbol, error) {122 if len(allsyms) == 0 {123 return nil, nil124 }125 syms := make([]*Symbol, 0)126 aux := uint8(0)127 for _, sym := range allsyms {128 if aux > 0 {129 aux--130 continue131 }132 name, err := sym.FullName(st)133 if err != nil {134 return nil, err135 }136 aux = sym.NumberOfAuxSymbols137 s := &Symbol{138 Name: name,139 Value: sym.Value,140 SectionNumber: sym.SectionNumber,141 Type: sym.Type,142 StorageClass: sym.StorageClass,143 }144 syms = append(syms, s)145 }146 return syms, nil147}148 149// Symbol is similar to [COFFSymbol] with Name field replaced150// by Go string. Symbol also does not have NumberOfAuxSymbols.151type Symbol struct {152 Name string153 Value uint32154 SectionNumber int16155 Type uint16156 StorageClass uint8157}158 159// COFFSymbolAuxFormat5 describes the expected form of an aux symbol160// attached to a section definition symbol. The PE format defines a161// number of different aux symbol formats: format 1 for function162// definitions, format 2 for .be and .ef symbols, and so on. Format 5163// holds extra info associated with a section definition, including164// number of relocations + line numbers, as well as COMDAT info. See165// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#auxiliary-format-5-section-definitions166// for more on what's going on here.167type COFFSymbolAuxFormat5 struct {168 Size uint32169 NumRelocs uint16170 NumLineNumbers uint16171 Checksum uint32172 SecNum uint16173 Selection uint8174 _ [3]uint8 // padding175}176 177// These constants make up the possible values for the 'Selection'178// field in an AuxFormat5.179const (180 IMAGE_COMDAT_SELECT_NODUPLICATES = 1181 IMAGE_COMDAT_SELECT_ANY = 2182 IMAGE_COMDAT_SELECT_SAME_SIZE = 3183 IMAGE_COMDAT_SELECT_EXACT_MATCH = 4184 IMAGE_COMDAT_SELECT_ASSOCIATIVE = 5185 IMAGE_COMDAT_SELECT_LARGEST = 6186)187 188// COFFSymbolReadSectionDefAux returns a blob of auxiliary information189// (including COMDAT info) for a section definition symbol. Here 'idx'190// is the index of a section symbol in the main [COFFSymbol] array for191// the File. Return value is a pointer to the appropriate aux symbol192// struct. For more info, see:193//194// auxiliary symbols: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#auxiliary-symbol-records195// COMDAT sections: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#comdat-sections-object-only196// auxiliary info for section definitions: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#auxiliary-format-5-section-definitions197func (f *File) COFFSymbolReadSectionDefAux(idx int) (*COFFSymbolAuxFormat5, error) {198 var rv *COFFSymbolAuxFormat5199 if idx < 0 || idx >= len(f.COFFSymbols) {200 return rv, fmt.Errorf("invalid symbol index")201 }202 pesym := &f.COFFSymbols[idx]203 const IMAGE_SYM_CLASS_STATIC = 3204 if pesym.StorageClass != uint8(IMAGE_SYM_CLASS_STATIC) {205 return rv, fmt.Errorf("incorrect symbol storage class")206 }207 if pesym.NumberOfAuxSymbols == 0 || idx+1 >= len(f.COFFSymbols) {208 return rv, fmt.Errorf("aux symbol unavailable")209 }210 // Locate and return a pointer to the successor aux symbol.211 pesymn := &f.COFFSymbols[idx+1]212 rv = (*COFFSymbolAuxFormat5)(unsafe.Pointer(pesymn))213 return rv, nil214}215 