github.com/bir3/gocompiler@v0.9.2202/src/debug/pe/symbol.go (about) 1 // Copyright 2016 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package pe 6 7 import ( 8 "encoding/binary" 9 "errors" 10 "fmt" 11 "github.com/bir3/gocompiler/src/internal/saferio" 12 "io" 13 "unsafe" 14 ) 15 16 const COFFSymbolSize = 18 17 18 // COFFSymbol represents single COFF symbol table record. 19 type COFFSymbol struct { 20 Name [8]uint8 21 Value uint32 22 SectionNumber int16 23 Type uint16 24 StorageClass uint8 25 NumberOfAuxSymbols uint8 26 } 27 28 // readCOFFSymbols reads in the symbol table for a PE file, returning 29 // a slice of COFFSymbol objects. The PE format includes both primary 30 // symbols (whose fields are described by COFFSymbol above) and 31 // auxiliary symbols; all symbols are 18 bytes in size. The auxiliary 32 // symbols for a given primary symbol are placed following it in the 33 // array, e.g. 34 // 35 // ... 36 // k+0: regular sym k 37 // k+1: 1st aux symbol for k 38 // k+2: 2nd aux symbol for k 39 // k+3: regular sym k+3 40 // k+4: 1st aux symbol for k+3 41 // k+5: regular sym k+5 42 // k+6: regular sym k+6 43 // 44 // The PE format allows for several possible aux symbol formats. For 45 // more info see: 46 // 47 // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#auxiliary-symbol-records 48 // 49 // At the moment this package only provides APIs for looking at 50 // aux symbols of format 5 (associated with section definition symbols). 51 func readCOFFSymbols(fh *FileHeader, r io.ReadSeeker) ([]COFFSymbol, error) { 52 if fh.PointerToSymbolTable == 0 { 53 return nil, nil 54 } 55 if fh.NumberOfSymbols <= 0 { 56 return nil, nil 57 } 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 := 0 68 for k := uint32(0); k < fh.NumberOfSymbols; k++ { 69 var sym COFFSymbol 70 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 all 80 // aux symbols are format 5 (obviously this doesn't always 81 // hold; more cases will be needed below if more aux formats 82 // 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, nil 96 } 97 98 // isSymNameOffset checks symbol name if it is encoded as offset into string table. 99 func isSymNameOffset(name [8]byte) (bool, uint32) { 100 if name[0] == 0 && name[1] == 0 && name[2] == 0 && name[3] == 0 { 101 return true, binary.LittleEndian.Uint32(name[4:]) 102 } 103 return false, 0 104 } 105 106 // FullName finds real name of symbol sym. Normally name is stored 107 // in sym.Name, but if it is longer then 8 characters, it is stored 108 // in COFF string table st instead. 109 func (sym *COFFSymbol) FullName(st StringTable) (string, error) { 110 if ok, offset := isSymNameOffset(sym.Name); ok { 111 return st.String(offset) 112 } 113 return cstring(sym.Name[:]), nil 114 } 115 116 func removeAuxSymbols(allsyms []COFFSymbol, st StringTable) ([]*Symbol, error) { 117 if len(allsyms) == 0 { 118 return nil, nil 119 } 120 syms := make([]*Symbol, 0) 121 aux := uint8(0) 122 for _, sym := range allsyms { 123 if aux > 0 { 124 aux-- 125 continue 126 } 127 name, err := sym.FullName(st) 128 if err != nil { 129 return nil, err 130 } 131 aux = sym.NumberOfAuxSymbols 132 s := &Symbol{ 133 Name: name, 134 Value: sym.Value, 135 SectionNumber: sym.SectionNumber, 136 Type: sym.Type, 137 StorageClass: sym.StorageClass, 138 } 139 syms = append(syms, s) 140 } 141 return syms, nil 142 } 143 144 // Symbol is similar to [COFFSymbol] with Name field replaced 145 // by Go string. Symbol also does not have NumberOfAuxSymbols. 146 type Symbol struct { 147 Name string 148 Value uint32 149 SectionNumber int16 150 Type uint16 151 StorageClass uint8 152 } 153 154 // COFFSymbolAuxFormat5 describes the expected form of an aux symbol 155 // attached to a section definition symbol. The PE format defines a 156 // number of different aux symbol formats: format 1 for function 157 // definitions, format 2 for .be and .ef symbols, and so on. Format 5 158 // holds extra info associated with a section definition, including 159 // number of relocations + line numbers, as well as COMDAT info. See 160 // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#auxiliary-format-5-section-definitions 161 // for more on what's going on here. 162 type COFFSymbolAuxFormat5 struct { 163 Size uint32 164 NumRelocs uint16 165 NumLineNumbers uint16 166 Checksum uint32 167 SecNum uint16 168 Selection uint8 169 _ [3]uint8 // padding 170 } 171 172 // These constants make up the possible values for the 'Selection' 173 // field in an AuxFormat5. 174 const ( 175 IMAGE_COMDAT_SELECT_NODUPLICATES = 1 176 IMAGE_COMDAT_SELECT_ANY = 2 177 IMAGE_COMDAT_SELECT_SAME_SIZE = 3 178 IMAGE_COMDAT_SELECT_EXACT_MATCH = 4 179 IMAGE_COMDAT_SELECT_ASSOCIATIVE = 5 180 IMAGE_COMDAT_SELECT_LARGEST = 6 181 ) 182 183 // COFFSymbolReadSectionDefAux returns a blob of auxiliary information 184 // (including COMDAT info) for a section definition symbol. Here 'idx' 185 // is the index of a section symbol in the main [COFFSymbol] array for 186 // the File. Return value is a pointer to the appropriate aux symbol 187 // struct. For more info, see: 188 // 189 // auxiliary symbols: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#auxiliary-symbol-records 190 // COMDAT sections: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#comdat-sections-object-only 191 // auxiliary info for section definitions: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#auxiliary-format-5-section-definitions 192 func (f *File) COFFSymbolReadSectionDefAux(idx int) (*COFFSymbolAuxFormat5, error) { 193 var rv *COFFSymbolAuxFormat5 194 if idx < 0 || idx >= len(f.COFFSymbols) { 195 return rv, fmt.Errorf("invalid symbol index") 196 } 197 pesym := &f.COFFSymbols[idx] 198 const IMAGE_SYM_CLASS_STATIC = 3 199 if pesym.StorageClass != uint8(IMAGE_SYM_CLASS_STATIC) { 200 return rv, fmt.Errorf("incorrect symbol storage class") 201 } 202 if pesym.NumberOfAuxSymbols == 0 || idx+1 >= len(f.COFFSymbols) { 203 return rv, fmt.Errorf("aux symbol unavailable") 204 } 205 // Locate and return a pointer to the successor aux symbol. 206 pesymn := &f.COFFSymbols[idx+1] 207 rv = (*COFFSymbolAuxFormat5)(unsafe.Pointer(pesymn)) 208 return rv, nil 209 }