summaryrefslogtreecommitdiffstats
path: root/src/cmd/link/internal/loadmacho/ldmacho.go
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 13:14:23 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 13:14:23 +0000
commit73df946d56c74384511a194dd01dbe099584fd1a (patch)
treefd0bcea490dd81327ddfbb31e215439672c9a068 /src/cmd/link/internal/loadmacho/ldmacho.go
parentInitial commit. (diff)
downloadgolang-1.16-73df946d56c74384511a194dd01dbe099584fd1a.tar.xz
golang-1.16-73df946d56c74384511a194dd01dbe099584fd1a.zip
Adding upstream version 1.16.10.upstream/1.16.10upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cmd/link/internal/loadmacho/ldmacho.go')
-rw-r--r--src/cmd/link/internal/loadmacho/ldmacho.go806
1 files changed, 806 insertions, 0 deletions
diff --git a/src/cmd/link/internal/loadmacho/ldmacho.go b/src/cmd/link/internal/loadmacho/ldmacho.go
new file mode 100644
index 0000000..6d1d9bb
--- /dev/null
+++ b/src/cmd/link/internal/loadmacho/ldmacho.go
@@ -0,0 +1,806 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package loadmacho implements a Mach-O file reader.
+package loadmacho
+
+import (
+ "bytes"
+ "cmd/internal/bio"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "encoding/binary"
+ "fmt"
+)
+
+/*
+Derived from Plan 9 from User Space's src/libmach/elf.h, elf.c
+http://code.swtch.com/plan9port/src/tip/src/libmach/
+
+ Copyright © 2004 Russ Cox.
+ Portions Copyright © 2008-2010 Google Inc.
+ Portions Copyright © 2010 The Go Authors.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+// TODO(crawshaw): de-duplicate these symbols with cmd/link/internal/ld
+const (
+ MACHO_X86_64_RELOC_UNSIGNED = 0
+ MACHO_X86_64_RELOC_SIGNED = 1
+ MACHO_ARM64_RELOC_ADDEND = 10
+)
+
+type ldMachoObj struct {
+ f *bio.Reader
+ base int64 // off in f where Mach-O begins
+ length int64 // length of Mach-O
+ is64 bool
+ name string
+ e binary.ByteOrder
+ cputype uint
+ subcputype uint
+ filetype uint32
+ flags uint32
+ cmd []ldMachoCmd
+ ncmd uint
+}
+
+type ldMachoCmd struct {
+ type_ int
+ off uint32
+ size uint32
+ seg ldMachoSeg
+ sym ldMachoSymtab
+ dsym ldMachoDysymtab
+}
+
+type ldMachoSeg struct {
+ name string
+ vmaddr uint64
+ vmsize uint64
+ fileoff uint32
+ filesz uint32
+ maxprot uint32
+ initprot uint32
+ nsect uint32
+ flags uint32
+ sect []ldMachoSect
+}
+
+type ldMachoSect struct {
+ name string
+ segname string
+ addr uint64
+ size uint64
+ off uint32
+ align uint32
+ reloff uint32
+ nreloc uint32
+ flags uint32
+ res1 uint32
+ res2 uint32
+ sym loader.Sym
+ rel []ldMachoRel
+}
+
+type ldMachoRel struct {
+ addr uint32
+ symnum uint32
+ pcrel uint8
+ length uint8
+ extrn uint8
+ type_ uint8
+ scattered uint8
+ value uint32
+}
+
+type ldMachoSymtab struct {
+ symoff uint32
+ nsym uint32
+ stroff uint32
+ strsize uint32
+ str []byte
+ sym []ldMachoSym
+}
+
+type ldMachoSym struct {
+ name string
+ type_ uint8
+ sectnum uint8
+ desc uint16
+ kind int8
+ value uint64
+ sym loader.Sym
+}
+
+type ldMachoDysymtab struct {
+ ilocalsym uint32
+ nlocalsym uint32
+ iextdefsym uint32
+ nextdefsym uint32
+ iundefsym uint32
+ nundefsym uint32
+ tocoff uint32
+ ntoc uint32
+ modtaboff uint32
+ nmodtab uint32
+ extrefsymoff uint32
+ nextrefsyms uint32
+ indirectsymoff uint32
+ nindirectsyms uint32
+ extreloff uint32
+ nextrel uint32
+ locreloff uint32
+ nlocrel uint32
+ indir []uint32
+}
+
+// ldMachoSym.type_
+const (
+ N_EXT = 0x01
+ N_TYPE = 0x1e
+ N_STAB = 0xe0
+)
+
+// ldMachoSym.desc
+const (
+ N_WEAK_REF = 0x40
+ N_WEAK_DEF = 0x80
+)
+
+const (
+ LdMachoCpuVax = 1
+ LdMachoCpu68000 = 6
+ LdMachoCpu386 = 7
+ LdMachoCpuAmd64 = 1<<24 | 7
+ LdMachoCpuMips = 8
+ LdMachoCpu98000 = 10
+ LdMachoCpuHppa = 11
+ LdMachoCpuArm = 12
+ LdMachoCpuArm64 = 1<<24 | 12
+ LdMachoCpu88000 = 13
+ LdMachoCpuSparc = 14
+ LdMachoCpu860 = 15
+ LdMachoCpuAlpha = 16
+ LdMachoCpuPower = 18
+ LdMachoCmdSegment = 1
+ LdMachoCmdSymtab = 2
+ LdMachoCmdSymseg = 3
+ LdMachoCmdThread = 4
+ LdMachoCmdDysymtab = 11
+ LdMachoCmdSegment64 = 25
+ LdMachoFileObject = 1
+ LdMachoFileExecutable = 2
+ LdMachoFileFvmlib = 3
+ LdMachoFileCore = 4
+ LdMachoFilePreload = 5
+)
+
+func unpackcmd(p []byte, m *ldMachoObj, c *ldMachoCmd, type_ uint, sz uint) int {
+ e4 := m.e.Uint32
+ e8 := m.e.Uint64
+
+ c.type_ = int(type_)
+ c.size = uint32(sz)
+ switch type_ {
+ default:
+ return -1
+
+ case LdMachoCmdSegment:
+ if sz < 56 {
+ return -1
+ }
+ c.seg.name = cstring(p[8:24])
+ c.seg.vmaddr = uint64(e4(p[24:]))
+ c.seg.vmsize = uint64(e4(p[28:]))
+ c.seg.fileoff = e4(p[32:])
+ c.seg.filesz = e4(p[36:])
+ c.seg.maxprot = e4(p[40:])
+ c.seg.initprot = e4(p[44:])
+ c.seg.nsect = e4(p[48:])
+ c.seg.flags = e4(p[52:])
+ c.seg.sect = make([]ldMachoSect, c.seg.nsect)
+ if uint32(sz) < 56+c.seg.nsect*68 {
+ return -1
+ }
+ p = p[56:]
+ var s *ldMachoSect
+ for i := 0; uint32(i) < c.seg.nsect; i++ {
+ s = &c.seg.sect[i]
+ s.name = cstring(p[0:16])
+ s.segname = cstring(p[16:32])
+ s.addr = uint64(e4(p[32:]))
+ s.size = uint64(e4(p[36:]))
+ s.off = e4(p[40:])
+ s.align = e4(p[44:])
+ s.reloff = e4(p[48:])
+ s.nreloc = e4(p[52:])
+ s.flags = e4(p[56:])
+ s.res1 = e4(p[60:])
+ s.res2 = e4(p[64:])
+ p = p[68:]
+ }
+
+ case LdMachoCmdSegment64:
+ if sz < 72 {
+ return -1
+ }
+ c.seg.name = cstring(p[8:24])
+ c.seg.vmaddr = e8(p[24:])
+ c.seg.vmsize = e8(p[32:])
+ c.seg.fileoff = uint32(e8(p[40:]))
+ c.seg.filesz = uint32(e8(p[48:]))
+ c.seg.maxprot = e4(p[56:])
+ c.seg.initprot = e4(p[60:])
+ c.seg.nsect = e4(p[64:])
+ c.seg.flags = e4(p[68:])
+ c.seg.sect = make([]ldMachoSect, c.seg.nsect)
+ if uint32(sz) < 72+c.seg.nsect*80 {
+ return -1
+ }
+ p = p[72:]
+ var s *ldMachoSect
+ for i := 0; uint32(i) < c.seg.nsect; i++ {
+ s = &c.seg.sect[i]
+ s.name = cstring(p[0:16])
+ s.segname = cstring(p[16:32])
+ s.addr = e8(p[32:])
+ s.size = e8(p[40:])
+ s.off = e4(p[48:])
+ s.align = e4(p[52:])
+ s.reloff = e4(p[56:])
+ s.nreloc = e4(p[60:])
+ s.flags = e4(p[64:])
+ s.res1 = e4(p[68:])
+ s.res2 = e4(p[72:])
+
+ // p+76 is reserved
+ p = p[80:]
+ }
+
+ case LdMachoCmdSymtab:
+ if sz < 24 {
+ return -1
+ }
+ c.sym.symoff = e4(p[8:])
+ c.sym.nsym = e4(p[12:])
+ c.sym.stroff = e4(p[16:])
+ c.sym.strsize = e4(p[20:])
+
+ case LdMachoCmdDysymtab:
+ if sz < 80 {
+ return -1
+ }
+ c.dsym.ilocalsym = e4(p[8:])
+ c.dsym.nlocalsym = e4(p[12:])
+ c.dsym.iextdefsym = e4(p[16:])
+ c.dsym.nextdefsym = e4(p[20:])
+ c.dsym.iundefsym = e4(p[24:])
+ c.dsym.nundefsym = e4(p[28:])
+ c.dsym.tocoff = e4(p[32:])
+ c.dsym.ntoc = e4(p[36:])
+ c.dsym.modtaboff = e4(p[40:])
+ c.dsym.nmodtab = e4(p[44:])
+ c.dsym.extrefsymoff = e4(p[48:])
+ c.dsym.nextrefsyms = e4(p[52:])
+ c.dsym.indirectsymoff = e4(p[56:])
+ c.dsym.nindirectsyms = e4(p[60:])
+ c.dsym.extreloff = e4(p[64:])
+ c.dsym.nextrel = e4(p[68:])
+ c.dsym.locreloff = e4(p[72:])
+ c.dsym.nlocrel = e4(p[76:])
+ }
+
+ return 0
+}
+
+func macholoadrel(m *ldMachoObj, sect *ldMachoSect) int {
+ if sect.rel != nil || sect.nreloc == 0 {
+ return 0
+ }
+ rel := make([]ldMachoRel, sect.nreloc)
+ m.f.MustSeek(m.base+int64(sect.reloff), 0)
+ buf, _, err := m.f.Slice(uint64(sect.nreloc * 8))
+ if err != nil {
+ return -1
+ }
+ for i := uint32(0); i < sect.nreloc; i++ {
+ r := &rel[i]
+ p := buf[i*8:]
+ r.addr = m.e.Uint32(p)
+
+ // TODO(rsc): Wrong interpretation for big-endian bitfields?
+ if r.addr&0x80000000 != 0 {
+ // scatterbrained relocation
+ r.scattered = 1
+
+ v := r.addr >> 24
+ r.addr &= 0xFFFFFF
+ r.type_ = uint8(v & 0xF)
+ v >>= 4
+ r.length = 1 << (v & 3)
+ v >>= 2
+ r.pcrel = uint8(v & 1)
+ r.value = m.e.Uint32(p[4:])
+ } else {
+ v := m.e.Uint32(p[4:])
+ r.symnum = v & 0xFFFFFF
+ v >>= 24
+ r.pcrel = uint8(v & 1)
+ v >>= 1
+ r.length = 1 << (v & 3)
+ v >>= 2
+ r.extrn = uint8(v & 1)
+ v >>= 1
+ r.type_ = uint8(v)
+ }
+ }
+
+ sect.rel = rel
+ return 0
+}
+
+func macholoaddsym(m *ldMachoObj, d *ldMachoDysymtab) int {
+ n := int(d.nindirectsyms)
+ m.f.MustSeek(m.base+int64(d.indirectsymoff), 0)
+ p, _, err := m.f.Slice(uint64(n * 4))
+ if err != nil {
+ return -1
+ }
+
+ d.indir = make([]uint32, n)
+ for i := 0; i < n; i++ {
+ d.indir[i] = m.e.Uint32(p[4*i:])
+ }
+ return 0
+}
+
+func macholoadsym(m *ldMachoObj, symtab *ldMachoSymtab) int {
+ if symtab.sym != nil {
+ return 0
+ }
+
+ m.f.MustSeek(m.base+int64(symtab.stroff), 0)
+ strbuf, _, err := m.f.Slice(uint64(symtab.strsize))
+ if err != nil {
+ return -1
+ }
+
+ symsize := 12
+ if m.is64 {
+ symsize = 16
+ }
+ n := int(symtab.nsym * uint32(symsize))
+ m.f.MustSeek(m.base+int64(symtab.symoff), 0)
+ symbuf, _, err := m.f.Slice(uint64(n))
+ if err != nil {
+ return -1
+ }
+ sym := make([]ldMachoSym, symtab.nsym)
+ p := symbuf
+ for i := uint32(0); i < symtab.nsym; i++ {
+ s := &sym[i]
+ v := m.e.Uint32(p)
+ if v >= symtab.strsize {
+ return -1
+ }
+ s.name = cstring(strbuf[v:])
+ s.type_ = p[4]
+ s.sectnum = p[5]
+ s.desc = m.e.Uint16(p[6:])
+ if m.is64 {
+ s.value = m.e.Uint64(p[8:])
+ } else {
+ s.value = uint64(m.e.Uint32(p[8:]))
+ }
+ p = p[symsize:]
+ }
+
+ symtab.str = strbuf
+ symtab.sym = sym
+ return 0
+}
+
+// Load the Mach-O file pn from f.
+// Symbols are written into syms, and a slice of the text symbols is returned.
+func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader, pkg string, length int64, pn string) (textp []loader.Sym, err error) {
+ errorf := func(str string, args ...interface{}) ([]loader.Sym, error) {
+ return nil, fmt.Errorf("loadmacho: %v: %v", pn, fmt.Sprintf(str, args...))
+ }
+
+ base := f.Offset()
+
+ hdr, _, err := f.Slice(7 * 4)
+ if err != nil {
+ return errorf("reading hdr: %v", err)
+ }
+
+ var e binary.ByteOrder
+ if binary.BigEndian.Uint32(hdr[:])&^1 == 0xFEEDFACE {
+ e = binary.BigEndian
+ } else if binary.LittleEndian.Uint32(hdr[:])&^1 == 0xFEEDFACE {
+ e = binary.LittleEndian
+ } else {
+ return errorf("bad magic - not mach-o file")
+ }
+
+ is64 := e.Uint32(hdr[:]) == 0xFEEDFACF
+ ncmd := e.Uint32(hdr[4*4:])
+ cmdsz := e.Uint32(hdr[5*4:])
+ if ncmd > 0x10000 || cmdsz >= 0x01000000 {
+ return errorf("implausible mach-o header ncmd=%d cmdsz=%d", ncmd, cmdsz)
+ }
+
+ if is64 {
+ f.MustSeek(4, 1) // skip reserved word in header
+ }
+
+ m := &ldMachoObj{
+ f: f,
+ e: e,
+ cputype: uint(e.Uint32(hdr[1*4:])),
+ subcputype: uint(e.Uint32(hdr[2*4:])),
+ filetype: e.Uint32(hdr[3*4:]),
+ ncmd: uint(ncmd),
+ flags: e.Uint32(hdr[6*4:]),
+ is64: is64,
+ base: base,
+ length: length,
+ name: pn,
+ }
+
+ switch arch.Family {
+ default:
+ return errorf("mach-o %s unimplemented", arch.Name)
+ case sys.AMD64:
+ if e != binary.LittleEndian || m.cputype != LdMachoCpuAmd64 {
+ return errorf("mach-o object but not amd64")
+ }
+ case sys.ARM64:
+ if e != binary.LittleEndian || m.cputype != LdMachoCpuArm64 {
+ return errorf("mach-o object but not arm64")
+ }
+ }
+
+ m.cmd = make([]ldMachoCmd, ncmd)
+ cmdp, _, err := f.Slice(uint64(cmdsz))
+ if err != nil {
+ return errorf("reading cmds: %v", err)
+ }
+
+ // read and parse load commands
+ var c *ldMachoCmd
+
+ var symtab *ldMachoSymtab
+ var dsymtab *ldMachoDysymtab
+
+ off := uint32(len(hdr))
+ for i := uint32(0); i < ncmd; i++ {
+ ty := e.Uint32(cmdp)
+ sz := e.Uint32(cmdp[4:])
+ m.cmd[i].off = off
+ unpackcmd(cmdp, m, &m.cmd[i], uint(ty), uint(sz))
+ cmdp = cmdp[sz:]
+ off += sz
+ if ty == LdMachoCmdSymtab {
+ if symtab != nil {
+ return errorf("multiple symbol tables")
+ }
+
+ symtab = &m.cmd[i].sym
+ macholoadsym(m, symtab)
+ }
+
+ if ty == LdMachoCmdDysymtab {
+ dsymtab = &m.cmd[i].dsym
+ macholoaddsym(m, dsymtab)
+ }
+
+ if (is64 && ty == LdMachoCmdSegment64) || (!is64 && ty == LdMachoCmdSegment) {
+ if c != nil {
+ return errorf("multiple load commands")
+ }
+
+ c = &m.cmd[i]
+ }
+ }
+
+ // load text and data segments into memory.
+ // they are not as small as the load commands, but we'll need
+ // the memory anyway for the symbol images, so we might
+ // as well use one large chunk.
+ if c == nil {
+ return errorf("no load command")
+ }
+
+ if symtab == nil {
+ // our work is done here - no symbols means nothing can refer to this file
+ return
+ }
+
+ if int64(c.seg.fileoff+c.seg.filesz) >= length {
+ return errorf("load segment out of range")
+ }
+
+ f.MustSeek(m.base+int64(c.seg.fileoff), 0)
+ dat, readOnly, err := f.Slice(uint64(c.seg.filesz))
+ if err != nil {
+ return errorf("cannot load object data: %v", err)
+ }
+
+ for i := uint32(0); i < c.seg.nsect; i++ {
+ sect := &c.seg.sect[i]
+ if sect.segname != "__TEXT" && sect.segname != "__DATA" {
+ continue
+ }
+ if sect.name == "__eh_frame" {
+ continue
+ }
+ name := fmt.Sprintf("%s(%s/%s)", pkg, sect.segname, sect.name)
+ s := l.LookupOrCreateSym(name, localSymVersion)
+ bld := l.MakeSymbolUpdater(s)
+ if bld.Type() != 0 {
+ return errorf("duplicate %s/%s", sect.segname, sect.name)
+ }
+
+ if sect.flags&0xff == 1 { // S_ZEROFILL
+ bld.SetData(make([]byte, sect.size))
+ } else {
+ bld.SetReadOnly(readOnly)
+ bld.SetData(dat[sect.addr-c.seg.vmaddr:][:sect.size])
+ }
+ bld.SetSize(int64(len(bld.Data())))
+
+ if sect.segname == "__TEXT" {
+ if sect.name == "__text" {
+ bld.SetType(sym.STEXT)
+ } else {
+ bld.SetType(sym.SRODATA)
+ }
+ } else {
+ if sect.name == "__bss" {
+ bld.SetType(sym.SNOPTRBSS)
+ bld.SetData(nil)
+ } else {
+ bld.SetType(sym.SNOPTRDATA)
+ }
+ }
+
+ sect.sym = s
+ }
+
+ // enter sub-symbols into symbol table.
+ // have to guess sizes from next symbol.
+ for i := uint32(0); i < symtab.nsym; i++ {
+ machsym := &symtab.sym[i]
+ if machsym.type_&N_STAB != 0 {
+ continue
+ }
+
+ // TODO: check sym->type against outer->type.
+ name := machsym.name
+
+ if name[0] == '_' && name[1] != '\x00' {
+ name = name[1:]
+ }
+ v := 0
+ if machsym.type_&N_EXT == 0 {
+ v = localSymVersion
+ }
+ s := l.LookupOrCreateSym(name, v)
+ if machsym.type_&N_EXT == 0 {
+ l.SetAttrDuplicateOK(s, true)
+ }
+ if machsym.desc&(N_WEAK_REF|N_WEAK_DEF) != 0 {
+ l.SetAttrDuplicateOK(s, true)
+ }
+ machsym.sym = s
+ if machsym.sectnum == 0 { // undefined
+ continue
+ }
+ if uint32(machsym.sectnum) > c.seg.nsect {
+ return errorf("reference to invalid section %d", machsym.sectnum)
+ }
+
+ sect := &c.seg.sect[machsym.sectnum-1]
+ bld := l.MakeSymbolUpdater(s)
+ outer := sect.sym
+ if outer == 0 {
+ continue // ignore reference to invalid section
+ }
+
+ if osym := l.OuterSym(s); osym != 0 {
+ if l.AttrDuplicateOK(s) {
+ continue
+ }
+ return errorf("duplicate symbol reference: %s in both %s and %s", l.SymName(s), l.SymName(osym), l.SymName(sect.sym))
+ }
+
+ bld.SetType(l.SymType(outer))
+ if l.SymSize(outer) != 0 { // skip empty section (0-sized symbol)
+ l.AddInteriorSym(outer, s)
+ }
+
+ bld.SetValue(int64(machsym.value - sect.addr))
+ if !l.AttrCgoExportDynamic(s) {
+ bld.SetDynimplib("") // satisfy dynimport
+ }
+ if l.SymType(outer) == sym.STEXT {
+ if bld.External() && !bld.DuplicateOK() {
+ return errorf("%v: duplicate symbol definition", s)
+ }
+ bld.SetExternal(true)
+ }
+ }
+
+ // Sort outer lists by address, adding to textp.
+ // This keeps textp in increasing address order.
+ for i := 0; uint32(i) < c.seg.nsect; i++ {
+ sect := &c.seg.sect[i]
+ s := sect.sym
+ if s == 0 {
+ continue
+ }
+ bld := l.MakeSymbolUpdater(s)
+ if bld.SubSym() != 0 {
+
+ bld.SortSub()
+
+ // assign sizes, now that we know symbols in sorted order.
+ for s1 := bld.Sub(); s1 != 0; s1 = l.SubSym(s1) {
+ s1Bld := l.MakeSymbolUpdater(s1)
+ if sub := l.SubSym(s1); sub != 0 {
+ s1Bld.SetSize(l.SymValue(sub) - l.SymValue(s1))
+ } else {
+ dlen := int64(len(l.Data(s)))
+ s1Bld.SetSize(l.SymValue(s) + dlen - l.SymValue(s1))
+ }
+ }
+ }
+
+ if bld.Type() == sym.STEXT {
+ if bld.OnList() {
+ return errorf("symbol %s listed multiple times", bld.Name())
+ }
+ bld.SetOnList(true)
+ textp = append(textp, s)
+ for s1 := bld.Sub(); s1 != 0; s1 = l.SubSym(s1) {
+ if l.AttrOnList(s1) {
+ return errorf("symbol %s listed multiple times", l.RawSymName(s1))
+ }
+ l.SetAttrOnList(s1, true)
+ textp = append(textp, s1)
+ }
+ }
+ }
+
+ // load relocations
+ for i := 0; uint32(i) < c.seg.nsect; i++ {
+ sect := &c.seg.sect[i]
+ s := sect.sym
+ if s == 0 {
+ continue
+ }
+ macholoadrel(m, sect)
+ if sect.rel == nil {
+ continue
+ }
+
+ sb := l.MakeSymbolUpdater(sect.sym)
+ var rAdd int64
+ for j := uint32(0); j < sect.nreloc; j++ {
+ var (
+ rOff int32
+ rSize uint8
+ rType objabi.RelocType
+ rSym loader.Sym
+ )
+ rel := &sect.rel[j]
+ if rel.scattered != 0 {
+ // mach-o only uses scattered relocation on 32-bit platforms,
+ // which are no longer supported.
+ return errorf("%v: unexpected scattered relocation", s)
+ }
+
+ if arch.Family == sys.ARM64 && rel.type_ == MACHO_ARM64_RELOC_ADDEND {
+ // Two relocations. This addend will be applied to the next one.
+ rAdd = int64(rel.symnum) << 40 >> 40 // convert unsigned 24-bit to signed 24-bit
+ continue
+ }
+
+ rSize = rel.length
+ rType = objabi.MachoRelocOffset + (objabi.RelocType(rel.type_) << 1) + objabi.RelocType(rel.pcrel)
+ rOff = int32(rel.addr)
+
+ // Handle X86_64_RELOC_SIGNED referencing a section (rel.extrn == 0).
+ p := l.Data(s)
+ if arch.Family == sys.AMD64 {
+ if rel.extrn == 0 && rel.type_ == MACHO_X86_64_RELOC_SIGNED {
+ // Calculate the addend as the offset into the section.
+ //
+ // The rip-relative offset stored in the object file is encoded
+ // as follows:
+ //
+ // movsd 0x00000360(%rip),%xmm0
+ //
+ // To get the absolute address of the value this rip-relative address is pointing
+ // to, we must add the address of the next instruction to it. This is done by
+ // taking the address of the relocation and adding 4 to it (since the rip-relative
+ // offset can at most be 32 bits long). To calculate the offset into the section the
+ // relocation is referencing, we subtract the vaddr of the start of the referenced
+ // section found in the original object file.
+ //
+ // [For future reference, see Darwin's /usr/include/mach-o/x86_64/reloc.h]
+ secaddr := c.seg.sect[rel.symnum-1].addr
+ rAdd = int64(uint64(int64(int32(e.Uint32(p[rOff:])))+int64(rOff)+4) - secaddr)
+ } else {
+ rAdd = int64(int32(e.Uint32(p[rOff:])))
+ }
+ }
+
+ // An unsigned internal relocation has a value offset
+ // by the section address.
+ if arch.Family == sys.AMD64 && rel.extrn == 0 && rel.type_ == MACHO_X86_64_RELOC_UNSIGNED {
+ secaddr := c.seg.sect[rel.symnum-1].addr
+ rAdd -= int64(secaddr)
+ }
+
+ if rel.extrn == 0 {
+ if rel.symnum < 1 || rel.symnum > c.seg.nsect {
+ return errorf("invalid relocation: section reference out of range %d vs %d", rel.symnum, c.seg.nsect)
+ }
+
+ rSym = c.seg.sect[rel.symnum-1].sym
+ if rSym == 0 {
+ return errorf("invalid relocation: %s", c.seg.sect[rel.symnum-1].name)
+ }
+ } else {
+ if rel.symnum >= symtab.nsym {
+ return errorf("invalid relocation: symbol reference out of range")
+ }
+
+ rSym = symtab.sym[rel.symnum].sym
+ }
+
+ r, _ := sb.AddRel(rType)
+ r.SetOff(rOff)
+ r.SetSiz(rSize)
+ r.SetSym(rSym)
+ r.SetAdd(rAdd)
+
+ rAdd = 0 // clear rAdd for next iteration
+ }
+
+ sb.SortRelocs()
+ }
+
+ return textp, nil
+}
+
+func cstring(x []byte) string {
+ i := bytes.IndexByte(x, '\x00')
+ if i >= 0 {
+ x = x[:i]
+ }
+ return string(x)
+}