diff --git a/lld/ELF/Arch/PPC64.cpp b/lld/ELF/Arch/PPC64.cpp --- a/lld/ELF/Arch/PPC64.cpp +++ b/lld/ELF/Arch/PPC64.cpp @@ -6,11 +6,13 @@ // //===----------------------------------------------------------------------===// +#include "SymbolTable.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" #include "Thunks.h" #include "lld/Common/ErrorHandler.h" +#include "lld/Common/Memory.h" #include "llvm/Support/Endian.h" using namespace llvm; @@ -104,6 +106,84 @@ return type == R_PPC64_TOC16 || type == R_PPC64_TOC16_DS; } +static bool addOptional(StringRef name, uint64_t value, + std::vector &defined) { + Symbol *sym = symtab->find(name); + if (!sym || sym->isDefined()) + return false; + sym->resolve(Defined{/*file=*/nullptr, saver.save(name), STB_GLOBAL, + STV_HIDDEN, STT_FUNC, value, + /*size=*/0, /*section=*/nullptr}); + defined.push_back(cast(sym)); + return true; +} + +// If from is 14, write ${prefix}14: firstInsn; ${prefix}15: +// firstInsn+0x200008; ...; ${prefix}31: firstInsn+(31-14)*0x200008; $tail +// The labels are defined only if they exist in the symbol table. +static void writeSequence(MutableArrayRef buf, const char *prefix, + int from, uint32_t firstInsn, + ArrayRef tail) { + std::vector defined; + char name[16]; + int first; + uint32_t *ptr = buf.data(); + for (int r = from; r < 32; ++r) { + format("%s%d", prefix, r).snprint(name, sizeof(name)); + if (addOptional(name, 4 * (r - from), defined) && defined.size() == 1) + first = r - from; + write32(ptr++, firstInsn + 0x200008 * (r - from)); + } + for (uint32_t insn : tail) + write32(ptr++, insn); + assert(ptr == &*buf.end()); + + if (defined.empty()) + return; + // The full section content has the extent of [begin, end). We drop unused + // instructions and write [first,end). + auto *sec = make( + nullptr, SHF_ALLOC, SHT_PROGBITS, 4, + makeArrayRef(reinterpret_cast(buf.data() + first), + 4 * (buf.size() - first)), + ".text"); + inputSections.push_back(sec); + for (Defined *sym : defined) { + sym->section = sec; + sym->value -= 4 * first; + } +} + +// Implements some save and restore functions as described by ELF V2 ABI to be +// compatible with GCC. With GCC -Os, when the number of call-saved registers +// exceeds a certain threshold, GCC generates _savegpr0_* _restgpr0_* calls and +// expects the linker to define them. See +// https://sourceware.org/pipermail/binutils/2002-February/017444.html and +// https://sourceware.org/pipermail/binutils/2004-August/036765.html . This is +// weird because libgcc.a would be the natural place. The linker generation +// approach has the advantage that the linker can generate multiple copies to +// avoid long branch thunks. However, we don't consider the advantage +// significant enough to complicate our trunk implementation, so we take the +// simple approach and synthesize .text sections providing the implementation. +void elf::addPPC64SaveRestore() { + static uint32_t savegpr0[20], restgpr0[21], savegpr1[19], restgpr1[19]; + constexpr uint32_t blr = 0x4e800020, mtlr_0 = 0x7c0803a6; + + // _restgpr0_14: ld 14, -144(1); _restgpr0_15: ld 15, -136(1); ... + // Tail: ld 0, 16(1); mtlr 0; blr + writeSequence(restgpr0, "_restgpr0_", 14, 0xe9c1ff70, + {0xe8010010, mtlr_0, blr}); + // _restgpr1_14: ld 14, -144(12); _restgpr1_15: ld 15, -136(12); ... + // Tail: blr + writeSequence(restgpr1, "_restgpr1_", 14, 0xe9ccff70, {blr}); + // _savegpr0_14: std 14, -144(1); _savegpr0_15: std 15, -136(1); ... + // Tail: std 0, 16(1); blr + writeSequence(savegpr0, "_savegpr0_", 14, 0xf9c1ff70, {0xf8010010, blr}); + // _savegpr1_14: std 14, -144(12); _savegpr1_15: std 15, -136(12); ... + // Tail: blr + writeSequence(savegpr1, "_savegpr1_", 14, 0xf9ccff70, {blr}); +} + // Find the R_PPC64_ADDR64 in .rela.toc with matching offset. template static std::pair diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -213,6 +213,7 @@ // the .toc section. bool isPPC64SmallCodeModelTocReloc(RelType type); +void addPPC64SaveRestore(); uint64_t getPPC64TocBase(); uint64_t getAArch64Page(uint64_t expr); diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -264,6 +264,8 @@ // glibc *crt1.o has a undefined reference to _SDA_BASE_. Since we don't // support Small Data Area, define it arbitrarily as 0. addOptionalRegular("_SDA_BASE_", nullptr, 0, STV_HIDDEN); + } else if (config->emachine == EM_PPC64) { + addPPC64SaveRestore(); } // The Power Architecture 64-bit v2 ABI defines a TableOfContents (TOC) which diff --git a/lld/test/ELF/ppc64-restgpr0.s b/lld/test/ELF/ppc64-restgpr0.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/ppc64-restgpr0.s @@ -0,0 +1,38 @@ +# REQUIRES: ppc +## Test code sequences of synthesized _restgpr0_{14..31} + +# RUN: llvm-mc -filetype=obj -triple=ppc64le %s -o %t14.o +# RUN: ld.lld %t14.o -o %t14 +# RUN: llvm-objdump -d %t14 | FileCheck --check-prefix=R14 %s + +# R14-LABEL: <_restgpr0_14>: +# R14-NEXT: ld 14, -144(1) +# R14-NEXT: ld 15, -136(1) +# R14-EMPTY: +# R14-NEXT: <_restgpr0_16>: +# R14-NEXT: ld 16, -128(1) +# R14: ld 31, -8(1) +# R14-NEXT: ld 0, 16(1) +# R14-NEXT: mtlr 0 +# R14-NEXT: blr + +## Don't synthesize _restgpr0_{14..30} because they are unused. +# RUN: echo 'bl _restgpr0_31' | llvm-mc -filetype=obj -triple=ppc64 - -o %t31.o +# RUN: ld.lld %t31.o -o %t31 +# RUN: llvm-objdump -d %t31 | FileCheck --check-prefix=R31 %s + +# R31-LABEL: Disassembly of section .text: +# R31-EMPTY: +# R31-NEXT: <_restgpr0_31>: +# R31-NEXT: ld 31, -8(1) +# R31-NEXT: ld 0, 16(1) +# R31-NEXT: mtlr 0 +# R31-NEXT: blr + +# RUN: echo 'bl _restgpr0_32' | llvm-mc -filetype=obj -triple=ppc64 - -o %t32.o +# RUN: not ld.lld %t32.o -o /dev/null + +.globl _start +_start: + bl _restgpr0_14 + bl _restgpr0_16 diff --git a/lld/test/ELF/ppc64-restgpr1.s b/lld/test/ELF/ppc64-restgpr1.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/ppc64-restgpr1.s @@ -0,0 +1,34 @@ +# REQUIRES: ppc +## Test code sequences of synthesized _restgpr1_{14..31} + +# RUN: llvm-mc -filetype=obj -triple=ppc64le %s -o %t14.o +# RUN: ld.lld %t14.o -o %t14 +# RUN: llvm-objdump -d %t14 | FileCheck --check-prefix=R14 %s + +# R14: <_restgpr1_14>: +# R14-NEXT: ld 14, -144(12) +# R14-NEXT: ld 15, -136(12) +# R14-EMPTY: +# R14-NEXT: <_restgpr1_16>: +# R14-NEXT: ld 16, -128(12) +# R14: ld 31, -8(12) +# R14-NEXT: blr + +## Don't synthesize _restgpr1_{14..30} because they are unused. +# RUN: echo 'bl _restgpr1_31' | llvm-mc -filetype=obj -triple=ppc64 - -o %t31.o +# RUN: ld.lld %t31.o -o %t31 +# RUN: llvm-objdump -d %t31 | FileCheck --check-prefix=R31 %s + +# R31-LABEL: Disassembly of section .text: +# R31-EMPTY: +# R31-NEXT: <_restgpr1_31>: +# R31-NEXT: ld 31, -8(12) +# R31-NEXT: blr + +# RUN: echo 'bl _restgpr1_32' | llvm-mc -filetype=obj -triple=ppc64le - -o %t32.o +# RUN: not ld.lld %t32.o -o /dev/null + +.globl _start +_start: + bl _restgpr1_14 + bl _restgpr1_16 diff --git a/lld/test/ELF/ppc64-savegpr0.s b/lld/test/ELF/ppc64-savegpr0.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/ppc64-savegpr0.s @@ -0,0 +1,36 @@ +# REQUIRES: ppc +## Test code sequences of synthesized _savegpr0_{14..31} + +# RUN: llvm-mc -filetype=obj -triple=ppc64le %s -o %t14.o +# RUN: ld.lld %t14.o -o %t14 +# RUN: llvm-objdump -d %t14 | FileCheck --check-prefix=R14 %s + +# R14-LABEL: <_savegpr0_14>: +# R14-NEXT: std 14, -144(1) +# R14-NEXT: std 15, -136(1) +# R14-EMPTY: +# R14-NEXT: <_savegpr0_16>: +# R14-NEXT: std 16, -128(1) +# R14: std 31, -8(1) +# R14-NEXT: std 0, 16(1) +# R14-NEXT: blr + +## Don't synthesize _savegpr0_{14..30} because they are unused. +# RUN: echo 'bl _savegpr0_31' | llvm-mc -filetype=obj -triple=ppc64 - -o %t31.o +# RUN: ld.lld %t31.o -o %t31 +# RUN: llvm-objdump -d %t31 | FileCheck --check-prefix=R31 %s + +# R31-LABEL: Disassembly of section .text: +# R31-EMPTY: +# R31-NEXT: <_savegpr0_31>: +# R31-NEXT: std 31, -8(1) +# R31-NEXT: std 0, 16(1) +# R31-NEXT: blr + +# RUN: echo 'bl _savegpr0_32' | llvm-mc -filetype=obj -triple=ppc64 - -o %t32.o +# RUN: not ld.lld %t32.o -o /dev/null + +.globl _start +_start: + bl _savegpr0_14 + bl _savegpr0_16 diff --git a/lld/test/ELF/ppc64-savegpr1.s b/lld/test/ELF/ppc64-savegpr1.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/ppc64-savegpr1.s @@ -0,0 +1,34 @@ +# REQUIRES: ppc +## Test code sequences of synthesized _savegpr1_{14..31} + +# RUN: llvm-mc -filetype=obj -triple=ppc64le %s -o %t14.o +# RUN: ld.lld %t14.o -o %t14 +# RUN: llvm-objdump -d %t14 | FileCheck --check-prefix=R14 %s + +# R14-LABEL: <_savegpr1_14>: +# R14-NEXT: std 14, -144(12) +# R14-NEXT: std 15, -136(12) +# R14-EMPTY: +# R14-NEXT: <_savegpr1_16>: +# R14-NEXT: std 16, -128(12) +# R14: std 31, -8(12) +# R14-NEXT: blr + +## Don't synthesize _savegpr1_{14..30} because they are unused. +# RUN: echo 'bl _savegpr1_31' | llvm-mc -filetype=obj -triple=ppc64 - -o %t31.o +# RUN: ld.lld %t31.o -o %t31 +# RUN: llvm-objdump -d %t31 | FileCheck --check-prefix=R31 %s + +# R31-LABEL: Disassembly of section .text: +# R31-EMPTY: +# R31-NEXT: <_savegpr1_31>: +# R31-NEXT: std 31, -8(12) +# R31-NEXT: blr + +# RUN: echo 'bl _savegpr1_32' | llvm-mc -filetype=obj -triple=ppc64le - -o %t32.o +# RUN: not ld.lld %t32.o -o /dev/null + +.globl _start +_start: + bl _savegpr1_14 + bl _savegpr1_16 diff --git a/lld/test/ELF/ppc64-saveres.s b/lld/test/ELF/ppc64-saveres.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/ppc64-saveres.s @@ -0,0 +1,31 @@ +# REQUIRES: ppc +## Test that some save and restore functions can be synthesized. +## The code sequences are tested by ppc64-restgpr*.s and ppc64-savegpr*.s + +# RUN: llvm-mc -filetype=obj -triple=ppc64le %s -o %t.o +# RUN: ld.lld -shared %t.o -o %t.so +# RUN: llvm-readelf -s %t.so | FileCheck --check-prefix=NM %s +# RUN: llvm-objdump -d %t.so | FileCheck %s + +## The synthesized symbols are not exported. +# NM: FUNC LOCAL HIDDEN {{.*}} _restgpr0_30 +# NM-NEXT: FUNC LOCAL HIDDEN {{.*}} _restgpr1_30 +# NM-NEXT: FUNC LOCAL HIDDEN {{.*}} _savegpr0_30 +# NM-NEXT: FUNC LOCAL HIDDEN {{.*}} _savegpr1_30 + +# CHECK: 00000000000[[#%x,RESTGPR0:]] <_restgpr0_30>: +# CHECK: 00000000000[[#%x,RESTGPR1:]] <_restgpr1_30>: +# CHECK: 00000000000[[#%x,SAVEGPR0:]] <_savegpr0_30>: +# CHECK: 00000000000[[#%x,SAVEGPR1:]] <_savegpr1_30>: +# CHECK-LABEL: <_start>: +# CHECK-NEXT: bl 0x[[#RESTGPR0]] +# CHECK-NEXT: bl 0x[[#RESTGPR1]] +# CHECK-NEXT: bl 0x[[#SAVEGPR0]] +# CHECK-NEXT: bl 0x[[#SAVEGPR1]] + +.globl _start +_start: + bl _restgpr0_30 + bl _restgpr1_30 + bl _savegpr0_30 + bl _savegpr1_30