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 ...interface{}) ([]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
574 if sect.segname == "__TEXT" {
575 if sect.name == "__text" {
576 bld.SetType(sym.STEXT)
577 } else {
578 bld.SetType(sym.SRODATA)
579 }
580 } else {
581 if sect.name == "__bss" {
582 bld.SetType(sym.SNOPTRBSS)
583 bld.SetData(nil)
584 } else {
585 bld.SetType(sym.SNOPTRDATA)
586 }
587 }
588
589 sect.sym = s
590 }
591
592
593
594 for i := uint32(0); i < symtab.nsym; i++ {
595 machsym := &symtab.sym[i]
596 if machsym.type_&N_STAB != 0 {
597 continue
598 }
599
600
601 name := machsym.name
602
603 if name[0] == '_' && name[1] != '\x00' {
604 name = name[1:]
605 }
606 v := 0
607 if machsym.type_&N_EXT == 0 {
608 v = localSymVersion
609 }
610 s := l.LookupOrCreateCgoExport(name, v)
611 if machsym.type_&N_EXT == 0 {
612 l.SetAttrDuplicateOK(s, true)
613 }
614 if machsym.desc&(N_WEAK_REF|N_WEAK_DEF) != 0 {
615 l.SetAttrDuplicateOK(s, true)
616 }
617 machsym.sym = s
618 if machsym.sectnum == 0 {
619 continue
620 }
621 if uint32(machsym.sectnum) > c.seg.nsect {
622 return errorf("reference to invalid section %d", machsym.sectnum)
623 }
624
625 sect := &c.seg.sect[machsym.sectnum-1]
626 bld := l.MakeSymbolUpdater(s)
627 outer := sect.sym
628 if outer == 0 {
629 continue
630 }
631
632 if osym := l.OuterSym(s); osym != 0 {
633 if l.AttrDuplicateOK(s) {
634 continue
635 }
636 return errorf("duplicate symbol reference: %s in both %s and %s", l.SymName(s), l.SymName(osym), l.SymName(sect.sym))
637 }
638
639 bld.SetType(l.SymType(outer))
640 if l.SymSize(outer) != 0 {
641 l.AddInteriorSym(outer, s)
642 }
643
644 bld.SetValue(int64(machsym.value - sect.addr))
645 if !l.AttrCgoExportDynamic(s) {
646 bld.SetDynimplib("")
647 }
648 if l.SymType(outer).IsText() {
649 if bld.External() && !bld.DuplicateOK() {
650 return errorf("%v: duplicate symbol definition", s)
651 }
652 bld.SetExternal(true)
653 }
654 }
655
656
657
658 for i := 0; uint32(i) < c.seg.nsect; i++ {
659 sect := &c.seg.sect[i]
660 s := sect.sym
661 if s == 0 {
662 continue
663 }
664 bld := l.MakeSymbolUpdater(s)
665 if bld.SubSym() != 0 {
666
667 bld.SortSub()
668
669
670 for s1 := bld.Sub(); s1 != 0; s1 = l.SubSym(s1) {
671 s1Bld := l.MakeSymbolUpdater(s1)
672 if sub := l.SubSym(s1); sub != 0 {
673 s1Bld.SetSize(l.SymValue(sub) - l.SymValue(s1))
674 } else {
675 dlen := int64(len(l.Data(s)))
676 s1Bld.SetSize(l.SymValue(s) + dlen - l.SymValue(s1))
677 }
678 }
679 }
680
681 if bld.Type().IsText() {
682 if bld.OnList() {
683 return errorf("symbol %s listed multiple times", bld.Name())
684 }
685 bld.SetOnList(true)
686 textp = append(textp, s)
687 for s1 := bld.Sub(); s1 != 0; s1 = l.SubSym(s1) {
688 if l.AttrOnList(s1) {
689 return errorf("symbol %s listed multiple times", l.SymName(s1))
690 }
691 l.SetAttrOnList(s1, true)
692 textp = append(textp, s1)
693 }
694 }
695 }
696
697
698 for i := 0; uint32(i) < c.seg.nsect; i++ {
699 sect := &c.seg.sect[i]
700 s := sect.sym
701 if s == 0 {
702 continue
703 }
704 macholoadrel(m, sect)
705 if sect.rel == nil {
706 continue
707 }
708
709 sb := l.MakeSymbolUpdater(sect.sym)
710 var rAdd int64
711 for j := uint32(0); j < sect.nreloc; j++ {
712 var (
713 rOff int32
714 rSize uint8
715 rType objabi.RelocType
716 rSym loader.Sym
717 )
718 rel := §.rel[j]
719 if rel.scattered != 0 {
720
721
722 return errorf("%v: unexpected scattered relocation", s)
723 }
724
725 if arch.Family == sys.ARM64 && rel.type_ == MACHO_ARM64_RELOC_ADDEND {
726
727 rAdd = int64(rel.symnum) << 40 >> 40
728 continue
729 }
730
731 rSize = rel.length
732 rType = objabi.MachoRelocOffset + (objabi.RelocType(rel.type_) << 1) + objabi.RelocType(rel.pcrel)
733 rOff = int32(rel.addr)
734
735
736 p := l.Data(s)
737 if arch.Family == sys.AMD64 {
738 if rel.extrn == 0 && rel.type_ == MACHO_X86_64_RELOC_SIGNED {
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754 secaddr := c.seg.sect[rel.symnum-1].addr
755 rAdd = int64(uint64(int64(int32(e.Uint32(p[rOff:])))+int64(rOff)+4) - secaddr)
756 } else {
757 rAdd = int64(int32(e.Uint32(p[rOff:])))
758 }
759 }
760
761
762
763 if arch.Family == sys.AMD64 && rel.extrn == 0 && rel.type_ == MACHO_X86_64_RELOC_UNSIGNED {
764 secaddr := c.seg.sect[rel.symnum-1].addr
765 rAdd -= int64(secaddr)
766 }
767
768 if rel.extrn == 0 {
769 if rel.symnum < 1 || rel.symnum > c.seg.nsect {
770 return errorf("invalid relocation: section reference out of range %d vs %d", rel.symnum, c.seg.nsect)
771 }
772
773 rSym = c.seg.sect[rel.symnum-1].sym
774 if rSym == 0 {
775 return errorf("invalid relocation: %s", c.seg.sect[rel.symnum-1].name)
776 }
777 } else {
778 if rel.symnum >= symtab.nsym {
779 return errorf("invalid relocation: symbol reference out of range")
780 }
781
782 rSym = symtab.sym[rel.symnum].sym
783 }
784
785 r, _ := sb.AddRel(rType)
786 r.SetOff(rOff)
787 r.SetSiz(rSize)
788 r.SetSym(rSym)
789 r.SetAdd(rAdd)
790
791 rAdd = 0
792 }
793
794 sb.SortRelocs()
795 }
796
797 return textp, nil
798 }
799
800 func cstring(x []byte) string {
801 i := bytes.IndexByte(x, '\x00')
802 if i >= 0 {
803 x = x[:i]
804 }
805 return string(x)
806 }
807
View as plain text