diff --git a/lld/ELF/Arch/AArch64.cpp b/lld/ELF/Arch/AArch64.cpp index 9538dd4a70bae..b42711a812f9f 100644 --- a/lld/ELF/Arch/AArch64.cpp +++ b/lld/ELF/Arch/AArch64.cpp @@ -8,10 +8,13 @@ #include "InputFiles.h" #include "OutputSections.h" +#include "Relocations.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" #include "lld/Common/ErrorHandler.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/Support/Endian.h" @@ -83,6 +86,7 @@ class AArch64 : public TargetInfo { uint64_t val) const override; RelExpr adjustTlsExpr(RelType type, RelExpr expr) const override; void relocateAlloc(InputSectionBase &sec, uint8_t *buf) const override; + bool canFoldSection(const SmallVector &relocs) const override; private: void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const; @@ -975,6 +979,20 @@ void AArch64::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const { } } +bool AArch64::canFoldSection(const SmallVector &relocs) const { + // Section cannot be folded as part of ICF if it contains unpaired relocations + // eg: ADR_GOT_PAGE and LD64_GOT_LO12_NC don't point to same symbol. + SmallSet syms; + for (const Relocation &reloc : relocs) { + if (reloc.type == R_AARCH64_ADR_GOT_PAGE) + syms.insert(reloc.sym); + else if (reloc.type == R_AARCH64_LD64_GOT_LO12_NC && !syms.contains(reloc.sym)) + return false; + } + + return true; +} + // AArch64 may use security features in variant PLT sequences. These are: // Pointer Authentication (PAC), introduced in armv8.3-a and Branch Target // Indicator (BTI) introduced in armv8.5-a. The additional instructions used diff --git a/lld/ELF/ICF.cpp b/lld/ELF/ICF.cpp index 1cdcf6be9d8a9..4493ba4a00dff 100644 --- a/lld/ELF/ICF.cpp +++ b/lld/ELF/ICF.cpp @@ -77,9 +77,11 @@ #include "InputFiles.h" #include "LinkerScript.h" #include "OutputSections.h" +#include "Relocations.h" #include "SymbolTable.h" #include "Symbols.h" #include "SyntheticSections.h" +#include "Target.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/Object/ELF.h" #include "llvm/Support/Parallel.h" @@ -239,6 +241,7 @@ template template bool ICF::constantEq(const InputSection *secA, Relocs ra, const InputSection *secB, Relocs rb) { + SmallVector relocsA, relocsB; if (ra.size() != rb.size()) return false; auto rai = ra.begin(), rae = ra.end(), rbi = rb.begin(); @@ -247,11 +250,21 @@ bool ICF::constantEq(const InputSection *secA, Relocs ra, rai->getType(ctx.arg.isMips64EL) != rbi->getType(ctx.arg.isMips64EL)) return false; - uint64_t addA = getAddend(*rai); - uint64_t addB = getAddend(*rbi); + int64_t addA = getAddend(*rai); + int64_t addB = getAddend(*rbi); Symbol &sa = secA->file->getRelocTargetSym(*rai); Symbol &sb = secB->file->getRelocTargetSym(*rbi); + + // We only need relocation type and relocation target symbol. Offset is + // not used (as of now). So we don't bother populating one. + relocsA.push_back( + {ctx.target->getRelExpr(rai->getType(ctx.arg.isMips64EL), sa, 0), + rai->getType(ctx.arg.isMips64EL), 0, addA, &sa}); + relocsB.push_back( + {ctx.target->getRelExpr(rbi->getType(ctx.arg.isMips64EL), sb, 0), + rbi->getType(ctx.arg.isMips64EL), 0, addB, &sb}); + if (&sa == &sb) { if (addA == addB) continue; @@ -308,6 +321,10 @@ bool ICF::constantEq(const InputSection *secA, Relocs ra, return false; } + // Target-specific section-folding logic based on section's relocation list + if (!ctx.target->canFoldSection(relocsA) || !ctx.target->canFoldSection(relocsB)) + return false; + return true; } diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h index fd1e5d33c438a..169bd17ecb951 100644 --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -102,6 +102,9 @@ class TargetInfo { virtual void applyJumpInstrMod(uint8_t *loc, JumpModType type, JumpModType val) const {} + // Used by ICF to determine if section full of @relocs can be folded safely with another + virtual bool canFoldSection(const SmallVector &relocs) const { return true; } + virtual ~TargetInfo(); // This deletes a jump insn at the end of the section if it is a fall thru to diff --git a/lld/test/ELF/aarch64-icf-unpaired.s b/lld/test/ELF/aarch64-icf-unpaired.s new file mode 100644 index 0000000000000..e74b2bd103466 --- /dev/null +++ b/lld/test/ELF/aarch64-icf-unpaired.s @@ -0,0 +1,48 @@ +// REQUIRES: aarch64 + +# RUN: llvm-mc -filetype=obj -triple=aarch64 %s -o %t +# RUN: ld.lld %t -o %t2 --icf=all --print-icf-sections | FileCheck %s + +# CHECK-NOT: selected section {{.*}}(.text.f2_0) +# CHECK-NOT: removing identical section {{.*}}(.text.f2_1) +# CHECK-NOT: removing identical section {{.*}}(.text.f2_2) + +.addrsig + +callee: +ret + +.macro f, index + +.section .text.f1_\index,"ax",@progbits +f1_\index: +adrp x0, :got:g\index +mov x1, #\index +b f2_\index + +.section .text.f2_\index,"ax",@progbits +f2_\index: +ldr x0, [x0, :got_lo12:g\index] +b callee + +.globl g\index +.section .rodata.g\index,"a",@progbits +g_\index: +.long 111 +.long 122 + +g\index: +.byte 123 + +.section .text._start,"ax",@progbits +bl f1_\index + +.endm + +.section .text._start,"ax",@progbits +.globl _start +_start: + +f 0 +f 1 +f 2 \ No newline at end of file