github.com/rsc/tmp@v0.0.0-20240517235954-6deaab19748b/bootstrap/internal/ld/ldmacho.go (about)

     1  // Do not edit. Bootstrap copy of /Users/rsc/g/go/src/cmd/internal/ld/ldmacho.go
     2  
     3  package ld
     4  
     5  import (
     6  	"rsc.io/tmp/bootstrap/internal/obj"
     7  	"encoding/binary"
     8  	"fmt"
     9  	"log"
    10  	"sort"
    11  )
    12  
    13  /*
    14  Derived from Plan 9 from User Space's src/libmach/elf.h, elf.c
    15  http://code.swtch.com/plan9port/src/tip/src/libmach/
    16  
    17  	Copyright © 2004 Russ Cox.
    18  	Portions Copyright © 2008-2010 Google Inc.
    19  	Portions Copyright © 2010 The Go Authors.
    20  
    21  Permission is hereby granted, free of charge, to any person obtaining a copy
    22  of this software and associated documentation files (the "Software"), to deal
    23  in the Software without restriction, including without limitation the rights
    24  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    25  copies of the Software, and to permit persons to whom the Software is
    26  furnished to do so, subject to the following conditions:
    27  
    28  The above copyright notice and this permission notice shall be included in
    29  all copies or substantial portions of the Software.
    30  
    31  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    32  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    33  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
    34  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    35  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    36  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    37  THE SOFTWARE.
    38  */
    39  const (
    40  	N_EXT  = 0x01
    41  	N_TYPE = 0x1e
    42  	N_STAB = 0xe0
    43  )
    44  
    45  type LdMachoObj struct {
    46  	f          *Biobuf
    47  	base       int64 // off in f where Mach-O begins
    48  	length     int64 // length of Mach-O
    49  	is64       bool
    50  	name       string
    51  	e          binary.ByteOrder
    52  	cputype    uint
    53  	subcputype uint
    54  	filetype   uint32
    55  	flags      uint32
    56  	cmd        []LdMachoCmd
    57  	ncmd       uint
    58  }
    59  
    60  type LdMachoCmd struct {
    61  	type_ int
    62  	off   uint32
    63  	size  uint32
    64  	seg   LdMachoSeg
    65  	sym   LdMachoSymtab
    66  	dsym  LdMachoDysymtab
    67  }
    68  
    69  type LdMachoSeg struct {
    70  	name     string
    71  	vmaddr   uint64
    72  	vmsize   uint64
    73  	fileoff  uint32
    74  	filesz   uint32
    75  	maxprot  uint32
    76  	initprot uint32
    77  	nsect    uint32
    78  	flags    uint32
    79  	sect     []LdMachoSect
    80  }
    81  
    82  type LdMachoSect struct {
    83  	name    string
    84  	segname string
    85  	addr    uint64
    86  	size    uint64
    87  	off     uint32
    88  	align   uint32
    89  	reloff  uint32
    90  	nreloc  uint32
    91  	flags   uint32
    92  	res1    uint32
    93  	res2    uint32
    94  	sym     *LSym
    95  	rel     []LdMachoRel
    96  }
    97  
    98  type LdMachoRel struct {
    99  	addr      uint32
   100  	symnum    uint32
   101  	pcrel     uint8
   102  	length    uint8
   103  	extrn     uint8
   104  	type_     uint8
   105  	scattered uint8
   106  	value     uint32
   107  }
   108  
   109  type LdMachoSymtab struct {
   110  	symoff  uint32
   111  	nsym    uint32
   112  	stroff  uint32
   113  	strsize uint32
   114  	str     []byte
   115  	sym     []LdMachoSym
   116  }
   117  
   118  type LdMachoSym struct {
   119  	name    string
   120  	type_   uint8
   121  	sectnum uint8
   122  	desc    uint16
   123  	kind    int8
   124  	value   uint64
   125  	sym     *LSym
   126  }
   127  
   128  type LdMachoDysymtab struct {
   129  	ilocalsym      uint32
   130  	nlocalsym      uint32
   131  	iextdefsym     uint32
   132  	nextdefsym     uint32
   133  	iundefsym      uint32
   134  	nundefsym      uint32
   135  	tocoff         uint32
   136  	ntoc           uint32
   137  	modtaboff      uint32
   138  	nmodtab        uint32
   139  	extrefsymoff   uint32
   140  	nextrefsyms    uint32
   141  	indirectsymoff uint32
   142  	nindirectsyms  uint32
   143  	extreloff      uint32
   144  	nextrel        uint32
   145  	locreloff      uint32
   146  	nlocrel        uint32
   147  	indir          []uint32
   148  }
   149  
   150  const (
   151  	LdMachoCpuVax         = 1
   152  	LdMachoCpu68000       = 6
   153  	LdMachoCpu386         = 7
   154  	LdMachoCpuAmd64       = 0x1000007
   155  	LdMachoCpuMips        = 8
   156  	LdMachoCpu98000       = 10
   157  	LdMachoCpuHppa        = 11
   158  	LdMachoCpuArm         = 12
   159  	LdMachoCpu88000       = 13
   160  	LdMachoCpuSparc       = 14
   161  	LdMachoCpu860         = 15
   162  	LdMachoCpuAlpha       = 16
   163  	LdMachoCpuPower       = 18
   164  	LdMachoCmdSegment     = 1
   165  	LdMachoCmdSymtab      = 2
   166  	LdMachoCmdSymseg      = 3
   167  	LdMachoCmdThread      = 4
   168  	LdMachoCmdDysymtab    = 11
   169  	LdMachoCmdSegment64   = 25
   170  	LdMachoFileObject     = 1
   171  	LdMachoFileExecutable = 2
   172  	LdMachoFileFvmlib     = 3
   173  	LdMachoFileCore       = 4
   174  	LdMachoFilePreload    = 5
   175  )
   176  
   177  func unpackcmd(p []byte, m *LdMachoObj, c *LdMachoCmd, type_ uint, sz uint) int {
   178  	e4 := m.e.Uint32
   179  	e8 := m.e.Uint64
   180  
   181  	c.type_ = int(type_)
   182  	c.size = uint32(sz)
   183  	switch type_ {
   184  	default:
   185  		return -1
   186  
   187  	case LdMachoCmdSegment:
   188  		if sz < 56 {
   189  			return -1
   190  		}
   191  		c.seg.name = cstring(p[8:24])
   192  		c.seg.vmaddr = uint64(e4(p[24:]))
   193  		c.seg.vmsize = uint64(e4(p[28:]))
   194  		c.seg.fileoff = e4(p[32:])
   195  		c.seg.filesz = e4(p[36:])
   196  		c.seg.maxprot = e4(p[40:])
   197  		c.seg.initprot = e4(p[44:])
   198  		c.seg.nsect = e4(p[48:])
   199  		c.seg.flags = e4(p[52:])
   200  		c.seg.sect = make([]LdMachoSect, c.seg.nsect)
   201  		if uint32(sz) < 56+c.seg.nsect*68 {
   202  			return -1
   203  		}
   204  		p = p[56:]
   205  		var s *LdMachoSect
   206  		for i := 0; uint32(i) < c.seg.nsect; i++ {
   207  			s = &c.seg.sect[i]
   208  			s.name = cstring(p[0:16])
   209  			s.segname = cstring(p[16:32])
   210  			s.addr = uint64(e4(p[32:]))
   211  			s.size = uint64(e4(p[36:]))
   212  			s.off = e4(p[40:])
   213  			s.align = e4(p[44:])
   214  			s.reloff = e4(p[48:])
   215  			s.nreloc = e4(p[52:])
   216  			s.flags = e4(p[56:])
   217  			s.res1 = e4(p[60:])
   218  			s.res2 = e4(p[64:])
   219  			p = p[68:]
   220  		}
   221  
   222  	case LdMachoCmdSegment64:
   223  		if sz < 72 {
   224  			return -1
   225  		}
   226  		c.seg.name = cstring(p[8:24])
   227  		c.seg.vmaddr = e8(p[24:])
   228  		c.seg.vmsize = e8(p[32:])
   229  		c.seg.fileoff = uint32(e8(p[40:]))
   230  		c.seg.filesz = uint32(e8(p[48:]))
   231  		c.seg.maxprot = e4(p[56:])
   232  		c.seg.initprot = e4(p[60:])
   233  		c.seg.nsect = e4(p[64:])
   234  		c.seg.flags = e4(p[68:])
   235  		c.seg.sect = make([]LdMachoSect, c.seg.nsect)
   236  		if uint32(sz) < 72+c.seg.nsect*80 {
   237  			return -1
   238  		}
   239  		p = p[72:]
   240  		var s *LdMachoSect
   241  		for i := 0; uint32(i) < c.seg.nsect; i++ {
   242  			s = &c.seg.sect[i]
   243  			s.name = cstring(p[0:16])
   244  			s.segname = cstring(p[16:32])
   245  			s.addr = e8(p[32:])
   246  			s.size = e8(p[40:])
   247  			s.off = e4(p[48:])
   248  			s.align = e4(p[52:])
   249  			s.reloff = e4(p[56:])
   250  			s.nreloc = e4(p[60:])
   251  			s.flags = e4(p[64:])
   252  			s.res1 = e4(p[68:])
   253  			s.res2 = e4(p[72:])
   254  
   255  			// p+76 is reserved
   256  			p = p[80:]
   257  		}
   258  
   259  	case LdMachoCmdSymtab:
   260  		if sz < 24 {
   261  			return -1
   262  		}
   263  		c.sym.symoff = e4(p[8:])
   264  		c.sym.nsym = e4(p[12:])
   265  		c.sym.stroff = e4(p[16:])
   266  		c.sym.strsize = e4(p[20:])
   267  
   268  	case LdMachoCmdDysymtab:
   269  		if sz < 80 {
   270  			return -1
   271  		}
   272  		c.dsym.ilocalsym = e4(p[8:])
   273  		c.dsym.nlocalsym = e4(p[12:])
   274  		c.dsym.iextdefsym = e4(p[16:])
   275  		c.dsym.nextdefsym = e4(p[20:])
   276  		c.dsym.iundefsym = e4(p[24:])
   277  		c.dsym.nundefsym = e4(p[28:])
   278  		c.dsym.tocoff = e4(p[32:])
   279  		c.dsym.ntoc = e4(p[36:])
   280  		c.dsym.modtaboff = e4(p[40:])
   281  		c.dsym.nmodtab = e4(p[44:])
   282  		c.dsym.extrefsymoff = e4(p[48:])
   283  		c.dsym.nextrefsyms = e4(p[52:])
   284  		c.dsym.indirectsymoff = e4(p[56:])
   285  		c.dsym.nindirectsyms = e4(p[60:])
   286  		c.dsym.extreloff = e4(p[64:])
   287  		c.dsym.nextrel = e4(p[68:])
   288  		c.dsym.locreloff = e4(p[72:])
   289  		c.dsym.nlocrel = e4(p[76:])
   290  	}
   291  
   292  	return 0
   293  }
   294  
   295  func macholoadrel(m *LdMachoObj, sect *LdMachoSect) int {
   296  	if sect.rel != nil || sect.nreloc == 0 {
   297  		return 0
   298  	}
   299  	rel := make([]LdMachoRel, sect.nreloc)
   300  	n := int(sect.nreloc * 8)
   301  	buf := make([]byte, n)
   302  	if Bseek(m.f, m.base+int64(sect.reloff), 0) < 0 || Bread(m.f, buf) != n {
   303  		return -1
   304  	}
   305  	var p []byte
   306  	var r *LdMachoRel
   307  	var v uint32
   308  	for i := 0; uint32(i) < sect.nreloc; i++ {
   309  		r = &rel[i]
   310  		p = buf[i*8:]
   311  		r.addr = m.e.Uint32(p)
   312  
   313  		// TODO(rsc): Wrong interpretation for big-endian bitfields?
   314  		if r.addr&0x80000000 != 0 {
   315  			// scatterbrained relocation
   316  			r.scattered = 1
   317  
   318  			v = r.addr >> 24
   319  			r.addr &= 0xFFFFFF
   320  			r.type_ = uint8(v & 0xF)
   321  			v >>= 4
   322  			r.length = 1 << (v & 3)
   323  			v >>= 2
   324  			r.pcrel = uint8(v & 1)
   325  			r.value = m.e.Uint32(p[4:])
   326  		} else {
   327  			v = m.e.Uint32(p[4:])
   328  			r.symnum = v & 0xFFFFFF
   329  			v >>= 24
   330  			r.pcrel = uint8(v & 1)
   331  			v >>= 1
   332  			r.length = 1 << (v & 3)
   333  			v >>= 2
   334  			r.extrn = uint8(v & 1)
   335  			v >>= 1
   336  			r.type_ = uint8(v)
   337  		}
   338  	}
   339  
   340  	sect.rel = rel
   341  	return 0
   342  }
   343  
   344  func macholoaddsym(m *LdMachoObj, d *LdMachoDysymtab) int {
   345  	n := int(d.nindirectsyms)
   346  
   347  	p := make([]byte, n*4)
   348  	if Bseek(m.f, m.base+int64(d.indirectsymoff), 0) < 0 || Bread(m.f, p) != len(p) {
   349  		return -1
   350  	}
   351  
   352  	d.indir = make([]uint32, n)
   353  	for i := 0; i < n; i++ {
   354  		d.indir[i] = m.e.Uint32(p[4*i:])
   355  	}
   356  	return 0
   357  }
   358  
   359  func macholoadsym(m *LdMachoObj, symtab *LdMachoSymtab) int {
   360  	if symtab.sym != nil {
   361  		return 0
   362  	}
   363  
   364  	strbuf := make([]byte, symtab.strsize)
   365  	if Bseek(m.f, m.base+int64(symtab.stroff), 0) < 0 || Bread(m.f, strbuf) != len(strbuf) {
   366  		return -1
   367  	}
   368  
   369  	symsize := 12
   370  	if m.is64 {
   371  		symsize = 16
   372  	}
   373  	n := int(symtab.nsym * uint32(symsize))
   374  	symbuf := make([]byte, n)
   375  	if Bseek(m.f, m.base+int64(symtab.symoff), 0) < 0 || Bread(m.f, symbuf) != len(symbuf) {
   376  		return -1
   377  	}
   378  	sym := make([]LdMachoSym, symtab.nsym)
   379  	p := symbuf
   380  	var s *LdMachoSym
   381  	var v uint32
   382  	for i := 0; uint32(i) < symtab.nsym; i++ {
   383  		s = &sym[i]
   384  		v = m.e.Uint32(p)
   385  		if v >= symtab.strsize {
   386  			return -1
   387  		}
   388  		s.name = cstring(strbuf[v:])
   389  		s.type_ = uint8(p[4])
   390  		s.sectnum = uint8(p[5])
   391  		s.desc = m.e.Uint16(p[6:])
   392  		if m.is64 {
   393  			s.value = m.e.Uint64(p[8:])
   394  		} else {
   395  			s.value = uint64(m.e.Uint32(p[8:]))
   396  		}
   397  		p = p[symsize:]
   398  	}
   399  
   400  	symtab.str = strbuf
   401  	symtab.sym = sym
   402  	return 0
   403  }
   404  
   405  func ldmacho(f *Biobuf, pkg string, length int64, pn string) {
   406  	var err error
   407  	var j int
   408  	var is64 bool
   409  	var secaddr uint64
   410  	var hdr [7 * 4]uint8
   411  	var cmdp []byte
   412  	var dat []byte
   413  	var ncmd uint32
   414  	var cmdsz uint32
   415  	var ty uint32
   416  	var sz uint32
   417  	var off uint32
   418  	var m *LdMachoObj
   419  	var e binary.ByteOrder
   420  	var sect *LdMachoSect
   421  	var rel *LdMachoRel
   422  	var rpi int
   423  	var s *LSym
   424  	var s1 *LSym
   425  	var outer *LSym
   426  	var c *LdMachoCmd
   427  	var symtab *LdMachoSymtab
   428  	var dsymtab *LdMachoDysymtab
   429  	var sym *LdMachoSym
   430  	var r []Reloc
   431  	var rp *Reloc
   432  	var name string
   433  
   434  	Ctxt.Version++
   435  	base := Boffset(f)
   436  	if Bread(f, hdr[:]) != len(hdr) {
   437  		goto bad
   438  	}
   439  
   440  	if binary.BigEndian.Uint32(hdr[:])&^1 == 0xFEEDFACE {
   441  		e = binary.BigEndian
   442  	} else if binary.LittleEndian.Uint32(hdr[:])&^1 == 0xFEEDFACE {
   443  		e = binary.LittleEndian
   444  	} else {
   445  		err = fmt.Errorf("bad magic - not mach-o file")
   446  		goto bad
   447  	}
   448  
   449  	is64 = e.Uint32(hdr[:]) == 0xFEEDFACF
   450  	ncmd = e.Uint32([]byte(hdr[4*4:]))
   451  	cmdsz = e.Uint32([]byte(hdr[5*4:]))
   452  	if ncmd > 0x10000 || cmdsz >= 0x01000000 {
   453  		err = fmt.Errorf("implausible mach-o header ncmd=%d cmdsz=%d", ncmd, cmdsz)
   454  		goto bad
   455  	}
   456  
   457  	if is64 {
   458  		var tmp [4]uint8
   459  		Bread(f, tmp[:4]) // skip reserved word in header
   460  	}
   461  
   462  	m = new(LdMachoObj)
   463  
   464  	m.f = f
   465  	m.e = e
   466  	m.cputype = uint(e.Uint32([]byte(hdr[1*4:])))
   467  	m.subcputype = uint(e.Uint32([]byte(hdr[2*4:])))
   468  	m.filetype = e.Uint32([]byte(hdr[3*4:]))
   469  	m.ncmd = uint(ncmd)
   470  	m.flags = e.Uint32([]byte(hdr[6*4:]))
   471  	m.is64 = is64
   472  	m.base = base
   473  	m.length = length
   474  	m.name = pn
   475  
   476  	switch Thearch.Thechar {
   477  	default:
   478  		Diag("%s: mach-o %s unimplemented", pn, Thestring)
   479  		return
   480  
   481  	case '6':
   482  		if e != binary.LittleEndian || m.cputype != LdMachoCpuAmd64 {
   483  			Diag("%s: mach-o object but not amd64", pn)
   484  			return
   485  		}
   486  
   487  	case '8':
   488  		if e != binary.LittleEndian || m.cputype != LdMachoCpu386 {
   489  			Diag("%s: mach-o object but not 386", pn)
   490  			return
   491  		}
   492  	}
   493  
   494  	m.cmd = make([]LdMachoCmd, ncmd)
   495  	off = uint32(len(hdr))
   496  	cmdp = make([]byte, cmdsz)
   497  	if Bread(f, cmdp) != len(cmdp) {
   498  		err = fmt.Errorf("reading cmds: %v", err)
   499  		goto bad
   500  	}
   501  
   502  	// read and parse load commands
   503  	c = nil
   504  
   505  	symtab = nil
   506  	dsymtab = nil
   507  
   508  	for i := 0; uint32(i) < ncmd; i++ {
   509  		ty = e.Uint32(cmdp)
   510  		sz = e.Uint32(cmdp[4:])
   511  		m.cmd[i].off = off
   512  		unpackcmd(cmdp, m, &m.cmd[i], uint(ty), uint(sz))
   513  		cmdp = cmdp[sz:]
   514  		off += sz
   515  		if ty == LdMachoCmdSymtab {
   516  			if symtab != nil {
   517  				err = fmt.Errorf("multiple symbol tables")
   518  				goto bad
   519  			}
   520  
   521  			symtab = &m.cmd[i].sym
   522  			macholoadsym(m, symtab)
   523  		}
   524  
   525  		if ty == LdMachoCmdDysymtab {
   526  			dsymtab = &m.cmd[i].dsym
   527  			macholoaddsym(m, dsymtab)
   528  		}
   529  
   530  		if (is64 && ty == LdMachoCmdSegment64) || (!is64 && ty == LdMachoCmdSegment) {
   531  			if c != nil {
   532  				err = fmt.Errorf("multiple load commands")
   533  				goto bad
   534  			}
   535  
   536  			c = &m.cmd[i]
   537  		}
   538  	}
   539  
   540  	// load text and data segments into memory.
   541  	// they are not as small as the load commands, but we'll need
   542  	// the memory anyway for the symbol images, so we might
   543  	// as well use one large chunk.
   544  	if c == nil {
   545  		err = fmt.Errorf("no load command")
   546  		goto bad
   547  	}
   548  
   549  	if symtab == nil {
   550  		// our work is done here - no symbols means nothing can refer to this file
   551  		return
   552  	}
   553  
   554  	if int64(c.seg.fileoff+c.seg.filesz) >= length {
   555  		err = fmt.Errorf("load segment out of range")
   556  		goto bad
   557  	}
   558  
   559  	dat = make([]byte, c.seg.filesz)
   560  	if Bseek(f, m.base+int64(c.seg.fileoff), 0) < 0 || Bread(f, dat) != len(dat) {
   561  		err = fmt.Errorf("cannot load object data: %v", err)
   562  		goto bad
   563  	}
   564  
   565  	for i := 0; uint32(i) < c.seg.nsect; i++ {
   566  		sect = &c.seg.sect[i]
   567  		if sect.segname != "__TEXT" && sect.segname != "__DATA" {
   568  			continue
   569  		}
   570  		if sect.name == "__eh_frame" {
   571  			continue
   572  		}
   573  		name = fmt.Sprintf("%s(%s/%s)", pkg, sect.segname, sect.name)
   574  		s = Linklookup(Ctxt, name, Ctxt.Version)
   575  		if s.Type != 0 {
   576  			err = fmt.Errorf("duplicate %s/%s", sect.segname, sect.name)
   577  			goto bad
   578  		}
   579  
   580  		if sect.flags&0xff == 1 { // S_ZEROFILL
   581  			s.P = make([]byte, sect.size)
   582  		} else {
   583  			s.P = dat[sect.addr-c.seg.vmaddr:][:sect.size]
   584  		}
   585  		s.Size = int64(len(s.P))
   586  
   587  		if sect.segname == "__TEXT" {
   588  			if sect.name == "__text" {
   589  				s.Type = obj.STEXT
   590  			} else {
   591  				s.Type = obj.SRODATA
   592  			}
   593  		} else {
   594  			if sect.name == "__bss" {
   595  				s.Type = obj.SNOPTRBSS
   596  				s.P = s.P[:0]
   597  			} else {
   598  				s.Type = obj.SNOPTRDATA
   599  			}
   600  		}
   601  
   602  		sect.sym = s
   603  	}
   604  
   605  	// enter sub-symbols into symbol table.
   606  	// have to guess sizes from next symbol.
   607  	for i := 0; uint32(i) < symtab.nsym; i++ {
   608  		sym = &symtab.sym[i]
   609  		if sym.type_&N_STAB != 0 {
   610  			continue
   611  		}
   612  
   613  		// TODO: check sym->type against outer->type.
   614  		name = sym.name
   615  
   616  		if name[0] == '_' && name[1] != '\x00' {
   617  			name = name[1:]
   618  		}
   619  		v := 0
   620  		if sym.type_&N_EXT == 0 {
   621  			v = Ctxt.Version
   622  		}
   623  		s = Linklookup(Ctxt, name, v)
   624  		if sym.type_&N_EXT == 0 {
   625  			s.Dupok = 1
   626  		}
   627  		sym.sym = s
   628  		if sym.sectnum == 0 { // undefined
   629  			continue
   630  		}
   631  		if uint32(sym.sectnum) > c.seg.nsect {
   632  			err = fmt.Errorf("reference to invalid section %d", sym.sectnum)
   633  			goto bad
   634  		}
   635  
   636  		sect = &c.seg.sect[sym.sectnum-1]
   637  		outer = sect.sym
   638  		if outer == nil {
   639  			err = fmt.Errorf("reference to invalid section %s/%s", sect.segname, sect.name)
   640  			continue
   641  		}
   642  
   643  		if s.Outer != nil {
   644  			if s.Dupok != 0 {
   645  				continue
   646  			}
   647  			Exitf("%s: duplicate symbol reference: %s in both %s and %s", pn, s.Name, s.Outer.Name, sect.sym.Name)
   648  		}
   649  
   650  		s.Type = outer.Type | obj.SSUB
   651  		s.Sub = outer.Sub
   652  		outer.Sub = s
   653  		s.Outer = outer
   654  		s.Value = int64(sym.value - sect.addr)
   655  		if s.Cgoexport&CgoExportDynamic == 0 {
   656  			s.Dynimplib = "" // satisfy dynimport
   657  		}
   658  		if outer.Type == obj.STEXT {
   659  			if s.External != 0 && s.Dupok == 0 {
   660  				Diag("%s: duplicate definition of %s", pn, s.Name)
   661  			}
   662  			s.External = 1
   663  		}
   664  
   665  		sym.sym = s
   666  	}
   667  
   668  	// Sort outer lists by address, adding to textp.
   669  	// This keeps textp in increasing address order.
   670  	for i := 0; uint32(i) < c.seg.nsect; i++ {
   671  		sect = &c.seg.sect[i]
   672  		s = sect.sym
   673  		if s == nil {
   674  			continue
   675  		}
   676  		if s.Sub != nil {
   677  			s.Sub = listsort(s.Sub, valuecmp, listsubp)
   678  
   679  			// assign sizes, now that we know symbols in sorted order.
   680  			for s1 = s.Sub; s1 != nil; s1 = s1.Sub {
   681  				if s1.Sub != nil {
   682  					s1.Size = s1.Sub.Value - s1.Value
   683  				} else {
   684  					s1.Size = s.Value + s.Size - s1.Value
   685  				}
   686  			}
   687  		}
   688  
   689  		if s.Type == obj.STEXT {
   690  			if s.Onlist != 0 {
   691  				log.Fatalf("symbol %s listed multiple times", s.Name)
   692  			}
   693  			s.Onlist = 1
   694  			if Ctxt.Etextp != nil {
   695  				Ctxt.Etextp.Next = s
   696  			} else {
   697  				Ctxt.Textp = s
   698  			}
   699  			Ctxt.Etextp = s
   700  			for s1 = s.Sub; s1 != nil; s1 = s1.Sub {
   701  				if s1.Onlist != 0 {
   702  					log.Fatalf("symbol %s listed multiple times", s1.Name)
   703  				}
   704  				s1.Onlist = 1
   705  				Ctxt.Etextp.Next = s1
   706  				Ctxt.Etextp = s1
   707  			}
   708  		}
   709  	}
   710  
   711  	// load relocations
   712  	for i := 0; uint32(i) < c.seg.nsect; i++ {
   713  		sect = &c.seg.sect[i]
   714  		s = sect.sym
   715  		if s == nil {
   716  			continue
   717  		}
   718  		macholoadrel(m, sect)
   719  		if sect.rel == nil {
   720  			continue
   721  		}
   722  		r = make([]Reloc, sect.nreloc)
   723  		rpi = 0
   724  	Reloc:
   725  		for j = 0; uint32(j) < sect.nreloc; j++ {
   726  			rp = &r[rpi]
   727  			rel = &sect.rel[j]
   728  			if rel.scattered != 0 {
   729  				if Thearch.Thechar != '8' {
   730  					// mach-o only uses scattered relocation on 32-bit platforms
   731  					Diag("unexpected scattered relocation")
   732  
   733  					continue
   734  				}
   735  
   736  				// on 386, rewrite scattered 4/1 relocation and some
   737  				// scattered 2/1 relocation into the pseudo-pc-relative
   738  				// reference that it is.
   739  				// assume that the second in the pair is in this section
   740  				// and use that as the pc-relative base.
   741  				if uint32(j+1) >= sect.nreloc {
   742  					err = fmt.Errorf("unsupported scattered relocation %d", int(rel.type_))
   743  					goto bad
   744  				}
   745  
   746  				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 {
   747  					err = fmt.Errorf("unsupported scattered relocation %d/%d", int(rel.type_), int(sect.rel[j+1].type_))
   748  					goto bad
   749  				}
   750  
   751  				rp.Siz = rel.length
   752  				rp.Off = int32(rel.addr)
   753  
   754  				// NOTE(rsc): I haven't worked out why (really when)
   755  				// we should ignore the addend on a
   756  				// scattered relocation, but it seems that the
   757  				// common case is we ignore it.
   758  				// It's likely that this is not strictly correct
   759  				// and that the math should look something
   760  				// like the non-scattered case below.
   761  				rp.Add = 0
   762  
   763  				// want to make it pc-relative aka relative to rp->off+4
   764  				// but the scatter asks for relative to off = sect->rel[j+1].value - sect->addr.
   765  				// adjust rp->add accordingly.
   766  				rp.Type = obj.R_PCREL
   767  
   768  				rp.Add += int64(uint64(int64(rp.Off)+4) - (uint64(sect.rel[j+1].value) - sect.addr))
   769  
   770  				// now consider the desired symbol.
   771  				// find the section where it lives.
   772  				var ks *LdMachoSect
   773  				for k := 0; uint32(k) < c.seg.nsect; k++ {
   774  					ks = &c.seg.sect[k]
   775  					if ks.addr <= uint64(rel.value) && uint64(rel.value) < ks.addr+ks.size {
   776  						if ks.sym != nil {
   777  							rp.Sym = ks.sym
   778  							rp.Add += int64(uint64(rel.value) - ks.addr)
   779  						} else if ks.segname == "__IMPORT" && ks.name == "__pointers" {
   780  							// handle reference to __IMPORT/__pointers.
   781  							// how much worse can this get?
   782  							// why are we supporting 386 on the mac anyway?
   783  							rp.Type = 512 + MACHO_FAKE_GOTPCREL
   784  
   785  							// figure out which pointer this is a reference to.
   786  							k = int(uint64(ks.res1) + (uint64(rel.value)-ks.addr)/4)
   787  
   788  							// load indirect table for __pointers
   789  							// fetch symbol number
   790  							if dsymtab == nil || k < 0 || uint32(k) >= dsymtab.nindirectsyms || dsymtab.indir == nil {
   791  								err = fmt.Errorf("invalid scattered relocation: indirect symbol reference out of range")
   792  								goto bad
   793  							}
   794  
   795  							k = int(dsymtab.indir[k])
   796  							if k < 0 || uint32(k) >= symtab.nsym {
   797  								err = fmt.Errorf("invalid scattered relocation: symbol reference out of range")
   798  								goto bad
   799  							}
   800  
   801  							rp.Sym = symtab.sym[k].sym
   802  						} else {
   803  							err = fmt.Errorf("unsupported scattered relocation: reference to %s/%s", ks.segname, ks.name)
   804  							goto bad
   805  						}
   806  
   807  						rpi++
   808  
   809  						// skip #1 of 2 rel; continue skips #2 of 2.
   810  						j++
   811  
   812  						continue Reloc
   813  					}
   814  				}
   815  
   816  				err = fmt.Errorf("unsupported scattered relocation: invalid address %#x", rel.addr)
   817  				goto bad
   818  
   819  			}
   820  
   821  			rp.Siz = rel.length
   822  			rp.Type = 512 + (int32(rel.type_) << 1) + int32(rel.pcrel)
   823  			rp.Off = int32(rel.addr)
   824  
   825  			// Handle X86_64_RELOC_SIGNED referencing a section (rel->extrn == 0).
   826  			if Thearch.Thechar == '6' && rel.extrn == 0 && rel.type_ == 1 {
   827  				// Calculate the addend as the offset into the section.
   828  				//
   829  				// The rip-relative offset stored in the object file is encoded
   830  				// as follows:
   831  				//
   832  				//    movsd	0x00000360(%rip),%xmm0
   833  				//
   834  				// To get the absolute address of the value this rip-relative address is pointing
   835  				// to, we must add the address of the next instruction to it. This is done by
   836  				// taking the address of the relocation and adding 4 to it (since the rip-relative
   837  				// offset can at most be 32 bits long).  To calculate the offset into the section the
   838  				// relocation is referencing, we subtract the vaddr of the start of the referenced
   839  				// section found in the original object file.
   840  				//
   841  				// [For future reference, see Darwin's /usr/include/mach-o/x86_64/reloc.h]
   842  				secaddr = c.seg.sect[rel.symnum-1].addr
   843  
   844  				rp.Add = int64(uint64(int64(int32(e.Uint32(s.P[rp.Off:])))+int64(rp.Off)+4) - secaddr)
   845  			} else {
   846  				rp.Add = int64(int32(e.Uint32(s.P[rp.Off:])))
   847  			}
   848  
   849  			// For i386 Mach-O PC-relative, the addend is written such that
   850  			// it *is* the PC being subtracted.  Use that to make
   851  			// it match our version of PC-relative.
   852  			if rel.pcrel != 0 && Thearch.Thechar == '8' {
   853  				rp.Add += int64(rp.Off) + int64(rp.Siz)
   854  			}
   855  			if rel.extrn == 0 {
   856  				if rel.symnum < 1 || rel.symnum > c.seg.nsect {
   857  					err = fmt.Errorf("invalid relocation: section reference out of range %d vs %d", rel.symnum, c.seg.nsect)
   858  					goto bad
   859  				}
   860  
   861  				rp.Sym = c.seg.sect[rel.symnum-1].sym
   862  				if rp.Sym == nil {
   863  					err = fmt.Errorf("invalid relocation: %s", c.seg.sect[rel.symnum-1].name)
   864  					goto bad
   865  				}
   866  
   867  				// References to symbols in other sections
   868  				// include that information in the addend.
   869  				// We only care about the delta from the
   870  				// section base.
   871  				if Thearch.Thechar == '8' {
   872  					rp.Add -= int64(c.seg.sect[rel.symnum-1].addr)
   873  				}
   874  			} else {
   875  				if rel.symnum >= symtab.nsym {
   876  					err = fmt.Errorf("invalid relocation: symbol reference out of range")
   877  					goto bad
   878  				}
   879  
   880  				rp.Sym = symtab.sym[rel.symnum].sym
   881  			}
   882  
   883  			rpi++
   884  		}
   885  
   886  		sort.Sort(rbyoff(r[:rpi]))
   887  		s.R = r
   888  		s.R = s.R[:rpi]
   889  	}
   890  
   891  	return
   892  
   893  bad:
   894  	Diag("%s: malformed mach-o file: %v", pn, err)
   895  }