...
Run Format

Source file src/cmd/link/internal/ld/macho.go

Documentation: cmd/link/internal/ld

  // Copyright 2009 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 ld
  
  import (
  	"cmd/internal/sys"
  	"sort"
  	"strings"
  )
  
  type MachoHdr struct {
  	cpu    uint32
  	subcpu uint32
  }
  
  type MachoSect struct {
  	name    string
  	segname string
  	addr    uint64
  	size    uint64
  	off     uint32
  	align   uint32
  	reloc   uint32
  	nreloc  uint32
  	flag    uint32
  	res1    uint32
  	res2    uint32
  }
  
  type MachoSeg struct {
  	name       string
  	vsize      uint64
  	vaddr      uint64
  	fileoffset uint64
  	filesize   uint64
  	prot1      uint32
  	prot2      uint32
  	nsect      uint32
  	msect      uint32
  	sect       []MachoSect
  	flag       uint32
  }
  
  type MachoLoad struct {
  	type_ uint32
  	data  []uint32
  }
  
  /*
   * Total amount of space to reserve at the start of the file
   * for Header, PHeaders, and SHeaders.
   * May waste some.
   */
  const (
  	INITIAL_MACHO_HEADR = 4 * 1024
  )
  
  const (
  	MACHO_CPU_AMD64               = 1<<24 | 7
  	MACHO_CPU_386                 = 7
  	MACHO_SUBCPU_X86              = 3
  	MACHO_CPU_ARM                 = 12
  	MACHO_SUBCPU_ARM              = 0
  	MACHO_SUBCPU_ARMV7            = 9
  	MACHO_CPU_ARM64               = 1<<24 | 12
  	MACHO_SUBCPU_ARM64_ALL        = 0
  	MACHO32SYMSIZE                = 12
  	MACHO64SYMSIZE                = 16
  	MACHO_X86_64_RELOC_UNSIGNED   = 0
  	MACHO_X86_64_RELOC_SIGNED     = 1
  	MACHO_X86_64_RELOC_BRANCH     = 2
  	MACHO_X86_64_RELOC_GOT_LOAD   = 3
  	MACHO_X86_64_RELOC_GOT        = 4
  	MACHO_X86_64_RELOC_SUBTRACTOR = 5
  	MACHO_X86_64_RELOC_SIGNED_1   = 6
  	MACHO_X86_64_RELOC_SIGNED_2   = 7
  	MACHO_X86_64_RELOC_SIGNED_4   = 8
  	MACHO_ARM_RELOC_VANILLA       = 0
  	MACHO_ARM_RELOC_PAIR          = 1
  	MACHO_ARM_RELOC_SECTDIFF      = 2
  	MACHO_ARM_RELOC_BR24          = 5
  	MACHO_ARM64_RELOC_UNSIGNED    = 0
  	MACHO_ARM64_RELOC_BRANCH26    = 2
  	MACHO_ARM64_RELOC_PAGE21      = 3
  	MACHO_ARM64_RELOC_PAGEOFF12   = 4
  	MACHO_ARM64_RELOC_ADDEND      = 10
  	MACHO_GENERIC_RELOC_VANILLA   = 0
  	MACHO_FAKE_GOTPCREL           = 100
  )
  
  // Copyright 2009 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.
  
  // Mach-O file writing
  // http://developer.apple.com/mac/library/DOCUMENTATION/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html
  
  var macho64 bool
  
  var machohdr MachoHdr
  
  var load []MachoLoad
  
  var seg [16]MachoSeg
  
  var nseg int
  
  var ndebug int
  
  var nsect int
  
  const (
  	SymKindLocal = 0 + iota
  	SymKindExtdef
  	SymKindUndef
  	NumSymKind
  )
  
  var nkind [NumSymKind]int
  
  var sortsym []*Symbol
  
  var nsortsym int
  
  // Amount of space left for adding load commands
  // that refer to dynamic libraries. Because these have
  // to go in the Mach-O header, we can't just pick a
  // "big enough" header size. The initial header is
  // one page, the non-dynamic library stuff takes
  // up about 1300 bytes; we overestimate that as 2k.
  var loadBudget int = INITIAL_MACHO_HEADR - 2*1024
  
  func Machoinit() {
  	macho64 = SysArch.RegSize == 8
  }
  
  func getMachoHdr() *MachoHdr {
  	return &machohdr
  }
  
  func newMachoLoad(type_ uint32, ndata uint32) *MachoLoad {
  	if macho64 && (ndata&1 != 0) {
  		ndata++
  	}
  
  	load = append(load, MachoLoad{})
  	l := &load[len(load)-1]
  	l.type_ = type_
  	l.data = make([]uint32, ndata)
  	return l
  }
  
  func newMachoSeg(name string, msect int) *MachoSeg {
  	if nseg >= len(seg) {
  		Exitf("too many segs")
  	}
  
  	s := &seg[nseg]
  	nseg++
  	s.name = name
  	s.msect = uint32(msect)
  	s.sect = make([]MachoSect, msect)
  	return s
  }
  
  func newMachoSect(seg *MachoSeg, name string, segname string) *MachoSect {
  	if seg.nsect >= seg.msect {
  		Exitf("too many sects in segment %s", seg.name)
  	}
  
  	s := &seg.sect[seg.nsect]
  	seg.nsect++
  	s.name = name
  	s.segname = segname
  	nsect++
  	return s
  }
  
  // Generic linking code.
  
  var dylib []string
  
  var linkoff int64
  
  func machowrite() int {
  	o1 := coutbuf.Offset()
  
  	loadsize := 4 * 4 * ndebug
  	for i := 0; i < len(load); i++ {
  		loadsize += 4 * (len(load[i].data) + 2)
  	}
  	if macho64 {
  		loadsize += 18 * 4 * nseg
  		loadsize += 20 * 4 * nsect
  	} else {
  		loadsize += 14 * 4 * nseg
  		loadsize += 17 * 4 * nsect
  	}
  
  	if macho64 {
  		Thearch.Lput(0xfeedfacf)
  	} else {
  		Thearch.Lput(0xfeedface)
  	}
  	Thearch.Lput(machohdr.cpu)
  	Thearch.Lput(machohdr.subcpu)
  	if Linkmode == LinkExternal {
  		Thearch.Lput(1) /* file type - mach object */
  	} else {
  		Thearch.Lput(2) /* file type - mach executable */
  	}
  	Thearch.Lput(uint32(len(load)) + uint32(nseg) + uint32(ndebug))
  	Thearch.Lput(uint32(loadsize))
  	Thearch.Lput(1) /* flags - no undefines */
  	if macho64 {
  		Thearch.Lput(0) /* reserved */
  	}
  
  	var j int
  	var s *MachoSeg
  	var t *MachoSect
  	for i := 0; i < nseg; i++ {
  		s = &seg[i]
  		if macho64 {
  			Thearch.Lput(25) /* segment 64 */
  			Thearch.Lput(72 + 80*s.nsect)
  			strnput(s.name, 16)
  			Thearch.Vput(s.vaddr)
  			Thearch.Vput(s.vsize)
  			Thearch.Vput(s.fileoffset)
  			Thearch.Vput(s.filesize)
  			Thearch.Lput(s.prot1)
  			Thearch.Lput(s.prot2)
  			Thearch.Lput(s.nsect)
  			Thearch.Lput(s.flag)
  		} else {
  			Thearch.Lput(1) /* segment 32 */
  			Thearch.Lput(56 + 68*s.nsect)
  			strnput(s.name, 16)
  			Thearch.Lput(uint32(s.vaddr))
  			Thearch.Lput(uint32(s.vsize))
  			Thearch.Lput(uint32(s.fileoffset))
  			Thearch.Lput(uint32(s.filesize))
  			Thearch.Lput(s.prot1)
  			Thearch.Lput(s.prot2)
  			Thearch.Lput(s.nsect)
  			Thearch.Lput(s.flag)
  		}
  
  		for j = 0; uint32(j) < s.nsect; j++ {
  			t = &s.sect[j]
  			if macho64 {
  				strnput(t.name, 16)
  				strnput(t.segname, 16)
  				Thearch.Vput(t.addr)
  				Thearch.Vput(t.size)
  				Thearch.Lput(t.off)
  				Thearch.Lput(t.align)
  				Thearch.Lput(t.reloc)
  				Thearch.Lput(t.nreloc)
  				Thearch.Lput(t.flag)
  				Thearch.Lput(t.res1) /* reserved */
  				Thearch.Lput(t.res2) /* reserved */
  				Thearch.Lput(0)      /* reserved */
  			} else {
  				strnput(t.name, 16)
  				strnput(t.segname, 16)
  				Thearch.Lput(uint32(t.addr))
  				Thearch.Lput(uint32(t.size))
  				Thearch.Lput(t.off)
  				Thearch.Lput(t.align)
  				Thearch.Lput(t.reloc)
  				Thearch.Lput(t.nreloc)
  				Thearch.Lput(t.flag)
  				Thearch.Lput(t.res1) /* reserved */
  				Thearch.Lput(t.res2) /* reserved */
  			}
  		}
  	}
  
  	var l *MachoLoad
  	for i := 0; i < len(load); i++ {
  		l = &load[i]
  		Thearch.Lput(l.type_)
  		Thearch.Lput(4 * (uint32(len(l.data)) + 2))
  		for j = 0; j < len(l.data); j++ {
  			Thearch.Lput(l.data[j])
  		}
  	}
  
  	return int(coutbuf.Offset() - o1)
  }
  
  func (ctxt *Link) domacho() {
  	if *FlagD {
  		return
  	}
  
  	// empirically, string table must begin with " \x00".
  	s := ctxt.Syms.Lookup(".machosymstr", 0)
  
  	s.Type = SMACHOSYMSTR
  	s.Attr |= AttrReachable
  	Adduint8(ctxt, s, ' ')
  	Adduint8(ctxt, s, '\x00')
  
  	s = ctxt.Syms.Lookup(".machosymtab", 0)
  	s.Type = SMACHOSYMTAB
  	s.Attr |= AttrReachable
  
  	if Linkmode != LinkExternal {
  		s := ctxt.Syms.Lookup(".plt", 0) // will be __symbol_stub
  		s.Type = SMACHOPLT
  		s.Attr |= AttrReachable
  
  		s = ctxt.Syms.Lookup(".got", 0) // will be __nl_symbol_ptr
  		s.Type = SMACHOGOT
  		s.Attr |= AttrReachable
  		s.Align = 4
  
  		s = ctxt.Syms.Lookup(".linkedit.plt", 0) // indirect table for .plt
  		s.Type = SMACHOINDIRECTPLT
  		s.Attr |= AttrReachable
  
  		s = ctxt.Syms.Lookup(".linkedit.got", 0) // indirect table for .got
  		s.Type = SMACHOINDIRECTGOT
  		s.Attr |= AttrReachable
  	}
  }
  
  func Machoadddynlib(lib string) {
  	// Will need to store the library name rounded up
  	// and 24 bytes of header metadata. If not enough
  	// space, grab another page of initial space at the
  	// beginning of the output file.
  	loadBudget -= (len(lib)+7)/8*8 + 24
  
  	if loadBudget < 0 {
  		HEADR += 4096
  		*FlagTextAddr += 4096
  		loadBudget += 4096
  	}
  
  	dylib = append(dylib, lib)
  }
  
  func machoshbits(ctxt *Link, mseg *MachoSeg, sect *Section, segname string) {
  	buf := "__" + strings.Replace(sect.Name[1:], ".", "_", -1)
  
  	var msect *MachoSect
  	if sect.Rwx&1 == 0 && segname != "__DWARF" && (SysArch.Family == sys.ARM64 ||
  		(SysArch.Family == sys.AMD64 && (Buildmode == BuildmodeCShared || Buildmode == BuildmodeCArchive || Buildmode == BuildmodePlugin)) ||
  		(SysArch.Family == sys.ARM && (Buildmode == BuildmodeCShared || Buildmode == BuildmodeCArchive || Buildmode == BuildmodePlugin))) {
  		// Darwin external linker on arm64 and on amd64 and arm in c-shared/c-archive buildmode
  		// complains about absolute relocs in __TEXT, so if the section is not
  		// executable, put it in __DATA segment.
  		msect = newMachoSect(mseg, buf, "__DATA")
  	} else {
  		msect = newMachoSect(mseg, buf, segname)
  	}
  
  	if sect.Rellen > 0 {
  		msect.reloc = uint32(sect.Reloff)
  		msect.nreloc = uint32(sect.Rellen / 8)
  	}
  
  	for 1<<msect.align < sect.Align {
  		msect.align++
  	}
  	msect.addr = sect.Vaddr
  	msect.size = sect.Length
  
  	if sect.Vaddr < sect.Seg.Vaddr+sect.Seg.Filelen {
  		// data in file
  		if sect.Length > sect.Seg.Vaddr+sect.Seg.Filelen-sect.Vaddr {
  			Errorf(nil, "macho cannot represent section %s crossing data and bss", sect.Name)
  		}
  		msect.off = uint32(sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr)
  	} else {
  		// zero fill
  		msect.off = 0
  
  		msect.flag |= 1
  	}
  
  	if sect.Rwx&1 != 0 {
  		msect.flag |= 0x400 /* has instructions */
  	}
  
  	if sect.Name == ".plt" {
  		msect.name = "__symbol_stub1"
  		msect.flag = 0x80000408 /* only instructions, code, symbol stubs */
  		msect.res1 = 0          //nkind[SymKindLocal];
  		msect.res2 = 6
  	}
  
  	if sect.Name == ".got" {
  		msect.name = "__nl_symbol_ptr"
  		msect.flag = 6                                                     /* section with nonlazy symbol pointers */
  		msect.res1 = uint32(ctxt.Syms.Lookup(".linkedit.plt", 0).Size / 4) /* offset into indirect symbol table */
  	}
  
  	if sect.Name == ".init_array" {
  		msect.name = "__mod_init_func"
  		msect.flag = 9 // S_MOD_INIT_FUNC_POINTERS
  	}
  
  	if segname == "__DWARF" {
  		msect.flag |= 0x02000000
  	}
  }
  
  func Asmbmacho(ctxt *Link) {
  	/* apple MACH */
  	va := *FlagTextAddr - int64(HEADR)
  
  	mh := getMachoHdr()
  	switch SysArch.Family {
  	default:
  		Exitf("unknown macho architecture: %v", SysArch.Family)
  
  	case sys.ARM:
  		mh.cpu = MACHO_CPU_ARM
  		mh.subcpu = MACHO_SUBCPU_ARMV7
  
  	case sys.AMD64:
  		mh.cpu = MACHO_CPU_AMD64
  		mh.subcpu = MACHO_SUBCPU_X86
  
  	case sys.ARM64:
  		mh.cpu = MACHO_CPU_ARM64
  		mh.subcpu = MACHO_SUBCPU_ARM64_ALL
  
  	case sys.I386:
  		mh.cpu = MACHO_CPU_386
  		mh.subcpu = MACHO_SUBCPU_X86
  	}
  
  	var ms *MachoSeg
  	if Linkmode == LinkExternal {
  		/* segment for entire file */
  		ms = newMachoSeg("", 40)
  
  		ms.fileoffset = Segtext.Fileoff
  		if SysArch.Family == sys.ARM || Buildmode == BuildmodeCArchive {
  			ms.filesize = Segdata.Fileoff + Segdata.Filelen - Segtext.Fileoff
  		} else {
  			ms.filesize = Segdwarf.Fileoff + Segdwarf.Filelen - Segtext.Fileoff
  			ms.vsize = Segdwarf.Vaddr + Segdwarf.Length - Segtext.Vaddr
  		}
  	}
  
  	/* segment for zero page */
  	if Linkmode != LinkExternal {
  		ms = newMachoSeg("__PAGEZERO", 0)
  		ms.vsize = uint64(va)
  	}
  
  	/* text */
  	v := Rnd(int64(uint64(HEADR)+Segtext.Length), int64(*FlagRound))
  
  	if Linkmode != LinkExternal {
  		ms = newMachoSeg("__TEXT", 20)
  		ms.vaddr = uint64(va)
  		ms.vsize = uint64(v)
  		ms.fileoffset = 0
  		ms.filesize = uint64(v)
  		ms.prot1 = 7
  		ms.prot2 = 5
  	}
  
  	for _, sect := range Segtext.Sections {
  		machoshbits(ctxt, ms, sect, "__TEXT")
  	}
  
  	/* data */
  	if Linkmode != LinkExternal {
  		w := int64(Segdata.Length)
  		ms = newMachoSeg("__DATA", 20)
  		ms.vaddr = uint64(va) + uint64(v)
  		ms.vsize = uint64(w)
  		ms.fileoffset = uint64(v)
  		ms.filesize = Segdata.Filelen
  		ms.prot1 = 3
  		ms.prot2 = 3
  	}
  
  	for _, sect := range Segdata.Sections {
  		machoshbits(ctxt, ms, sect, "__DATA")
  	}
  
  	/* dwarf */
  	if !*FlagW {
  		if Linkmode != LinkExternal {
  			ms = newMachoSeg("__DWARF", 20)
  			ms.vaddr = Segdwarf.Vaddr
  			ms.vsize = 0
  			ms.fileoffset = Segdwarf.Fileoff
  			ms.filesize = Segdwarf.Filelen
  		}
  		for _, sect := range Segdwarf.Sections {
  			machoshbits(ctxt, ms, sect, "__DWARF")
  		}
  	}
  
  	if Linkmode != LinkExternal {
  		switch SysArch.Family {
  		default:
  			Exitf("unknown macho architecture: %v", SysArch.Family)
  
  		case sys.ARM:
  			ml := newMachoLoad(5, 17+2)              /* unix thread */
  			ml.data[0] = 1                           /* thread type */
  			ml.data[1] = 17                          /* word count */
  			ml.data[2+15] = uint32(Entryvalue(ctxt)) /* start pc */
  
  		case sys.AMD64:
  			ml := newMachoLoad(5, 42+2)              /* unix thread */
  			ml.data[0] = 4                           /* thread type */
  			ml.data[1] = 42                          /* word count */
  			ml.data[2+32] = uint32(Entryvalue(ctxt)) /* start pc */
  			ml.data[2+32+1] = uint32(Entryvalue(ctxt) >> 32)
  
  		case sys.ARM64:
  			ml := newMachoLoad(5, 68+2)              /* unix thread */
  			ml.data[0] = 6                           /* thread type */
  			ml.data[1] = 68                          /* word count */
  			ml.data[2+64] = uint32(Entryvalue(ctxt)) /* start pc */
  			ml.data[2+64+1] = uint32(Entryvalue(ctxt) >> 32)
  
  		case sys.I386:
  			ml := newMachoLoad(5, 16+2)              /* unix thread */
  			ml.data[0] = 1                           /* thread type */
  			ml.data[1] = 16                          /* word count */
  			ml.data[2+10] = uint32(Entryvalue(ctxt)) /* start pc */
  		}
  	}
  
  	if !*FlagD {
  		// must match domacholink below
  		s1 := ctxt.Syms.Lookup(".machosymtab", 0)
  		s2 := ctxt.Syms.Lookup(".linkedit.plt", 0)
  		s3 := ctxt.Syms.Lookup(".linkedit.got", 0)
  		s4 := ctxt.Syms.Lookup(".machosymstr", 0)
  
  		if Linkmode != LinkExternal {
  			ms := newMachoSeg("__LINKEDIT", 0)
  			ms.vaddr = uint64(va) + uint64(v) + uint64(Rnd(int64(Segdata.Length), int64(*FlagRound)))
  			ms.vsize = uint64(s1.Size) + uint64(s2.Size) + uint64(s3.Size) + uint64(s4.Size)
  			ms.fileoffset = uint64(linkoff)
  			ms.filesize = ms.vsize
  			ms.prot1 = 7
  			ms.prot2 = 3
  		}
  
  		ml := newMachoLoad(2, 4)                                   /* LC_SYMTAB */
  		ml.data[0] = uint32(linkoff)                               /* symoff */
  		ml.data[1] = uint32(nsortsym)                              /* nsyms */
  		ml.data[2] = uint32(linkoff + s1.Size + s2.Size + s3.Size) /* stroff */
  		ml.data[3] = uint32(s4.Size)                               /* strsize */
  
  		machodysymtab(ctxt)
  
  		if Linkmode != LinkExternal {
  			ml := newMachoLoad(14, 6) /* LC_LOAD_DYLINKER */
  			ml.data[0] = 12           /* offset to string */
  			stringtouint32(ml.data[1:], "/usr/lib/dyld")
  
  			for i := 0; i < len(dylib); i++ {
  				ml = newMachoLoad(12, 4+(uint32(len(dylib[i]))+1+7)/8*2) /* LC_LOAD_DYLIB */
  				ml.data[0] = 24                                          /* offset of string from beginning of load */
  				ml.data[1] = 0                                           /* time stamp */
  				ml.data[2] = 0                                           /* version */
  				ml.data[3] = 0                                           /* compatibility version */
  				stringtouint32(ml.data[4:], dylib[i])
  			}
  		}
  	}
  
  	if Linkmode == LinkInternal {
  		// For lldb, must say LC_VERSION_MIN_MACOSX or else
  		// it won't know that this Mach-O binary is from OS X
  		// (could be iOS or WatchOS instead).
  		// Go on iOS uses linkmode=external, and linkmode=external
  		// adds this itself. So we only need this code for linkmode=internal
  		// and we can assume OS X.
  		//
  		// See golang.org/issues/12941.
  		const LC_VERSION_MIN_MACOSX = 0x24
  
  		ml := newMachoLoad(LC_VERSION_MIN_MACOSX, 2)
  		ml.data[0] = 10<<16 | 7<<8 | 0<<0 // OS X version 10.7.0
  		ml.data[1] = 10<<16 | 7<<8 | 0<<0 // SDK 10.7.0
  	}
  
  	a := machowrite()
  	if int32(a) > HEADR {
  		Exitf("HEADR too small: %d > %d", a, HEADR)
  	}
  }
  
  func symkind(s *Symbol) int {
  	if s.Type == SDYNIMPORT {
  		return SymKindUndef
  	}
  	if s.Attr.CgoExport() {
  		return SymKindExtdef
  	}
  	return SymKindLocal
  }
  
  func addsym(ctxt *Link, s *Symbol, name string, type_ SymbolType, addr int64, gotype *Symbol) {
  	if s == nil {
  		return
  	}
  
  	switch type_ {
  	default:
  		return
  
  	case DataSym, BSSSym, TextSym:
  		break
  	}
  
  	if sortsym != nil {
  		sortsym[nsortsym] = s
  		nkind[symkind(s)]++
  	}
  
  	nsortsym++
  }
  
  type machoscmp []*Symbol
  
  func (x machoscmp) Len() int {
  	return len(x)
  }
  
  func (x machoscmp) Swap(i, j int) {
  	x[i], x[j] = x[j], x[i]
  }
  
  func (x machoscmp) Less(i, j int) bool {
  	s1 := x[i]
  	s2 := x[j]
  
  	k1 := symkind(s1)
  	k2 := symkind(s2)
  	if k1 != k2 {
  		return k1 < k2
  	}
  
  	return s1.Extname < s2.Extname
  }
  
  func machogenasmsym(ctxt *Link) {
  	genasmsym(ctxt, addsym)
  	for _, s := range ctxt.Syms.Allsym {
  		if s.Type == SDYNIMPORT || s.Type == SHOSTOBJ {
  			if s.Attr.Reachable() {
  				addsym(ctxt, s, "", DataSym, 0, nil)
  			}
  		}
  	}
  }
  
  func machosymorder(ctxt *Link) {
  	// On Mac OS X Mountain Lion, we must sort exported symbols
  	// So we sort them here and pre-allocate dynid for them
  	// See https://golang.org/issue/4029
  	for i := 0; i < len(dynexp); i++ {
  		dynexp[i].Attr |= AttrReachable
  	}
  	machogenasmsym(ctxt)
  	sortsym = make([]*Symbol, nsortsym)
  	nsortsym = 0
  	machogenasmsym(ctxt)
  	sort.Sort(machoscmp(sortsym[:nsortsym]))
  	for i := 0; i < nsortsym; i++ {
  		sortsym[i].Dynid = int32(i)
  	}
  }
  
  // machoShouldExport reports whether a symbol needs to be exported.
  //
  // When dynamically linking, all non-local variables and plugin-exported
  // symbols need to be exported.
  func machoShouldExport(ctxt *Link, s *Symbol) bool {
  	if !ctxt.DynlinkingGo() || s.Attr.Local() {
  		return false
  	}
  	if Buildmode == BuildmodePlugin && strings.HasPrefix(s.Extname, *flagPluginPath) {
  		return true
  	}
  	if strings.HasPrefix(s.Name, "type.") && !strings.HasPrefix(s.Name, "type..") {
  		// reduce runtime typemap pressure, but do not
  		// export alg functions (type..*), as these
  		// appear in pclntable.
  		return true
  	}
  	if strings.HasPrefix(s.Name, "go.link.pkghash") {
  		return true
  	}
  	return s.Type >= SELFSECT // only writable sections
  }
  
  func machosymtab(ctxt *Link) {
  	symtab := ctxt.Syms.Lookup(".machosymtab", 0)
  	symstr := ctxt.Syms.Lookup(".machosymstr", 0)
  
  	for i := 0; i < nsortsym; i++ {
  		s := sortsym[i]
  		Adduint32(ctxt, symtab, uint32(symstr.Size))
  
  		export := machoShouldExport(ctxt, s)
  
  		// In normal buildmodes, only add _ to C symbols, as
  		// Go symbols have dot in the name.
  		//
  		// Do not export C symbols in plugins, as runtime C
  		// symbols like crosscall2 are in pclntab and end up
  		// pointing at the host binary, breaking unwinding.
  		// See Issue #18190.
  		cexport := !strings.Contains(s.Extname, ".") && (Buildmode != BuildmodePlugin || onlycsymbol(s))
  		if cexport || export {
  			Adduint8(ctxt, symstr, '_')
  		}
  
  		// replace "·" as ".", because DTrace cannot handle it.
  		Addstring(symstr, strings.Replace(s.Extname, "·", ".", -1))
  
  		if s.Type == SDYNIMPORT || s.Type == SHOSTOBJ {
  			Adduint8(ctxt, symtab, 0x01)                // type N_EXT, external symbol
  			Adduint8(ctxt, symtab, 0)                   // no section
  			Adduint16(ctxt, symtab, 0)                  // desc
  			adduintxx(ctxt, symtab, 0, SysArch.PtrSize) // no value
  		} else {
  			if s.Attr.CgoExport() || export {
  				Adduint8(ctxt, symtab, 0x0f)
  			} else {
  				Adduint8(ctxt, symtab, 0x0e)
  			}
  			o := s
  			for o.Outer != nil {
  				o = o.Outer
  			}
  			if o.Sect == nil {
  				Errorf(s, "missing section for symbol")
  				Adduint8(ctxt, symtab, 0)
  			} else {
  				Adduint8(ctxt, symtab, uint8(o.Sect.Extnum))
  			}
  			Adduint16(ctxt, symtab, 0) // desc
  			adduintxx(ctxt, symtab, uint64(Symaddr(s)), SysArch.PtrSize)
  		}
  	}
  }
  
  func machodysymtab(ctxt *Link) {
  	ml := newMachoLoad(11, 18) /* LC_DYSYMTAB */
  
  	n := 0
  	ml.data[0] = uint32(n)                   /* ilocalsym */
  	ml.data[1] = uint32(nkind[SymKindLocal]) /* nlocalsym */
  	n += nkind[SymKindLocal]
  
  	ml.data[2] = uint32(n)                    /* iextdefsym */
  	ml.data[3] = uint32(nkind[SymKindExtdef]) /* nextdefsym */
  	n += nkind[SymKindExtdef]
  
  	ml.data[4] = uint32(n)                   /* iundefsym */
  	ml.data[5] = uint32(nkind[SymKindUndef]) /* nundefsym */
  
  	ml.data[6] = 0  /* tocoffset */
  	ml.data[7] = 0  /* ntoc */
  	ml.data[8] = 0  /* modtaboff */
  	ml.data[9] = 0  /* nmodtab */
  	ml.data[10] = 0 /* extrefsymoff */
  	ml.data[11] = 0 /* nextrefsyms */
  
  	// must match domacholink below
  	s1 := ctxt.Syms.Lookup(".machosymtab", 0)
  
  	s2 := ctxt.Syms.Lookup(".linkedit.plt", 0)
  	s3 := ctxt.Syms.Lookup(".linkedit.got", 0)
  	ml.data[12] = uint32(linkoff + s1.Size)       /* indirectsymoff */
  	ml.data[13] = uint32((s2.Size + s3.Size) / 4) /* nindirectsyms */
  
  	ml.data[14] = 0 /* extreloff */
  	ml.data[15] = 0 /* nextrel */
  	ml.data[16] = 0 /* locreloff */
  	ml.data[17] = 0 /* nlocrel */
  }
  
  func Domacholink(ctxt *Link) int64 {
  	machosymtab(ctxt)
  
  	// write data that will be linkedit section
  	s1 := ctxt.Syms.Lookup(".machosymtab", 0)
  
  	s2 := ctxt.Syms.Lookup(".linkedit.plt", 0)
  	s3 := ctxt.Syms.Lookup(".linkedit.got", 0)
  	s4 := ctxt.Syms.Lookup(".machosymstr", 0)
  
  	// Force the linkedit section to end on a 16-byte
  	// boundary. This allows pure (non-cgo) Go binaries
  	// to be code signed correctly.
  	//
  	// Apple's codesign_allocate (a helper utility for
  	// the codesign utility) can do this fine itself if
  	// it is run on a dynamic Mach-O binary. However,
  	// when it is run on a pure (non-cgo) Go binary, where
  	// the linkedit section is mostly empty, it fails to
  	// account for the extra padding that it itself adds
  	// when adding the LC_CODE_SIGNATURE load command
  	// (which must be aligned on a 16-byte boundary).
  	//
  	// By forcing the linkedit section to end on a 16-byte
  	// boundary, codesign_allocate will not need to apply
  	// any alignment padding itself, working around the
  	// issue.
  	for s4.Size%16 != 0 {
  		Adduint8(ctxt, s4, 0)
  	}
  
  	size := int(s1.Size + s2.Size + s3.Size + s4.Size)
  
  	if size > 0 {
  		linkoff = Rnd(int64(uint64(HEADR)+Segtext.Length), int64(*FlagRound)) + Rnd(int64(Segdata.Filelen), int64(*FlagRound)) + Rnd(int64(Segdwarf.Filelen), int64(*FlagRound))
  		Cseek(linkoff)
  
  		Cwrite(s1.P[:s1.Size])
  		Cwrite(s2.P[:s2.Size])
  		Cwrite(s3.P[:s3.Size])
  		Cwrite(s4.P[:s4.Size])
  	}
  
  	return Rnd(int64(size), int64(*FlagRound))
  }
  
  func machorelocsect(ctxt *Link, sect *Section, syms []*Symbol) {
  	// If main section has no bits, nothing to relocate.
  	if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen {
  		return
  	}
  
  	sect.Reloff = uint64(coutbuf.Offset())
  	for i, s := range syms {
  		if !s.Attr.Reachable() {
  			continue
  		}
  		if uint64(s.Value) >= sect.Vaddr {
  			syms = syms[i:]
  			break
  		}
  	}
  
  	eaddr := int32(sect.Vaddr + sect.Length)
  	for _, sym := range syms {
  		if !sym.Attr.Reachable() {
  			continue
  		}
  		if sym.Value >= int64(eaddr) {
  			break
  		}
  		for ri := 0; ri < len(sym.R); ri++ {
  			r := &sym.R[ri]
  			if r.Done != 0 {
  				continue
  			}
  			if r.Xsym == nil {
  				Errorf(sym, "missing xsym in relocation")
  				continue
  			}
  			if !r.Xsym.Attr.Reachable() {
  				Errorf(sym, "unreachable reloc %v target %v", r.Type, r.Xsym.Name)
  			}
  			if Thearch.Machoreloc1(sym, r, int64(uint64(sym.Value+int64(r.Off))-sect.Vaddr)) < 0 {
  				Errorf(sym, "unsupported obj reloc %v/%d to %s", r.Type, r.Siz, r.Sym.Name)
  			}
  		}
  	}
  
  	sect.Rellen = uint64(coutbuf.Offset()) - sect.Reloff
  }
  
  func Machoemitreloc(ctxt *Link) {
  	for coutbuf.Offset()&7 != 0 {
  		Cput(0)
  	}
  
  	machorelocsect(ctxt, Segtext.Sections[0], ctxt.Textp)
  	for _, sect := range Segtext.Sections[1:] {
  		machorelocsect(ctxt, sect, datap)
  	}
  	for _, sect := range Segdata.Sections {
  		machorelocsect(ctxt, sect, datap)
  	}
  	for _, sect := range Segdwarf.Sections {
  		machorelocsect(ctxt, sect, dwarfp)
  	}
  }
  

View as plain text