[lld] b257d3c - [ELF][PPC64] Suppress toc-indirect to toc-relative relaxation if R_PPC64_TOC16_LO is seen
Fangrui Song via llvm-commits
llvm-commits at lists.llvm.org
Thu Apr 30 09:17:00 PDT 2020
Author: Fangrui Song
Date: 2020-04-30T09:16:51-07:00
New Revision: b257d3c8a897b9999b9bdb660fc8eaad772a0085
URL: https://github.com/llvm/llvm-project/commit/b257d3c8a897b9999b9bdb660fc8eaad772a0085
DIFF: https://github.com/llvm/llvm-project/commit/b257d3c8a897b9999b9bdb660fc8eaad772a0085.diff
LOG: [ELF][PPC64] Suppress toc-indirect to toc-relative relaxation if R_PPC64_TOC16_LO is seen
The current implementation assumes that R_PPC64_TOC16_HA is always followed
by R_PPC64_TOC16_LO_DS. This can break with R_PPC64_TOC16_LO:
// Load the address of the TOC entry, instead of the value stored at that address
addis 3, 2, .LC0 at tloc@ha # R_PPC64_TOC16_HA
addi 3, 3, .LC0 at tloc@l # R_PPC64_TOC16_LO
blr
which is used by boringssl's util/fipstools/delocate/delocate.go
https://github.com/google/boringssl/blob/master/crypto/fipsmodule/FIPS.md has some documentation.
In short, this tool converts an assembly file to avoid any potential relocations.
The distance to an input .toc is not a constant after linking, so it cannot use an `addis;ld` pair.
Instead, it jumps to a stub which loads the TOC entry address with `addis;addi`.
This patch checks the presence of R_PPC64_TOC16_LO and suppresses
toc-indirect to toc-relative relaxation if R_PPC64_TOC16_LO is seen.
This approach is conservative and loses some relaxation opportunities but is easy to implement.
addis 3, 2, .LC0 at toc@ha # no relaxation
addi 3, 3, .LC0 at toc@l # no relaxation
li 9, 0
addis 4, 2, .LC0 at toc@ha # can relax but suppressed
ld 4, .LC0 at toc@l(4) # can relax but suppressed
Also note that interleaved R_PPC64_TOC16_HA and R_PPC64_TOC16_LO_DS is
possible and this patch accounts for that.
addis 3, 2, .LC1 at toc@ha # can relax
addis 4, 2, .LC2 at toc@ha # can relax
ld 3, .LC1 at toc@l(3) # can relax
ld 4, .LC2 at toc@l(4) # can relax
Reviewed By: #powerpc, sfertile
Differential Revision: https://reviews.llvm.org/D78431
Added:
lld/test/ELF/ppc64-toc-relax2.s
Modified:
lld/ELF/InputSection.cpp
lld/ELF/InputSection.h
lld/ELF/Relocations.cpp
lld/ELF/Writer.cpp
Removed:
################################################################################
diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp
index b9b6fef9dc48..dc8c1ee0c56d 100644
--- a/lld/ELF/InputSection.cpp
+++ b/lld/ELF/InputSection.cpp
@@ -45,6 +45,7 @@ std::string toString(const elf::InputSectionBase *sec) {
namespace elf {
std::vector<InputSectionBase *> inputSections;
+DenseSet<std::pair<const Symbol *, uint64_t>> ppc64noTocRelax;
template <class ELFT>
static ArrayRef<uint8_t> getSectionContents(ObjFile<ELFT> &file,
@@ -970,7 +971,13 @@ void InputSectionBase::relocateAlloc(uint8_t *buf, uint8_t *bufEnd) {
target->relaxGot(bufLoc, rel, targetVA);
break;
case R_PPC64_RELAX_TOC:
- if (!tryRelaxPPC64TocIndirection(rel, bufLoc))
+ // rel.sym refers to the STT_SECTION symbol associated to the .toc input
+ // section. If an R_PPC64_TOC16_LO (.toc + addend) references the TOC
+ // entry, there may be R_PPC64_TOC16_HA not paired with
+ // R_PPC64_TOC16_LO_DS. Don't relax. This loses some relaxation
+ // opportunities but is safe.
+ if (ppc64noTocRelax.count({rel.sym, rel.addend}) ||
+ !tryRelaxPPC64TocIndirection(rel, bufLoc))
target->relocate(bufLoc, rel, targetVA);
break;
case R_RELAX_TLS_IE_TO_LE:
diff --git a/lld/ELF/InputSection.h b/lld/ELF/InputSection.h
index 719971b9c72a..112c6ab49a38 100644
--- a/lld/ELF/InputSection.h
+++ b/lld/ELF/InputSection.h
@@ -397,6 +397,11 @@ inline bool isDebugSection(const InputSectionBase &sec) {
// The list of all input sections.
extern std::vector<InputSectionBase *> inputSections;
+// The set of TOC entries (.toc + addend) for which we should not apply
+// toc-indirect to toc-relative relaxation. const Symbol * refers to the
+// STT_SECTION symbol associated to the .toc input section.
+extern llvm::DenseSet<std::pair<const Symbol *, uint64_t>> ppc64noTocRelax;
+
} // namespace elf
std::string toString(const elf::InputSectionBase *);
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index 21fec6e44aa0..ff0680191587 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -1294,17 +1294,6 @@ static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
if (expr == R_NONE)
return;
- // We can separate the small code model relocations into 2 categories:
- // 1) Those that access the compiler generated .toc sections.
- // 2) Those that access the linker allocated got entries.
- // lld allocates got entries to symbols on demand. Since we don't try to sort
- // the got entries in any way, we don't have to track which objects have
- // got-based small code model relocs. The .toc sections get placed after the
- // end of the linker allocated .got section and we do sort those so sections
- // addressed with small code model relocations come first.
- if (config->emachine == EM_PPC64 && isPPC64SmallCodeModelTocReloc(type))
- sec.file->ppc64SmallCodeModelTocRelocs = true;
-
if (sym.isGnuIFunc() && !config->zText && config->warnIfuncTextrel) {
warn("using ifunc symbols when text relocations are allowed may produce "
"a binary that will segfault, if the object file is linked with "
@@ -1318,6 +1307,25 @@ static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
// Read an addend.
int64_t addend = computeAddend<ELFT>(rel, end, sec, expr, sym.isLocal());
+ if (config->emachine == EM_PPC64) {
+ // We can separate the small code model relocations into 2 categories:
+ // 1) Those that access the compiler generated .toc sections.
+ // 2) Those that access the linker allocated got entries.
+ // lld allocates got entries to symbols on demand. Since we don't try to
+ // sort the got entries in any way, we don't have to track which objects
+ // have got-based small code model relocs. The .toc sections get placed
+ // after the end of the linker allocated .got section and we do sort those
+ // so sections addressed with small code model relocations come first.
+ if (isPPC64SmallCodeModelTocReloc(type))
+ sec.file->ppc64SmallCodeModelTocRelocs = true;
+
+ // Record the TOC entry (.toc + addend) as not relaxable. See the comment in
+ // InputSectionBase::relocateAlloc().
+ if (type == R_PPC64_TOC16_LO && sym.isSection() && isa<Defined>(sym) &&
+ cast<Defined>(sym).section->name == ".toc")
+ ppc64noTocRelax.insert({&sym, addend});
+ }
+
// Relax relocations.
//
// If we know that a PLT entry will be resolved within the same ELF module, we
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 6236ac51b1bb..3c197391a91a 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -1933,6 +1933,7 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
// we can correctly decide if a dynamic relocation is needed. This is called
// after processSymbolAssignments() because it needs to know whether a
// linker-script-defined symbol is absolute.
+ ppc64noTocRelax.clear();
if (!config->relocatable) {
forEachRelSec(scanRelocations<ELFT>);
reportUndefinedSymbols<ELFT>();
diff --git a/lld/test/ELF/ppc64-toc-relax2.s b/lld/test/ELF/ppc64-toc-relax2.s
new file mode 100644
index 000000000000..77b4a1c99948
--- /dev/null
+++ b/lld/test/ELF/ppc64-toc-relax2.s
@@ -0,0 +1,66 @@
+# REQUIRES: ppc
+# RUN: llvm-mc -filetype=obj -triple=powerpc64le %s -o %t.o
+# RUN: echo 'addis 5, 2, .LC0 at toc@ha; ld 5, .LC0 at toc@l(5); foo: \
+# RUN: .section .toc,"aw", at progbits; .LC0: .tc foo[TC], foo' \
+# RUN: | llvm-mc -filetype=obj -triple=powerpc64le - -o %t1.o
+# RUN: ld.lld %t.o %t1.o -o %t
+# RUN: llvm-objdump -d %t | FileCheck %s
+
+# CHECK-LABEL: <_start>:
+.globl _start
+_start:
+## Perform toc-indirect to toc-relative relaxation even if there are unrelated instructions in between.
+# CHECK-NEXT: addis 3, 2, -2
+# CHECK-NEXT: li 9, 0
+# CHECK-NEXT: addi 3, 3, 32752
+# CHECK-NEXT: lwa 3, 0(3)
+ addis 3, 2, .LC1 at toc@ha # R_PPC64_TOC16_HA
+ li 9, 0
+ ld 3, .LC1 at toc@l(3) # R_PPC64_TOC16_LO_DS
+ lwa 3, 0(3)
+
+## R_PPC64_TOC16_HA and R_PPC64_TOC16_LO_DS can interleave.
+# CHECK-NEXT: addis 3, 2, -2
+# CHECK-NEXT: addis 4, 2, -2
+# CHECK-NEXT: addi 3, 3, 32752
+# CHECK-NEXT: addi 4, 4, 32756
+ addis 3, 2, .LC1 at toc@ha
+ addis 4, 2, .LC2 at toc@ha
+ ld 3, .LC1 at toc@l(3)
+ ld 4, .LC2 at toc@l(4)
+
+## We choose to be conservative: the presence of R_PPC64_TOC16_LO
+## suppresses relaxation for the symbol.
+## R_PPC64_TOC16_HA and R_PPC64_TOC16_LO_DS pairs are not relaxed as well.
+# CHECK-NEXT: nop
+# CHECK-NEXT: addi 3, 2, -32768
+# CHECK-NEXT: li 9, 0
+# CHECK-NEXT: nop
+# CHECK-NEXT: ld 4, -32768(2)
+ addis 3, 2, .LC0 at toc@ha # R_PPC64_TOC16_HA
+ addi 3, 3, .LC0 at toc@l # R_PPC64_TOC16_LO
+ li 9, 0
+ addis 4, 2, .LC0 at toc@ha
+ ld 4, .LC0 at toc@l(4)
+
+# CHECK-COUNT-3: blr
+AES_encrypt:
+ blr
+AES_decrypt:
+ blr
+BN_free:
+ blr
+
+## %t1.o has relaxable relocation pairs referencing its .toc which is
diff erent
+## from %t.o(.toc). The suppression in %t.o does not affect %t1.o even if
+## the relocation addends are the same.
+# CHECK-NEXT: addis 5, 2, -1
+# CHECK-NEXT: addi 5, 5, -32768
+
+.section .toc,"aw", at progbits
+.LC0:
+ .tc AES_encrypt[TC], AES_encrypt
+.LC1:
+ .tc AES_decrypt[TC], AES_decrypt
+.LC2:
+ .tc BN_free[TC], BN_free
More information about the llvm-commits
mailing list