From 7fda98a8d90139fed07d7f8ca80d248a5cbc1e93 Mon Sep 17 00:00:00 2001 From: "Paul E. Murphy" Date: Mon, 13 Jun 2022 10:59:31 -0500 Subject: [PATCH] cmd/link: support -fno-plt compiled gcc objects on ppc64le This is the initial trivial implemenation. Further improvements can be made for local calls. A test is added, but the -fno-plt option is ignored by gcc if binutils does not support inline plt relocations, so the test is effectively skipped on such hosts. Fixes #53345 Change-Id: Ibf31c26b1a8551c942b21019df8782c00b7a563e Reviewed-on: https://go-review.googlesource.com/c/go/+/412714 Reviewed-by: Lynn Boger Reviewed-by: Cherry Mui Run-TryBot: Paul Murphy TryBot-Result: Gopher Robot Reviewed-by: Jenny Rakoczy Auto-Submit: Jenny Rakoczy Run-TryBot: Jenny Rakoczy --- .../script/test_ppc64le_cgo_inline_plt.txt | 38 +++++++++++++++ src/cmd/link/internal/loadelf/ldelf.go | 13 ++++- src/cmd/link/internal/ppc64/asm.go | 47 +++++++++++++++++++ 3 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 src/cmd/go/testdata/script/test_ppc64le_cgo_inline_plt.txt diff --git a/src/cmd/go/testdata/script/test_ppc64le_cgo_inline_plt.txt b/src/cmd/go/testdata/script/test_ppc64le_cgo_inline_plt.txt new file mode 100644 index 0000000000..7a9cd7b6d8 --- /dev/null +++ b/src/cmd/go/testdata/script/test_ppc64le_cgo_inline_plt.txt @@ -0,0 +1,38 @@ +# Verify the linker will correctly resolve +# ppc64le objects compiled with gcc's -fno-plt +# option. This inlines PLT calls, and generates +# additional reloc types which the internal linker +# should handle. +# +# Verifies golang.org/issue/53345 +# +# Note, older gcc/clang may accept this option, but +# ignore it if binutils does not support the relocs. +[!gc] skip +[!cgo] skip +[!ppc64le] skip + +env CGO_CFLAGS='-fno-plt -O2 -g' + +go build -ldflags='-linkmode=internal' +exec ./noplttest +stdout helloworld + +-- go.mod -- +module noplttest + +-- noplttest.go -- +package main + +/* +#include +void helloworld(void) { + printf("helloworld\n"); + fflush(stdout); +} +*/ +import "C" + +func main() { + C.helloworld() +} diff --git a/src/cmd/link/internal/loadelf/ldelf.go b/src/cmd/link/internal/loadelf/ldelf.go index 6014caca09..74f7cb15a0 100644 --- a/src/cmd/link/internal/loadelf/ldelf.go +++ b/src/cmd/link/internal/loadelf/ldelf.go @@ -1108,8 +1108,19 @@ func relSize(arch *sys.Arch, pn string, elftype uint32) (uint8, uint8, error) { PPC64 | uint32(elf.R_PPC64_TOC16_LO_DS)<<16, PPC64 | uint32(elf.R_PPC64_REL16_LO)<<16, PPC64 | uint32(elf.R_PPC64_REL16_HI)<<16, - PPC64 | uint32(elf.R_PPC64_REL16_HA)<<16: + PPC64 | uint32(elf.R_PPC64_REL16_HA)<<16, + PPC64 | uint32(elf.R_PPC64_PLT16_HA)<<16, + PPC64 | uint32(elf.R_PPC64_PLT16_LO_DS)<<16: return 2, 4, nil + + // PPC64 inline PLT sequence hint relocations (-fno-plt) + // These are informational annotations to assist linker optimizations. + case PPC64 | uint32(elf.R_PPC64_PLTSEQ)<<16, + PPC64 | uint32(elf.R_PPC64_PLTCALL)<<16, + PPC64 | uint32(elf.R_PPC64_PLTCALL_NOTOC)<<16, + PPC64 | uint32(elf.R_PPC64_PLTSEQ_NOTOC)<<16: + return 0, 0, nil + } } diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go index 5d5fbe2a97..21bc430e04 100644 --- a/src/cmd/link/internal/ppc64/asm.go +++ b/src/cmd/link/internal/ppc64/asm.go @@ -476,6 +476,53 @@ func addelfdynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s lo ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_HA|sym.RV_CHECK_OVERFLOW) su.SetRelocAdd(rIdx, r.Add()+2) return true + + // When compiling with gcc's -fno-plt option (no PLT), the following code and relocation + // sequences may be present to call an external function: + // + // 1. addis Rx,foo@R_PPC64_PLT16_HA + // 2. ld 12,foo@R_PPC64_PLT16_LO_DS(Rx) + // 3. mtctr 12 ; foo@R_PPC64_PLTSEQ + // 4. bctrl ; foo@R_PPC64_PLTCALL + // 5. ld r2,24(r1) + // + // Note, 5 is required to follow the R_PPC64_PLTCALL. Similarly, relocations targeting + // instructions 3 and 4 are zero sized informational relocations. + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_PLT16_HA), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_PLT16_LO_DS): + su := ldr.MakeSymbolUpdater(s) + isPLT16_LO_DS := r.Type() == objabi.ElfRelocOffset+objabi.RelocType(elf.R_PPC64_PLT16_LO_DS) + if isPLT16_LO_DS { + ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_DS) + } else { + ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_HA|sym.RV_CHECK_OVERFLOW) + } + su.SetRelocType(rIdx, objabi.R_POWER_TOC) + if targType == sym.SDYNIMPORT { + // This is an external symbol, make space in the GOT and retarget the reloc. + ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_PPC64_GLOB_DAT)) + su.SetRelocSym(rIdx, syms.GOT) + su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ))) + } else if targType == sym.STEXT { + // This is the half-way solution to transforming a PLT sequence into nops + bl targ + // We turn it into an indirect call by transforming step 2 into an addi. + // Fixing up the whole sequence is a bit more involved. + if isPLT16_LO_DS { + const MASK_OP_LD = 63<<26 | 0x3 + const OP_LD = 58 << 26 + const OP_ADDI = 14 << 26 + op := target.Arch.ByteOrder.Uint32(su.Data()[r.Off():]) + if op&MASK_OP_LD != OP_LD { + ldr.Errorf(s, "relocation R_PPC64_PLT16_LO_DS expected an ld opcode. Found non-ld opcode %08X.", op) + } + op = (op &^ MASK_OP_LD) | OP_ADDI + su.MakeWritable() + su.SetUint32(target.Arch, int64(r.Off()), op) + } + } else { + ldr.Errorf(s, "unexpected PLT relocation target symbol type %s", targType.String()) + } + return true } // Handle references to ELF symbols from our own object files.