mirror of
https://github.com/golang/go
synced 2024-11-11 21:10:21 -07: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
@ -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)
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ func Init() (*sys.Arch, ld.Arch) {
|
||||
ElfrelocSize: 24,
|
||||
Elfsetupplt: elfsetupplt,
|
||||
Gentext: gentext,
|
||||
GenSymsLate: gensymlate,
|
||||
Machoreloc1: machoreloc1,
|
||||
MachorelocSize: 8,
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user