github.com/eh-steve/goloader@v0.0.0-20240111193454-90ff3cfdae39/obj/readobj.1.16.go (about) 1 //go:build go1.16 && !go1.23 2 // +build go1.16,!go1.23 3 4 package obj 5 6 import ( 7 "bytes" 8 "cmd/objfile/archive" 9 "cmd/objfile/goobj" 10 "cmd/objfile/obj" 11 "cmd/objfile/objabi" 12 "compress/zlib" 13 "debug/elf" 14 "debug/macho" 15 "debug/pe" 16 "encoding/binary" 17 "encoding/hex" 18 "fmt" 19 "github.com/eh-steve/goloader/objabi/reloctype" 20 "github.com/eh-steve/goloader/objabi/symkind" 21 "go/token" 22 "io" 23 "sort" 24 "strings" 25 ) 26 27 func (pkg *Pkg) Symbols() error { 28 a, err := archive.Parse(pkg.F, false) 29 if err != nil { 30 return err 31 } 32 33 for _, e := range a.Entries { 34 switch e.Type { 35 case archive.EntryPkgDef: 36 // nothing todo 37 case archive.EntryGoObj: 38 b := make([]byte, e.Obj.Size) 39 _, err := pkg.F.ReadAt(b, e.Obj.Offset) 40 if err != nil { 41 return err 42 } 43 r := goobj.NewReaderFromBytes(b, false) 44 // Name of referenced indexed symbols. 45 nrefName := r.NRefName() 46 refNames := make(map[goobj.SymRef]string, nrefName) 47 for i := 0; i < nrefName; i++ { 48 rn := r.RefName(i) 49 refNames[rn.Sym()] = rn.Name(r) 50 } 51 prevPkgLen := len(pkg.ReferencedPkgs) 52 npkg := r.NPkg() 53 pkg.ReferencedPkgs = append(pkg.ReferencedPkgs, make([]string, npkg)...) 54 55 for i := 1; i < npkg; i++ { // PkgIdx 0 is a dummy invalid package 56 pkgName := r.Pkg(i) 57 pkg.ReferencedPkgs[i+prevPkgLen] = pkgName 58 } 59 for _, imported := range r.Autolib() { 60 pkg.AutoLib = append(pkg.AutoLib, imported.Pkg) 61 } 62 pkg.Arch = e.Obj.Arch 63 nsym := r.NSym() + r.NHashed64def() + r.NHasheddef() + r.NNonpkgdef() 64 for i := 0; i < nsym; i++ { 65 pkg.addSym(r, uint32(i), &refNames, objabi.PathToPrefix(pkg.PkgPath)) 66 } 67 files := make([]string, r.NFile()) 68 for i := range files { 69 files[i] = r.File(i) 70 } 71 72 pkg.CUFiles = append(pkg.CUFiles, CompilationUnitFiles{ 73 ArchiveName: e.Name, 74 Files: files, 75 }) 76 case archive.EntryNativeObj: 77 // CGo files must be parsed by an elf/macho etc. native reader 78 nr := io.NewSectionReader(pkg.F, e.Offset, e.Size) 79 elfFile, err := elf.NewFile(nr) 80 if err != nil { 81 _, _ = nr.Seek(0, 0) 82 machoFile, errMacho := macho.NewFile(nr) 83 if errMacho != nil { 84 _, _ = nr.Seek(0, 0) 85 peFile, errPE := pe.NewFile(nr) 86 if errPE != nil { 87 return fmt.Errorf("only elf, macho and PE relocations currently supported, failed to open as either: (%s): %w", err, errPE) 88 } 89 err = pkg.convertPERelocs(peFile, e) 90 if err != nil { 91 return err 92 } 93 } else { 94 err = pkg.convertMachoRelocs(machoFile, e) 95 if err != nil { 96 return err 97 } 98 } 99 } else { 100 err = pkg.convertElfRelocs(elfFile, e) 101 if err != nil { 102 return err 103 } 104 } 105 default: 106 return fmt.Errorf("Parse open %s: unrecognized archive member %s (%d)\n", pkg.F.Name(), e.Name, e.Type) 107 } 108 } 109 for _, sym := range pkg.Syms { 110 if !strings.HasPrefix(sym.Name, TypeStringPrefix) { 111 sym.Name = strings.Replace(sym.Name, EmptyPkgPath, pkg.PkgPath, -1) 112 } 113 } 114 for i, symName := range pkg.SymNameOrder { 115 if !strings.HasPrefix(symName, TypeStringPrefix) { 116 pkg.SymNameOrder[i] = strings.Replace(symName, EmptyPkgPath, pkg.PkgPath, -1) 117 } 118 } 119 return nil 120 } 121 122 func typePkgPath(symName string) (pkgName string) { 123 if strings.HasPrefix(symName, TypePrefix) && !strings.HasPrefix(symName, TypeDoubleDotPrefix) { 124 typeName := strings.TrimLeft(strings.TrimPrefix(symName, TypePrefix), "*") 125 if !strings.HasPrefix(typeName, "func(") && 126 !strings.HasPrefix(typeName, "noalg") && 127 !strings.HasPrefix(typeName, "map[") && 128 !strings.HasPrefix(typeName, "map.bucket[") && 129 !strings.HasPrefix(typeName, "map.iter[") && 130 !strings.HasPrefix(typeName, "map.hdr[") && 131 !strings.HasPrefix(typeName, "struct {") { 132 // Likely a named type defined in a package, but for some reason PkgIdx is PkgIdxNone 133 if strings.Count(typeName, ".") > 0 { 134 pkgName = funcPkgPath(typeName) 135 } 136 } 137 } 138 return 139 } 140 141 // TODO - share this implementation with goloader's 142 func funcPkgPath(funcName string) string { 143 funcName = strings.TrimPrefix(funcName, TypeDoubleDotPrefix+"eq.") 144 145 // Anonymous struct methods can't have a package 146 if strings.HasPrefix(funcName, "go"+ObjSymbolSeparator+"struct {") || strings.HasPrefix(funcName, "go"+ObjSymbolSeparator+"*struct {") || strings.HasPrefix(funcName, "struct {") { 147 return "" 148 } 149 lastSlash := strings.LastIndexByte(funcName, '/') 150 if lastSlash == -1 { 151 lastSlash = 0 152 } 153 154 // Generic dictionaries 155 firstDict := strings.Index(funcName, "..dict") 156 if firstDict > 0 { 157 return funcName[:firstDict] 158 } else { 159 // Methods on structs embedding structs from other packages look funny, e.g.: 160 // regexp.(*onePassInst).regexp/syntax.op 161 firstBracket := strings.LastIndex(funcName, ".(") 162 if firstBracket > 0 && lastSlash > firstBracket { 163 lastSlash = firstBracket 164 } 165 firstSquareBracket := strings.Index(funcName, "[") 166 if firstSquareBracket > 0 && lastSlash > firstSquareBracket { 167 i := firstSquareBracket 168 for ; funcName[i] != '.' && i > 0; i-- { 169 } 170 return funcName[:i] 171 } 172 } 173 174 dot := lastSlash 175 for ; dot < len(funcName) && funcName[dot] != '.' && funcName[dot] != '(' && funcName[dot] != '['; dot++ { 176 } 177 pkgPath := funcName[:dot] 178 return strings.Trim(strings.TrimPrefix(pkgPath, "[...]"), " ") 179 } 180 181 func resolveSymRef(s goobj.SymRef, r *goobj.Reader, refNames *map[goobj.SymRef]string, pkgName string) (string, string, uint32) { 182 i := InvalidIndex 183 switch p := s.PkgIdx; p { 184 case goobj.PkgIdxInvalid: 185 if s.SymIdx != 0 { 186 panic("bad sym ref") 187 } 188 return EmptyString, "", i 189 case goobj.PkgIdxHashed64: 190 i = s.SymIdx + uint32(r.NSym()) 191 case goobj.PkgIdxHashed: 192 i = s.SymIdx + uint32(r.NSym()+r.NHashed64def()) 193 case goobj.PkgIdxNone: 194 i = s.SymIdx + uint32(r.NSym()+r.NHashed64def()+r.NHasheddef()) 195 symName := r.Sym(i).Name(r) 196 if (strings.HasPrefix(symName, TypePrefix) && !strings.HasPrefix(symName, TypeDoubleDotPrefix+"eq.")) || strings.HasPrefix(symName, "go"+ObjSymbolSeparator+"info") || strings.HasPrefix(symName, "go"+ObjSymbolSeparator+"cuinfo") || strings.HasPrefix(symName, "go"+ObjSymbolSeparator+"interface {") { 197 pkgName = typePkgPath(symName) 198 } else { 199 pkgName = funcPkgPath(symName) 200 } 201 case goobj.PkgIdxBuiltin: 202 name, _ := goobj.BuiltinName(int(s.SymIdx)) 203 return name, "", i 204 case goobj.PkgIdxSelf: 205 i = s.SymIdx 206 default: 207 return (*refNames)[s], r.Pkg(int(s.PkgIdx)), i 208 } 209 return r.Sym(i).Name(r), pkgName, i 210 } 211 212 func UnresolvedIdxString(symRef goobj.SymRef) string { 213 buf := make([]byte, len(UnresolvedSymRefPrefix)+8+8) 214 copy(buf, UnresolvedSymRefPrefix) 215 uint32buf := make([]byte, 4) 216 binary.LittleEndian.PutUint32(uint32buf, symRef.PkgIdx) 217 hex.Encode(buf[len(UnresolvedSymRefPrefix):], uint32buf) 218 binary.LittleEndian.PutUint32(uint32buf, symRef.SymIdx) 219 hex.Encode(buf[len(UnresolvedSymRefPrefix)+8:], uint32buf) 220 return string(buf) 221 } 222 223 func ParseUnresolvedIdxString(unresolved string) goobj.SymRef { 224 buf := make([]byte, 8) 225 n, err := hex.Decode(buf, []byte(unresolved[len(UnresolvedSymRefPrefix):])) 226 if err != nil || n != 8 { 227 panic(fmt.Sprintf("failed to decode %s: %s", unresolved, err)) 228 } 229 return goobj.SymRef{ 230 PkgIdx: binary.LittleEndian.Uint32(buf), 231 SymIdx: binary.LittleEndian.Uint32(buf[4:]), 232 } 233 } 234 235 func (pkg *Pkg) addSym(r *goobj.Reader, idx uint32, refNames *map[goobj.SymRef]string, pkgPath string) { 236 s := r.Sym(idx) 237 symbol := ObjSymbol{Name: s.Name(r), Kind: int(s.Type()), DupOK: s.Dupok(), Size: (int64)(s.Siz()), Func: &FuncInfo{ABI: s.ABI()}, Objidx: pkg.Objidx, Pkg: pkgPath} 238 if original, ok := pkg.Syms[symbol.Name]; ok { 239 if symbol.Kind == original.Kind && symbol.Func.ABI == original.Func.ABI && symbol.Size == original.Size { 240 return 241 } 242 if objabi.SymKind(symbol.Kind) == objabi.STEXT { 243 // We have only read FuncInfo of the original symbol by this point, not the new symbol (yet), so we have to infer which one is a wrapper and which one is the real func based on that 244 // Valid duplicate symbol names may be caused by ASM ABI0 versions of functions, and their autogenerated ABIInternal wrappers (or vice versa, ABIInternal funcs with ABI0 wrappers) 245 // We can keep the wrapper func under a separate mangled symbol name in case it's needed for direct calls, 246 // and replace the main symbol with the non-wrapper version (the compiler will have inlined the wrapper at any callsites anyway) 247 // https://go.googlesource.com/go/+/refs/heads/dev.regabi/src/cmd/compile/internal-abi.md 248 if obj.ABI(symbol.Func.ABI) == obj.ABI0 { 249 // reflect.callReflect/reflect.makeFuncStub are special ABI wrappers, not like typical ABI0/ABIInternal wrappers 250 if obj.ABI(original.Func.ABI) == obj.ABIInternal && symbol.Name != "reflect.callReflect" && symbol.Name != "reflect.makeFuncStub" { 251 if original.Func.FuncID == uint8(FuncIDWrapper) { 252 original.Name += ABIInternalSuffix 253 original.Pkg = symbol.Pkg 254 if _, ok := pkg.Syms[original.Name]; !ok { 255 pkg.SymNameOrder = append(pkg.SymNameOrder, original.Name) 256 } 257 pkg.Syms[original.Name] = original 258 } else { 259 symbol.Name += ABI0Suffix 260 } 261 } else { 262 if symbol.Name != "reflect.callReflect" && symbol.Name != "reflect.makeFuncStub" { 263 panic(fmt.Sprintf("unexpected duplicate symbol %s (original %s, new %s)", symbol.Name, obj.ABI(original.Func.ABI), obj.ABI(symbol.Func.ABI))) 264 } 265 } 266 } else if obj.ABI(symbol.Func.ABI) == obj.ABIInternal { 267 if obj.ABI(original.Func.ABI) == obj.ABI0 { 268 if original.Func.FuncID == uint8(FuncIDWrapper) { 269 original.Name += ABI0Suffix 270 original.Pkg = symbol.Pkg 271 if _, ok := pkg.Syms[original.Name]; !ok { 272 pkg.SymNameOrder = append(pkg.SymNameOrder, original.Name) 273 } 274 pkg.Syms[original.Name] = original 275 } else { 276 symbol.Name += ABIInternalSuffix 277 } 278 } else { 279 panic(fmt.Sprintf("unexpected duplicate symbol %s (original %s, new %s)", symbol.Name, obj.ABI(original.Func.ABI), obj.ABI(symbol.Func.ABI))) 280 } 281 } 282 } else if symbol.Size == 0 { 283 return 284 } 285 } 286 if objabi.SymKind(symbol.Kind) == objabi.Sxxx || symbol.Name == EmptyString { 287 return 288 } 289 290 if int(idx) > r.NSym()+r.NHashed64def()+r.NHasheddef() { 291 // Is nonpkgdef or nonpkgref 292 if !strings.HasPrefix(symbol.Name, "_cgo") { 293 // TODO - this really isn't ideal - needs to be more robust 294 if (strings.HasPrefix(symbol.Name, TypePrefix) && !strings.HasPrefix(symbol.Name, TypeDoubleDotPrefix+"eq.")) || strings.HasPrefix(symbol.Name, "go"+ObjSymbolSeparator+"info") || strings.HasPrefix(symbol.Name, "go"+ObjSymbolSeparator+"cuinfo") || strings.HasPrefix(symbol.Name, "go"+ObjSymbolSeparator+"interface {") { 295 symbol.Pkg = "" 296 } else { 297 symbol.Pkg = funcPkgPath(symbol.Name) 298 } 299 } 300 } 301 if objabi.SymKind(symbol.Kind) == objabi.SNOPTRBSS && strings.HasPrefix(symbol.Name, "_cgo_") && symbol.Size == 1 { 302 // This is a dummy symbol representing a byte whose address is taken to act as the function pointer to a CGo text address via the //go:linkname pragma 303 // We handle this separately at the end of convertMachoRelocs() by adding the actual target address as text under this symbol name. 304 return 305 } 306 if symbol.Size > 0 { 307 symbol.Data = r.Data(idx) 308 grow(&symbol.Data, (int)(symbol.Size)) 309 } else { 310 symbol.Data = make([]byte, 0) 311 } 312 313 if _, ok := pkg.Syms[symbol.Name]; !ok { 314 pkg.SymNameOrder = append(pkg.SymNameOrder, symbol.Name) 315 } 316 pkg.SymNamesByIdx[idx] = symbol.Name 317 pkg.Syms[symbol.Name] = &symbol 318 319 auxs := r.Auxs(idx) 320 for k := 0; k < len(auxs); k++ { 321 auxSymRef := auxs[k].Sym() 322 parentPkgPath := pkgPath 323 name, pkgPath, index := resolveSymRef(auxSymRef, r, refNames, pkgPath) 324 325 switch auxs[k].Type() { 326 case goobj.AuxGotype: 327 if name == "" { 328 // Likely this type is defined in another package not yet loaded, so mark it as unresolved and resolve it later, after all packages 329 symbol.Type = UnresolvedIdxString(auxSymRef) 330 } else if objabi.SymKind(r.Sym(index).Type()) == objabi.Sxxx { 331 // This aux symref doesn't actually exist in the current package reader, so we add a fake reloc to force the package containing the symbol to be built 332 symbol.Reloc = append(symbol.Reloc, Reloc{ 333 Offset: InvalidOffset, 334 Sym: &Sym{Name: name, Offset: InvalidOffset, Pkg: pkgPath}, 335 Type: reloctype.R_KEEP, 336 }) 337 symbol.Type = name 338 } else { 339 symbol.Type = name 340 symName := strings.TrimPrefix(symbol.Name, parentPkgPath+".") 341 if token.IsExported(symName) { 342 pkg.Exports[symName] = ExportSymType{ 343 SymName: symbol.Name, 344 TypeName: name, 345 } 346 } 347 } 348 case goobj.AuxFuncInfo: 349 funcInfo := goobj.FuncInfo{} 350 readFuncInfo(&funcInfo, r.Data(index), symbol.Func) 351 for _, index := range funcInfo.File { 352 symbol.Func.File = append(symbol.Func.File, r.File(int(index))) 353 } 354 cuOffset := 0 355 for _, cuFiles := range pkg.CUFiles { 356 cuOffset += len(cuFiles.Files) 357 } 358 symbol.Func.CUOffset = cuOffset 359 for _, inl := range funcInfo.InlTree { 360 funcname, pkgPath, _ := resolveSymRef(inl.Func, r, refNames, pkgPath) 361 funcname = strings.Replace(funcname, EmptyPkgPath, pkgPath, -1) 362 inlNode := InlTreeNode{ 363 Parent: int64(inl.Parent), 364 File: r.File(int(inl.File)), 365 Line: int64(inl.Line), 366 Func: funcname, 367 ParentPC: int64(inl.ParentPC), 368 } 369 symbol.Func.InlTree = append(symbol.Func.InlTree, inlNode) 370 } 371 case goobj.AuxFuncdata: 372 symbol.Func.FuncData = append(symbol.Func.FuncData, name) 373 case goobj.AuxDwarfInfo: 374 case goobj.AuxDwarfLoc: 375 case goobj.AuxDwarfRanges: 376 case goobj.AuxDwarfLines: 377 case goobj.AuxPcsp: 378 symbol.Func.PCSP = r.Data(index) 379 case goobj.AuxPcfile: 380 symbol.Func.PCFile = r.Data(index) 381 case goobj.AuxPcline: 382 symbol.Func.PCLine = r.Data(index) 383 case goobj.AuxPcinline: 384 symbol.Func.PCInline = r.Data(index) 385 case goobj.AuxPcdata: 386 symbol.Func.PCData = append(symbol.Func.PCData, r.Data(index)) 387 } 388 if _, ok := pkg.Syms[name]; !ok && index != InvalidIndex { 389 pkg.addSym(r, index, refNames, pkgPath) 390 } 391 } 392 393 relocs := r.Relocs(idx) 394 priorRelocs := len(symbol.Reloc) 395 for k := priorRelocs; k < len(relocs)+priorRelocs; k++ { 396 symbol.Reloc = append(symbol.Reloc, Reloc{}) 397 symbol.Reloc[k].Add = int(relocs[k-priorRelocs].Add()) 398 symbol.Reloc[k].Offset = int(relocs[k-priorRelocs].Off()) 399 symbol.Reloc[k].Size = int(relocs[k-priorRelocs].Siz()) 400 symbol.Reloc[k].Type = int(relocs[k-priorRelocs].Type()) 401 name, pkgPath, index := resolveSymRef(relocs[k-priorRelocs].Sym(), r, refNames, pkgPath) 402 symbol.Reloc[k].Sym = &Sym{Name: name, Offset: InvalidOffset, Pkg: pkgPath} 403 if _, ok := pkg.Syms[name]; !ok && index != InvalidIndex { 404 pkg.addSym(r, index, refNames, pkgPath) 405 } 406 } 407 } 408 409 func (pkg *Pkg) convertElfRelocs(f *elf.File, e archive.Entry) error { 410 if f.Class != elf.ELFCLASS64 { 411 return fmt.Errorf("only 64-bit elf relocations currently supported") 412 } 413 if f.Machine != elf.EM_X86_64 && f.Machine != elf.EM_AARCH64 { 414 return fmt.Errorf("only amd64 and arm64 elf relocations currently supported") 415 } 416 417 elfSyms, err := f.Symbols() 418 419 if err != nil { 420 return fmt.Errorf("failed to extract symbols from objfile entry %s: %w", e.Name, err) 421 } 422 423 var textSect *elf.Section 424 var textIndex int 425 for i, s := range f.Sections { 426 if s.Name == ".text" { 427 textSect = s 428 textIndex = i 429 break 430 } 431 } 432 433 if textSect == nil { 434 return fmt.Errorf("failed to find .text elf section in objfile entry %s: %w", e.Name, err) 435 } 436 437 text, err := textSect.Data() 438 if err != nil { 439 return fmt.Errorf("failed to read text data from elf .text section %s: %w", e.Name, err) 440 } 441 textOffset := textSect.Addr 442 443 var ( 444 dlen uint64 445 compressionOffset int 446 dbuf []byte 447 ) 448 if len(text) >= 12 && string(text[:4]) == "ZLIB" { 449 dlen = binary.BigEndian.Uint64(text[4:12]) 450 compressionOffset = 12 451 } 452 if dlen == 0 && len(text) >= 12 && textSect.Flags&elf.SHF_COMPRESSED != 0 && 453 textSect.Flags&elf.SHF_ALLOC == 0 && 454 f.FileHeader.ByteOrder.Uint32(text[:]) == uint32(elf.COMPRESS_ZLIB) { 455 switch f.FileHeader.Class { 456 case elf.ELFCLASS32: 457 // Chdr32.Size offset 458 dlen = uint64(f.FileHeader.ByteOrder.Uint32(text[4:])) 459 compressionOffset = 12 460 case elf.ELFCLASS64: 461 if len(text) < 24 { 462 return fmt.Errorf("invalid compress header 64") 463 } 464 // Chdr64.Size offset 465 dlen = f.FileHeader.ByteOrder.Uint64(text[8:]) 466 compressionOffset = 24 467 default: 468 return fmt.Errorf("unsupported compress header:%s", f.FileHeader.Class) 469 } 470 } 471 if dlen > 0 { 472 dbuf = make([]byte, dlen) 473 r, err := zlib.NewReader(bytes.NewBuffer(text[compressionOffset:])) 474 if err != nil { 475 return fmt.Errorf("failed to decompress zlib elf section %s: %w", e.Name, err) 476 } 477 if _, err := io.ReadFull(r, dbuf); err != nil { 478 return fmt.Errorf("failed to read decompressed zlib elf section %s: %w", e.Name, err) 479 } 480 if err := r.Close(); err != nil { 481 return fmt.Errorf("failed to close zlib elf section %s: %w", e.Name, err) 482 } 483 text = dbuf 484 } 485 486 var objSymbols []*ObjSymbol 487 var objSymAddr []uint64 488 for _, s := range elfSyms { 489 sectionData := text 490 if s.Section < elf.SHN_LORESERVE && !(s.Section < 0 || int(s.Section) >= len(f.Sections)) { 491 sect := f.Sections[s.Section] 492 if sect.Type != elf.SHT_NOBITS { 493 sectionData, err = sect.Data() 494 if err != nil { 495 return fmt.Errorf("failed to read section data from elf section %s %s (size %d): %w", e.Name, sect.Name, sect.Size, err) 496 } 497 } 498 } 499 var sym *ObjSymbol 500 var addr uint64 501 if s.Name != "" { 502 addr = s.Value 503 data := make([]byte, s.Size) 504 copy(data, sectionData[addr+textOffset:]) 505 sym = &ObjSymbol{Name: s.Name, Data: data, Size: int64(s.Size), Func: &FuncInfo{}, Pkg: pkg.PkgPath} 506 } 507 objSymbols = append(objSymbols, sym) 508 objSymAddr = append(objSymAddr, addr) 509 if sym == nil { 510 continue 511 } 512 513 switch s.Section { 514 case elf.SHN_UNDEF: 515 sym.Kind = symkind.Sxxx 516 case elf.SHN_COMMON: 517 sym.Kind = symkind.SBSS 518 default: 519 i := int(s.Section) 520 if i < 0 || i >= len(f.Sections) { 521 break 522 } 523 sect := f.Sections[i] 524 switch sect.Flags & (elf.SHF_WRITE | elf.SHF_ALLOC | elf.SHF_EXECINSTR) { 525 case elf.SHF_ALLOC | elf.SHF_EXECINSTR: 526 sym.Kind = symkind.STEXT 527 case elf.SHF_ALLOC: 528 sym.Kind = symkind.SRODATA 529 530 case elf.SHF_ALLOC | elf.SHF_WRITE: 531 sym.Kind = symkind.SDATA 532 } 533 } 534 } 535 536 for _, r := range f.Sections { 537 if r.Type != elf.SHT_RELA && r.Type != elf.SHT_REL { 538 continue 539 } 540 if int(r.Info) != textIndex { 541 continue 542 } 543 rd, err := r.Data() 544 if err != nil { 545 return fmt.Errorf("failed to read relocation data from elf section %s %s: %w", e.Name, r.Name, err) 546 } 547 548 relR := bytes.NewReader(rd) 549 var rela elf.Rela64 550 551 for relR.Len() > 0 { 552 binary.Read(relR, f.ByteOrder, &rela) 553 symNo := rela.Info >> 32 554 if symNo == 0 || symNo > uint64(len(elfSyms)) { 555 continue 556 } 557 sym := &elfSyms[symNo-1] 558 559 var target *ObjSymbol 560 var targetAddr uint64 561 for i, objSymbol := range objSymbols { 562 if objSymbol == nil { 563 continue 564 } 565 nextAddr := objSymAddr[i] + uint64(objSymbol.Size) 566 if rela.Off >= objSymAddr[i] && rela.Off < nextAddr { 567 target = objSymbol 568 targetAddr = objSymAddr[i] 569 break 570 } 571 } 572 if target == nil { 573 fmt.Println("Couldn't find target for offset ", rela.Off, sym.Name) 574 continue 575 } 576 577 if sym.Section == elf.SHN_UNDEF || sym.Section < elf.SHN_LORESERVE || sym.Section == elf.SHN_COMMON { 578 if sym.Name == "" || target.Kind != symkind.STEXT { 579 // Don't create PCREL relocs for data for now... but might need to fix this at some point 580 continue 581 } 582 switch f.Machine { 583 case elf.EM_AARCH64: 584 t := elf.R_AARCH64(rela.Info & 0xffff) 585 switch t { 586 case elf.R_AARCH64_CALL26, elf.R_AARCH64_JUMP26: 587 target.Reloc = append(target.Reloc, Reloc{ 588 Offset: int(rela.Off - targetAddr), 589 Sym: &Sym{Name: sym.Name, Offset: InvalidOffset}, 590 Size: 4, 591 Type: reloctype.R_CALLARM64, 592 Add: 0, // Even though elf addend is -4, a Go PCREL reloc doesn't need this. 593 }) 594 case elf.R_AARCH64_ADR_GOT_PAGE: 595 target.Reloc = append(target.Reloc, Reloc{ 596 Offset: int(rela.Off - targetAddr), 597 Sym: &Sym{Name: sym.Name, Offset: InvalidOffset}, 598 Size: 8, 599 Type: reloctype.R_ARM64_GOTPCREL, 600 Add: int(rela.Addend), // TODO - test that this is correct 601 }) 602 case elf.R_AARCH64_LD64_GOT_LO12_NC: 603 // Should be taken care of in above as these should always come in pairs? 604 // TODO - test that this is correct 605 default: 606 return fmt.Errorf("only a limited subset of elf relocations currently supported, got %s for symbol %s reloc to %s", t.GoString(), target.Name, sym.Name) 607 } 608 case elf.EM_X86_64: 609 t := elf.R_X86_64(rela.Info & 0xffff) 610 switch t { 611 case elf.R_X86_64_64, elf.R_X86_64_32: 612 return fmt.Errorf("TODO: only a limited subset of elf relocations currently supported, got %s", t.GoString()) 613 case elf.R_X86_64_PLT32, elf.R_X86_64_PC32: 614 target.Reloc = append(target.Reloc, Reloc{ 615 Offset: int(rela.Off - targetAddr), 616 Sym: &Sym{Name: sym.Name, Offset: InvalidOffset}, 617 Size: 4, 618 Type: reloctype.R_PCREL, 619 Add: 0, // Even though elf addend is -4, a Go PCREL reloc doesn't need this. 620 }) 621 case elf.R_X86_64_REX_GOTPCRELX: 622 target.Reloc = append(target.Reloc, Reloc{ 623 Offset: int(rela.Off - targetAddr), 624 Sym: &Sym{Name: sym.Name, Offset: InvalidOffset}, 625 Size: 4, 626 Type: reloctype.R_GOTPCREL, 627 Add: int(rela.Addend), 628 }) 629 default: 630 return fmt.Errorf("only a limited subset of elf relocations currently supported, got %s for symbol %s reloc to %s", t.GoString(), target.Name, sym.Name) 631 } 632 } 633 } else { 634 return fmt.Errorf("got an unexpected symbol section %d", sym.Section) 635 } 636 } 637 } 638 639 for _, symbol := range objSymbols { 640 if symbol != nil && symbol.Name != "" && symbol.Kind != symkind.Sxxx { 641 if _, ok := pkg.Syms[symbol.Name]; !ok { 642 pkg.SymNameOrder = append(pkg.SymNameOrder, symbol.Name) 643 } 644 pkg.Syms[symbol.Name] = symbol 645 } 646 } 647 return nil 648 } 649 650 type uint64s []uint64 651 652 func (x uint64s) Len() int { return len(x) } 653 func (x uint64s) Swap(i, j int) { x[i], x[j] = x[j], x[i] } 654 func (x uint64s) Less(i, j int) bool { return x[i] < x[j] } 655 656 func (pkg *Pkg) convertMachoRelocs(f *macho.File, e archive.Entry) error { 657 if f.Symtab == nil { 658 return nil 659 } 660 var allData []byte 661 662 sectionsSortedByAddr := make([]*macho.Section, len(f.Sections)) 663 copy(sectionsSortedByAddr, f.Sections) 664 665 sort.Slice(sectionsSortedByAddr, func(i, j int) bool { 666 return sectionsSortedByAddr[i].Addr < sectionsSortedByAddr[j].Addr 667 }) 668 669 for _, section := range sectionsSortedByAddr { 670 data, err := section.Data() 671 if err != nil { 672 return fmt.Errorf("failed to read data for section %s: %w", section.Name, err) 673 } 674 grow(&data, int(section.Size+uint64(section.Align))) 675 allData = append(allData, data...) 676 } 677 // Build sorted list of addresses of all symbols. 678 // We infer the size of a symbol by looking at where the next symbol begins. 679 var addrs []uint64 680 for _, s := range f.Symtab.Syms { 681 addrs = append(addrs, s.Value) 682 } 683 sort.Sort(uint64s(addrs)) 684 685 var objSymbols []*ObjSymbol 686 for _, s := range f.Symtab.Syms { 687 if s.Type&0xe0 != 0 { 688 // Skip stab debug info. 689 continue 690 } 691 692 if s.Name == "" || s.Sect == 0 { 693 continue 694 } 695 696 var sym *ObjSymbol 697 var addr uint64 698 699 sym = &ObjSymbol{Name: s.Name, Func: &FuncInfo{}, Pkg: pkg.PkgPath} 700 701 i := sort.Search(len(addrs), func(x int) bool { return addrs[x] > s.Value }) 702 if i < len(addrs) { 703 sym.Size = int64(addrs[i] - s.Value) 704 } else { 705 sym.Size = int64(f.Sections[s.Sect-1].Addr + f.Sections[s.Sect-1].Size - s.Value) 706 } 707 708 if sym.Size > 0 && s.Sect > 0 { 709 addr = s.Value 710 data := make([]byte, sym.Size) 711 copy(data, allData[addr:]) 712 sym.Data = data 713 } 714 715 objSymbols = append(objSymbols, sym) 716 717 if int(s.Sect) <= len(f.Sections) { 718 sect := f.Sections[s.Sect-1] 719 switch sect.Seg { 720 case "__TEXT", "__DATA_CONST": 721 sym.Kind = symkind.SRODATA 722 case "__DATA": 723 sym.Kind = symkind.SDATA 724 } 725 switch sect.Seg + " " + sect.Name { 726 case "__TEXT __text": 727 sym.Kind = symkind.STEXT 728 case "__DATA __bss": 729 sym.Kind = symkind.SBSS 730 case "__DATA __noptrbss": 731 sym.Kind = symkind.SNOPTRBSS 732 } 733 } 734 735 for _, reloc := range append(f.Sections[s.Sect-1].Relocs) { 736 // TODO - review https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/reloc.h.auto.html 737 if uint64(reloc.Addr) < s.Value+uint64(sym.Size) && uint64(reloc.Addr) > s.Value { 738 // when Scattered == false && Extern == true, Value is the symbol number. 739 // when Scattered == false && Extern == false, Value is the section number. 740 // when Scattered == true, Value is the value that this reloc refers to. 741 742 if pkg.Arch == "arm64" { 743 // https://opensource.apple.com/source/cctools/cctools-877.5/include/mach-o/arm64/reloc.h.auto.html 744 var rType int 745 var rSize int 746 switch macho.RelocTypeARM64(reloc.Type) { 747 case macho.ARM64_RELOC_BRANCH26: 748 rType = reloctype.R_CALLARM64 749 rSize = 4 750 if rSize != 1<<reloc.Len { 751 return fmt.Errorf("unexpected size of macho reloc - Expected 4, got %d %#v", 1<<reloc.Len, reloc) 752 } 753 case macho.ARM64_RELOC_GOT_LOAD_PAGE21: 754 rType = reloctype.R_ADDRARM64 755 rSize = 8 756 case macho.ARM64_RELOC_GOT_LOAD_PAGEOFF12: 757 // Presumably paired with above? 758 continue 759 case macho.ARM64_RELOC_PAGE21: 760 rType = reloctype.R_ADDRARM64 761 rSize = 8 762 case macho.ARM64_RELOC_PAGEOFF12: 763 // Presumably paired with above? 764 continue 765 case macho.ARM64_RELOC_POINTER_TO_GOT: 766 rType = reloctype.R_ARM64_GOTPCREL 767 rSize = 8 768 case macho.ARM64_RELOC_UNSIGNED, 769 macho.ARM64_RELOC_SUBTRACTOR, 770 macho.ARM64_RELOC_TLVP_LOAD_PAGE21, 771 macho.ARM64_RELOC_TLVP_LOAD_PAGEOFF12, 772 macho.ARM64_RELOC_ADDEND: 773 774 return fmt.Errorf("got an unsupported macho reloc: %#v", reloc) 775 } 776 if !reloc.Scattered && reloc.Pcrel { 777 if reloc.Extern { 778 sym.Reloc = append(sym.Reloc, Reloc{ 779 Offset: int(uint64(reloc.Addr) - s.Value), 780 Sym: &Sym{Name: f.Symtab.Syms[reloc.Value].Name, Offset: InvalidOffset}, 781 Size: rSize, 782 Type: rType, 783 Add: 0, // TODO - Is this correct? 784 }) 785 } else { 786 sym.Reloc = append(sym.Reloc, Reloc{ 787 Offset: int(uint64(reloc.Addr) - f.Sections[s.Value].Addr), 788 Sym: &Sym{Name: f.Sections[s.Value].Name, Offset: InvalidOffset}, 789 Size: rSize, 790 Type: rType, 791 Add: 0, 792 }) 793 } 794 795 } else if !reloc.Scattered && !reloc.Pcrel { 796 sym.Reloc = append(sym.Reloc, Reloc{ 797 Offset: int(uint64(reloc.Addr) - s.Value), 798 Sym: &Sym{Name: f.Symtab.Syms[reloc.Value].Name, Offset: InvalidOffset}, 799 Size: rSize, 800 Type: rType, 801 Add: 0, 802 }) 803 } else { 804 return fmt.Errorf("got an unsupported macho reloc: %#v", reloc) 805 } 806 } else if pkg.Arch == "amd64" { 807 // https://opensource.apple.com/source/xnu/xnu-1504.7.4/EXTERNAL_HEADERS/mach-o/x86_64/reloc.h.auto.html 808 809 switch macho.RelocTypeX86_64(reloc.Type) { 810 case macho.X86_64_RELOC_UNSIGNED, 811 macho.X86_64_RELOC_SIGNED, 812 macho.X86_64_RELOC_BRANCH, 813 macho.X86_64_RELOC_GOT_LOAD, 814 macho.X86_64_RELOC_GOT, 815 macho.X86_64_RELOC_SUBTRACTOR, 816 macho.X86_64_RELOC_SIGNED_1, 817 macho.X86_64_RELOC_SIGNED_2, 818 macho.X86_64_RELOC_SIGNED_4, 819 macho.X86_64_RELOC_TLV: 820 // TODO - set the size and reloctype based on these instead of blindly assuming R_PCREL 821 } 822 if !reloc.Scattered && reloc.Pcrel { 823 sym.Reloc = append(sym.Reloc, Reloc{ 824 Offset: int(uint64(reloc.Addr) - s.Value), 825 Sym: &Sym{Name: f.Symtab.Syms[reloc.Value].Name, Offset: InvalidOffset}, 826 Size: 4, 827 Type: reloctype.R_PCREL, 828 Add: 0, 829 }) 830 } else { 831 return fmt.Errorf("got an unsupported macho reloc: %#v", reloc) 832 } 833 } else { 834 return fmt.Errorf("unsupported arch: %s", pkg.Arch) 835 } 836 837 } 838 } 839 } 840 841 for _, symbol := range objSymbols { 842 if _, ok := pkg.Syms[symbol.Name]; !ok { 843 pkg.SymNameOrder = append(pkg.SymNameOrder, symbol.Name) 844 } 845 pkg.Syms[symbol.Name] = symbol 846 if strings.HasPrefix(symbol.Name, "__cgo_") { 847 // Need to add symbol as _cgo_* so that the Go generated 848 pkg.Syms[symbol.Name[1:]] = &ObjSymbol{ 849 Name: symbol.Name[1:], 850 Kind: symbol.Kind, 851 DupOK: true, 852 Size: symbol.Size, 853 Data: symbol.Data, 854 Type: symbol.Type, 855 Reloc: symbol.Reloc, 856 Func: symbol.Func, 857 Pkg: symbol.Pkg, 858 } 859 } 860 } 861 return nil 862 }