1// Copyright 2017 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Package loadmacho implements a Mach-O file reader.
6package loadmacho
7
8import (
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/*
20Derived from Plan 9 from User Space's src/libmach/elf.h, elf.c
21https://github.com/9fans/plan9port/tree/master/src/libmach/
22
23	Copyright © 2004 Russ Cox.
24	Portions Copyright © 2008-2010 Google Inc.
25	Portions Copyright © 2010 The Go Authors.
26
27Permission is hereby granted, free of charge, to any person obtaining a copy
28of this software and associated documentation files (the "Software"), to deal
29in the Software without restriction, including without limitation the rights
30to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
31copies of the Software, and to permit persons to whom the Software is
32furnished to do so, subject to the following conditions:
33
34The above copyright notice and this permission notice shall be included in
35all copies or substantial portions of the Software.
36
37THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
38IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
39FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
40AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
41LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
42OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
43THE SOFTWARE.
44*/
45
46// TODO(crawshaw): de-duplicate these symbols with cmd/link/internal/ld
47const (
48	MACHO_X86_64_RELOC_UNSIGNED = 0
49	MACHO_X86_64_RELOC_SIGNED   = 1
50	MACHO_ARM64_RELOC_ADDEND    = 10
51)
52
53type ldMachoObj struct {
54	f          *bio.Reader
55	base       int64 // off in f where Mach-O begins
56	length     int64 // length of Mach-O
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
68type ldMachoCmd struct {
69	type_ int
70	off   uint32
71	size  uint32
72	seg   ldMachoSeg
73	sym   ldMachoSymtab
74	dsym  ldMachoDysymtab
75}
76
77type 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
90type 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
106type 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
117type ldMachoSymtab struct {
118	symoff  uint32
119	nsym    uint32
120	stroff  uint32
121	strsize uint32
122	str     []byte
123	sym     []ldMachoSym
124}
125
126type 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
136type 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// ldMachoSym.type_
159const (
160	N_EXT  = 0x01
161	N_TYPE = 0x1e
162	N_STAB = 0xe0
163)
164
165// ldMachoSym.desc
166const (
167	N_WEAK_REF = 0x40
168	N_WEAK_DEF = 0x80
169)
170
171const (
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
199func 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			// p+76 is reserved
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
317func 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		// TODO(rsc): Wrong interpretation for big-endian bitfields?
333		if r.addr&0x80000000 != 0 {
334			// scatterbrained relocation
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
363func 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
378func 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// Load the Mach-O file pn from f.
425// Symbols are written into syms, and a slice of the text symbols is returned.
426func 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) // skip reserved word in header
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	// read and parse load commands
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	// load text and data segments into memory.
529	// they are not as small as the load commands, but we'll need
530	// the memory anyway for the symbol images, so we might
531	// as well use one large chunk.
532	if c == nil {
533		return errorf("no load command")
534	}
535
536	if symtab == nil {
537		// our work is done here - no symbols means nothing can refer to this file
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 { // S_ZEROFILL
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	// enter sub-symbols into symbol table.
593	// have to guess sizes from next symbol.
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		// TODO: check sym->type against outer->type.
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 { // undefined
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 // ignore reference to invalid section
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 { // skip empty section (0-sized symbol)
641			l.AddInteriorSym(outer, s)
642		}
643
644		bld.SetValue(int64(machsym.value - sect.addr))
645		if !l.AttrCgoExportDynamic(s) {
646			bld.SetDynimplib("") // satisfy dynimport
647		}
648		if l.SymType(outer) == sym.STEXT {
649			if bld.External() && !bld.DuplicateOK() {
650				return errorf("%v: duplicate symbol definition", s)
651			}
652			bld.SetExternal(true)
653		}
654	}
655
656	// Sort outer lists by address, adding to textp.
657	// This keeps textp in increasing address order.
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			// assign sizes, now that we know symbols in sorted order.
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() == sym.STEXT {
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.RawSymName(s1))
690				}
691				l.SetAttrOnList(s1, true)
692				textp = append(textp, s1)
693			}
694		}
695	}
696
697	// load relocations
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 := &sect.rel[j]
719			if rel.scattered != 0 {
720				// mach-o only uses scattered relocation on 32-bit platforms,
721				// which are no longer supported.
722				return errorf("%v: unexpected scattered relocation", s)
723			}
724
725			if arch.Family == sys.ARM64 && rel.type_ == MACHO_ARM64_RELOC_ADDEND {
726				// Two relocations. This addend will be applied to the next one.
727				rAdd = int64(rel.symnum) << 40 >> 40 // convert unsigned 24-bit to signed 24-bit
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			// Handle X86_64_RELOC_SIGNED referencing a section (rel.extrn == 0).
736			p := l.Data(s)
737			if arch.Family == sys.AMD64 {
738				if rel.extrn == 0 && rel.type_ == MACHO_X86_64_RELOC_SIGNED {
739					// Calculate the addend as the offset into the section.
740					//
741					// The rip-relative offset stored in the object file is encoded
742					// as follows:
743					//
744					//    movsd	0x00000360(%rip),%xmm0
745					//
746					// To get the absolute address of the value this rip-relative address is pointing
747					// to, we must add the address of the next instruction to it. This is done by
748					// taking the address of the relocation and adding 4 to it (since the rip-relative
749					// offset can at most be 32 bits long).  To calculate the offset into the section the
750					// relocation is referencing, we subtract the vaddr of the start of the referenced
751					// section found in the original object file.
752					//
753					// [For future reference, see Darwin's /usr/include/mach-o/x86_64/reloc.h]
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			// An unsigned internal relocation has a value offset
762			// by the section address.
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 // clear rAdd for next iteration
792		}
793
794		sb.SortRelocs()
795	}
796
797	return textp, nil
798}
799
800func 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