mirror of
https://github.com/golang/go
synced 2024-11-11 19:51:37 -07:00
cmd/link: support PIE internal linking on darwin/amd64
This CL adds support of PIE internal linking on darwin/amd64. This is also preparation for supporting internal linking on darwin/arm64 (macOS), which requires PIE for everything. Updates #38485. Change-Id: I2ed58583dcc102f5e0521982491fc7ba6f2754ed Reviewed-on: https://go-review.googlesource.com/c/go/+/261642 Trust: Cherry Zhang <cherryyz@google.com> Reviewed-by: Than McIntosh <thanm@google.com>
This commit is contained in:
parent
c8eea1633e
commit
f46a5b1e45
@ -3,6 +3,7 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !windows,!static
|
||||
// +build !darwin !internal_pie
|
||||
|
||||
#include <stdint.h>
|
||||
#include <dlfcn.h>
|
||||
|
@ -3,6 +3,10 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !windows,!static
|
||||
// +build !darwin !internal_pie
|
||||
|
||||
// Excluded in darwin internal linking PIE mode, as dynamic export is not
|
||||
// supported.
|
||||
|
||||
package cgotest
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build windows static
|
||||
// +build windows static darwin,internal_pie
|
||||
|
||||
package cgotest
|
||||
|
||||
|
17
src/cmd/dist/test.go
vendored
17
src/cmd/dist/test.go
vendored
@ -964,10 +964,10 @@ func (t *tester) internalLink() bool {
|
||||
|
||||
func (t *tester) internalLinkPIE() bool {
|
||||
switch goos + "-" + goarch {
|
||||
case "linux-amd64", "linux-arm64",
|
||||
"android-arm64":
|
||||
return true
|
||||
case "windows-amd64", "windows-386", "windows-arm":
|
||||
case "darwin-amd64",
|
||||
"linux-amd64", "linux-arm64",
|
||||
"android-arm64",
|
||||
"windows-amd64", "windows-386", "windows-arm":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@ -1100,6 +1100,13 @@ func (t *tester) cgoTest(dt *distTest) error {
|
||||
|
||||
cmd = t.addCmd(dt, "misc/cgo/test", t.goTest(), "-ldflags", "-linkmode=external -s")
|
||||
|
||||
if t.supportedBuildmode("pie") {
|
||||
t.addCmd(dt, "misc/cgo/test", t.goTest(), "-buildmode=pie")
|
||||
if t.internalLink() && t.internalLinkPIE() {
|
||||
t.addCmd(dt, "misc/cgo/test", t.goTest(), "-buildmode=pie", "-ldflags=-linkmode=internal", "-tags=internal,internal_pie")
|
||||
}
|
||||
}
|
||||
|
||||
case "aix-ppc64",
|
||||
"android-arm", "android-arm64",
|
||||
"dragonfly-amd64",
|
||||
@ -1151,7 +1158,7 @@ func (t *tester) cgoTest(dt *distTest) error {
|
||||
if t.supportedBuildmode("pie") {
|
||||
t.addCmd(dt, "misc/cgo/test", t.goTest(), "-buildmode=pie")
|
||||
if t.internalLink() && t.internalLinkPIE() {
|
||||
t.addCmd(dt, "misc/cgo/test", t.goTest(), "-buildmode=pie", "-ldflags=-linkmode=internal")
|
||||
t.addCmd(dt, "misc/cgo/test", t.goTest(), "-buildmode=pie", "-ldflags=-linkmode=internal", "-tags=internal,internal_pie")
|
||||
}
|
||||
t.addCmd(dt, "misc/cgo/testtls", t.goTest(), "-buildmode=pie")
|
||||
t.addCmd(dt, "misc/cgo/nocgo", t.goTest(), "-buildmode=pie")
|
||||
|
@ -118,7 +118,8 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool {
|
||||
|
||||
func InternalLinkPIESupported(goos, goarch string) bool {
|
||||
switch goos + "/" + goarch {
|
||||
case "linux/amd64", "linux/arm64",
|
||||
case "darwin/amd64",
|
||||
"linux/amd64", "linux/arm64",
|
||||
"android/arm64",
|
||||
"windows-amd64", "windows-386", "windows-arm":
|
||||
return true
|
||||
|
@ -76,9 +76,9 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
|
||||
targType = ldr.SymType(targ)
|
||||
}
|
||||
|
||||
switch r.Type() {
|
||||
switch rt := r.Type(); rt {
|
||||
default:
|
||||
if r.Type() >= objabi.ElfRelocOffset {
|
||||
if rt >= objabi.ElfRelocOffset {
|
||||
ldr.Errorf(s, "unexpected relocation type %d (%s)", r.Type(), sym.RelocName(target.Arch, r.Type()))
|
||||
return false
|
||||
}
|
||||
@ -167,13 +167,24 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
|
||||
case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_UNSIGNED*2 + 0,
|
||||
objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_SIGNED*2 + 0,
|
||||
objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_BRANCH*2 + 0:
|
||||
// TODO: What is the difference between all these?
|
||||
su := ldr.MakeSymbolUpdater(s)
|
||||
su.SetRelocType(rIdx, objabi.R_ADDR)
|
||||
|
||||
if targType == sym.SDYNIMPORT {
|
||||
ldr.Errorf(s, "unexpected reloc for dynamic symbol %s", ldr.SymName(targ))
|
||||
}
|
||||
if target.IsPIE() && target.IsInternal() {
|
||||
// For internal linking PIE, this R_ADDR relocation cannot
|
||||
// be resolved statically. We need to generate a dynamic
|
||||
// relocation. Let the code below handle it.
|
||||
if rt == objabi.MachoRelocOffset+ld.MACHO_X86_64_RELOC_UNSIGNED*2 {
|
||||
break
|
||||
} else {
|
||||
// MACHO_X86_64_RELOC_SIGNED or MACHO_X86_64_RELOC_BRANCH
|
||||
// Can this happen? The object is expected to be PIC.
|
||||
ldr.Errorf(s, "unsupported relocation for PIE: %v", rt)
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_BRANCH*2 + 1:
|
||||
@ -223,7 +234,7 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
|
||||
if targType != sym.SDYNIMPORT {
|
||||
ldr.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", ldr.SymName(targ))
|
||||
}
|
||||
ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_X86_64_GLOB_DAT))
|
||||
ld.AddGotSym(target, ldr, syms, targ, 0)
|
||||
su := ldr.MakeSymbolUpdater(s)
|
||||
su.SetRelocType(rIdx, objabi.R_PCREL)
|
||||
su.SetRelocSym(rIdx, syms.GOT)
|
||||
@ -355,28 +366,15 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
|
||||
return true
|
||||
}
|
||||
|
||||
if target.IsDarwin() && ldr.SymSize(s) == int64(target.Arch.PtrSize) && r.Off() == 0 {
|
||||
if target.IsDarwin() {
|
||||
// Mach-O relocations are a royal pain to lay out.
|
||||
// They use a compact stateful bytecode representation
|
||||
// that is too much bother to deal with.
|
||||
// Instead, interpret the C declaration
|
||||
// void *_Cvar_stderr = &stderr;
|
||||
// as making _Cvar_stderr the name of a GOT entry
|
||||
// for stderr. This is separate from the usual GOT entry,
|
||||
// just in case the C code assigns to the variable,
|
||||
// and of course it only works for single pointers,
|
||||
// but we only need to support cgo and that's all it needs.
|
||||
ld.Adddynsym(ldr, target, syms, targ)
|
||||
|
||||
got := ldr.MakeSymbolUpdater(syms.GOT)
|
||||
su := ldr.MakeSymbolUpdater(s)
|
||||
su.SetType(got.Type())
|
||||
got.AddInteriorSym(s)
|
||||
su.SetValue(got.Size())
|
||||
got.AddUint64(target.Arch, 0)
|
||||
leg := ldr.MakeSymbolUpdater(syms.LinkEditGOT)
|
||||
leg.AddUint32(target.Arch, uint32(ldr.SymDynid(targ)))
|
||||
su.SetRelocType(rIdx, objabi.ElfRelocOffset) // ignore during relocsym
|
||||
// They use a compact stateful bytecode representation.
|
||||
// Here we record what are needed and encode them later.
|
||||
ld.MachoAddRebase(s, int64(r.Off()))
|
||||
// Not mark r done here. So we still apply it statically,
|
||||
// so in the file content we'll also have the right offset
|
||||
// to the relocation target. So it can be examined statically
|
||||
// (e.g. go version).
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -627,26 +625,16 @@ func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
|
||||
|
||||
ldr.SetPlt(s, int32(plt.Size()-16))
|
||||
} else if target.IsDarwin() {
|
||||
// To do lazy symbol lookup right, we're supposed
|
||||
// to tell the dynamic loader which library each
|
||||
// symbol comes from and format the link info
|
||||
// section just so. I'm too lazy (ha!) to do that
|
||||
// so for now we'll just use non-lazy pointers,
|
||||
// which don't need to be told which library to use.
|
||||
//
|
||||
// https://networkpx.blogspot.com/2009/09/about-lcdyldinfoonly-command.html
|
||||
// has details about what we're avoiding.
|
||||
|
||||
ld.AddGotSym(target, ldr, syms, s, uint32(elf.R_X86_64_GLOB_DAT))
|
||||
plt := ldr.MakeSymbolUpdater(syms.PLT)
|
||||
ld.AddGotSym(target, ldr, syms, s, 0)
|
||||
|
||||
sDynid := ldr.SymDynid(s)
|
||||
lep := ldr.MakeSymbolUpdater(syms.LinkEditPLT)
|
||||
lep.AddUint32(target.Arch, uint32(sDynid))
|
||||
|
||||
// jmpq *got+size(IP)
|
||||
plt := ldr.MakeSymbolUpdater(syms.PLT)
|
||||
ldr.SetPlt(s, int32(plt.Size()))
|
||||
|
||||
// jmpq *got+size(IP)
|
||||
plt.AddUint8(0xff)
|
||||
plt.AddUint8(0x25)
|
||||
plt.AddPCRelPlus(target.Arch, syms.GOT, int64(ldr.SymGot(s)))
|
||||
@ -654,6 +642,7 @@ func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
|
||||
ldr.Errorf(s, "addpltsym: unsupported binary format")
|
||||
}
|
||||
}
|
||||
|
||||
func tlsIEtoLE(P []byte, off, size int) {
|
||||
// Transform the PC-relative instruction into a constant load.
|
||||
// That is,
|
||||
|
@ -222,6 +222,7 @@ func mustLinkExternal(ctxt *Link) (res bool, reason string) {
|
||||
switch objabi.GOOS + "/" + objabi.GOARCH {
|
||||
case "linux/amd64", "linux/arm64", "android/arm64":
|
||||
case "windows/386", "windows/amd64", "windows/arm":
|
||||
case "darwin/amd64":
|
||||
default:
|
||||
// Internal linking does not support TLS_IE.
|
||||
return true, "buildmode=pie"
|
||||
|
@ -2526,6 +2526,12 @@ func AddGotSym(target *Target, ldr *loader.Loader, syms *ArchSyms, s loader.Sym,
|
||||
} else if target.IsDarwin() {
|
||||
leg := ldr.MakeSymbolUpdater(syms.LinkEditGOT)
|
||||
leg.AddUint32(target.Arch, uint32(ldr.SymDynid(s)))
|
||||
if target.IsPIE() && target.IsInternal() {
|
||||
// Mach-O relocations are a royal pain to lay out.
|
||||
// They use a compact stateful bytecode representation.
|
||||
// Here we record what are needed and encode them later.
|
||||
MachoAddBind(int64(ldr.SymGot(s)), s)
|
||||
}
|
||||
} else {
|
||||
ldr.Errorf(s, "addgotsym: unsupported binary format")
|
||||
}
|
||||
|
@ -118,6 +118,8 @@ const (
|
||||
MH_EXECUTE = 0x2
|
||||
|
||||
MH_NOUNDEFS = 0x1
|
||||
MH_DYLDLINK = 0x4
|
||||
MH_PIE = 0x200000
|
||||
)
|
||||
|
||||
const (
|
||||
@ -193,6 +195,56 @@ const (
|
||||
PLATFORM_BRIDGEOS MachoPlatform = 5
|
||||
)
|
||||
|
||||
// rebase table opcode
|
||||
const (
|
||||
REBASE_TYPE_POINTER = 1
|
||||
REBASE_TYPE_TEXT_ABSOLUTE32 = 2
|
||||
REBASE_TYPE_TEXT_PCREL32 = 3
|
||||
|
||||
REBASE_OPCODE_MASK = 0xF0
|
||||
REBASE_IMMEDIATE_MASK = 0x0F
|
||||
REBASE_OPCODE_DONE = 0x00
|
||||
REBASE_OPCODE_SET_TYPE_IMM = 0x10
|
||||
REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB = 0x20
|
||||
REBASE_OPCODE_ADD_ADDR_ULEB = 0x30
|
||||
REBASE_OPCODE_ADD_ADDR_IMM_SCALED = 0x40
|
||||
REBASE_OPCODE_DO_REBASE_IMM_TIMES = 0x50
|
||||
REBASE_OPCODE_DO_REBASE_ULEB_TIMES = 0x60
|
||||
REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB = 0x70
|
||||
REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB = 0x80
|
||||
)
|
||||
|
||||
// bind table opcode
|
||||
const (
|
||||
BIND_TYPE_POINTER = 1
|
||||
BIND_TYPE_TEXT_ABSOLUTE32 = 2
|
||||
BIND_TYPE_TEXT_PCREL32 = 3
|
||||
|
||||
BIND_SPECIAL_DYLIB_SELF = 0
|
||||
BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE = -1
|
||||
BIND_SPECIAL_DYLIB_FLAT_LOOKUP = -2
|
||||
BIND_SPECIAL_DYLIB_WEAK_LOOKUP = -3
|
||||
|
||||
BIND_OPCODE_MASK = 0xF0
|
||||
BIND_IMMEDIATE_MASK = 0x0F
|
||||
BIND_OPCODE_DONE = 0x00
|
||||
BIND_OPCODE_SET_DYLIB_ORDINAL_IMM = 0x10
|
||||
BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB = 0x20
|
||||
BIND_OPCODE_SET_DYLIB_SPECIAL_IMM = 0x30
|
||||
BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM = 0x40
|
||||
BIND_OPCODE_SET_TYPE_IMM = 0x50
|
||||
BIND_OPCODE_SET_ADDEND_SLEB = 0x60
|
||||
BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB = 0x70
|
||||
BIND_OPCODE_ADD_ADDR_ULEB = 0x80
|
||||
BIND_OPCODE_DO_BIND = 0x90
|
||||
BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB = 0xA0
|
||||
BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED = 0xB0
|
||||
BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB = 0xC0
|
||||
BIND_OPCODE_THREADED = 0xD0
|
||||
BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB = 0x00
|
||||
BIND_SUBOPCODE_THREADED_APPLY = 0x01
|
||||
)
|
||||
|
||||
// Mach-O file writing
|
||||
// https://developer.apple.com/mac/library/DOCUMENTATION/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html
|
||||
|
||||
@ -279,7 +331,7 @@ var dylib []string
|
||||
|
||||
var linkoff int64
|
||||
|
||||
func machowrite(arch *sys.Arch, out *OutBuf, linkmode LinkMode) int {
|
||||
func machowrite(ctxt *Link, arch *sys.Arch, out *OutBuf, linkmode LinkMode) int {
|
||||
o1 := out.Offset()
|
||||
|
||||
loadsize := 4 * 4 * ndebug
|
||||
@ -308,11 +360,14 @@ func machowrite(arch *sys.Arch, out *OutBuf, linkmode LinkMode) int {
|
||||
}
|
||||
out.Write32(uint32(len(load)) + uint32(nseg) + uint32(ndebug))
|
||||
out.Write32(uint32(loadsize))
|
||||
flags := uint32(0)
|
||||
if nkind[SymKindUndef] == 0 {
|
||||
out.Write32(MH_NOUNDEFS) /* flags - no undefines */
|
||||
} else {
|
||||
out.Write32(0) /* flags */
|
||||
flags |= MH_NOUNDEFS
|
||||
}
|
||||
if ctxt.IsPIE() && linkmode == LinkInternal {
|
||||
flags |= MH_PIE | MH_DYLDLINK
|
||||
}
|
||||
out.Write32(flags) /* flags */
|
||||
if arch.PtrSize == 8 {
|
||||
out.Write32(0) /* reserved */
|
||||
}
|
||||
@ -712,15 +767,17 @@ func asmbMacho(ctxt *Link) {
|
||||
s2 := ldr.SymSize(ctxt.ArchSyms.LinkEditPLT)
|
||||
s3 := ldr.SymSize(ctxt.ArchSyms.LinkEditGOT)
|
||||
s4 := ldr.SymSize(ldr.Lookup(".machosymstr", 0))
|
||||
s5 := ldr.SymSize(ldr.Lookup(".machorebase", 0))
|
||||
s6 := ldr.SymSize(ldr.Lookup(".machobind", 0))
|
||||
|
||||
if ctxt.LinkMode != LinkExternal {
|
||||
ms := newMachoSeg("__LINKEDIT", 0)
|
||||
ms.vaddr = uint64(Rnd(int64(Segdata.Vaddr+Segdata.Length), int64(*FlagRound)))
|
||||
ms.vsize = uint64(s1) + uint64(s2) + uint64(s3) + uint64(s4)
|
||||
ms.vsize = uint64(s1 + s2 + s3 + s4 + s5 + s6)
|
||||
ms.fileoffset = uint64(linkoff)
|
||||
ms.filesize = ms.vsize
|
||||
ms.prot1 = 7
|
||||
ms.prot2 = 3
|
||||
ms.prot1 = 1
|
||||
ms.prot2 = 1
|
||||
}
|
||||
|
||||
ml := newMachoLoad(ctxt.Arch, LC_SYMTAB, 4)
|
||||
@ -745,9 +802,23 @@ func asmbMacho(ctxt *Link) {
|
||||
stringtouint32(ml.data[4:], lib)
|
||||
}
|
||||
}
|
||||
|
||||
if ctxt.LinkMode != LinkExternal && ctxt.IsPIE() {
|
||||
ml := newMachoLoad(ctxt.Arch, LC_DYLD_INFO_ONLY, 10)
|
||||
ml.data[0] = uint32(linkoff + s1 + s2 + s3 + s4) // rebase off
|
||||
ml.data[1] = uint32(s5) // rebase size
|
||||
ml.data[2] = uint32(linkoff + s1 + s2 + s3 + s4 + s5) // bind off
|
||||
ml.data[3] = uint32(s6) // bind size
|
||||
ml.data[4] = 0 // weak bind off
|
||||
ml.data[5] = 0 // weak bind size
|
||||
ml.data[6] = 0 // lazy bind off
|
||||
ml.data[7] = 0 // lazy bind size
|
||||
ml.data[8] = 0 // export
|
||||
ml.data[9] = 0 // export size
|
||||
}
|
||||
}
|
||||
|
||||
a := machowrite(ctxt.Arch, ctxt.Out, ctxt.LinkMode)
|
||||
a := machowrite(ctxt, ctxt.Arch, ctxt.Out, ctxt.LinkMode)
|
||||
if int32(a) > HEADR {
|
||||
Exitf("HEADR too small: %d > %d", a, HEADR)
|
||||
}
|
||||
@ -989,6 +1060,8 @@ func machodysymtab(ctxt *Link) {
|
||||
func doMachoLink(ctxt *Link) int64 {
|
||||
machosymtab(ctxt)
|
||||
|
||||
machoDyldInfo(ctxt)
|
||||
|
||||
ldr := ctxt.loader
|
||||
|
||||
// write data that will be linkedit section
|
||||
@ -996,6 +1069,8 @@ func doMachoLink(ctxt *Link) int64 {
|
||||
s2 := ctxt.ArchSyms.LinkEditPLT
|
||||
s3 := ctxt.ArchSyms.LinkEditGOT
|
||||
s4 := ldr.Lookup(".machosymstr", 0)
|
||||
s5 := ldr.Lookup(".machorebase", 0)
|
||||
s6 := ldr.Lookup(".machobind", 0)
|
||||
|
||||
// Force the linkedit section to end on a 16-byte
|
||||
// boundary. This allows pure (non-cgo) Go binaries
|
||||
@ -1019,7 +1094,7 @@ func doMachoLink(ctxt *Link) int64 {
|
||||
s4b.AddUint8(0)
|
||||
}
|
||||
|
||||
size := int(ldr.SymSize(s1) + ldr.SymSize(s2) + ldr.SymSize(s3) + ldr.SymSize(s4))
|
||||
size := int(ldr.SymSize(s1) + ldr.SymSize(s2) + ldr.SymSize(s3) + ldr.SymSize(s4) + ldr.SymSize(s5) + ldr.SymSize(s6))
|
||||
|
||||
if size > 0 {
|
||||
linkoff = Rnd(int64(uint64(HEADR)+Segtext.Length), int64(*FlagRound)) + Rnd(int64(Segrelrodata.Filelen), int64(*FlagRound)) + Rnd(int64(Segdata.Filelen), int64(*FlagRound)) + Rnd(int64(Segdwarf.Filelen), int64(*FlagRound))
|
||||
@ -1029,6 +1104,8 @@ func doMachoLink(ctxt *Link) int64 {
|
||||
ctxt.Out.Write(ldr.Data(s2))
|
||||
ctxt.Out.Write(ldr.Data(s3))
|
||||
ctxt.Out.Write(ldr.Data(s4))
|
||||
ctxt.Out.Write(ldr.Data(s5))
|
||||
ctxt.Out.Write(ldr.Data(s6))
|
||||
}
|
||||
|
||||
return Rnd(int64(size), int64(*FlagRound))
|
||||
@ -1172,3 +1249,134 @@ func peekMachoPlatform(m *macho.File) (*MachoPlatformLoad, error) {
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// A rebase entry tells the dynamic linker the data at sym+off needs to be
|
||||
// relocated when the in-memory image moves. (This is somewhat like, say,
|
||||
// ELF R_X86_64_RELATIVE).
|
||||
// For now, the only kind of entry we support is that the data is an absolute
|
||||
// address. That seems all we need.
|
||||
// In the binary it uses a compact stateful bytecode encoding. So we record
|
||||
// entries as we go and build the table at the end.
|
||||
type machoRebaseRecord struct {
|
||||
sym loader.Sym
|
||||
off int64
|
||||
}
|
||||
|
||||
var machorebase []machoRebaseRecord
|
||||
|
||||
func MachoAddRebase(s loader.Sym, off int64) {
|
||||
machorebase = append(machorebase, machoRebaseRecord{s, off})
|
||||
}
|
||||
|
||||
// A bind entry tells the dynamic linker the data at GOT+off should be bound
|
||||
// to the address of the target symbol, which is a dynamic import.
|
||||
// For now, the only kind of entry we support is that the data is an absolute
|
||||
// address, and the source symbol is always the GOT. That seems all we need.
|
||||
// In the binary it uses a compact stateful bytecode encoding. So we record
|
||||
// entries as we go and build the table at the end.
|
||||
type machoBindRecord struct {
|
||||
off int64
|
||||
targ loader.Sym
|
||||
}
|
||||
|
||||
var machobind []machoBindRecord
|
||||
|
||||
func MachoAddBind(off int64, targ loader.Sym) {
|
||||
machobind = append(machobind, machoBindRecord{off, targ})
|
||||
}
|
||||
|
||||
// Generate data for the dynamic linker, used in LC_DYLD_INFO_ONLY load command.
|
||||
// See mach-o/loader.h, struct dyld_info_command, for the encoding.
|
||||
// e.g. https://opensource.apple.com/source/xnu/xnu-6153.81.5/EXTERNAL_HEADERS/mach-o/loader.h
|
||||
func machoDyldInfo(ctxt *Link) {
|
||||
ldr := ctxt.loader
|
||||
rebase := ldr.CreateSymForUpdate(".machorebase", 0)
|
||||
bind := ldr.CreateSymForUpdate(".machobind", 0)
|
||||
|
||||
if !(ctxt.IsPIE() && ctxt.IsInternal()) {
|
||||
return
|
||||
}
|
||||
|
||||
segId := func(seg *sym.Segment) uint8 {
|
||||
switch seg {
|
||||
case &Segtext:
|
||||
return 1
|
||||
case &Segrelrodata:
|
||||
return 2
|
||||
case &Segdata:
|
||||
if Segrelrodata.Length > 0 {
|
||||
return 3
|
||||
}
|
||||
return 2
|
||||
}
|
||||
panic("unknown segment")
|
||||
}
|
||||
|
||||
dylibId := func(s loader.Sym) int {
|
||||
slib := ldr.SymDynimplib(s)
|
||||
for i, lib := range dylib {
|
||||
if lib == slib {
|
||||
return i + 1
|
||||
}
|
||||
}
|
||||
return BIND_SPECIAL_DYLIB_FLAT_LOOKUP // don't know where it is from
|
||||
}
|
||||
|
||||
// Rebase table.
|
||||
// TODO: use more compact encoding. The encoding is stateful, and
|
||||
// we can use delta encoding.
|
||||
rebase.AddUint8(REBASE_OPCODE_SET_TYPE_IMM | REBASE_TYPE_POINTER)
|
||||
for _, r := range machorebase {
|
||||
seg := ldr.SymSect(r.sym).Seg
|
||||
off := uint64(ldr.SymValue(r.sym)+r.off) - seg.Vaddr
|
||||
rebase.AddUint8(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | segId(seg))
|
||||
rebase.AddUleb(off)
|
||||
|
||||
rebase.AddUint8(REBASE_OPCODE_DO_REBASE_IMM_TIMES | 1)
|
||||
}
|
||||
rebase.AddUint8(REBASE_OPCODE_DONE)
|
||||
sz := Rnd(rebase.Size(), 8)
|
||||
rebase.Grow(sz)
|
||||
rebase.SetSize(sz)
|
||||
|
||||
// Bind table.
|
||||
// TODO: compact encoding, as above.
|
||||
// TODO: lazy binding?
|
||||
got := ctxt.GOT
|
||||
seg := ldr.SymSect(got).Seg
|
||||
gotAddr := ldr.SymValue(got)
|
||||
bind.AddUint8(BIND_OPCODE_SET_TYPE_IMM | BIND_TYPE_POINTER)
|
||||
for _, r := range machobind {
|
||||
off := uint64(gotAddr+r.off) - seg.Vaddr
|
||||
bind.AddUint8(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | segId(seg))
|
||||
bind.AddUleb(off)
|
||||
|
||||
d := dylibId(r.targ)
|
||||
if d > 0 && d < 128 {
|
||||
bind.AddUint8(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | uint8(d)&0xf)
|
||||
} else if d >= 128 {
|
||||
bind.AddUint8(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB)
|
||||
bind.AddUleb(uint64(d))
|
||||
} else { // d <= 0
|
||||
bind.AddUint8(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | uint8(d)&0xf)
|
||||
}
|
||||
|
||||
bind.AddUint8(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM)
|
||||
// target symbol name as a C string, with _ prefix
|
||||
bind.AddUint8('_')
|
||||
bind.Addstring(ldr.SymExtname(r.targ))
|
||||
|
||||
bind.AddUint8(BIND_OPCODE_DO_BIND)
|
||||
}
|
||||
bind.AddUint8(BIND_OPCODE_DONE)
|
||||
sz = Rnd(bind.Size(), 16) // make it 16-byte aligned, see the comment in doMachoLink
|
||||
bind.Grow(sz)
|
||||
bind.SetSize(sz)
|
||||
|
||||
// TODO: export table.
|
||||
// The symbols names are encoded as a trie. I'm really too lazy to do that
|
||||
// for now.
|
||||
// Without it, the symbols are not dynamically exported, so they cannot be
|
||||
// e.g. dlsym'd. But internal linking is not the default in that case, so
|
||||
// it is fine.
|
||||
}
|
||||
|
@ -420,3 +420,21 @@ func (sb *SymbolBuilder) MakeWritable() {
|
||||
sb.l.SetAttrReadOnly(sb.symIdx, false)
|
||||
}
|
||||
}
|
||||
|
||||
func (sb *SymbolBuilder) AddUleb(v uint64) {
|
||||
if v < 128 { // common case: 1 byte
|
||||
sb.AddUint8(uint8(v))
|
||||
return
|
||||
}
|
||||
for {
|
||||
c := uint8(v & 0x7f)
|
||||
v >>= 7
|
||||
if v != 0 {
|
||||
c |= 0x80
|
||||
}
|
||||
sb.AddUint8(c)
|
||||
if c&0x80 == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user