]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/link: handle large relocation addend on darwin/arm64
authorCherry Zhang <cherryyz@google.com>
Mon, 14 Dec 2020 23:52:13 +0000 (18:52 -0500)
committerCherry Zhang <cherryyz@google.com>
Wed, 16 Dec 2020 16:40:57 +0000 (16:40 +0000)
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>
src/cmd/link/internal/arm64/asm.go
src/cmd/link/internal/arm64/obj.go
src/cmd/link/internal/ld/data.go
src/cmd/link/internal/ld/macho.go
src/cmd/link/internal/ld/pcln.go
src/cmd/link/internal/ld/symtab.go
src/cmd/link/internal/ld/xcoff.go
src/cmd/link/link_test.go

index 30819db4c67e5880f055e96ccbb179eb883be176..d6c25fac41162f72edb8ae8c1ceaffd4009eafa3 100644 (file)
@@ -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)
+}
index ab3dfd99f73211cf5b39da8a1c7e3fdfe5b66f5a..bd13295e6130bee64c6d7c44e3dfe0400c95d283 100644 (file)
@@ -55,6 +55,7 @@ func Init() (*sys.Arch, ld.Arch) {
                ElfrelocSize:     24,
                Elfsetupplt:      elfsetupplt,
                Gentext:          gentext,
+               GenSymsLate:      gensymlate,
                Machoreloc1:      machoreloc1,
                MachorelocSize:   8,
 
index 00130044abbb9d3a1d9e1f65ca3fa85095f88e6e..3c5091e6a0cbf75816e6d4220116d0017bea264c 100644 (file)
@@ -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)
        }
index 460564476782d129733d53fb78a04968b831ec82..3630e67c25d60c7b1fddb843a57b8edb80c8884b 100644 (file)
@@ -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
index facb30fe15c7b4aeb60f308fad65d9054877e579..72bf33e611e59ea5b249e9639b0baacb73258a89 100644 (file)
@@ -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)
index 49713896138a7faa0d7cd463eb29b3e9bb1615a8..c98e4de03f8bfe12fdad7165956ef6f56e5ee724 100644 (file)
@@ -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
+}
index 7bf06eaa4614e0c8d433c794ce1f417b8b072243..ba818eaa961e525293d4f573bdf7f62af56bfae1 100644 (file)
@@ -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 {
index 158c6707391bb9a3654b8a087c8bcd2b991d5fd3..4eb02c9e8a16fe17f06574be1b98decf640b1ad5 100644 (file)
@@ -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)
+               }
+       }
+}