aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCherry Zhang <cherryyz@google.com>2020-12-14 18:52:13 -0500
committerCherry Zhang <cherryyz@google.com>2020-12-16 16:40:57 +0000
commit5abda2618b6cda692ae9b04a9a9fc706888a0e71 (patch)
tree8a75cce0ed4c64bc2dcb23831cc82be6e837f1cf
parenta318d56c1e6e89996a3852a780f45c792d860d64 (diff)
downloadgo-5abda2618b6cda692ae9b04a9a9fc706888a0e71.tar.gz
go-5abda2618b6cda692ae9b04a9a9fc706888a0e71.zip
cmd/link: handle large relocation addend on darwin/arm64
Mach-O relocation addend is signed 24-bit. When external linking, if the addend is larger, we cannot put it directly into a Mach-O relocation. This CL handles large addend by creating "label" symbols at sym+0x800000, sym+(0x800000*2), etc., and emitting Mach-O relocations that target the label symbols with a smaller addend. The label symbols are generated late (similar to what we do for RISC-V64). One complexity comes from handling of carrier symbols, which does not track its size or its inner symbols. But relocations can target them. We track them in a side table (similar to what we do for XCOFF, xcoffUpdateOuterSize). Fixes #42738. Change-Id: I8c53ab2397f8b88870d26f00e9026285e5ff5584 Reviewed-on: https://go-review.googlesource.com/c/go/+/278332 Trust: Cherry Zhang <cherryyz@google.com> Run-TryBot: Cherry Zhang <cherryyz@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Than McIntosh <thanm@google.com>
-rw-r--r--src/cmd/link/internal/arm64/asm.go94
-rw-r--r--src/cmd/link/internal/arm64/obj.go1
-rw-r--r--src/cmd/link/internal/ld/data.go3
-rw-r--r--src/cmd/link/internal/ld/macho.go9
-rw-r--r--src/cmd/link/internal/ld/pcln.go1
-rw-r--r--src/cmd/link/internal/ld/symtab.go23
-rw-r--r--src/cmd/link/internal/ld/xcoff.go1
-rw-r--r--src/cmd/link/link_test.go54
8 files changed, 176 insertions, 10 deletions
diff --git a/src/cmd/link/internal/arm64/asm.go b/src/cmd/link/internal/arm64/asm.go
index 30819db4c6..d6c25fac41 100644
--- a/src/cmd/link/internal/arm64/asm.go
+++ b/src/cmd/link/internal/arm64/asm.go
@@ -37,6 +37,7 @@ import (
"cmd/link/internal/loader"
"cmd/link/internal/sym"
"debug/elf"
+ "fmt"
"log"
)
@@ -472,6 +473,20 @@ func machoreloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sy
rs := r.Xsym
rt := r.Type
siz := r.Size
+ xadd := r.Xadd
+
+ if xadd != signext24(xadd) {
+ // If the relocation target would overflow the addend, then target
+ // a linker-manufactured label symbol with a smaller addend instead.
+ label := ldr.Lookup(machoLabelName(ldr, rs, xadd), ldr.SymVersion(rs))
+ if label != 0 {
+ xadd = ldr.SymValue(rs) + xadd - ldr.SymValue(label)
+ rs = label
+ }
+ if xadd != signext24(xadd) {
+ ldr.Errorf(s, "internal error: relocation addend overflow: %s+0x%x", ldr.SymName(rs), xadd)
+ }
+ }
if ldr.SymType(rs) == sym.SHOSTOBJ || rt == objabi.R_CALLARM64 || rt == objabi.R_ADDRARM64 || rt == objabi.R_ARM64_GOTPCREL {
if ldr.SymDynid(rs) < 0 {
@@ -489,18 +504,14 @@ func machoreloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sy
}
}
- if r.Xadd != signext24(r.Xadd) {
- ldr.Errorf(s, "relocation addend overflow: %s+0x%x", ldr.SymName(rs), r.Xadd)
- }
-
switch rt {
default:
return false
case objabi.R_ADDR:
v |= ld.MACHO_ARM64_RELOC_UNSIGNED << 28
case objabi.R_CALLARM64:
- if r.Xadd != 0 {
- ldr.Errorf(s, "ld64 doesn't allow BR26 reloc with non-zero addend: %s+%d", ldr.SymName(rs), r.Xadd)
+ if xadd != 0 {
+ ldr.Errorf(s, "ld64 doesn't allow BR26 reloc with non-zero addend: %s+%d", ldr.SymName(rs), xadd)
}
v |= 1 << 24 // pc-relative bit
@@ -511,13 +522,13 @@ func machoreloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sy
// if r.Xadd is non-zero, add two MACHO_ARM64_RELOC_ADDEND.
if r.Xadd != 0 {
out.Write32(uint32(sectoff + 4))
- out.Write32((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(r.Xadd&0xffffff))
+ out.Write32((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(xadd&0xffffff))
}
out.Write32(uint32(sectoff + 4))
out.Write32(v | (ld.MACHO_ARM64_RELOC_PAGEOFF12 << 28) | (2 << 25))
if r.Xadd != 0 {
out.Write32(uint32(sectoff))
- out.Write32((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(r.Xadd&0xffffff))
+ out.Write32((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(xadd&0xffffff))
}
v |= 1 << 24 // pc-relative bit
v |= ld.MACHO_ARM64_RELOC_PAGE21 << 28
@@ -527,13 +538,13 @@ func machoreloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sy
// if r.Xadd is non-zero, add two MACHO_ARM64_RELOC_ADDEND.
if r.Xadd != 0 {
out.Write32(uint32(sectoff + 4))
- out.Write32((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(r.Xadd&0xffffff))
+ out.Write32((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(xadd&0xffffff))
}
out.Write32(uint32(sectoff + 4))
out.Write32(v | (ld.MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12 << 28) | (2 << 25))
if r.Xadd != 0 {
out.Write32(uint32(sectoff))
- out.Write32((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(r.Xadd&0xffffff))
+ out.Write32((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(xadd&0xffffff))
}
v |= 1 << 24 // pc-relative bit
v |= ld.MACHO_ARM64_RELOC_GOT_LOAD_PAGE21 << 28
@@ -972,3 +983,66 @@ func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
ldr.Errorf(s, "addpltsym: unsupported binary format")
}
}
+
+const machoRelocLimit = 1 << 23
+
+func gensymlate(ctxt *ld.Link, ldr *loader.Loader) {
+ // When external linking on darwin, Mach-O relocation has only signed 24-bit
+ // addend. For large symbols, we generate "label" symbols in the middle, so
+ // that relocations can target them with smaller addends.
+ if !ctxt.IsDarwin() || !ctxt.IsExternal() {
+ return
+ }
+
+ big := false
+ for _, seg := range ld.Segments {
+ if seg.Length >= machoRelocLimit {
+ big = true
+ break
+ }
+ }
+ if !big {
+ return // skip work if nothing big
+ }
+
+ // addLabelSyms adds "label" symbols at s+machoRelocLimit, s+2*machoRelocLimit, etc.
+ addLabelSyms := func(s loader.Sym, sz int64) {
+ v := ldr.SymValue(s)
+ for off := int64(machoRelocLimit); off < sz; off += machoRelocLimit {
+ p := ldr.LookupOrCreateSym(machoLabelName(ldr, s, off), ldr.SymVersion(s))
+ ldr.SetAttrReachable(p, true)
+ ldr.SetSymValue(p, v+off)
+ ldr.SetSymSect(p, ldr.SymSect(s))
+ ld.AddMachoSym(ldr, p)
+ //fmt.Printf("gensymlate %s %x\n", ldr.SymName(p), ldr.SymValue(p))
+ }
+ }
+
+ for s, n := loader.Sym(1), loader.Sym(ldr.NSym()); s < n; s++ {
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+ if ldr.SymType(s) == sym.STEXT {
+ continue // we don't target the middle of a function
+ }
+ sz := ldr.SymSize(s)
+ if sz <= machoRelocLimit {
+ continue
+ }
+ addLabelSyms(s, sz)
+ }
+
+ // Also for carrier symbols (for which SymSize is 0)
+ for _, ss := range ld.CarrierSymByType {
+ if ss.Sym != 0 && ss.Size > machoRelocLimit {
+ addLabelSyms(ss.Sym, ss.Size)
+ }
+ }
+}
+
+// machoLabelName returns the name of the "label" symbol used for a
+// relocation targetting s+off. The label symbols is used on darwin
+// when external linking, so that the addend fits in a Mach-O relocation.
+func machoLabelName(ldr *loader.Loader, s loader.Sym, off int64) string {
+ return fmt.Sprintf("%s.%d", ldr.SymExtname(s), off/machoRelocLimit)
+}
diff --git a/src/cmd/link/internal/arm64/obj.go b/src/cmd/link/internal/arm64/obj.go
index ab3dfd99f7..bd13295e61 100644
--- a/src/cmd/link/internal/arm64/obj.go
+++ b/src/cmd/link/internal/arm64/obj.go
@@ -55,6 +55,7 @@ func Init() (*sys.Arch, ld.Arch) {
ElfrelocSize: 24,
Elfsetupplt: elfsetupplt,
Gentext: gentext,
+ GenSymsLate: gensymlate,
Machoreloc1: machoreloc1,
MachorelocSize: 8,
diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
index 00130044ab..3c5091e6a0 100644
--- a/src/cmd/link/internal/ld/data.go
+++ b/src/cmd/link/internal/ld/data.go
@@ -1815,6 +1815,7 @@ func (state *dodataState) allocateDataSections(ctxt *Link) {
for _, symn := range sym.ReadOnly {
symnStartValue := state.datsize
state.assignToSection(sect, symn, sym.SRODATA)
+ setCarrierSize(symn, state.datsize-symnStartValue)
if ctxt.HeadType == objabi.Haix {
// Read-only symbols might be wrapped inside their outer
// symbol.
@@ -1902,6 +1903,7 @@ func (state *dodataState) allocateDataSections(ctxt *Link) {
}
}
state.assignToSection(sect, symn, sym.SRODATA)
+ setCarrierSize(symn, state.datsize-symnStartValue)
if ctxt.HeadType == objabi.Haix {
// Read-only symbols might be wrapped inside their outer
// symbol.
@@ -1949,6 +1951,7 @@ func (state *dodataState) allocateDataSections(ctxt *Link) {
ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pctab", 0), sect)
ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.functab", 0), sect)
ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.epclntab", 0), sect)
+ setCarrierSize(sym.SPCLNTAB, int64(sect.Length))
if ctxt.HeadType == objabi.Haix {
xcoffUpdateOuterSize(ctxt, int64(sect.Length), sym.SPCLNTAB)
}
diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go
index 4605644767..3630e67c25 100644
--- a/src/cmd/link/internal/ld/macho.go
+++ b/src/cmd/link/internal/ld/macho.go
@@ -969,6 +969,15 @@ func machosymorder(ctxt *Link) {
}
}
+// AddMachoSym adds s to Mach-O symbol table, used in GenSymLate.
+// Currently only used on ARM64 when external linking.
+func AddMachoSym(ldr *loader.Loader, s loader.Sym) {
+ ldr.SetSymDynid(s, int32(nsortsym))
+ sortsym = append(sortsym, s)
+ nsortsym++
+ nkind[symkind(ldr, s)]++
+}
+
// machoShouldExport reports whether a symbol needs to be exported.
//
// When dynamically linking, all non-local variables and plugin-exported
diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go
index facb30fe15..72bf33e611 100644
--- a/src/cmd/link/internal/ld/pcln.go
+++ b/src/cmd/link/internal/ld/pcln.go
@@ -859,6 +859,7 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
state.carrier = ldr.LookupOrCreateSym("runtime.pclntab", 0)
ldr.MakeSymbolUpdater(state.carrier).SetType(sym.SPCLNTAB)
ldr.SetAttrReachable(state.carrier, true)
+ setCarrierSym(sym.SPCLNTAB, state.carrier)
state.generatePCHeader(ctxt)
nameOffsets := state.generateFuncnametab(ctxt, funcs)
diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go
index 4971389613..c98e4de03f 100644
--- a/src/cmd/link/internal/ld/symtab.go
+++ b/src/cmd/link/internal/ld/symtab.go
@@ -483,6 +483,8 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind {
symtype = s.Sym()
symtyperel = s.Sym()
}
+ setCarrierSym(sym.STYPE, symtype)
+ setCarrierSym(sym.STYPERELRO, symtyperel)
}
groupSym := func(name string, t sym.SymKind) loader.Sym {
@@ -490,6 +492,7 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind {
s.SetType(t)
s.SetSize(0)
s.SetLocal(true)
+ setCarrierSym(t, s.Sym())
return s.Sym()
}
var (
@@ -800,3 +803,23 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind {
}
return symGroupType
}
+
+// CarrierSymByType tracks carrier symbols and their sizes.
+var CarrierSymByType [sym.SXREF]struct {
+ Sym loader.Sym
+ Size int64
+}
+
+func setCarrierSym(typ sym.SymKind, s loader.Sym) {
+ if CarrierSymByType[typ].Sym != 0 {
+ panic(fmt.Sprintf("carrier symbol for type %v already set", typ))
+ }
+ CarrierSymByType[typ].Sym = s
+}
+
+func setCarrierSize(typ sym.SymKind, sz int64) {
+ if CarrierSymByType[typ].Size != 0 {
+ panic(fmt.Sprintf("carrier symbol size for type %v already set", typ))
+ }
+ CarrierSymByType[typ].Size = sz
+}
diff --git a/src/cmd/link/internal/ld/xcoff.go b/src/cmd/link/internal/ld/xcoff.go
index 7bf06eaa46..ba818eaa96 100644
--- a/src/cmd/link/internal/ld/xcoff.go
+++ b/src/cmd/link/internal/ld/xcoff.go
@@ -574,6 +574,7 @@ func xcoffUpdateOuterSize(ctxt *Link, size int64, stype sym.SymKind) {
if size == 0 {
return
}
+ // TODO: use CarrierSymByType
ldr := ctxt.loader
switch stype {
diff --git a/src/cmd/link/link_test.go b/src/cmd/link/link_test.go
index 158c670739..4eb02c9e8a 100644
--- a/src/cmd/link/link_test.go
+++ b/src/cmd/link/link_test.go
@@ -925,3 +925,57 @@ func TestIssue42396(t *testing.T) {
t.Fatalf("error message incorrect: expected it to contain %q but instead got:\n%s\n", want, out)
}
}
+
+const testLargeRelocSrc = `
+package main
+
+var x = [1<<25]byte{1<<23: 23, 1<<24: 24}
+
+func main() {
+ check(x[1<<23-1], 0)
+ check(x[1<<23], 23)
+ check(x[1<<23+1], 0)
+ check(x[1<<24-1], 0)
+ check(x[1<<24], 24)
+ check(x[1<<24+1], 0)
+}
+
+func check(x, y byte) {
+ if x != y {
+ panic("FAIL")
+ }
+}
+`
+
+func TestLargeReloc(t *testing.T) {
+ // Test that large relocation addend is handled correctly.
+ // In particular, on darwin/arm64 when external linking,
+ // Mach-O relocation has only 24-bit addend. See issue #42738.
+ testenv.MustHaveGoBuild(t)
+ t.Parallel()
+
+ tmpdir, err := ioutil.TempDir("", "TestIssue42396")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ src := filepath.Join(tmpdir, "x.go")
+ err = ioutil.WriteFile(src, []byte(testLargeRelocSrc), 0666)
+ if err != nil {
+ t.Fatalf("failed to write source file: %v", err)
+ }
+ cmd := exec.Command(testenv.GoToolPath(t), "run", src)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Errorf("build failed: %v. output:\n%s", err, out)
+ }
+
+ if testenv.HasCGO() { // currently all targets that support cgo can external link
+ cmd = exec.Command(testenv.GoToolPath(t), "run", "-ldflags=-linkmode=external", src)
+ out, err = cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("build failed: %v. output:\n%s", err, out)
+ }
+ }
+}