mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
Many (most!) of the values of objapi.SymKind are used only in the linker, so this creates a separate cmd/link/internal/ld.SymKind type, removes most values from SymKind and maps one to the other when reading object files in the linker. Two of the remaining objapi.SymKind values are only checked for, never set and so will never be actually found but I wanted to keep this to the most mechanical change possible. Change-Id: I4bbc5aed6713cab3e8de732e6e288eb77be0474c Reviewed-on: https://go-review.googlesource.com/40985 Run-TryBot: Michael Hudson-Doyle <michael.hudson@canonical.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Matthew Dempsky <mdempsky@google.com>
904 lines
23 KiB
Go
904 lines
23 KiB
Go
// 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)
|
|
}
|
|
}
|