From fa42da156a166ecf81ab7d72f53619ad9e6f134a Mon Sep 17 00:00:00 2001 From: Cherry Mui Date: Wed, 8 Mar 2023 16:38:32 -0500 Subject: [PATCH] [release-branch.go1.20] cmd/link: use label symbols for Duff's devices on darwin/arm64 On darwin, the external linker generally supports CALL relocations with addend. One exception is that for a very large binary when it decides to insert a trampoline, instead of applying the addend to the call target (in the trampoline), it applies the addend to the CALL instruction in the caller, i.e. generating a call to trampoline+addend, which is not the correct address and usually points to unreloated functions. To work around this, we use label symbols so the CALL is targeting a label symbol without addend. To make things simple we always use label symbols for CALLs with addend (in external linking mode on darwin/arm64), even for small binaries. Updates #58935. Fixes #58954. Change-Id: I38aed6b62a0496c277c589b5accbbef6aace8dd5 Reviewed-on: https://go-review.googlesource.com/c/go/+/474620 TryBot-Result: Gopher Robot Run-TryBot: Cherry Mui Reviewed-by: Than McIntosh (cherry picked from commit 7dbd6de7d45da622e532e149de616e159286e1d4) Reviewed-on: https://go-review.googlesource.com/c/go/+/475175 --- src/cmd/link/internal/arm64/asm.go | 66 ++++++++++++++++-------------- 1 file changed, 36 insertions(+), 30 deletions(-) diff --git a/src/cmd/link/internal/arm64/asm.go b/src/cmd/link/internal/arm64/asm.go index fc0ad3fb4e4d24..7109d84fa32b81 100644 --- a/src/cmd/link/internal/arm64/asm.go +++ b/src/cmd/link/internal/arm64/asm.go @@ -537,6 +537,13 @@ func machoreloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sy ldr.Errorf(s, "internal error: relocation addend overflow: %s+0x%x", ldr.SymName(rs), xadd) } } + if rt == objabi.R_CALLARM64 && xadd != 0 { + label := ldr.Lookup(offsetLabelName(ldr, rs, xadd), ldr.SymVersion(rs)) + if label != 0 { + xadd = ldr.SymValue(rs) + xadd - ldr.SymValue(label) // should always be 0 (checked below) + rs = label + } + } if ldr.SymType(rs) == sym.SHOSTOBJ || rt == objabi.R_CALLARM64 || rt == objabi.R_ARM64_PCREL_LDST8 || rt == objabi.R_ARM64_PCREL_LDST16 || @@ -564,10 +571,9 @@ func machoreloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sy v |= ld.MACHO_ARM64_RELOC_UNSIGNED << 28 case objabi.R_CALLARM64: if xadd != 0 { - out.Write32(uint32(sectoff)) - out.Write32((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(xadd&0xffffff)) + // Addend should be handled above via label symbols. + ldr.Errorf(s, "unexpected non-zero addend: %s+%d", ldr.SymName(rs), xadd) } - v |= 1 << 24 // pc-relative bit v |= ld.MACHO_ARM64_RELOC_BRANCH26 << 28 case objabi.R_ADDRARM64, @@ -788,9 +794,6 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade case objabi.R_CALLARM64: nExtReloc = 1 - if target.IsDarwin() && r.Add() != 0 { - nExtReloc = 2 // need another relocation for addend - } return val, nExtReloc, isOk case objabi.R_ARM64_TLS_LE: @@ -1184,6 +1187,9 @@ func gensymlate(ctxt *ld.Link, ldr *loader.Loader) { // addend. For large symbols, we generate "label" symbols in the middle, so // that relocations can target them with smaller addends. // On Windows, we only get 21 bits, again (presumably) signed. + // Also, on Windows (always) and Darwin (for very large binaries), the external + // linker does't support CALL relocations with addend, so we generate "label" + // symbols for functions of which we can target the middle (Duff's devices). if !ctxt.IsDarwin() && !ctxt.IsWindows() || !ctxt.IsExternal() { return } @@ -1193,19 +1199,6 @@ func gensymlate(ctxt *ld.Link, ldr *loader.Loader) { limit = peRelocLimit } - if ctxt.IsDarwin() { - 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+limit, s+2*limit, etc. addLabelSyms := func(s loader.Sym, limit, sz int64) { v := ldr.SymValue(s) @@ -1225,23 +1218,36 @@ func gensymlate(ctxt *ld.Link, ldr *loader.Loader) { } } + // Generate symbol names for every offset we need in duffcopy/duffzero (only 64 each). + if s := ldr.Lookup("runtime.duffcopy", sym.SymVerABIInternal); s != 0 && ldr.AttrReachable(s) { + addLabelSyms(s, 8, 8*64) + } + if s := ldr.Lookup("runtime.duffzero", sym.SymVerABIInternal); s != 0 && ldr.AttrReachable(s) { + addLabelSyms(s, 4, 4*64) + } + + if ctxt.IsDarwin() { + big := false + for _, seg := range ld.Segments { + if seg.Length >= machoRelocLimit { + big = true + break + } + } + if !big { + return // skip work if nothing big + } + } + for s, n := loader.Sym(1), loader.Sym(ldr.NSym()); s < n; s++ { if !ldr.AttrReachable(s) { continue } t := ldr.SymType(s) if t == sym.STEXT { - if ctxt.IsDarwin() || ctxt.IsWindows() { - // Cannot relocate into middle of function. - // Generate symbol names for every offset we need in duffcopy/duffzero (only 64 each). - switch ldr.SymName(s) { - case "runtime.duffcopy": - addLabelSyms(s, 8, 8*64) - case "runtime.duffzero": - addLabelSyms(s, 4, 4*64) - } - } - continue // we don't target the middle of other functions + // Except for Duff's devices (handled above), we don't + // target the middle of a function. + continue } if t >= sym.SDWARFSECT { continue // no need to add label for DWARF symbols