diff --git a/src/cmd/link/internal/arm64/asm.go b/src/cmd/link/internal/arm64/asm.go index 2d964e9cb6..093aadb343 100644 --- a/src/cmd/link/internal/arm64/asm.go +++ b/src/cmd/link/internal/arm64/asm.go @@ -456,15 +456,14 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade // set up addend for eventual relocation via outer symbol. rs, off := ld.FoldSubSymbolOffset(ldr, rs) - rr.Xadd = r.Add() + off + xadd := r.Add() + off rst := ldr.SymType(rs) if rst != sym.SHOSTOBJ && rst != sym.SDYNIMPORT && ldr.SymSect(rs) == nil { ldr.Errorf(s, "missing section for %s", ldr.SymName(rs)) } - rr.Xsym = rs nExtReloc = 2 // need two ELF/Mach-O relocations. see elfreloc1/machoreloc1 - if target.IsDarwin() && rt == objabi.R_ADDRARM64 && rr.Xadd != 0 { + if target.IsDarwin() && rt == objabi.R_ADDRARM64 && xadd != 0 { nExtReloc = 4 // need another two relocations for non-zero addend } @@ -488,9 +487,8 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade // can only encode 24-bit of signed addend, but the instructions // supports 33-bit of signed addend, so we always encode the // addend in place. - o0 |= (uint32((rr.Xadd>>12)&3) << 29) | (uint32((rr.Xadd>>12>>2)&0x7ffff) << 5) - o1 |= uint32(rr.Xadd&0xfff) << 10 - rr.Xadd = 0 + o0 |= (uint32((xadd>>12)&3) << 29) | (uint32((xadd>>12>>2)&0x7ffff) << 5) + o1 |= uint32(xadd&0xfff) << 10 // when laid out, the instruction order must always be o1, o2. if target.IsBigEndian() { @@ -508,8 +506,6 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade if rt == objabi.R_ARM64_TLS_IE { nExtReloc = 2 // need two ELF relocations. see elfreloc1 } - rr.Xsym = rs - rr.Xadd = r.Add() return val, nExtReloc, isOk } } @@ -693,6 +689,42 @@ func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc2, sym.RelocVarian return -1 } +func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc2, s loader.Sym) (loader.ExtReloc, bool) { + rs := ldr.ResolveABIAlias(r.Sym()) + var rr loader.ExtReloc + switch rt := r.Type(); rt { + case objabi.R_ARM64_GOTPCREL, + objabi.R_ADDRARM64: + + // set up addend for eventual relocation via outer symbol. + rs, off := ld.FoldSubSymbolOffset(ldr, rs) + rr.Xadd = r.Add() + off + rr.Xsym = rs + + // Note: ld64 currently has a bug that any non-zero addend for BR26 relocation + // will make the linking fail because it thinks the code is not PIC even though + // the BR26 relocation should be fully resolved at link time. + // That is the reason why the next if block is disabled. When the bug in ld64 + // is fixed, we can enable this block and also enable duff's device in cmd/7g. + if false && target.IsDarwin() { + // Mach-O wants the addend to be encoded in the instruction + // Note that although Mach-O supports ARM64_RELOC_ADDEND, it + // can only encode 24-bit of signed addend, but the instructions + // supports 33-bit of signed addend, so we always encode the + // addend in place. + rr.Xadd = 0 + } + return rr, true + case objabi.R_CALLARM64, + objabi.R_ARM64_TLS_LE, + objabi.R_ARM64_TLS_IE: + rr.Xsym = rs + rr.Xadd = r.Add() + return rr, true + } + return rr, false +} + func elfsetupplt(ctxt *ld.Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) { if plt.Size() == 0 { // stp x16, x30, [sp, #-16]! diff --git a/src/cmd/link/internal/arm64/obj.go b/src/cmd/link/internal/arm64/obj.go index e7c23978a6..37b72b6c37 100644 --- a/src/cmd/link/internal/arm64/obj.go +++ b/src/cmd/link/internal/arm64/obj.go @@ -50,6 +50,7 @@ func Init() (*sys.Arch, ld.Arch) { Archinit: archinit, Archreloc: archreloc, Archrelocvariant: archrelocvariant, + Extreloc: extreloc, Elfreloc1: elfreloc1, ElfrelocSize: 24, Elfsetupplt: elfsetupplt, diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index d5034ae01c..26bad1b891 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -264,7 +264,7 @@ func (st *relocSymState) relocsym(s loader.Sym, P []byte) { o = int64(target.Arch.ByteOrder.Uint64(P[off:])) } var rp *loader.ExtReloc - if target.IsExternal() { + if target.IsExternal() && !target.StreamExtRelocs() { // Don't pass &rr directly to Archreloc, which will escape rr // even if this case is not taken. Instead, as Archreloc will // likely return true, we speculatively add rr to extRelocs @@ -274,12 +274,16 @@ func (st *relocSymState) relocsym(s loader.Sym, P []byte) { } out, nExtReloc, ok := thearch.Archreloc(target, ldr, syms, r, rp, s, o) if target.IsExternal() { - if nExtReloc == 0 { - // No external relocation needed. Speculation failed. Undo the append. - extRelocs = extRelocs[:len(extRelocs)-1] + if target.StreamExtRelocs() { + extraExtReloc += nExtReloc } else { - // Account for the difference between host relocations and Go relocations. - extraExtReloc += nExtReloc - 1 + if nExtReloc == 0 { + // No external relocation needed. Speculation failed. Undo the append. + extRelocs = extRelocs[:len(extRelocs)-1] + } else { + // Account for the difference between host relocations and Go relocations. + extraExtReloc += nExtReloc - 1 + } } } needExtReloc = false // already appended @@ -638,8 +642,7 @@ func extreloc(ctxt *Link, ldr *loader.Loader, s loader.Sym, r loader.Reloc2, ri switch rt { default: - // TODO: handle arch-specific relocations - panic("unsupported") + return thearch.Extreloc(&target, ldr, r, s) case objabi.R_TLS_LE, objabi.R_TLS_IE: if target.IsElf() { diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index a489da02bc..e45458d262 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -239,6 +239,11 @@ type Arch struct { Asmb func(*Link, *loader.Loader) Asmb2 func(*Link, *loader.Loader) + // Extreloc is an arch-specific hook that converts a Go relocation to an + // external relocation. Return the external relocation and whether it is + // needed. + Extreloc func(*Target, *loader.Loader, loader.Reloc2, loader.Sym) (loader.ExtReloc, bool) + Elfreloc1 func(*Link, *OutBuf, *loader.Loader, loader.Sym, loader.ExtRelocView, int64) bool ElfrelocSize uint32 // size of an ELF relocation record, must match Elfreloc1. Elfsetupplt func(ctxt *Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go index 4a0bb5d381..1089d309a1 100644 --- a/src/cmd/link/internal/ld/macho.go +++ b/src/cmd/link/internal/ld/macho.go @@ -1045,17 +1045,25 @@ func machorelocsect(ctxt *Link, out *OutBuf, sect *sym.Section, syms []loader.Sy if ldr.SymValue(s) >= int64(eaddr) { break } - relocs := ldr.ExtRelocs(s) + + // Compute external relocations on the go, and pass to Machoreloc1 + // to stream out. + relocs := ldr.Relocs(s) for ri := 0; ri < relocs.Count(); ri++ { - r := relocs.At(ri) - if r.Xsym == 0 { + r := relocs.At2(ri) + rr, ok := extreloc(ctxt, ldr, s, r, ri) + if !ok { + continue + } + if rr.Xsym == 0 { ldr.Errorf(s, "missing xsym in relocation") continue } - if !ldr.AttrReachable(r.Xsym) { - ldr.Errorf(s, "unreachable reloc %d (%s) target %v", r.Type(), sym.RelocName(ctxt.Arch, r.Type()), ldr.SymName(r.Xsym)) + if !ldr.AttrReachable(rr.Xsym) { + ldr.Errorf(s, "unreachable reloc %d (%s) target %v", r.Type(), sym.RelocName(ctxt.Arch, r.Type()), ldr.SymName(rr.Xsym)) } - if !thearch.Machoreloc1(ctxt.Arch, out, ldr, s, r, int64(uint64(ldr.SymValue(s)+int64(r.Off()))-sect.Vaddr)) { + rv := loader.ExtRelocView{Reloc2: r, ExtReloc: rr} + if !thearch.Machoreloc1(ctxt.Arch, out, ldr, s, rv, int64(uint64(ldr.SymValue(s)+int64(r.Off()))-sect.Vaddr)) { ldr.Errorf(s, "unsupported obj reloc %d (%s)/%d to %s", r.Type(), sym.RelocName(ctxt.Arch, r.Type()), r.Siz(), ldr.SymName(r.Sym())) } } diff --git a/src/cmd/link/internal/ld/target.go b/src/cmd/link/internal/ld/target.go index 8702db121e..d075bce17f 100644 --- a/src/cmd/link/internal/ld/target.go +++ b/src/cmd/link/internal/ld/target.go @@ -184,5 +184,5 @@ func (t *Target) IsBigEndian() bool { // Temporary helper. func (t *Target) StreamExtRelocs() bool { - return t.IsELF && (t.IsAMD64() || t.Is386()) + return (t.IsELF || t.IsDarwin()) && (t.IsAMD64() || t.Is386() || t.IsARM64()) }