mirror of
https://github.com/golang/go
synced 2024-10-14 20:05:36 +00: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.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build !windows,!static
|
// +build !windows,!static
|
||||||
|
// +build !darwin !internal_pie
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
|
|
@ -3,6 +3,10 @@
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build !windows,!static
|
// +build !windows,!static
|
||||||
|
// +build !darwin !internal_pie
|
||||||
|
|
||||||
|
// Excluded in darwin internal linking PIE mode, as dynamic export is not
|
||||||
|
// supported.
|
||||||
|
|
||||||
package cgotest
|
package cgotest
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build windows static
|
// +build windows static darwin,internal_pie
|
||||||
|
|
||||||
package cgotest
|
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 {
|
func (t *tester) internalLinkPIE() bool {
|
||||||
switch goos + "-" + goarch {
|
switch goos + "-" + goarch {
|
||||||
case "linux-amd64", "linux-arm64",
|
case "darwin-amd64",
|
||||||
"android-arm64":
|
"linux-amd64", "linux-arm64",
|
||||||
return true
|
"android-arm64",
|
||||||
case "windows-amd64", "windows-386", "windows-arm":
|
"windows-amd64", "windows-386", "windows-arm":
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
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")
|
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",
|
case "aix-ppc64",
|
||||||
"android-arm", "android-arm64",
|
"android-arm", "android-arm64",
|
||||||
"dragonfly-amd64",
|
"dragonfly-amd64",
|
||||||
|
@ -1151,7 +1158,7 @@ func (t *tester) cgoTest(dt *distTest) error {
|
||||||
if t.supportedBuildmode("pie") {
|
if t.supportedBuildmode("pie") {
|
||||||
t.addCmd(dt, "misc/cgo/test", t.goTest(), "-buildmode=pie")
|
t.addCmd(dt, "misc/cgo/test", t.goTest(), "-buildmode=pie")
|
||||||
if t.internalLink() && t.internalLinkPIE() {
|
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/testtls", t.goTest(), "-buildmode=pie")
|
||||||
t.addCmd(dt, "misc/cgo/nocgo", 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 {
|
func InternalLinkPIESupported(goos, goarch string) bool {
|
||||||
switch goos + "/" + goarch {
|
switch goos + "/" + goarch {
|
||||||
case "linux/amd64", "linux/arm64",
|
case "darwin/amd64",
|
||||||
|
"linux/amd64", "linux/arm64",
|
||||||
"android/arm64",
|
"android/arm64",
|
||||||
"windows-amd64", "windows-386", "windows-arm":
|
"windows-amd64", "windows-386", "windows-arm":
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -76,9 +76,9 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
|
||||||
targType = ldr.SymType(targ)
|
targType = ldr.SymType(targ)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch r.Type() {
|
switch rt := r.Type(); rt {
|
||||||
default:
|
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()))
|
ldr.Errorf(s, "unexpected relocation type %d (%s)", r.Type(), sym.RelocName(target.Arch, r.Type()))
|
||||||
return false
|
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,
|
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_SIGNED*2 + 0,
|
||||||
objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_BRANCH*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 := ldr.MakeSymbolUpdater(s)
|
||||||
su.SetRelocType(rIdx, objabi.R_ADDR)
|
su.SetRelocType(rIdx, objabi.R_ADDR)
|
||||||
|
|
||||||
if targType == sym.SDYNIMPORT {
|
if targType == sym.SDYNIMPORT {
|
||||||
ldr.Errorf(s, "unexpected reloc for dynamic symbol %s", ldr.SymName(targ))
|
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
|
return true
|
||||||
|
|
||||||
case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_BRANCH*2 + 1:
|
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 {
|
if targType != sym.SDYNIMPORT {
|
||||||
ldr.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", ldr.SymName(targ))
|
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 := ldr.MakeSymbolUpdater(s)
|
||||||
su.SetRelocType(rIdx, objabi.R_PCREL)
|
su.SetRelocType(rIdx, objabi.R_PCREL)
|
||||||
su.SetRelocSym(rIdx, syms.GOT)
|
su.SetRelocSym(rIdx, syms.GOT)
|
||||||
|
@ -355,28 +366,15 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
|
||||||
return true
|
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.
|
// Mach-O relocations are a royal pain to lay out.
|
||||||
// They use a compact stateful bytecode representation
|
// They use a compact stateful bytecode representation.
|
||||||
// that is too much bother to deal with.
|
// Here we record what are needed and encode them later.
|
||||||
// Instead, interpret the C declaration
|
ld.MachoAddRebase(s, int64(r.Off()))
|
||||||
// void *_Cvar_stderr = &stderr;
|
// Not mark r done here. So we still apply it statically,
|
||||||
// as making _Cvar_stderr the name of a GOT entry
|
// so in the file content we'll also have the right offset
|
||||||
// for stderr. This is separate from the usual GOT entry,
|
// to the relocation target. So it can be examined statically
|
||||||
// just in case the C code assigns to the variable,
|
// (e.g. go version).
|
||||||
// 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
|
|
||||||
return true
|
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))
|
ldr.SetPlt(s, int32(plt.Size()-16))
|
||||||
} else if target.IsDarwin() {
|
} else if target.IsDarwin() {
|
||||||
// To do lazy symbol lookup right, we're supposed
|
ld.AddGotSym(target, ldr, syms, s, 0)
|
||||||
// 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)
|
|
||||||
|
|
||||||
sDynid := ldr.SymDynid(s)
|
sDynid := ldr.SymDynid(s)
|
||||||
lep := ldr.MakeSymbolUpdater(syms.LinkEditPLT)
|
lep := ldr.MakeSymbolUpdater(syms.LinkEditPLT)
|
||||||
lep.AddUint32(target.Arch, uint32(sDynid))
|
lep.AddUint32(target.Arch, uint32(sDynid))
|
||||||
|
|
||||||
// jmpq *got+size(IP)
|
plt := ldr.MakeSymbolUpdater(syms.PLT)
|
||||||
ldr.SetPlt(s, int32(plt.Size()))
|
ldr.SetPlt(s, int32(plt.Size()))
|
||||||
|
|
||||||
|
// jmpq *got+size(IP)
|
||||||
plt.AddUint8(0xff)
|
plt.AddUint8(0xff)
|
||||||
plt.AddUint8(0x25)
|
plt.AddUint8(0x25)
|
||||||
plt.AddPCRelPlus(target.Arch, syms.GOT, int64(ldr.SymGot(s)))
|
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")
|
ldr.Errorf(s, "addpltsym: unsupported binary format")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func tlsIEtoLE(P []byte, off, size int) {
|
func tlsIEtoLE(P []byte, off, size int) {
|
||||||
// Transform the PC-relative instruction into a constant load.
|
// Transform the PC-relative instruction into a constant load.
|
||||||
// That is,
|
// That is,
|
||||||
|
|
|
@ -222,6 +222,7 @@ func mustLinkExternal(ctxt *Link) (res bool, reason string) {
|
||||||
switch objabi.GOOS + "/" + objabi.GOARCH {
|
switch objabi.GOOS + "/" + objabi.GOARCH {
|
||||||
case "linux/amd64", "linux/arm64", "android/arm64":
|
case "linux/amd64", "linux/arm64", "android/arm64":
|
||||||
case "windows/386", "windows/amd64", "windows/arm":
|
case "windows/386", "windows/amd64", "windows/arm":
|
||||||
|
case "darwin/amd64":
|
||||||
default:
|
default:
|
||||||
// Internal linking does not support TLS_IE.
|
// Internal linking does not support TLS_IE.
|
||||||
return true, "buildmode=pie"
|
return true, "buildmode=pie"
|
||||||
|
|
|
@ -2526,6 +2526,12 @@ func AddGotSym(target *Target, ldr *loader.Loader, syms *ArchSyms, s loader.Sym,
|
||||||
} else if target.IsDarwin() {
|
} else if target.IsDarwin() {
|
||||||
leg := ldr.MakeSymbolUpdater(syms.LinkEditGOT)
|
leg := ldr.MakeSymbolUpdater(syms.LinkEditGOT)
|
||||||
leg.AddUint32(target.Arch, uint32(ldr.SymDynid(s)))
|
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 {
|
} else {
|
||||||
ldr.Errorf(s, "addgotsym: unsupported binary format")
|
ldr.Errorf(s, "addgotsym: unsupported binary format")
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,6 +118,8 @@ const (
|
||||||
MH_EXECUTE = 0x2
|
MH_EXECUTE = 0x2
|
||||||
|
|
||||||
MH_NOUNDEFS = 0x1
|
MH_NOUNDEFS = 0x1
|
||||||
|
MH_DYLDLINK = 0x4
|
||||||
|
MH_PIE = 0x200000
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -193,6 +195,56 @@ const (
|
||||||
PLATFORM_BRIDGEOS MachoPlatform = 5
|
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
|
// Mach-O file writing
|
||||||
// https://developer.apple.com/mac/library/DOCUMENTATION/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html
|
// https://developer.apple.com/mac/library/DOCUMENTATION/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html
|
||||||
|
|
||||||
|
@ -279,7 +331,7 @@ var dylib []string
|
||||||
|
|
||||||
var linkoff int64
|
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()
|
o1 := out.Offset()
|
||||||
|
|
||||||
loadsize := 4 * 4 * ndebug
|
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(len(load)) + uint32(nseg) + uint32(ndebug))
|
||||||
out.Write32(uint32(loadsize))
|
out.Write32(uint32(loadsize))
|
||||||
|
flags := uint32(0)
|
||||||
if nkind[SymKindUndef] == 0 {
|
if nkind[SymKindUndef] == 0 {
|
||||||
out.Write32(MH_NOUNDEFS) /* flags - no undefines */
|
flags |= MH_NOUNDEFS
|
||||||
} else {
|
|
||||||
out.Write32(0) /* flags */
|
|
||||||
}
|
}
|
||||||
|
if ctxt.IsPIE() && linkmode == LinkInternal {
|
||||||
|
flags |= MH_PIE | MH_DYLDLINK
|
||||||
|
}
|
||||||
|
out.Write32(flags) /* flags */
|
||||||
if arch.PtrSize == 8 {
|
if arch.PtrSize == 8 {
|
||||||
out.Write32(0) /* reserved */
|
out.Write32(0) /* reserved */
|
||||||
}
|
}
|
||||||
|
@ -712,15 +767,17 @@ func asmbMacho(ctxt *Link) {
|
||||||
s2 := ldr.SymSize(ctxt.ArchSyms.LinkEditPLT)
|
s2 := ldr.SymSize(ctxt.ArchSyms.LinkEditPLT)
|
||||||
s3 := ldr.SymSize(ctxt.ArchSyms.LinkEditGOT)
|
s3 := ldr.SymSize(ctxt.ArchSyms.LinkEditGOT)
|
||||||
s4 := ldr.SymSize(ldr.Lookup(".machosymstr", 0))
|
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 {
|
if ctxt.LinkMode != LinkExternal {
|
||||||
ms := newMachoSeg("__LINKEDIT", 0)
|
ms := newMachoSeg("__LINKEDIT", 0)
|
||||||
ms.vaddr = uint64(Rnd(int64(Segdata.Vaddr+Segdata.Length), int64(*FlagRound)))
|
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.fileoffset = uint64(linkoff)
|
||||||
ms.filesize = ms.vsize
|
ms.filesize = ms.vsize
|
||||||
ms.prot1 = 7
|
ms.prot1 = 1
|
||||||
ms.prot2 = 3
|
ms.prot2 = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
ml := newMachoLoad(ctxt.Arch, LC_SYMTAB, 4)
|
ml := newMachoLoad(ctxt.Arch, LC_SYMTAB, 4)
|
||||||
|
@ -745,9 +802,23 @@ func asmbMacho(ctxt *Link) {
|
||||||
stringtouint32(ml.data[4:], lib)
|
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 {
|
if int32(a) > HEADR {
|
||||||
Exitf("HEADR too small: %d > %d", a, HEADR)
|
Exitf("HEADR too small: %d > %d", a, HEADR)
|
||||||
}
|
}
|
||||||
|
@ -989,6 +1060,8 @@ func machodysymtab(ctxt *Link) {
|
||||||
func doMachoLink(ctxt *Link) int64 {
|
func doMachoLink(ctxt *Link) int64 {
|
||||||
machosymtab(ctxt)
|
machosymtab(ctxt)
|
||||||
|
|
||||||
|
machoDyldInfo(ctxt)
|
||||||
|
|
||||||
ldr := ctxt.loader
|
ldr := ctxt.loader
|
||||||
|
|
||||||
// write data that will be linkedit section
|
// write data that will be linkedit section
|
||||||
|
@ -996,6 +1069,8 @@ func doMachoLink(ctxt *Link) int64 {
|
||||||
s2 := ctxt.ArchSyms.LinkEditPLT
|
s2 := ctxt.ArchSyms.LinkEditPLT
|
||||||
s3 := ctxt.ArchSyms.LinkEditGOT
|
s3 := ctxt.ArchSyms.LinkEditGOT
|
||||||
s4 := ldr.Lookup(".machosymstr", 0)
|
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
|
// Force the linkedit section to end on a 16-byte
|
||||||
// boundary. This allows pure (non-cgo) Go binaries
|
// boundary. This allows pure (non-cgo) Go binaries
|
||||||
|
@ -1019,7 +1094,7 @@ func doMachoLink(ctxt *Link) int64 {
|
||||||
s4b.AddUint8(0)
|
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 {
|
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))
|
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(s2))
|
||||||
ctxt.Out.Write(ldr.Data(s3))
|
ctxt.Out.Write(ldr.Data(s3))
|
||||||
ctxt.Out.Write(ldr.Data(s4))
|
ctxt.Out.Write(ldr.Data(s4))
|
||||||
|
ctxt.Out.Write(ldr.Data(s5))
|
||||||
|
ctxt.Out.Write(ldr.Data(s6))
|
||||||
}
|
}
|
||||||
|
|
||||||
return Rnd(int64(size), int64(*FlagRound))
|
return Rnd(int64(size), int64(*FlagRound))
|
||||||
|
@ -1172,3 +1249,134 @@ func peekMachoPlatform(m *macho.File) (*MachoPlatformLoad, error) {
|
||||||
}
|
}
|
||||||
return nil, nil
|
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)
|
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 a new issue