| // 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 auxiliary 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 | |
| } |