github.com/rsc/tmp@v0.0.0-20240517235954-6deaab19748b/bootstrap/internal/ld/ldmacho.go (about) 1 // Do not edit. Bootstrap copy of /Users/rsc/g/go/src/cmd/internal/ld/ldmacho.go 2 3 package ld 4 5 import ( 6 "rsc.io/tmp/bootstrap/internal/obj" 7 "encoding/binary" 8 "fmt" 9 "log" 10 "sort" 11 ) 12 13 /* 14 Derived from Plan 9 from User Space's src/libmach/elf.h, elf.c 15 http://code.swtch.com/plan9port/src/tip/src/libmach/ 16 17 Copyright © 2004 Russ Cox. 18 Portions Copyright © 2008-2010 Google Inc. 19 Portions Copyright © 2010 The Go Authors. 20 21 Permission is hereby granted, free of charge, to any person obtaining a copy 22 of this software and associated documentation files (the "Software"), to deal 23 in the Software without restriction, including without limitation the rights 24 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 copies of the Software, and to permit persons to whom the Software is 26 furnished to do so, subject to the following conditions: 27 28 The above copyright notice and this permission notice shall be included in 29 all copies or substantial portions of the Software. 30 31 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 37 THE SOFTWARE. 38 */ 39 const ( 40 N_EXT = 0x01 41 N_TYPE = 0x1e 42 N_STAB = 0xe0 43 ) 44 45 type LdMachoObj struct { 46 f *Biobuf 47 base int64 // off in f where Mach-O begins 48 length int64 // length of Mach-O 49 is64 bool 50 name string 51 e binary.ByteOrder 52 cputype uint 53 subcputype uint 54 filetype uint32 55 flags uint32 56 cmd []LdMachoCmd 57 ncmd uint 58 } 59 60 type LdMachoCmd struct { 61 type_ int 62 off uint32 63 size uint32 64 seg LdMachoSeg 65 sym LdMachoSymtab 66 dsym LdMachoDysymtab 67 } 68 69 type LdMachoSeg struct { 70 name string 71 vmaddr uint64 72 vmsize uint64 73 fileoff uint32 74 filesz uint32 75 maxprot uint32 76 initprot uint32 77 nsect uint32 78 flags uint32 79 sect []LdMachoSect 80 } 81 82 type LdMachoSect struct { 83 name string 84 segname string 85 addr uint64 86 size uint64 87 off uint32 88 align uint32 89 reloff uint32 90 nreloc uint32 91 flags uint32 92 res1 uint32 93 res2 uint32 94 sym *LSym 95 rel []LdMachoRel 96 } 97 98 type LdMachoRel struct { 99 addr uint32 100 symnum uint32 101 pcrel uint8 102 length uint8 103 extrn uint8 104 type_ uint8 105 scattered uint8 106 value uint32 107 } 108 109 type LdMachoSymtab struct { 110 symoff uint32 111 nsym uint32 112 stroff uint32 113 strsize uint32 114 str []byte 115 sym []LdMachoSym 116 } 117 118 type LdMachoSym struct { 119 name string 120 type_ uint8 121 sectnum uint8 122 desc uint16 123 kind int8 124 value uint64 125 sym *LSym 126 } 127 128 type LdMachoDysymtab struct { 129 ilocalsym uint32 130 nlocalsym uint32 131 iextdefsym uint32 132 nextdefsym uint32 133 iundefsym uint32 134 nundefsym uint32 135 tocoff uint32 136 ntoc uint32 137 modtaboff uint32 138 nmodtab uint32 139 extrefsymoff uint32 140 nextrefsyms uint32 141 indirectsymoff uint32 142 nindirectsyms uint32 143 extreloff uint32 144 nextrel uint32 145 locreloff uint32 146 nlocrel uint32 147 indir []uint32 148 } 149 150 const ( 151 LdMachoCpuVax = 1 152 LdMachoCpu68000 = 6 153 LdMachoCpu386 = 7 154 LdMachoCpuAmd64 = 0x1000007 155 LdMachoCpuMips = 8 156 LdMachoCpu98000 = 10 157 LdMachoCpuHppa = 11 158 LdMachoCpuArm = 12 159 LdMachoCpu88000 = 13 160 LdMachoCpuSparc = 14 161 LdMachoCpu860 = 15 162 LdMachoCpuAlpha = 16 163 LdMachoCpuPower = 18 164 LdMachoCmdSegment = 1 165 LdMachoCmdSymtab = 2 166 LdMachoCmdSymseg = 3 167 LdMachoCmdThread = 4 168 LdMachoCmdDysymtab = 11 169 LdMachoCmdSegment64 = 25 170 LdMachoFileObject = 1 171 LdMachoFileExecutable = 2 172 LdMachoFileFvmlib = 3 173 LdMachoFileCore = 4 174 LdMachoFilePreload = 5 175 ) 176 177 func unpackcmd(p []byte, m *LdMachoObj, c *LdMachoCmd, type_ uint, sz uint) int { 178 e4 := m.e.Uint32 179 e8 := m.e.Uint64 180 181 c.type_ = int(type_) 182 c.size = uint32(sz) 183 switch type_ { 184 default: 185 return -1 186 187 case LdMachoCmdSegment: 188 if sz < 56 { 189 return -1 190 } 191 c.seg.name = cstring(p[8:24]) 192 c.seg.vmaddr = uint64(e4(p[24:])) 193 c.seg.vmsize = uint64(e4(p[28:])) 194 c.seg.fileoff = e4(p[32:]) 195 c.seg.filesz = e4(p[36:]) 196 c.seg.maxprot = e4(p[40:]) 197 c.seg.initprot = e4(p[44:]) 198 c.seg.nsect = e4(p[48:]) 199 c.seg.flags = e4(p[52:]) 200 c.seg.sect = make([]LdMachoSect, c.seg.nsect) 201 if uint32(sz) < 56+c.seg.nsect*68 { 202 return -1 203 } 204 p = p[56:] 205 var s *LdMachoSect 206 for i := 0; uint32(i) < c.seg.nsect; i++ { 207 s = &c.seg.sect[i] 208 s.name = cstring(p[0:16]) 209 s.segname = cstring(p[16:32]) 210 s.addr = uint64(e4(p[32:])) 211 s.size = uint64(e4(p[36:])) 212 s.off = e4(p[40:]) 213 s.align = e4(p[44:]) 214 s.reloff = e4(p[48:]) 215 s.nreloc = e4(p[52:]) 216 s.flags = e4(p[56:]) 217 s.res1 = e4(p[60:]) 218 s.res2 = e4(p[64:]) 219 p = p[68:] 220 } 221 222 case LdMachoCmdSegment64: 223 if sz < 72 { 224 return -1 225 } 226 c.seg.name = cstring(p[8:24]) 227 c.seg.vmaddr = e8(p[24:]) 228 c.seg.vmsize = e8(p[32:]) 229 c.seg.fileoff = uint32(e8(p[40:])) 230 c.seg.filesz = uint32(e8(p[48:])) 231 c.seg.maxprot = e4(p[56:]) 232 c.seg.initprot = e4(p[60:]) 233 c.seg.nsect = e4(p[64:]) 234 c.seg.flags = e4(p[68:]) 235 c.seg.sect = make([]LdMachoSect, c.seg.nsect) 236 if uint32(sz) < 72+c.seg.nsect*80 { 237 return -1 238 } 239 p = p[72:] 240 var s *LdMachoSect 241 for i := 0; uint32(i) < c.seg.nsect; i++ { 242 s = &c.seg.sect[i] 243 s.name = cstring(p[0:16]) 244 s.segname = cstring(p[16:32]) 245 s.addr = e8(p[32:]) 246 s.size = e8(p[40:]) 247 s.off = e4(p[48:]) 248 s.align = e4(p[52:]) 249 s.reloff = e4(p[56:]) 250 s.nreloc = e4(p[60:]) 251 s.flags = e4(p[64:]) 252 s.res1 = e4(p[68:]) 253 s.res2 = e4(p[72:]) 254 255 // p+76 is reserved 256 p = p[80:] 257 } 258 259 case LdMachoCmdSymtab: 260 if sz < 24 { 261 return -1 262 } 263 c.sym.symoff = e4(p[8:]) 264 c.sym.nsym = e4(p[12:]) 265 c.sym.stroff = e4(p[16:]) 266 c.sym.strsize = e4(p[20:]) 267 268 case LdMachoCmdDysymtab: 269 if sz < 80 { 270 return -1 271 } 272 c.dsym.ilocalsym = e4(p[8:]) 273 c.dsym.nlocalsym = e4(p[12:]) 274 c.dsym.iextdefsym = e4(p[16:]) 275 c.dsym.nextdefsym = e4(p[20:]) 276 c.dsym.iundefsym = e4(p[24:]) 277 c.dsym.nundefsym = e4(p[28:]) 278 c.dsym.tocoff = e4(p[32:]) 279 c.dsym.ntoc = e4(p[36:]) 280 c.dsym.modtaboff = e4(p[40:]) 281 c.dsym.nmodtab = e4(p[44:]) 282 c.dsym.extrefsymoff = e4(p[48:]) 283 c.dsym.nextrefsyms = e4(p[52:]) 284 c.dsym.indirectsymoff = e4(p[56:]) 285 c.dsym.nindirectsyms = e4(p[60:]) 286 c.dsym.extreloff = e4(p[64:]) 287 c.dsym.nextrel = e4(p[68:]) 288 c.dsym.locreloff = e4(p[72:]) 289 c.dsym.nlocrel = e4(p[76:]) 290 } 291 292 return 0 293 } 294 295 func macholoadrel(m *LdMachoObj, sect *LdMachoSect) int { 296 if sect.rel != nil || sect.nreloc == 0 { 297 return 0 298 } 299 rel := make([]LdMachoRel, sect.nreloc) 300 n := int(sect.nreloc * 8) 301 buf := make([]byte, n) 302 if Bseek(m.f, m.base+int64(sect.reloff), 0) < 0 || Bread(m.f, buf) != n { 303 return -1 304 } 305 var p []byte 306 var r *LdMachoRel 307 var v uint32 308 for i := 0; uint32(i) < sect.nreloc; i++ { 309 r = &rel[i] 310 p = buf[i*8:] 311 r.addr = m.e.Uint32(p) 312 313 // TODO(rsc): Wrong interpretation for big-endian bitfields? 314 if r.addr&0x80000000 != 0 { 315 // scatterbrained relocation 316 r.scattered = 1 317 318 v = r.addr >> 24 319 r.addr &= 0xFFFFFF 320 r.type_ = uint8(v & 0xF) 321 v >>= 4 322 r.length = 1 << (v & 3) 323 v >>= 2 324 r.pcrel = uint8(v & 1) 325 r.value = m.e.Uint32(p[4:]) 326 } else { 327 v = m.e.Uint32(p[4:]) 328 r.symnum = v & 0xFFFFFF 329 v >>= 24 330 r.pcrel = uint8(v & 1) 331 v >>= 1 332 r.length = 1 << (v & 3) 333 v >>= 2 334 r.extrn = uint8(v & 1) 335 v >>= 1 336 r.type_ = uint8(v) 337 } 338 } 339 340 sect.rel = rel 341 return 0 342 } 343 344 func macholoaddsym(m *LdMachoObj, d *LdMachoDysymtab) int { 345 n := int(d.nindirectsyms) 346 347 p := make([]byte, n*4) 348 if Bseek(m.f, m.base+int64(d.indirectsymoff), 0) < 0 || Bread(m.f, p) != len(p) { 349 return -1 350 } 351 352 d.indir = make([]uint32, n) 353 for i := 0; i < n; i++ { 354 d.indir[i] = m.e.Uint32(p[4*i:]) 355 } 356 return 0 357 } 358 359 func macholoadsym(m *LdMachoObj, symtab *LdMachoSymtab) int { 360 if symtab.sym != nil { 361 return 0 362 } 363 364 strbuf := make([]byte, symtab.strsize) 365 if Bseek(m.f, m.base+int64(symtab.stroff), 0) < 0 || Bread(m.f, strbuf) != len(strbuf) { 366 return -1 367 } 368 369 symsize := 12 370 if m.is64 { 371 symsize = 16 372 } 373 n := int(symtab.nsym * uint32(symsize)) 374 symbuf := make([]byte, n) 375 if Bseek(m.f, m.base+int64(symtab.symoff), 0) < 0 || Bread(m.f, symbuf) != len(symbuf) { 376 return -1 377 } 378 sym := make([]LdMachoSym, symtab.nsym) 379 p := symbuf 380 var s *LdMachoSym 381 var v uint32 382 for i := 0; uint32(i) < symtab.nsym; i++ { 383 s = &sym[i] 384 v = m.e.Uint32(p) 385 if v >= symtab.strsize { 386 return -1 387 } 388 s.name = cstring(strbuf[v:]) 389 s.type_ = uint8(p[4]) 390 s.sectnum = uint8(p[5]) 391 s.desc = m.e.Uint16(p[6:]) 392 if m.is64 { 393 s.value = m.e.Uint64(p[8:]) 394 } else { 395 s.value = uint64(m.e.Uint32(p[8:])) 396 } 397 p = p[symsize:] 398 } 399 400 symtab.str = strbuf 401 symtab.sym = sym 402 return 0 403 } 404 405 func ldmacho(f *Biobuf, pkg string, length int64, pn string) { 406 var err error 407 var j int 408 var is64 bool 409 var secaddr uint64 410 var hdr [7 * 4]uint8 411 var cmdp []byte 412 var dat []byte 413 var ncmd uint32 414 var cmdsz uint32 415 var ty uint32 416 var sz uint32 417 var off uint32 418 var m *LdMachoObj 419 var e binary.ByteOrder 420 var sect *LdMachoSect 421 var rel *LdMachoRel 422 var rpi int 423 var s *LSym 424 var s1 *LSym 425 var outer *LSym 426 var c *LdMachoCmd 427 var symtab *LdMachoSymtab 428 var dsymtab *LdMachoDysymtab 429 var sym *LdMachoSym 430 var r []Reloc 431 var rp *Reloc 432 var name string 433 434 Ctxt.Version++ 435 base := Boffset(f) 436 if Bread(f, hdr[:]) != len(hdr) { 437 goto bad 438 } 439 440 if binary.BigEndian.Uint32(hdr[:])&^1 == 0xFEEDFACE { 441 e = binary.BigEndian 442 } else if binary.LittleEndian.Uint32(hdr[:])&^1 == 0xFEEDFACE { 443 e = binary.LittleEndian 444 } else { 445 err = fmt.Errorf("bad magic - not mach-o file") 446 goto bad 447 } 448 449 is64 = e.Uint32(hdr[:]) == 0xFEEDFACF 450 ncmd = e.Uint32([]byte(hdr[4*4:])) 451 cmdsz = e.Uint32([]byte(hdr[5*4:])) 452 if ncmd > 0x10000 || cmdsz >= 0x01000000 { 453 err = fmt.Errorf("implausible mach-o header ncmd=%d cmdsz=%d", ncmd, cmdsz) 454 goto bad 455 } 456 457 if is64 { 458 var tmp [4]uint8 459 Bread(f, tmp[:4]) // skip reserved word in header 460 } 461 462 m = new(LdMachoObj) 463 464 m.f = f 465 m.e = e 466 m.cputype = uint(e.Uint32([]byte(hdr[1*4:]))) 467 m.subcputype = uint(e.Uint32([]byte(hdr[2*4:]))) 468 m.filetype = e.Uint32([]byte(hdr[3*4:])) 469 m.ncmd = uint(ncmd) 470 m.flags = e.Uint32([]byte(hdr[6*4:])) 471 m.is64 = is64 472 m.base = base 473 m.length = length 474 m.name = pn 475 476 switch Thearch.Thechar { 477 default: 478 Diag("%s: mach-o %s unimplemented", pn, Thestring) 479 return 480 481 case '6': 482 if e != binary.LittleEndian || m.cputype != LdMachoCpuAmd64 { 483 Diag("%s: mach-o object but not amd64", pn) 484 return 485 } 486 487 case '8': 488 if e != binary.LittleEndian || m.cputype != LdMachoCpu386 { 489 Diag("%s: mach-o object but not 386", pn) 490 return 491 } 492 } 493 494 m.cmd = make([]LdMachoCmd, ncmd) 495 off = uint32(len(hdr)) 496 cmdp = make([]byte, cmdsz) 497 if Bread(f, cmdp) != len(cmdp) { 498 err = fmt.Errorf("reading cmds: %v", err) 499 goto bad 500 } 501 502 // read and parse load commands 503 c = nil 504 505 symtab = nil 506 dsymtab = nil 507 508 for i := 0; uint32(i) < ncmd; i++ { 509 ty = e.Uint32(cmdp) 510 sz = e.Uint32(cmdp[4:]) 511 m.cmd[i].off = off 512 unpackcmd(cmdp, m, &m.cmd[i], uint(ty), uint(sz)) 513 cmdp = cmdp[sz:] 514 off += sz 515 if ty == LdMachoCmdSymtab { 516 if symtab != nil { 517 err = fmt.Errorf("multiple symbol tables") 518 goto bad 519 } 520 521 symtab = &m.cmd[i].sym 522 macholoadsym(m, symtab) 523 } 524 525 if ty == LdMachoCmdDysymtab { 526 dsymtab = &m.cmd[i].dsym 527 macholoaddsym(m, dsymtab) 528 } 529 530 if (is64 && ty == LdMachoCmdSegment64) || (!is64 && ty == LdMachoCmdSegment) { 531 if c != nil { 532 err = fmt.Errorf("multiple load commands") 533 goto bad 534 } 535 536 c = &m.cmd[i] 537 } 538 } 539 540 // load text and data segments into memory. 541 // they are not as small as the load commands, but we'll need 542 // the memory anyway for the symbol images, so we might 543 // as well use one large chunk. 544 if c == nil { 545 err = fmt.Errorf("no load command") 546 goto bad 547 } 548 549 if symtab == nil { 550 // our work is done here - no symbols means nothing can refer to this file 551 return 552 } 553 554 if int64(c.seg.fileoff+c.seg.filesz) >= length { 555 err = fmt.Errorf("load segment out of range") 556 goto bad 557 } 558 559 dat = make([]byte, c.seg.filesz) 560 if Bseek(f, m.base+int64(c.seg.fileoff), 0) < 0 || Bread(f, dat) != len(dat) { 561 err = fmt.Errorf("cannot load object data: %v", err) 562 goto bad 563 } 564 565 for i := 0; uint32(i) < c.seg.nsect; i++ { 566 sect = &c.seg.sect[i] 567 if sect.segname != "__TEXT" && sect.segname != "__DATA" { 568 continue 569 } 570 if sect.name == "__eh_frame" { 571 continue 572 } 573 name = fmt.Sprintf("%s(%s/%s)", pkg, sect.segname, sect.name) 574 s = Linklookup(Ctxt, name, Ctxt.Version) 575 if s.Type != 0 { 576 err = fmt.Errorf("duplicate %s/%s", sect.segname, sect.name) 577 goto bad 578 } 579 580 if sect.flags&0xff == 1 { // S_ZEROFILL 581 s.P = make([]byte, sect.size) 582 } else { 583 s.P = dat[sect.addr-c.seg.vmaddr:][:sect.size] 584 } 585 s.Size = int64(len(s.P)) 586 587 if sect.segname == "__TEXT" { 588 if sect.name == "__text" { 589 s.Type = obj.STEXT 590 } else { 591 s.Type = obj.SRODATA 592 } 593 } else { 594 if sect.name == "__bss" { 595 s.Type = obj.SNOPTRBSS 596 s.P = s.P[:0] 597 } else { 598 s.Type = obj.SNOPTRDATA 599 } 600 } 601 602 sect.sym = s 603 } 604 605 // enter sub-symbols into symbol table. 606 // have to guess sizes from next symbol. 607 for i := 0; uint32(i) < symtab.nsym; i++ { 608 sym = &symtab.sym[i] 609 if sym.type_&N_STAB != 0 { 610 continue 611 } 612 613 // TODO: check sym->type against outer->type. 614 name = sym.name 615 616 if name[0] == '_' && name[1] != '\x00' { 617 name = name[1:] 618 } 619 v := 0 620 if sym.type_&N_EXT == 0 { 621 v = Ctxt.Version 622 } 623 s = Linklookup(Ctxt, name, v) 624 if sym.type_&N_EXT == 0 { 625 s.Dupok = 1 626 } 627 sym.sym = s 628 if sym.sectnum == 0 { // undefined 629 continue 630 } 631 if uint32(sym.sectnum) > c.seg.nsect { 632 err = fmt.Errorf("reference to invalid section %d", sym.sectnum) 633 goto bad 634 } 635 636 sect = &c.seg.sect[sym.sectnum-1] 637 outer = sect.sym 638 if outer == nil { 639 err = fmt.Errorf("reference to invalid section %s/%s", sect.segname, sect.name) 640 continue 641 } 642 643 if s.Outer != nil { 644 if s.Dupok != 0 { 645 continue 646 } 647 Exitf("%s: duplicate symbol reference: %s in both %s and %s", pn, s.Name, s.Outer.Name, sect.sym.Name) 648 } 649 650 s.Type = outer.Type | obj.SSUB 651 s.Sub = outer.Sub 652 outer.Sub = s 653 s.Outer = outer 654 s.Value = int64(sym.value - sect.addr) 655 if s.Cgoexport&CgoExportDynamic == 0 { 656 s.Dynimplib = "" // satisfy dynimport 657 } 658 if outer.Type == obj.STEXT { 659 if s.External != 0 && s.Dupok == 0 { 660 Diag("%s: duplicate definition of %s", pn, s.Name) 661 } 662 s.External = 1 663 } 664 665 sym.sym = s 666 } 667 668 // Sort outer lists by address, adding to textp. 669 // This keeps textp in increasing address order. 670 for i := 0; uint32(i) < c.seg.nsect; i++ { 671 sect = &c.seg.sect[i] 672 s = sect.sym 673 if s == nil { 674 continue 675 } 676 if s.Sub != nil { 677 s.Sub = listsort(s.Sub, valuecmp, listsubp) 678 679 // assign sizes, now that we know symbols in sorted order. 680 for s1 = s.Sub; s1 != nil; s1 = s1.Sub { 681 if s1.Sub != nil { 682 s1.Size = s1.Sub.Value - s1.Value 683 } else { 684 s1.Size = s.Value + s.Size - s1.Value 685 } 686 } 687 } 688 689 if s.Type == obj.STEXT { 690 if s.Onlist != 0 { 691 log.Fatalf("symbol %s listed multiple times", s.Name) 692 } 693 s.Onlist = 1 694 if Ctxt.Etextp != nil { 695 Ctxt.Etextp.Next = s 696 } else { 697 Ctxt.Textp = s 698 } 699 Ctxt.Etextp = s 700 for s1 = s.Sub; s1 != nil; s1 = s1.Sub { 701 if s1.Onlist != 0 { 702 log.Fatalf("symbol %s listed multiple times", s1.Name) 703 } 704 s1.Onlist = 1 705 Ctxt.Etextp.Next = s1 706 Ctxt.Etextp = s1 707 } 708 } 709 } 710 711 // load relocations 712 for i := 0; uint32(i) < c.seg.nsect; i++ { 713 sect = &c.seg.sect[i] 714 s = sect.sym 715 if s == nil { 716 continue 717 } 718 macholoadrel(m, sect) 719 if sect.rel == nil { 720 continue 721 } 722 r = make([]Reloc, sect.nreloc) 723 rpi = 0 724 Reloc: 725 for j = 0; uint32(j) < sect.nreloc; j++ { 726 rp = &r[rpi] 727 rel = §.rel[j] 728 if rel.scattered != 0 { 729 if Thearch.Thechar != '8' { 730 // mach-o only uses scattered relocation on 32-bit platforms 731 Diag("unexpected scattered relocation") 732 733 continue 734 } 735 736 // on 386, rewrite scattered 4/1 relocation and some 737 // scattered 2/1 relocation into the pseudo-pc-relative 738 // reference that it is. 739 // assume that the second in the pair is in this section 740 // and use that as the pc-relative base. 741 if uint32(j+1) >= sect.nreloc { 742 err = fmt.Errorf("unsupported scattered relocation %d", int(rel.type_)) 743 goto bad 744 } 745 746 if sect.rel[j+1].scattered == 0 || sect.rel[j+1].type_ != 1 || (rel.type_ != 4 && rel.type_ != 2) || uint64(sect.rel[j+1].value) < sect.addr || uint64(sect.rel[j+1].value) >= sect.addr+sect.size { 747 err = fmt.Errorf("unsupported scattered relocation %d/%d", int(rel.type_), int(sect.rel[j+1].type_)) 748 goto bad 749 } 750 751 rp.Siz = rel.length 752 rp.Off = int32(rel.addr) 753 754 // NOTE(rsc): I haven't worked out why (really when) 755 // we should ignore the addend on a 756 // scattered relocation, but it seems that the 757 // common case is we ignore it. 758 // It's likely that this is not strictly correct 759 // and that the math should look something 760 // like the non-scattered case below. 761 rp.Add = 0 762 763 // want to make it pc-relative aka relative to rp->off+4 764 // but the scatter asks for relative to off = sect->rel[j+1].value - sect->addr. 765 // adjust rp->add accordingly. 766 rp.Type = obj.R_PCREL 767 768 rp.Add += int64(uint64(int64(rp.Off)+4) - (uint64(sect.rel[j+1].value) - sect.addr)) 769 770 // now consider the desired symbol. 771 // find the section where it lives. 772 var ks *LdMachoSect 773 for k := 0; uint32(k) < c.seg.nsect; k++ { 774 ks = &c.seg.sect[k] 775 if ks.addr <= uint64(rel.value) && uint64(rel.value) < ks.addr+ks.size { 776 if ks.sym != nil { 777 rp.Sym = ks.sym 778 rp.Add += int64(uint64(rel.value) - ks.addr) 779 } else if ks.segname == "__IMPORT" && ks.name == "__pointers" { 780 // handle reference to __IMPORT/__pointers. 781 // how much worse can this get? 782 // why are we supporting 386 on the mac anyway? 783 rp.Type = 512 + MACHO_FAKE_GOTPCREL 784 785 // figure out which pointer this is a reference to. 786 k = int(uint64(ks.res1) + (uint64(rel.value)-ks.addr)/4) 787 788 // load indirect table for __pointers 789 // fetch symbol number 790 if dsymtab == nil || k < 0 || uint32(k) >= dsymtab.nindirectsyms || dsymtab.indir == nil { 791 err = fmt.Errorf("invalid scattered relocation: indirect symbol reference out of range") 792 goto bad 793 } 794 795 k = int(dsymtab.indir[k]) 796 if k < 0 || uint32(k) >= symtab.nsym { 797 err = fmt.Errorf("invalid scattered relocation: symbol reference out of range") 798 goto bad 799 } 800 801 rp.Sym = symtab.sym[k].sym 802 } else { 803 err = fmt.Errorf("unsupported scattered relocation: reference to %s/%s", ks.segname, ks.name) 804 goto bad 805 } 806 807 rpi++ 808 809 // skip #1 of 2 rel; continue skips #2 of 2. 810 j++ 811 812 continue Reloc 813 } 814 } 815 816 err = fmt.Errorf("unsupported scattered relocation: invalid address %#x", rel.addr) 817 goto bad 818 819 } 820 821 rp.Siz = rel.length 822 rp.Type = 512 + (int32(rel.type_) << 1) + int32(rel.pcrel) 823 rp.Off = int32(rel.addr) 824 825 // Handle X86_64_RELOC_SIGNED referencing a section (rel->extrn == 0). 826 if Thearch.Thechar == '6' && rel.extrn == 0 && rel.type_ == 1 { 827 // Calculate the addend as the offset into the section. 828 // 829 // The rip-relative offset stored in the object file is encoded 830 // as follows: 831 // 832 // movsd 0x00000360(%rip),%xmm0 833 // 834 // To get the absolute address of the value this rip-relative address is pointing 835 // to, we must add the address of the next instruction to it. This is done by 836 // taking the address of the relocation and adding 4 to it (since the rip-relative 837 // offset can at most be 32 bits long). To calculate the offset into the section the 838 // relocation is referencing, we subtract the vaddr of the start of the referenced 839 // section found in the original object file. 840 // 841 // [For future reference, see Darwin's /usr/include/mach-o/x86_64/reloc.h] 842 secaddr = c.seg.sect[rel.symnum-1].addr 843 844 rp.Add = int64(uint64(int64(int32(e.Uint32(s.P[rp.Off:])))+int64(rp.Off)+4) - secaddr) 845 } else { 846 rp.Add = int64(int32(e.Uint32(s.P[rp.Off:]))) 847 } 848 849 // For i386 Mach-O PC-relative, the addend is written such that 850 // it *is* the PC being subtracted. Use that to make 851 // it match our version of PC-relative. 852 if rel.pcrel != 0 && Thearch.Thechar == '8' { 853 rp.Add += int64(rp.Off) + int64(rp.Siz) 854 } 855 if rel.extrn == 0 { 856 if rel.symnum < 1 || rel.symnum > c.seg.nsect { 857 err = fmt.Errorf("invalid relocation: section reference out of range %d vs %d", rel.symnum, c.seg.nsect) 858 goto bad 859 } 860 861 rp.Sym = c.seg.sect[rel.symnum-1].sym 862 if rp.Sym == nil { 863 err = fmt.Errorf("invalid relocation: %s", c.seg.sect[rel.symnum-1].name) 864 goto bad 865 } 866 867 // References to symbols in other sections 868 // include that information in the addend. 869 // We only care about the delta from the 870 // section base. 871 if Thearch.Thechar == '8' { 872 rp.Add -= int64(c.seg.sect[rel.symnum-1].addr) 873 } 874 } else { 875 if rel.symnum >= symtab.nsym { 876 err = fmt.Errorf("invalid relocation: symbol reference out of range") 877 goto bad 878 } 879 880 rp.Sym = symtab.sym[rel.symnum].sym 881 } 882 883 rpi++ 884 } 885 886 sort.Sort(rbyoff(r[:rpi])) 887 s.R = r 888 s.R = s.R[:rpi] 889 } 890 891 return 892 893 bad: 894 Diag("%s: malformed mach-o file: %v", pn, err) 895 }