aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/oldlink/internal/loadmacho/ldmacho.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/oldlink/internal/loadmacho/ldmacho.go')
-rw-r--r--src/cmd/oldlink/internal/loadmacho/ldmacho.go891
1 files changed, 891 insertions, 0 deletions
diff --git a/src/cmd/oldlink/internal/loadmacho/ldmacho.go b/src/cmd/oldlink/internal/loadmacho/ldmacho.go
new file mode 100644
index 0000000000..8982180b9c
--- /dev/null
+++ b/src/cmd/oldlink/internal/loadmacho/ldmacho.go
@@ -0,0 +1,891 @@
+// 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/oldlink/internal/loader"
+ "cmd/oldlink/internal/sym"
+ "encoding/binary"
+ "fmt"
+ "io"
+ "sort"
+)
+
+/*
+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/internal/ld
+const (
+ MACHO_X86_64_RELOC_UNSIGNED = 0
+ MACHO_X86_64_RELOC_SIGNED = 1
+ MACHO_FAKE_GOTPCREL = 100
+)
+
+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 *sym.Symbol
+ 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 *sym.Symbol
+}
+
+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 = 0x1000007
+ LdMachoCpuMips = 8
+ LdMachoCpu98000 = 10
+ LdMachoCpuHppa = 11
+ LdMachoCpuArm = 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)
+ n := int(sect.nreloc * 8)
+ buf := make([]byte, n)
+ m.f.MustSeek(m.base+int64(sect.reloff), 0)
+ if _, err := io.ReadFull(m.f, buf); 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)
+
+ p := make([]byte, n*4)
+ m.f.MustSeek(m.base+int64(d.indirectsymoff), 0)
+ if _, err := io.ReadFull(m.f, p); 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
+ }
+
+ strbuf := make([]byte, symtab.strsize)
+ m.f.MustSeek(m.base+int64(symtab.stroff), 0)
+ if _, err := io.ReadFull(m.f, strbuf); err != nil {
+ return -1
+ }
+
+ symsize := 12
+ if m.is64 {
+ symsize = 16
+ }
+ n := int(symtab.nsym * uint32(symsize))
+ symbuf := make([]byte, n)
+ m.f.MustSeek(m.base+int64(symtab.symoff), 0)
+ if _, err := io.ReadFull(m.f, symbuf); 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
+}
+
+func Load(l *loader.Loader, arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length int64, pn string) ([]*sym.Symbol, error) {
+ newSym := func(name string, version int) *sym.Symbol {
+ return l.LookupOrCreate(name, version, syms)
+ }
+ return load(arch, syms.IncVersion(), newSym, f, pkg, length, pn)
+}
+
+func LoadOld(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) {
+ return load(arch, syms.IncVersion(), syms.Lookup, f, pkg, length, pn)
+}
+
+// load the Mach-O file pn from f.
+// Symbols are written into syms, and a slice of the text symbols is returned.
+func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Symbol, f *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) {
+ errorf := func(str string, args ...interface{}) ([]*sym.Symbol, error) {
+ return nil, fmt.Errorf("loadmacho: %v: %v", pn, fmt.Sprintf(str, args...))
+ }
+
+ base := f.Offset()
+
+ var hdr [7 * 4]uint8
+ if _, err := io.ReadFull(f, hdr[:]); 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.I386:
+ if e != binary.LittleEndian || m.cputype != LdMachoCpu386 {
+ return errorf("mach-o object but not 386")
+ }
+ }
+
+ m.cmd = make([]ldMachoCmd, ncmd)
+ cmdp := make([]byte, cmdsz)
+ if _, err := io.ReadFull(f, cmdp); 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 := make([]byte, c.seg.filesz)
+ if _, err := io.ReadFull(f, dat); 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 := lookup(name, localSymVersion)
+ if s.Type != 0 {
+ return errorf("duplicate %s/%s", sect.segname, sect.name)
+ }
+
+ if sect.flags&0xff == 1 { // S_ZEROFILL
+ s.P = make([]byte, sect.size)
+ } else {
+ s.P = dat[sect.addr-c.seg.vmaddr:][:sect.size]
+ }
+ s.Size = int64(len(s.P))
+
+ if sect.segname == "__TEXT" {
+ if sect.name == "__text" {
+ s.Type = sym.STEXT
+ } else {
+ s.Type = sym.SRODATA
+ }
+ } else {
+ if sect.name == "__bss" {
+ s.Type = sym.SNOPTRBSS
+ s.P = s.P[:0]
+ } else {
+ s.Type = 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 := lookup(name, v)
+ if machsym.type_&N_EXT == 0 {
+ s.Attr |= sym.AttrDuplicateOK
+ }
+ if machsym.desc&(N_WEAK_REF|N_WEAK_DEF) != 0 {
+ s.Attr |= sym.AttrDuplicateOK
+ }
+ 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]
+ outer := sect.sym
+ if outer == nil {
+ continue // ignore reference to invalid section
+ }
+
+ if s.Outer != nil {
+ if s.Attr.DuplicateOK() {
+ continue
+ }
+ return errorf("duplicate symbol reference: %s in both %s and %s", s.Name, s.Outer.Name, sect.sym.Name)
+ }
+
+ s.Type = outer.Type
+ s.Attr |= sym.AttrSubSymbol
+ s.Sub = outer.Sub
+ outer.Sub = s
+ s.Outer = outer
+ s.Value = int64(machsym.value - sect.addr)
+ if !s.Attr.CgoExportDynamic() {
+ s.SetDynimplib("") // satisfy dynimport
+ }
+ if outer.Type == sym.STEXT {
+ if s.Attr.External() && !s.Attr.DuplicateOK() {
+ return errorf("%v: duplicate symbol definition", s)
+ }
+ s.Attr |= sym.AttrExternal
+ }
+
+ machsym.sym = s
+ }
+
+ // 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 == nil {
+ continue
+ }
+ if s.Sub != nil {
+ s.Sub = sym.SortSub(s.Sub)
+
+ // assign sizes, now that we know symbols in sorted order.
+ for s1 := s.Sub; s1 != nil; s1 = s1.Sub {
+ if s1.Sub != nil {
+ s1.Size = s1.Sub.Value - s1.Value
+ } else {
+ s1.Size = s.Value + s.Size - s1.Value
+ }
+ }
+ }
+
+ if s.Type == sym.STEXT {
+ if s.Attr.OnList() {
+ return errorf("symbol %s listed multiple times", s.Name)
+ }
+ s.Attr |= sym.AttrOnList
+ textp = append(textp, s)
+ for s1 := s.Sub; s1 != nil; s1 = s1.Sub {
+ if s1.Attr.OnList() {
+ return errorf("symbol %s listed multiple times", s1.Name)
+ }
+ s1.Attr |= sym.AttrOnList
+ 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 == nil {
+ continue
+ }
+ macholoadrel(m, sect)
+ if sect.rel == nil {
+ continue
+ }
+ r := make([]sym.Reloc, sect.nreloc)
+ rpi := 0
+ Reloc:
+ for j := uint32(0); j < sect.nreloc; j++ {
+ rp := &r[rpi]
+ rel := &sect.rel[j]
+ if rel.scattered != 0 {
+ if arch.Family != sys.I386 {
+ // mach-o only uses scattered relocation on 32-bit platforms
+ return errorf("%v: unexpected scattered relocation", s)
+ }
+
+ // on 386, rewrite scattered 4/1 relocation and some
+ // scattered 2/1 relocation into the pseudo-pc-relative
+ // reference that it is.
+ // assume that the second in the pair is in this section
+ // and use that as the pc-relative base.
+ if j+1 >= sect.nreloc {
+ return errorf("unsupported scattered relocation %d", int(rel.type_))
+ }
+
+ 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 {
+ return errorf("unsupported scattered relocation %d/%d", int(rel.type_), int(sect.rel[j+1].type_))
+ }
+
+ rp.Siz = rel.length
+ rp.Off = int32(rel.addr)
+
+ // NOTE(rsc): I haven't worked out why (really when)
+ // we should ignore the addend on a
+ // scattered relocation, but it seems that the
+ // common case is we ignore it.
+ // It's likely that this is not strictly correct
+ // and that the math should look something
+ // like the non-scattered case below.
+ rp.Add = 0
+
+ // want to make it pc-relative aka relative to rp->off+4
+ // but the scatter asks for relative to off = sect->rel[j+1].value - sect->addr.
+ // adjust rp->add accordingly.
+ rp.Type = objabi.R_PCREL
+
+ rp.Add += int64(uint64(int64(rp.Off)+4) - (uint64(sect.rel[j+1].value) - sect.addr))
+
+ // now consider the desired symbol.
+ // find the section where it lives.
+ for k := 0; uint32(k) < c.seg.nsect; k++ {
+ ks := &c.seg.sect[k]
+ if ks.addr <= uint64(rel.value) && uint64(rel.value) < ks.addr+ks.size {
+ if ks.sym != nil {
+ rp.Sym = ks.sym
+ rp.Add += int64(uint64(rel.value) - ks.addr)
+ } else if ks.segname == "__IMPORT" && ks.name == "__pointers" {
+ // handle reference to __IMPORT/__pointers.
+ // how much worse can this get?
+ // why are we supporting 386 on the mac anyway?
+ rp.Type = objabi.MachoRelocOffset + MACHO_FAKE_GOTPCREL
+
+ // figure out which pointer this is a reference to.
+ k = int(uint64(ks.res1) + (uint64(rel.value)-ks.addr)/4)
+
+ // load indirect table for __pointers
+ // fetch symbol number
+ if dsymtab == nil || k < 0 || uint32(k) >= dsymtab.nindirectsyms || dsymtab.indir == nil {
+ return errorf("invalid scattered relocation: indirect symbol reference out of range")
+ }
+
+ k = int(dsymtab.indir[k])
+ if k < 0 || uint32(k) >= symtab.nsym {
+ return errorf("invalid scattered relocation: symbol reference out of range")
+ }
+
+ rp.Sym = symtab.sym[k].sym
+ } else {
+ return errorf("unsupported scattered relocation: reference to %s/%s", ks.segname, ks.name)
+ }
+
+ rpi++
+
+ // skip #1 of 2 rel; continue skips #2 of 2.
+ j++
+
+ continue Reloc
+ }
+ }
+
+ return errorf("unsupported scattered relocation: invalid address %#x", rel.addr)
+ }
+
+ rp.Siz = rel.length
+ rp.Type = objabi.MachoRelocOffset + (objabi.RelocType(rel.type_) << 1) + objabi.RelocType(rel.pcrel)
+ rp.Off = int32(rel.addr)
+
+ // Handle X86_64_RELOC_SIGNED referencing a section (rel->extrn == 0).
+ if arch.Family == sys.AMD64 && 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
+
+ rp.Add = int64(uint64(int64(int32(e.Uint32(s.P[rp.Off:])))+int64(rp.Off)+4) - secaddr)
+ } else {
+ rp.Add = int64(int32(e.Uint32(s.P[rp.Off:])))
+ }
+
+ // 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
+ rp.Add -= int64(secaddr)
+ }
+
+ // For i386 Mach-O PC-relative, the addend is written such that
+ // it *is* the PC being subtracted. Use that to make
+ // it match our version of PC-relative.
+ if rel.pcrel != 0 && arch.Family == sys.I386 {
+ rp.Add += int64(rp.Off) + int64(rp.Siz)
+ }
+ 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)
+ }
+
+ rp.Sym = c.seg.sect[rel.symnum-1].sym
+ if rp.Sym == nil {
+ return errorf("invalid relocation: %s", c.seg.sect[rel.symnum-1].name)
+ }
+
+ // References to symbols in other sections
+ // include that information in the addend.
+ // We only care about the delta from the
+ // section base.
+ if arch.Family == sys.I386 {
+ rp.Add -= int64(c.seg.sect[rel.symnum-1].addr)
+ }
+ } else {
+ if rel.symnum >= symtab.nsym {
+ return errorf("invalid relocation: symbol reference out of range")
+ }
+
+ rp.Sym = symtab.sym[rel.symnum].sym
+ }
+
+ rpi++
+ }
+
+ sort.Sort(sym.RelocByOff(r[:rpi]))
+ s.R = r
+ s.R = s.R[:rpi]
+ }
+
+ return textp, nil
+}
+
+func cstring(x []byte) string {
+ i := bytes.IndexByte(x, '\x00')
+ if i >= 0 {
+ x = x[:i]
+ }
+ return string(x)
+}