github.com/eh-steve/goloader@v0.0.0-20240111193454-90ff3cfdae39/obj/pe_relocs.1.19.go (about) 1 //go:build go1.19 2 // +build go1.19 3 4 package obj 5 6 import ( 7 "cmd/objfile/archive" 8 "cmd/objfile/objabi" 9 "debug/pe" 10 "encoding/binary" 11 "fmt" 12 "github.com/eh-steve/goloader/objabi/symkind" 13 "sort" 14 "strings" 15 ) 16 17 // See cmd/link/internal/loadpd/ldpe.go for reference 18 const ( 19 IMAGE_SYM_CLASS_STATIC = 3 20 IMAGE_REL_I386_DIR32 = 0x0006 21 IMAGE_REL_I386_DIR32NB = 0x0007 22 IMAGE_REL_I386_REL32 = 0x0014 23 IMAGE_REL_AMD64_ADDR64 = 0x0001 24 IMAGE_REL_AMD64_ADDR32 = 0x0002 25 IMAGE_REL_AMD64_ADDR32NB = 0x0003 26 IMAGE_REL_AMD64_REL32 = 0x0004 27 ) 28 29 func readPESym(arch string, pesym *pe.COFFSymbol, f *pe.File, defWithImp map[string]struct{}) (string, error) { 30 symname, err := pesym.FullName(f.StringTable) 31 if err != nil { 32 return "", fmt.Errorf("failed to read full name of pesym: %w", err) 33 } 34 symIsSect := pesym.StorageClass == IMAGE_SYM_CLASS_STATIC && pesym.Type == 0 && pesym.Name[0] == '.' 35 var name string 36 if symIsSect { 37 name = f.Sections[pesym.SectionNumber-1].Name 38 } else { 39 name = symname 40 if strings.HasPrefix(symname, "__imp_") { 41 orig := symname[len("__imp_"):] 42 if _, ok := defWithImp[orig]; ok { 43 // Don't rename __imp_XXX to XXX, since if we do this 44 // we'll wind up with a duplicate definition. One 45 // example is "__acrt_iob_func"; see commit b295099 46 // from git://git.code.sf.net/p/mingw-w64/mingw-w64 47 // for details. 48 } else { 49 name = strings.TrimPrefix(name, "__imp_") // __imp_Name => Name 50 } 51 } 52 // A note on the "_main" exclusion below: the main routine 53 // defined by the Go runtime is named "_main", not "main", so 54 // when reading references to _main from a host object we want 55 // to avoid rewriting "_main" to "main" in this specific 56 // instance. See #issuecomment-1143698749 on #35006 for more 57 // details on this problem. 58 59 if arch == "386" && name[0] == '_' && name != "_main" { 60 name = name[1:] // _Name => Name 61 } 62 } 63 64 // remove last @XXX 65 if i := strings.LastIndex(name, "@"); i >= 0 { 66 name = name[:i] 67 } 68 return name, nil 69 } 70 71 func preprocessSymbols(f *pe.File) (map[string]struct{}, error) { 72 73 // preprocessSymbols walks the COFF symbols for the PE file we're 74 // reading and looks for cases where we have both a symbol definition 75 // for "XXX" and an "__imp_XXX" symbol, recording these cases in a map 76 // in the state struct. This information will be used in readpesym() 77 // above to give such symbols special treatment. This function also 78 // gathers information about COMDAT sections/symbols for later use 79 // in readpesym(). 80 81 // Locate comdat sections. 82 comdats := make(map[uint16]int64) 83 for i, s := range f.Sections { 84 if s.Characteristics&uint32(pe.IMAGE_SCN_LNK_COMDAT) != 0 { 85 comdats[uint16(i)] = int64(s.Size) 86 } 87 } 88 89 // Examine symbol defs. 90 imp := make(map[string]struct{}) 91 def := make(map[string]struct{}) 92 for i, numaux := 0, 0; i < len(f.COFFSymbols); i += numaux + 1 { 93 pesym := &f.COFFSymbols[i] 94 numaux = int(pesym.NumberOfAuxSymbols) 95 if pesym.SectionNumber == 0 { // extern 96 continue 97 } 98 symname, err := pesym.FullName(f.StringTable) 99 if err != nil { 100 return nil, err 101 } 102 def[symname] = struct{}{} 103 if strings.HasPrefix(symname, "__imp_") { 104 imp[strings.TrimPrefix(symname, "__imp_")] = struct{}{} 105 } 106 if _, isc := comdats[uint16(pesym.SectionNumber-1)]; !isc { 107 continue 108 } 109 if pesym.StorageClass != uint8(IMAGE_SYM_CLASS_STATIC) { 110 continue 111 } 112 // This symbol corresponds to a COMDAT section. Read the 113 // aux data for it. 114 auxsymp, err := f.COFFSymbolReadSectionDefAux(i) 115 if err != nil { 116 return nil, fmt.Errorf("unable to read aux info for section def symbol %d %s: pe.COFFSymbolReadComdatInfo returns %v", i, symname, err) 117 } 118 if auxsymp.Selection == pe.IMAGE_COMDAT_SELECT_SAME_SIZE { 119 // This is supported. 120 } else if auxsymp.Selection == pe.IMAGE_COMDAT_SELECT_ANY { 121 // Also supported. 122 comdats[uint16(pesym.SectionNumber-1)] = int64(-1) 123 } else { 124 // We don't support any of the other strategies at the 125 // moment. I suspect that we may need to also support 126 // "associative", we'll see. 127 return nil, fmt.Errorf("internal error: unsupported COMDAT selection strategy found in sec=%d strategy=%d idx=%d, please file a bug", auxsymp.SecNum, auxsymp.Selection, i) 128 } 129 } 130 defWithImp := make(map[string]struct{}) 131 for n := range imp { 132 if _, ok := def[n]; ok { 133 defWithImp[n] = struct{}{} 134 } 135 } 136 return defWithImp, nil 137 } 138 139 func issectCoff(s *pe.COFFSymbol) bool { 140 return s.StorageClass == IMAGE_SYM_CLASS_STATIC && s.Type == 0 && s.Name[0] == '.' 141 } 142 143 func issect(s *pe.Symbol) bool { 144 return s.StorageClass == IMAGE_SYM_CLASS_STATIC && s.Type == 0 && s.Name[0] == '.' 145 } 146 147 func (pkg *Pkg) convertPERelocs(f *pe.File, e archive.Entry) error { 148 textSect := f.Section(".text") 149 if textSect == nil { 150 return nil 151 } 152 defWithImp, err := preprocessSymbols(f) 153 if err != nil { 154 return err 155 } 156 157 // Build sorted list of addresses of all symbols. 158 // We infer the size of a symbol by looking at where the next symbol begins. 159 var objSymAddr []uint32 160 for _, s := range f.Symbols { 161 if issect(s) || s.SectionNumber <= 0 || int(s.SectionNumber) > len(f.Sections) { 162 continue 163 } 164 objSymAddr = append(objSymAddr, s.Value) 165 } 166 167 var objSymbols []*ObjSymbol 168 169 for _, s := range f.Symbols { 170 if issect(s) || s.SectionNumber <= 0 || int(s.SectionNumber) > len(f.Sections) { 171 continue 172 } 173 174 sect := f.Sections[s.SectionNumber-1] 175 var text []byte 176 if sect.Characteristics&pe.IMAGE_SCN_CNT_UNINITIALIZED_DATA != 0 { 177 text = make([]byte, sect.Size) 178 } else { 179 text, err = sect.Data() 180 if err != nil { 181 return fmt.Errorf("failed to read section data for symbol %s, section %d (%s): %w", s.Name, s.SectionNumber, sect.Name, err) 182 } 183 } 184 185 var sym *ObjSymbol 186 var addr uint32 187 188 sym = &ObjSymbol{Name: s.Name, Func: &FuncInfo{}, Pkg: pkg.PkgPath} 189 190 i := sort.Search(len(objSymAddr), func(x int) bool { return objSymAddr[x] > s.Value }) 191 if i < len(objSymAddr) { 192 sym.Size = int64(objSymAddr[i] - s.Value) 193 } else { 194 sym.Size = int64(len(text)) 195 } 196 197 if sym.Size > 0 && s.SectionNumber > 0 && f.Sections[s.SectionNumber-1] == textSect { 198 addr = s.Value 199 data := make([]byte, sym.Size) 200 copy(data, text[addr:]) 201 sym.Data = data 202 } 203 204 objSymbols = append(objSymbols, sym) 205 206 switch sect.Characteristics & (pe.IMAGE_SCN_CNT_UNINITIALIZED_DATA | pe.IMAGE_SCN_CNT_INITIALIZED_DATA | pe.IMAGE_SCN_MEM_READ | pe.IMAGE_SCN_MEM_WRITE | pe.IMAGE_SCN_CNT_CODE | pe.IMAGE_SCN_MEM_EXECUTE) { 207 case pe.IMAGE_SCN_CNT_INITIALIZED_DATA | pe.IMAGE_SCN_MEM_READ: //.rdata 208 sym.Kind = symkind.SRODATA 209 210 case pe.IMAGE_SCN_CNT_UNINITIALIZED_DATA | pe.IMAGE_SCN_MEM_READ | pe.IMAGE_SCN_MEM_WRITE: //.bss 211 sym.Kind = symkind.SNOPTRBSS 212 213 case pe.IMAGE_SCN_CNT_INITIALIZED_DATA | pe.IMAGE_SCN_MEM_READ | pe.IMAGE_SCN_MEM_WRITE: //.data 214 sym.Kind = symkind.SNOPTRDATA 215 216 case pe.IMAGE_SCN_CNT_CODE | pe.IMAGE_SCN_MEM_EXECUTE | pe.IMAGE_SCN_MEM_READ: //.text 217 sym.Kind = symkind.STEXT 218 default: 219 return fmt.Errorf("unexpected flags %#06x for PE section %s", sect.Characteristics, sect.Name) 220 221 } 222 } 223 224 for _, section := range f.Sections { 225 if strings.HasPrefix(section.Name, ".debug_") { 226 // Don't bother relocating debug sections 227 continue 228 } 229 if section.NumberOfRelocations == 0 { 230 continue 231 } 232 if section.Characteristics&pe.IMAGE_SCN_MEM_DISCARDABLE != 0 { 233 continue 234 } 235 if section.Characteristics&(pe.IMAGE_SCN_CNT_CODE|pe.IMAGE_SCN_CNT_INITIALIZED_DATA|pe.IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 { 236 // This has been seen for .idata sections, which we 237 // want to ignore. See issues 5106 and 5273. 238 continue 239 } 240 241 sectdata, err := section.Data() 242 if err != nil { 243 return fmt.Errorf("failed to read section data for %s: %w", section.Name, err) 244 } 245 for j, reloc := range section.Relocs { 246 if int(reloc.SymbolTableIndex) >= len(f.COFFSymbols) { 247 return fmt.Errorf("relocation number %d symbol index idx=%d cannot be larger than number of symbols %d", j, reloc.SymbolTableIndex, len(f.COFFSymbols)) 248 } 249 pesym := f.COFFSymbols[reloc.SymbolTableIndex] 250 relocSymName, err := readPESym(pkg.Arch, &pesym, f, defWithImp) 251 if err != nil { 252 return err 253 } 254 if f.Section(relocSymName) != nil { 255 continue 256 } 257 rSize := uint8(4) 258 var rAdd int64 259 var rType objabi.RelocType 260 rOff := int32(reloc.VirtualAddress) 261 262 switch reloc.Type { 263 case IMAGE_REL_I386_REL32, IMAGE_REL_AMD64_REL32, 264 IMAGE_REL_AMD64_ADDR32, // R_X86_64_PC32 265 IMAGE_REL_AMD64_ADDR32NB: 266 rType = objabi.R_PCREL 267 rAdd = int64(int32(binary.LittleEndian.Uint32(sectdata[rOff:]))) 268 269 case IMAGE_REL_I386_DIR32NB, IMAGE_REL_I386_DIR32: 270 rType = objabi.R_ADDR 271 272 // load addend from image 273 rAdd = int64(int32(binary.LittleEndian.Uint32(sectdata[rOff:]))) 274 275 case IMAGE_REL_AMD64_ADDR64: // R_X86_64_64 276 rSize = 8 277 278 rType = objabi.R_ADDR 279 280 // load addend from image 281 rAdd = int64(binary.LittleEndian.Uint64(sectdata[rOff:])) 282 default: 283 return fmt.Errorf("unsupported PE relocation type: %d in symbol %s", reloc.Type, relocSymName) 284 } 285 286 var target *ObjSymbol 287 var targetAddr uint32 288 for i, objSymbol := range objSymbols { 289 if objSymbol == nil { 290 continue 291 } 292 nextAddr := objSymAddr[i] + uint32(objSymbol.Size) 293 if reloc.VirtualAddress >= objSymAddr[i] && reloc.VirtualAddress < nextAddr { 294 target = objSymbol 295 targetAddr = objSymAddr[i] 296 break 297 } 298 } 299 if target == nil { 300 fmt.Println("Goloader PE Reloc error - couldn't find target for offset ", reloc.VirtualAddress, relocSymName) 301 continue 302 } 303 304 if issectCoff(&pesym) { 305 rAdd += int64(pesym.Value) 306 } 307 308 target.Reloc = append(target.Reloc, Reloc{ 309 Offset: int(reloc.VirtualAddress - targetAddr), 310 Sym: &Sym{Name: relocSymName, Offset: InvalidOffset}, 311 Size: int(rSize), 312 Type: int(rType), 313 Add: int(rAdd), 314 }) 315 } 316 } 317 318 for _, symbol := range objSymbols { 319 if symbol != nil && symbol.Name != "" && symbol.Size > 0 { 320 if _, ok := pkg.Syms[symbol.Name]; !ok { 321 pkg.SymNameOrder = append(pkg.SymNameOrder, symbol.Name) 322 } 323 pkg.Syms[symbol.Name] = symbol 324 } 325 } 326 return nil 327 }