aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPaul E. Murphy <murp@ibm.com>2022-06-13 10:59:31 -0500
committerGopher Robot <gobot@golang.org>2022-09-15 21:11:05 +0000
commit7fda98a8d90139fed07d7f8ca80d248a5cbc1e93 (patch)
treee73a3f56c03d149f67a8eef71741734fa6ea1418 /src
parente7a2014fac885392e14205c93fff3fd8db84fa8d (diff)
downloadgo-7fda98a8d90139fed07d7f8ca80d248a5cbc1e93.tar.gz
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 <laboger@linux.vnet.ibm.com> Reviewed-by: Cherry Mui <cherryyz@google.com> Run-TryBot: Paul Murphy <murp@ibm.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Jenny Rakoczy <jenny@golang.org> Auto-Submit: Jenny Rakoczy <jenny@golang.org> Run-TryBot: Jenny Rakoczy <jenny@golang.org>
Diffstat (limited to 'src')
-rw-r--r--src/cmd/go/testdata/script/test_ppc64le_cgo_inline_plt.txt38
-rw-r--r--src/cmd/link/internal/loadelf/ldelf.go13
-rw-r--r--src/cmd/link/internal/ppc64/asm.go47
3 files changed, 97 insertions, 1 deletions
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 <stdio.h>
+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.