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>
diff --git a/misc/cgo/test/issue4029.c b/misc/cgo/test/issue4029.c
index 30646ad..e6a777f 100644
--- a/misc/cgo/test/issue4029.c
+++ b/misc/cgo/test/issue4029.c
@@ -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>
diff --git a/misc/cgo/test/issue4029.go b/misc/cgo/test/issue4029.go
index 1bf029d..8602ce19 100644
--- a/misc/cgo/test/issue4029.go
+++ b/misc/cgo/test/issue4029.go
@@ -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
diff --git a/misc/cgo/test/issue4029w.go b/misc/cgo/test/issue4029w.go
index eee33f7..de0cf21 100644
--- a/misc/cgo/test/issue4029w.go
+++ b/misc/cgo/test/issue4029w.go
@@ -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
diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go
index 03e6866..94b8161 100644
--- a/src/cmd/dist/test.go
+++ b/src/cmd/dist/test.go
@@ -964,10 +964,10 @@
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 @@
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 @@
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")
diff --git a/src/cmd/internal/sys/supported.go b/src/cmd/internal/sys/supported.go
index 55709f3..07be998 100644
--- a/src/cmd/internal/sys/supported.go
+++ b/src/cmd/internal/sys/supported.go
@@ -118,7 +118,8 @@
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
diff --git a/src/cmd/link/internal/amd64/asm.go b/src/cmd/link/internal/amd64/asm.go
index e5a6ef5..3658ac0 100644
--- a/src/cmd/link/internal/amd64/asm.go
+++ b/src/cmd/link/internal/amd64/asm.go
@@ -76,9 +76,9 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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,
diff --git a/src/cmd/link/internal/ld/config.go b/src/cmd/link/internal/ld/config.go
index aaf74b5..c680d11 100644
--- a/src/cmd/link/internal/ld/config.go
+++ b/src/cmd/link/internal/ld/config.go
@@ -222,6 +222,7 @@
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"
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index 5fe028d..8d04973 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -2526,6 +2526,12 @@
} 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")
}
diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go
index eab6537..a19a4af 100644
--- a/src/cmd/link/internal/ld/macho.go
+++ b/src/cmd/link/internal/ld/macho.go
@@ -118,6 +118,8 @@
MH_EXECUTE = 0x2
MH_NOUNDEFS = 0x1
+ MH_DYLDLINK = 0x4
+ MH_PIE = 0x200000
)
const (
@@ -193,6 +195,56 @@
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 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 @@
}
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 @@
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 @@
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 doMachoLink(ctxt *Link) int64 {
machosymtab(ctxt)
+ machoDyldInfo(ctxt)
+
ldr := ctxt.loader
// write data that will be linkedit section
@@ -996,6 +1069,8 @@
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 @@
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 @@
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 @@
}
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.
+}
diff --git a/src/cmd/link/internal/loader/symbolbuilder.go b/src/cmd/link/internal/loader/symbolbuilder.go
index c0c723d..5d37da8 100644
--- a/src/cmd/link/internal/loader/symbolbuilder.go
+++ b/src/cmd/link/internal/loader/symbolbuilder.go
@@ -420,3 +420,21 @@
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
+ }
+ }
+}