mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
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>
This commit is contained in:
parent
a318d56c1e
commit
5abda2618b
8 changed files with 176 additions and 10 deletions
|
|
@ -37,6 +37,7 @@ import (
|
||||||
"cmd/link/internal/loader"
|
"cmd/link/internal/loader"
|
||||||
"cmd/link/internal/sym"
|
"cmd/link/internal/sym"
|
||||||
"debug/elf"
|
"debug/elf"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -472,6 +473,20 @@ func machoreloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sy
|
||||||
rs := r.Xsym
|
rs := r.Xsym
|
||||||
rt := r.Type
|
rt := r.Type
|
||||||
siz := r.Size
|
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.SymType(rs) == sym.SHOSTOBJ || rt == objabi.R_CALLARM64 || rt == objabi.R_ADDRARM64 || rt == objabi.R_ARM64_GOTPCREL {
|
||||||
if ldr.SymDynid(rs) < 0 {
|
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 {
|
switch rt {
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
case objabi.R_ADDR:
|
case objabi.R_ADDR:
|
||||||
v |= ld.MACHO_ARM64_RELOC_UNSIGNED << 28
|
v |= ld.MACHO_ARM64_RELOC_UNSIGNED << 28
|
||||||
case objabi.R_CALLARM64:
|
case objabi.R_CALLARM64:
|
||||||
if r.Xadd != 0 {
|
if xadd != 0 {
|
||||||
ldr.Errorf(s, "ld64 doesn't allow BR26 reloc with non-zero addend: %s+%d", ldr.SymName(rs), r.Xadd)
|
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
|
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 is non-zero, add two MACHO_ARM64_RELOC_ADDEND.
|
||||||
if r.Xadd != 0 {
|
if r.Xadd != 0 {
|
||||||
out.Write32(uint32(sectoff + 4))
|
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(uint32(sectoff + 4))
|
||||||
out.Write32(v | (ld.MACHO_ARM64_RELOC_PAGEOFF12 << 28) | (2 << 25))
|
out.Write32(v | (ld.MACHO_ARM64_RELOC_PAGEOFF12 << 28) | (2 << 25))
|
||||||
if r.Xadd != 0 {
|
if r.Xadd != 0 {
|
||||||
out.Write32(uint32(sectoff))
|
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 |= 1 << 24 // pc-relative bit
|
||||||
v |= ld.MACHO_ARM64_RELOC_PAGE21 << 28
|
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 is non-zero, add two MACHO_ARM64_RELOC_ADDEND.
|
||||||
if r.Xadd != 0 {
|
if r.Xadd != 0 {
|
||||||
out.Write32(uint32(sectoff + 4))
|
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(uint32(sectoff + 4))
|
||||||
out.Write32(v | (ld.MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12 << 28) | (2 << 25))
|
out.Write32(v | (ld.MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12 << 28) | (2 << 25))
|
||||||
if r.Xadd != 0 {
|
if r.Xadd != 0 {
|
||||||
out.Write32(uint32(sectoff))
|
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 |= 1 << 24 // pc-relative bit
|
||||||
v |= ld.MACHO_ARM64_RELOC_GOT_LOAD_PAGE21 << 28
|
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")
|
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)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@ func Init() (*sys.Arch, ld.Arch) {
|
||||||
ElfrelocSize: 24,
|
ElfrelocSize: 24,
|
||||||
Elfsetupplt: elfsetupplt,
|
Elfsetupplt: elfsetupplt,
|
||||||
Gentext: gentext,
|
Gentext: gentext,
|
||||||
|
GenSymsLate: gensymlate,
|
||||||
Machoreloc1: machoreloc1,
|
Machoreloc1: machoreloc1,
|
||||||
MachorelocSize: 8,
|
MachorelocSize: 8,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1815,6 +1815,7 @@ func (state *dodataState) allocateDataSections(ctxt *Link) {
|
||||||
for _, symn := range sym.ReadOnly {
|
for _, symn := range sym.ReadOnly {
|
||||||
symnStartValue := state.datsize
|
symnStartValue := state.datsize
|
||||||
state.assignToSection(sect, symn, sym.SRODATA)
|
state.assignToSection(sect, symn, sym.SRODATA)
|
||||||
|
setCarrierSize(symn, state.datsize-symnStartValue)
|
||||||
if ctxt.HeadType == objabi.Haix {
|
if ctxt.HeadType == objabi.Haix {
|
||||||
// Read-only symbols might be wrapped inside their outer
|
// Read-only symbols might be wrapped inside their outer
|
||||||
// symbol.
|
// symbol.
|
||||||
|
|
@ -1902,6 +1903,7 @@ func (state *dodataState) allocateDataSections(ctxt *Link) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state.assignToSection(sect, symn, sym.SRODATA)
|
state.assignToSection(sect, symn, sym.SRODATA)
|
||||||
|
setCarrierSize(symn, state.datsize-symnStartValue)
|
||||||
if ctxt.HeadType == objabi.Haix {
|
if ctxt.HeadType == objabi.Haix {
|
||||||
// Read-only symbols might be wrapped inside their outer
|
// Read-only symbols might be wrapped inside their outer
|
||||||
// symbol.
|
// symbol.
|
||||||
|
|
@ -1949,6 +1951,7 @@ func (state *dodataState) allocateDataSections(ctxt *Link) {
|
||||||
ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pctab", 0), sect)
|
ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pctab", 0), sect)
|
||||||
ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.functab", 0), sect)
|
ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.functab", 0), sect)
|
||||||
ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.epclntab", 0), sect)
|
ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.epclntab", 0), sect)
|
||||||
|
setCarrierSize(sym.SPCLNTAB, int64(sect.Length))
|
||||||
if ctxt.HeadType == objabi.Haix {
|
if ctxt.HeadType == objabi.Haix {
|
||||||
xcoffUpdateOuterSize(ctxt, int64(sect.Length), sym.SPCLNTAB)
|
xcoffUpdateOuterSize(ctxt, int64(sect.Length), sym.SPCLNTAB)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.
|
// machoShouldExport reports whether a symbol needs to be exported.
|
||||||
//
|
//
|
||||||
// When dynamically linking, all non-local variables and plugin-exported
|
// When dynamically linking, all non-local variables and plugin-exported
|
||||||
|
|
|
||||||
|
|
@ -859,6 +859,7 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
|
||||||
state.carrier = ldr.LookupOrCreateSym("runtime.pclntab", 0)
|
state.carrier = ldr.LookupOrCreateSym("runtime.pclntab", 0)
|
||||||
ldr.MakeSymbolUpdater(state.carrier).SetType(sym.SPCLNTAB)
|
ldr.MakeSymbolUpdater(state.carrier).SetType(sym.SPCLNTAB)
|
||||||
ldr.SetAttrReachable(state.carrier, true)
|
ldr.SetAttrReachable(state.carrier, true)
|
||||||
|
setCarrierSym(sym.SPCLNTAB, state.carrier)
|
||||||
|
|
||||||
state.generatePCHeader(ctxt)
|
state.generatePCHeader(ctxt)
|
||||||
nameOffsets := state.generateFuncnametab(ctxt, funcs)
|
nameOffsets := state.generateFuncnametab(ctxt, funcs)
|
||||||
|
|
|
||||||
|
|
@ -483,6 +483,8 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind {
|
||||||
symtype = s.Sym()
|
symtype = s.Sym()
|
||||||
symtyperel = s.Sym()
|
symtyperel = s.Sym()
|
||||||
}
|
}
|
||||||
|
setCarrierSym(sym.STYPE, symtype)
|
||||||
|
setCarrierSym(sym.STYPERELRO, symtyperel)
|
||||||
}
|
}
|
||||||
|
|
||||||
groupSym := func(name string, t sym.SymKind) loader.Sym {
|
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.SetType(t)
|
||||||
s.SetSize(0)
|
s.SetSize(0)
|
||||||
s.SetLocal(true)
|
s.SetLocal(true)
|
||||||
|
setCarrierSym(t, s.Sym())
|
||||||
return s.Sym()
|
return s.Sym()
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
|
|
@ -800,3 +803,23 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind {
|
||||||
}
|
}
|
||||||
return symGroupType
|
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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -574,6 +574,7 @@ func xcoffUpdateOuterSize(ctxt *Link, size int64, stype sym.SymKind) {
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// TODO: use CarrierSymByType
|
||||||
|
|
||||||
ldr := ctxt.loader
|
ldr := ctxt.loader
|
||||||
switch stype {
|
switch stype {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue