1
2
3
4
5
6 package loadmacho
7
8 import (
9 "bytes"
10 "cmd/internal/bio"
11 "cmd/internal/objabi"
12 "cmd/internal/sys"
13 "cmd/link/internal/loader"
14 "cmd/link/internal/sym"
15 "encoding/binary"
16 "fmt"
17 )
18
19
45
46
47 const (
48 MACHO_X86_64_RELOC_UNSIGNED = 0
49 MACHO_X86_64_RELOC_SIGNED = 1
50 MACHO_ARM64_RELOC_ADDEND = 10
51 )
52
53 type ldMachoObj struct {
54 f *bio.Reader
55 base int64
56 length int64
57 is64 bool
58 name string
59 e binary.ByteOrder
60 cputype uint
61 subcputype uint
62 filetype uint32
63 flags uint32
64 cmd []ldMachoCmd
65 ncmd uint
66 }
67
68 type ldMachoCmd struct {
69 type_ int
70 off uint32
71 size uint32
72 seg ldMachoSeg
73 sym ldMachoSymtab
74 dsym ldMachoDysymtab
75 }
76
77 type ldMachoSeg struct {
78 name string
79 vmaddr uint64
80 vmsize uint64
81 fileoff uint32
82 filesz uint32
83 maxprot uint32
84 initprot uint32
85 nsect uint32
86 flags uint32
87 sect []ldMachoSect
88 }
89
90 type ldMachoSect struct {
91 name string
92 segname string
93 addr uint64
94 size uint64
95 off uint32
96 align uint32
97 reloff uint32
98 nreloc uint32
99 flags uint32
100 res1 uint32
101 res2 uint32
102 sym loader.Sym
103 rel []ldMachoRel
104 }
105
106 type ldMachoRel struct {
107 addr uint32
108 symnum uint32
109 pcrel uint8
110 length uint8
111 extrn uint8
112 type_ uint8
113 scattered uint8
114 value uint32
115 }
116
117 type ldMachoSymtab struct {
118 symoff uint32
119 nsym uint32
120 stroff uint32
121 strsize uint32
122 str []byte
123 sym []ldMachoSym
124 }
125
126 type ldMachoSym struct {
127 name string
128 type_ uint8
129 sectnum uint8
130 desc uint16
131 kind int8
132 value uint64
133 sym loader.Sym
134 }
135
136 type ldMachoDysymtab struct {
137 ilocalsym uint32
138 nlocalsym uint32
139 iextdefsym uint32
140 nextdefsym uint32
141 iundefsym uint32
142 nundefsym uint32
143 tocoff uint32
144 ntoc uint32
145 modtaboff uint32
146 nmodtab uint32
147 extrefsymoff uint32
148 nextrefsyms uint32
149 indirectsymoff uint32
150 nindirectsyms uint32
151 extreloff uint32
152 nextrel uint32
153 locreloff uint32
154 nlocrel uint32
155 indir []uint32
156 }
157
158
159 const (
160 N_EXT = 0x01
161 N_TYPE = 0x1e
162 N_STAB = 0xe0
163 )
164
165
166 const (
167 N_WEAK_REF = 0x40
168 N_WEAK_DEF = 0x80
169 )
170
171 const (
172 LdMachoCpuVax = 1
173 LdMachoCpu68000 = 6
174 LdMachoCpu386 = 7
175 LdMachoCpuAmd64 = 1<<24 | 7
176 LdMachoCpuMips = 8
177 LdMachoCpu98000 = 10
178 LdMachoCpuHppa = 11
179 LdMachoCpuArm = 12
180 LdMachoCpuArm64 = 1<<24 | 12
181 LdMachoCpu88000 = 13
182 LdMachoCpuSparc = 14
183 LdMachoCpu860 = 15
184 LdMachoCpuAlpha = 16
185 LdMachoCpuPower = 18
186 LdMachoCmdSegment = 1
187 LdMachoCmdSymtab = 2
188 LdMachoCmdSymseg = 3
189 LdMachoCmdThread = 4
190 LdMachoCmdDysymtab = 11
191 LdMachoCmdSegment64 = 25
192 LdMachoFileObject = 1
193 LdMachoFileExecutable = 2
194 LdMachoFileFvmlib = 3
195 LdMachoFileCore = 4
196 LdMachoFilePreload = 5
197 )
198
199 func unpackcmd(p []byte, m *ldMachoObj, c *ldMachoCmd, type_ uint, sz uint) int {
200 e4 := m.e.Uint32
201 e8 := m.e.Uint64
202
203 c.type_ = int(type_)
204 c.size = uint32(sz)
205 switch type_ {
206 default:
207 return -1
208
209 case LdMachoCmdSegment:
210 if sz < 56 {
211 return -1
212 }
213 c.seg.name = cstring(p[8:24])
214 c.seg.vmaddr = uint64(e4(p[24:]))
215 c.seg.vmsize = uint64(e4(p[28:]))
216 c.seg.fileoff = e4(p[32:])
217 c.seg.filesz = e4(p[36:])
218 c.seg.maxprot = e4(p[40:])
219 c.seg.initprot = e4(p[44:])
220 c.seg.nsect = e4(p[48:])
221 c.seg.flags = e4(p[52:])
222 c.seg.sect = make([]ldMachoSect, c.seg.nsect)
223 if uint32(sz) < 56+c.seg.nsect*68 {
224 return -1
225 }
226 p = p[56:]
227 var s *ldMachoSect
228 for i := 0; uint32(i) < c.seg.nsect; i++ {
229 s = &c.seg.sect[i]
230 s.name = cstring(p[0:16])
231 s.segname = cstring(p[16:32])
232 s.addr = uint64(e4(p[32:]))
233 s.size = uint64(e4(p[36:]))
234 s.off = e4(p[40:])
235 s.align = e4(p[44:])
236 s.reloff = e4(p[48:])
237 s.nreloc = e4(p[52:])
238 s.flags = e4(p[56:])
239 s.res1 = e4(p[60:])
240 s.res2 = e4(p[64:])
241 p = p[68:]
242 }
243
244 case LdMachoCmdSegment64:
245 if sz < 72 {
246 return -1
247 }
248 c.seg.name = cstring(p[8:24])
249 c.seg.vmaddr = e8(p[24:])
250 c.seg.vmsize = e8(p[32:])
251 c.seg.fileoff = uint32(e8(p[40:]))
252 c.seg.filesz = uint32(e8(p[48:]))
253 c.seg.maxprot = e4(p[56:])
254 c.seg.initprot = e4(p[60:])
255 c.seg.nsect = e4(p[64:])
256 c.seg.flags = e4(p[68:])
257 c.seg.sect = make([]ldMachoSect, c.seg.nsect)
258 if uint32(sz) < 72+c.seg.nsect*80 {
259 return -1
260 }
261 p = p[72:]
262 var s *ldMachoSect
263 for i := 0; uint32(i) < c.seg.nsect; i++ {
264 s = &c.seg.sect[i]
265 s.name = cstring(p[0:16])
266 s.segname = cstring(p[16:32])
267 s.addr = e8(p[32:])
268 s.size = e8(p[40:])
269 s.off = e4(p[48:])
270 s.align = e4(p[52:])
271 s.reloff = e4(p[56:])
272 s.nreloc = e4(p[60:])
273 s.flags = e4(p[64:])
274 s.res1 = e4(p[68:])
275 s.res2 = e4(p[72:])
276
277
278 p = p[80:]
279 }
280
281 case LdMachoCmdSymtab:
282 if sz < 24 {
283 return -1
284 }
285 c.sym.symoff = e4(p[8:])
286 c.sym.nsym = e4(p[12:])
287 c.sym.stroff = e4(p[16:])
288 c.sym.strsize = e4(p[20:])
289
290 case LdMachoCmdDysymtab:
291 if sz < 80 {
292 return -1
293 }
294 c.dsym.ilocalsym = e4(p[8:])
295 c.dsym.nlocalsym = e4(p[12:])
296 c.dsym.iextdefsym = e4(p[16:])
297 c.dsym.nextdefsym = e4(p[20:])
298 c.dsym.iundefsym = e4(p[24:])
299 c.dsym.nundefsym = e4(p[28:])
300 c.dsym.tocoff = e4(p[32:])
301 c.dsym.ntoc = e4(p[36:])
302 c.dsym.modtaboff = e4(p[40:])
303 c.dsym.nmodtab = e4(p[44:])
304 c.dsym.extrefsymoff = e4(p[48:])
305 c.dsym.nextrefsyms = e4(p[52:])
306 c.dsym.indirectsymoff = e4(p[56:])
307 c.dsym.nindirectsyms = e4(p[60:])
308 c.dsym.extreloff = e4(p[64:])
309 c.dsym.nextrel = e4(p[68:])
310 c.dsym.locreloff = e4(p[72:])
311 c.dsym.nlocrel = e4(p[76:])
312 }
313
314 return 0
315 }
316
317 func macholoadrel(m *ldMachoObj, sect *ldMachoSect) int {
318 if sect.rel != nil || sect.nreloc == 0 {
319 return 0
320 }
321 rel := make([]ldMachoRel, sect.nreloc)
322 m.f.MustSeek(m.base+int64(sect.reloff), 0)
323 buf, _, err := m.f.Slice(uint64(sect.nreloc * 8))
324 if err != nil {
325 return -1
326 }
327 for i := uint32(0); i < sect.nreloc; i++ {
328 r := &rel[i]
329 p := buf[i*8:]
330 r.addr = m.e.Uint32(p)
331
332
333 if r.addr&0x80000000 != 0 {
334
335 r.scattered = 1
336
337 v := r.addr >> 24
338 r.addr &= 0xFFFFFF
339 r.type_ = uint8(v & 0xF)
340 v >>= 4
341 r.length = 1 << (v & 3)
342 v >>= 2
343 r.pcrel = uint8(v & 1)
344 r.value = m.e.Uint32(p[4:])
345 } else {
346 v := m.e.Uint32(p[4:])
347 r.symnum = v & 0xFFFFFF
348 v >>= 24
349 r.pcrel = uint8(v & 1)
350 v >>= 1
351 r.length = 1 << (v & 3)
352 v >>= 2
353 r.extrn = uint8(v & 1)
354 v >>= 1
355 r.type_ = uint8(v)
356 }
357 }
358
359 sect.rel = rel
360 return 0
361 }
362
363 func macholoaddsym(m *ldMachoObj, d *ldMachoDysymtab) int {
364 n := int(d.nindirectsyms)
365 m.f.MustSeek(m.base+int64(d.indirectsymoff), 0)
366 p, _, err := m.f.Slice(uint64(n * 4))
367 if err != nil {
368 return -1
369 }
370
371 d.indir = make([]uint32, n)
372 for i := 0; i < n; i++ {
373 d.indir[i] = m.e.Uint32(p[4*i:])
374 }
375 return 0
376 }
377
378 func macholoadsym(m *ldMachoObj, symtab *ldMachoSymtab) int {
379 if symtab.sym != nil {
380 return 0
381 }
382
383 m.f.MustSeek(m.base+int64(symtab.stroff), 0)
384 strbuf, _, err := m.f.Slice(uint64(symtab.strsize))
385 if err != nil {
386 return -1
387 }
388
389 symsize := 12
390 if m.is64 {
391 symsize = 16
392 }
393 n := int(symtab.nsym * uint32(symsize))
394 m.f.MustSeek(m.base+int64(symtab.symoff), 0)
395 symbuf, _, err := m.f.Slice(uint64(n))
396 if err != nil {
397 return -1
398 }
399 sym := make([]ldMachoSym, symtab.nsym)
400 p := symbuf
401 for i := uint32(0); i < symtab.nsym; i++ {
402 s := &sym[i]
403 v := m.e.Uint32(p)
404 if v >= symtab.strsize {
405 return -1
406 }
407 s.name = cstring(strbuf[v:])
408 s.type_ = p[4]
409 s.sectnum = p[5]
410 s.desc = m.e.Uint16(p[6:])
411 if m.is64 {
412 s.value = m.e.Uint64(p[8:])
413 } else {
414 s.value = uint64(m.e.Uint32(p[8:]))
415 }
416 p = p[symsize:]
417 }
418
419 symtab.str = strbuf
420 symtab.sym = sym
421 return 0
422 }
423
424
425
426 func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader, pkg string, length int64, pn string) (textp []loader.Sym, err error) {
427 errorf := func(str string, args ...any) ([]loader.Sym, error) {
428 return nil, fmt.Errorf("loadmacho: %v: %v", pn, fmt.Sprintf(str, args...))
429 }
430
431 base := f.Offset()
432
433 hdr, _, err := f.Slice(7 * 4)
434 if err != nil {
435 return errorf("reading hdr: %v", err)
436 }
437
438 var e binary.ByteOrder
439 if binary.BigEndian.Uint32(hdr[:])&^1 == 0xFEEDFACE {
440 e = binary.BigEndian
441 } else if binary.LittleEndian.Uint32(hdr[:])&^1 == 0xFEEDFACE {
442 e = binary.LittleEndian
443 } else {
444 return errorf("bad magic - not mach-o file")
445 }
446
447 is64 := e.Uint32(hdr[:]) == 0xFEEDFACF
448 ncmd := e.Uint32(hdr[4*4:])
449 cmdsz := e.Uint32(hdr[5*4:])
450 if ncmd > 0x10000 || cmdsz >= 0x01000000 {
451 return errorf("implausible mach-o header ncmd=%d cmdsz=%d", ncmd, cmdsz)
452 }
453
454 if is64 {
455 f.MustSeek(4, 1)
456 }
457
458 m := &ldMachoObj{
459 f: f,
460 e: e,
461 cputype: uint(e.Uint32(hdr[1*4:])),
462 subcputype: uint(e.Uint32(hdr[2*4:])),
463 filetype: e.Uint32(hdr[3*4:]),
464 ncmd: uint(ncmd),
465 flags: e.Uint32(hdr[6*4:]),
466 is64: is64,
467 base: base,
468 length: length,
469 name: pn,
470 }
471
472 switch arch.Family {
473 default:
474 return errorf("mach-o %s unimplemented", arch.Name)
475 case sys.AMD64:
476 if e != binary.LittleEndian || m.cputype != LdMachoCpuAmd64 {
477 return errorf("mach-o object but not amd64")
478 }
479 case sys.ARM64:
480 if e != binary.LittleEndian || m.cputype != LdMachoCpuArm64 {
481 return errorf("mach-o object but not arm64")
482 }
483 }
484
485 m.cmd = make([]ldMachoCmd, ncmd)
486 cmdp, _, err := f.Slice(uint64(cmdsz))
487 if err != nil {
488 return errorf("reading cmds: %v", err)
489 }
490
491
492 var c *ldMachoCmd
493
494 var symtab *ldMachoSymtab
495 var dsymtab *ldMachoDysymtab
496
497 off := uint32(len(hdr))
498 for i := uint32(0); i < ncmd; i++ {
499 ty := e.Uint32(cmdp)
500 sz := e.Uint32(cmdp[4:])
501 m.cmd[i].off = off
502 unpackcmd(cmdp, m, &m.cmd[i], uint(ty), uint(sz))
503 cmdp = cmdp[sz:]
504 off += sz
505 if ty == LdMachoCmdSymtab {
506 if symtab != nil {
507 return errorf("multiple symbol tables")
508 }
509
510 symtab = &m.cmd[i].sym
511 macholoadsym(m, symtab)
512 }
513
514 if ty == LdMachoCmdDysymtab {
515 dsymtab = &m.cmd[i].dsym
516 macholoaddsym(m, dsymtab)
517 }
518
519 if (is64 && ty == LdMachoCmdSegment64) || (!is64 && ty == LdMachoCmdSegment) {
520 if c != nil {
521 return errorf("multiple load commands")
522 }
523
524 c = &m.cmd[i]
525 }
526 }
527
528
529
530
531
532 if c == nil {
533 return errorf("no load command")
534 }
535
536 if symtab == nil {
537
538 return
539 }
540
541 if int64(c.seg.fileoff+c.seg.filesz) >= length {
542 return errorf("load segment out of range")
543 }
544
545 f.MustSeek(m.base+int64(c.seg.fileoff), 0)
546 dat, readOnly, err := f.Slice(uint64(c.seg.filesz))
547 if err != nil {
548 return errorf("cannot load object data: %v", err)
549 }
550
551 for i := uint32(0); i < c.seg.nsect; i++ {
552 sect := &c.seg.sect[i]
553 if sect.segname != "__TEXT" && sect.segname != "__DATA" {
554 continue
555 }
556 if sect.name == "__eh_frame" {
557 continue
558 }
559 name := fmt.Sprintf("%s(%s/%s)", pkg, sect.segname, sect.name)
560 s := l.LookupOrCreateSym(name, localSymVersion)
561 bld := l.MakeSymbolUpdater(s)
562 if bld.Type() != 0 {
563 return errorf("duplicate %s/%s", sect.segname, sect.name)
564 }
565
566 if sect.flags&0xff == 1 {
567 bld.SetData(make([]byte, sect.size))
568 } else {
569 bld.SetReadOnly(readOnly)
570 bld.SetData(dat[sect.addr-c.seg.vmaddr:][:sect.size])
571 }
572 bld.SetSize(int64(len(bld.Data())))
573 if sect.align != 0 {
574 bld.SetAlign(1 << sect.align)
575 }
576
577 if sect.segname == "__TEXT" {
578 if sect.name == "__text" {
579 bld.SetType(sym.STEXT)
580 } else {
581 bld.SetType(sym.SRODATA)
582 }
583 } else {
584 if sect.name == "__bss" {
585 bld.SetType(sym.SNOPTRBSS)
586 bld.SetData(nil)
587 } else {
588 bld.SetType(sym.SNOPTRDATA)
589 }
590 }
591
592 sect.sym = s
593 }
594
595
596
597 for i := uint32(0); i < symtab.nsym; i++ {
598 machsym := &symtab.sym[i]
599 if machsym.type_&N_STAB != 0 {
600 continue
601 }
602
603
604 name := machsym.name
605
606 if name[0] == '_' && name[1] != '\x00' {
607 name = name[1:]
608 }
609 v := 0
610 if machsym.type_&N_EXT == 0 {
611 v = localSymVersion
612 }
613 s := l.LookupOrCreateCgoExport(name, v)
614 if machsym.type_&N_EXT == 0 {
615 l.SetAttrDuplicateOK(s, true)
616 }
617 if machsym.desc&(N_WEAK_REF|N_WEAK_DEF) != 0 {
618 l.SetAttrDuplicateOK(s, true)
619 if machsym.desc&N_WEAK_REF != 0 {
620 l.SetSymWeakBinding(s, true)
621 }
622 }
623 machsym.sym = s
624 if machsym.sectnum == 0 {
625 continue
626 }
627 if uint32(machsym.sectnum) > c.seg.nsect {
628 return errorf("reference to invalid section %d", machsym.sectnum)
629 }
630
631 sect := &c.seg.sect[machsym.sectnum-1]
632 bld := l.MakeSymbolUpdater(s)
633 outer := sect.sym
634 if outer == 0 {
635 continue
636 }
637
638 if osym := l.OuterSym(s); osym != 0 {
639 if l.AttrDuplicateOK(s) {
640 continue
641 }
642 return errorf("duplicate symbol reference: %s in both %s and %s", l.SymName(s), l.SymName(osym), l.SymName(sect.sym))
643 }
644
645 bld.SetType(l.SymType(outer))
646 if l.SymSize(outer) != 0 {
647 l.AddInteriorSym(outer, s)
648 }
649
650 bld.SetValue(int64(machsym.value - sect.addr))
651 if !l.AttrCgoExportDynamic(s) {
652 bld.SetDynimplib("")
653 }
654 if l.SymType(outer).IsText() {
655 if bld.External() && !bld.DuplicateOK() {
656 return errorf("%v: duplicate symbol definition", s)
657 }
658 bld.SetExternal(true)
659 }
660 }
661
662
663
664 for i := 0; uint32(i) < c.seg.nsect; i++ {
665 sect := &c.seg.sect[i]
666 s := sect.sym
667 if s == 0 {
668 continue
669 }
670 bld := l.MakeSymbolUpdater(s)
671 if bld.SubSym() != 0 {
672
673 bld.SortSub()
674
675
676 for s1 := bld.Sub(); s1 != 0; s1 = l.SubSym(s1) {
677 s1Bld := l.MakeSymbolUpdater(s1)
678 if sub := l.SubSym(s1); sub != 0 {
679 s1Bld.SetSize(l.SymValue(sub) - l.SymValue(s1))
680 } else {
681 dlen := int64(len(l.Data(s)))
682 s1Bld.SetSize(l.SymValue(s) + dlen - l.SymValue(s1))
683 }
684 }
685 }
686
687 if bld.Type().IsText() {
688 if bld.OnList() {
689 return errorf("symbol %s listed multiple times", bld.Name())
690 }
691 bld.SetOnList(true)
692 textp = append(textp, s)
693 for s1 := bld.Sub(); s1 != 0; s1 = l.SubSym(s1) {
694 if l.AttrOnList(s1) {
695 return errorf("symbol %s listed multiple times", l.SymName(s1))
696 }
697 l.SetAttrOnList(s1, true)
698 textp = append(textp, s1)
699 }
700 }
701 }
702
703
704 for i := 0; uint32(i) < c.seg.nsect; i++ {
705 sect := &c.seg.sect[i]
706 s := sect.sym
707 if s == 0 {
708 continue
709 }
710 macholoadrel(m, sect)
711 if sect.rel == nil {
712 continue
713 }
714
715 sb := l.MakeSymbolUpdater(sect.sym)
716 var rAdd int64
717 for j := uint32(0); j < sect.nreloc; j++ {
718 var (
719 rOff int32
720 rSize uint8
721 rType objabi.RelocType
722 rSym loader.Sym
723 )
724 rel := §.rel[j]
725 if rel.scattered != 0 {
726
727
728 return errorf("%v: unexpected scattered relocation", s)
729 }
730
731 if arch.Family == sys.ARM64 && rel.type_ == MACHO_ARM64_RELOC_ADDEND {
732
733 rAdd = int64(rel.symnum) << 40 >> 40
734 continue
735 }
736
737 rSize = rel.length
738 rType = objabi.MachoRelocOffset + (objabi.RelocType(rel.type_) << 1) + objabi.RelocType(rel.pcrel)
739 rOff = int32(rel.addr)
740
741
742 p := l.Data(s)
743 if arch.Family == sys.AMD64 {
744 if rel.extrn == 0 && rel.type_ == MACHO_X86_64_RELOC_SIGNED {
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760 secaddr := c.seg.sect[rel.symnum-1].addr
761 rAdd = int64(uint64(int64(int32(e.Uint32(p[rOff:])))+int64(rOff)+4) - secaddr)
762 } else {
763 rAdd = int64(int32(e.Uint32(p[rOff:])))
764 }
765 }
766
767
768
769 if arch.Family == sys.AMD64 && rel.extrn == 0 && rel.type_ == MACHO_X86_64_RELOC_UNSIGNED {
770 secaddr := c.seg.sect[rel.symnum-1].addr
771 rAdd -= int64(secaddr)
772 }
773
774 if rel.extrn == 0 {
775 if rel.symnum < 1 || rel.symnum > c.seg.nsect {
776 return errorf("invalid relocation: section reference out of range %d vs %d", rel.symnum, c.seg.nsect)
777 }
778
779 rSym = c.seg.sect[rel.symnum-1].sym
780 if rSym == 0 {
781 return errorf("invalid relocation: %s", c.seg.sect[rel.symnum-1].name)
782 }
783 } else {
784 if rel.symnum >= symtab.nsym {
785 return errorf("invalid relocation: symbol reference out of range")
786 }
787
788 rSym = symtab.sym[rel.symnum].sym
789 }
790
791 r, _ := sb.AddRel(rType)
792 r.SetOff(rOff)
793 r.SetSiz(rSize)
794 r.SetSym(rSym)
795 r.SetAdd(rAdd)
796
797 rAdd = 0
798 }
799 }
800
801 return textp, nil
802 }
803
804 func cstring(x []byte) string {
805 i := bytes.IndexByte(x, '\x00')
806 if i >= 0 {
807 x = x[:i]
808 }
809 return string(x)
810 }
811
View as plain text