[lld] [LLD][AArch64] Implement relaxOnce to avoid dead GOT entries for relaxed ADRP+LDR (PR #224370)
Pranav Kant via llvm-commits
llvm-commits at lists.llvm.org
Thu Sep 17 20:06:40 PDT 2026
https://github.com/pranavk updated https://github.com/llvm/llvm-project/pull/224370
>From e1e1c4a30c84315c24221aa511c3eefb86579be7 Mon Sep 17 00:00:00 2001
From: Pranav Kant <prka at google.com>
Date: Wed, 16 Sep 2026 13:10:53 -0700
Subject: [PATCH] [LLD][AArch64] Implement relaxOnce to avoid dead GOT entries
for relaxed ADRP+LDR
When ADRP+LDR GOT accesses are relaxed to ADRP+ADD (or NOP+ADR), LLD
previously still allocated a GOT entry and emitted dynamic relocations
(R_AARCH64_RELATIVE / R_AARCH64_GLOB_DAT).
This patch defers GOT allocation for candidate ADRP+LDR pairs during
scanRelocs by marking them with RE_AARCH64_RELAX_GOT_PAGE_PC and
RE_AARCH64_RELAX_GOT_LO12. AArch64::relaxOnce checks if the target is
out of range (>4 GiB); if so, it restores the GOT entry and reverts the
relocation expressions back to RE_AARCH64_GOT_PAGE_PC and R_GOT.
In relocateAlloc, AArch64Relaxer uses RE_AARCH64_RELAX_GOT_PAGE_PC
directly, eliminating the redundant pre-scan pass in relocateAlloc.
---
lld/ELF/Arch/AArch64.cpp | 224 ++++++++++++--------
lld/ELF/InputSection.cpp | 4 +-
lld/ELF/Relocations.cpp | 4 +-
lld/ELF/Relocations.h | 2 +
lld/ELF/Writer.cpp | 6 +-
lld/test/ELF/aarch64-adrp-ldr-got-symbols.s | 15 ++
lld/test/ELF/aarch64-adrp-ldr-got.s | 30 +++
lld/test/ELF/aarch64-got-relocations.s | 25 ++-
8 files changed, 207 insertions(+), 103 deletions(-)
diff --git a/lld/ELF/Arch/AArch64.cpp b/lld/ELF/Arch/AArch64.cpp
index 223f0ecdd3ccd..f70accbd52e69 100644
--- a/lld/ELF/Arch/AArch64.cpp
+++ b/lld/ELF/Arch/AArch64.cpp
@@ -98,6 +98,7 @@ class AArch64 : public TargetInfo {
uint64_t val) const override;
void relocateAlloc(InputSection &sec, uint8_t *buf) const override;
void applyBranchToBranchOpt() const override;
+ bool relaxOnce(int pass) const override;
private:
void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
@@ -107,17 +108,12 @@ class AArch64 : public TargetInfo {
struct AArch64Relaxer {
Ctx &ctx;
- SmallPtrSet<Symbol *, 32> unsafeToRelaxAdrpLdr;
- AArch64Relaxer(Ctx &ctx, ArrayRef<Relocation> relocs, uint64_t secAddr,
- uint8_t *buf);
+ AArch64Relaxer(Ctx &ctx) : ctx(ctx) {}
bool tryRelaxAdrpAdd(const Relocation &adrpRel, const Relocation &addRel,
uint64_t secAddr, uint8_t *buf) const;
bool tryRelaxAdrpLdr(const Relocation &adrpRel, const Relocation &ldrRel,
uint64_t secAddr, uint8_t *buf) const;
- bool isLegalAdrpLdrRelaxationCandidate(const Relocation &adrpRel,
- const Relocation &ldrRel,
- uint64_t secAddr, uint8_t *buf) const;
};
} // namespace
@@ -192,12 +188,101 @@ bool AArch64::usesOnlyLowPageBits(RelType type) const {
}
}
+static bool isLegalAdrpLdrPair(Ctx &ctx, uint64_t adrpOffset,
+ int64_t adrpAddend, Symbol *adrpSym,
+ uint64_t ldrOffset, int64_t ldrAddend,
+ Symbol *ldrSym, const uint8_t *buf) {
+ // Check if the relocations apply to consecutive instructions.
+ if (adrpOffset + 4 != ldrOffset)
+ return false;
+ // Check if the relocations reference the same symbol and
+ // skip undefined, preemptible, STT_GNU_IFUNC, and tagged symbols.
+ if (!adrpSym || adrpSym != ldrSym || !adrpSym->isDefined() ||
+ adrpSym->isPreemptible || adrpSym->isGnuIFunc() || adrpSym->isTagged())
+ return false;
+ // Check if the addends of the both relocations are zero.
+ if (adrpAddend != 0 || ldrAddend != 0)
+ return false;
+ uint32_t adrpInstr = read32le(buf + adrpOffset);
+ uint32_t ldrInstr = read32le(buf + ldrOffset);
+ // Check if the first instruction is ADRP and the second instruction is LDR.
+ if ((adrpInstr & 0x9f000000) != 0x90000000 ||
+ (ldrInstr & 0x3b000000) != 0x39000000)
+ return false;
+ // Check the value of the sf bit.
+ if (!(ldrInstr >> 31))
+ return false;
+ uint32_t adrpDestReg = adrpInstr & 0x1f;
+ uint32_t ldrDestReg = ldrInstr & 0x1f;
+ uint32_t ldrSrcReg = (ldrInstr >> 5) & 0x1f;
+ // Check if ADRP and LDR use the same register.
+ if (adrpDestReg != ldrDestReg || adrpDestReg != ldrSrcReg)
+ return false;
+
+ // GOT references to absolute symbols can't be relaxed to use ADRP/ADD in
+ // position-independent code because these instructions produce a relative
+ // address.
+ if (ctx.arg.isPic && !cast<Defined>(*adrpSym).section)
+ return false;
+
+ return true;
+}
+
+template <class ELFT, class RelTy>
+static bool isLegalAdrpLdrPair(Ctx &ctx, InputSectionBase &sec,
+ const RelTy &adrpRel, const RelTy &ldrRel,
+ RelocScan &rs, const uint8_t *buf) {
+ if (ldrRel.r_offset + 4 > sec.content().size())
+ return false;
+ Symbol *adrpSym = &sec.getFile<ELFT>()->getSymbol(adrpRel.getSymbol(false));
+ Symbol *ldrSym = &sec.getFile<ELFT>()->getSymbol(ldrRel.getSymbol(false));
+ return isLegalAdrpLdrPair(
+ ctx, adrpRel.r_offset,
+ rs.getAddend<ELFT>(adrpRel, R_AARCH64_ADR_GOT_PAGE), adrpSym,
+ ldrRel.r_offset, rs.getAddend<ELFT>(ldrRel, R_AARCH64_LD64_GOT_LO12_NC),
+ ldrSym, buf);
+}
+
+template <class ELFT, class RelTy>
+static void findUnsafeAdrpLdr(Ctx &ctx, InputSectionBase &sec,
+ Relocs<RelTy> rels, RelocScan &rs,
+ SmallPtrSet<Symbol *, 32> &unsafeToRelaxAdrpLdr) {
+ if (!ctx.arg.relax || !(sec.flags & SHF_EXECINSTR) ||
+ sec.content().size() < 8)
+ return;
+
+ // For a given symbol R_AARCH64_ADR_GOT_PAGE and R_AARCH64_LD64_GOT_LO12_NC
+ // relaxation is all-or-nothing. We can't relax only some of them, as there
+ // may be a jump destination between the two relocations.
+ auto getSym = [&](const RelTy &rel) {
+ return &sec.getFile<ELFT>()->getSymbol(rel.getSymbol(false));
+ };
+ const uint8_t *buf = sec.content().data();
+ for (auto it = rels.begin(), end = rels.end(); it != end; ++it) {
+ if (it->getType(false) == R_AARCH64_ADR_GOT_PAGE) {
+ auto next = std::next(it);
+ if (next != end && next->getType(false) == R_AARCH64_LD64_GOT_LO12_NC &&
+ !unsafeToRelaxAdrpLdr.contains(getSym(*it)) &&
+ isLegalAdrpLdrPair<ELFT>(ctx, sec, *it, *next, rs, buf)) {
+ it = next;
+ continue;
+ }
+ unsafeToRelaxAdrpLdr.insert(getSym(*it));
+ } else if (it->getType(false) == R_AARCH64_LD64_GOT_LO12_NC) {
+ unsafeToRelaxAdrpLdr.insert(getSym(*it));
+ }
+ }
+}
+
template <class ELFT, class RelTy>
void AArch64::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
unsigned shard) {
RelocScan rs(ctx, &sec, shard);
sec.relocations.reserve(rels.size());
+ SmallPtrSet<Symbol *, 32> unsafeToRelaxAdrpLdr;
+ findUnsafeAdrpLdr<ELFT>(ctx, sec, rels, rs, unsafeToRelaxAdrpLdr);
+
for (auto it = rels.begin(); it != rels.end(); ++it) {
const RelTy &rel = *it;
uint32_t symIdx = rel.getSymbol(false);
@@ -288,10 +373,18 @@ void AArch64::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
// GOT relocations:
case R_AARCH64_ADR_GOT_PAGE:
- expr = RE_AARCH64_GOT_PAGE_PC;
+ if (ctx.arg.relax && (sec.flags & SHF_EXECINSTR) &&
+ !unsafeToRelaxAdrpLdr.contains(&sym))
+ expr = RE_AARCH64_RELAX_GOT_PAGE_PC;
+ else
+ expr = RE_AARCH64_GOT_PAGE_PC;
break;
case R_AARCH64_LD64_GOT_LO12_NC:
- expr = R_GOT;
+ if (ctx.arg.relax && (sec.flags & SHF_EXECINSTR) &&
+ !unsafeToRelaxAdrpLdr.contains(&sym))
+ expr = RE_AARCH64_RELAX_GOT_LO12;
+ else
+ expr = R_GOT;
break;
case R_AARCH64_LD64_GOTPAGE_LO15:
expr = RE_AARCH64_GOT_PAGE;
@@ -901,32 +994,6 @@ void AArch64::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
llvm_unreachable("invalid relocation for TLS IE to LE relaxation");
}
-AArch64Relaxer::AArch64Relaxer(Ctx &ctx, ArrayRef<Relocation> relocs,
- uint64_t secAddr, uint8_t *buf)
- : ctx(ctx) {
- if (!ctx.arg.relax)
- return;
- // For a given symbol R_AARCH64_ADR_GOT_PAGE and R_AARCH64_LD64_GOT_LO12_NC
- // relaxation is all-or-nothing. We can't relax only some of them, as there
- // may be a jump destination between the two relocations.
- size_t i = 0;
- const size_t size = relocs.size();
- for (; i != size; ++i) {
- if (relocs[i].type == R_AARCH64_ADR_GOT_PAGE) {
- if (i + 1 < size && relocs[i + 1].type == R_AARCH64_LD64_GOT_LO12_NC &&
- !unsafeToRelaxAdrpLdr.contains(relocs[i].sym) &&
- isLegalAdrpLdrRelaxationCandidate(relocs[i], relocs[i + 1], secAddr,
- buf)) {
- ++i;
- continue;
- }
- unsafeToRelaxAdrpLdr.insert(relocs[i].sym);
- } else if (relocs[i].type == R_AARCH64_LD64_GOT_LO12_NC) {
- unsafeToRelaxAdrpLdr.insert(relocs[i].sym);
- }
- }
-}
-
bool AArch64Relaxer::tryRelaxAdrpAdd(const Relocation &adrpRel,
const Relocation &addRel, uint64_t secAddr,
uint8_t *buf) const {
@@ -975,51 +1042,6 @@ bool AArch64Relaxer::tryRelaxAdrpAdd(const Relocation &adrpRel,
return true;
}
-bool AArch64Relaxer::isLegalAdrpLdrRelaxationCandidate(
- const Relocation &adrpRel, const Relocation &ldrRel, uint64_t secAddr,
- uint8_t *buf) const {
- // Check if the relocations apply to consecutive instructions.
- if (adrpRel.offset + 4 != ldrRel.offset)
- return false;
- // Check if the relocations reference the same symbol and
- // skip undefined, preemptible and STT_GNU_IFUNC symbols.
- if (!adrpRel.sym || adrpRel.sym != ldrRel.sym || !adrpRel.sym->isDefined() ||
- adrpRel.sym->isPreemptible || adrpRel.sym->isGnuIFunc())
- return false;
- // Check if the addends of the both relocations are zero.
- if (adrpRel.addend != 0 || ldrRel.addend != 0)
- return false;
- uint32_t adrpInstr = read32le(buf + adrpRel.offset);
- uint32_t ldrInstr = read32le(buf + ldrRel.offset);
- // Check if the first instruction is ADRP and the second instruction is LDR.
- if ((adrpInstr & 0x9f000000) != 0x90000000 ||
- (ldrInstr & 0x3b000000) != 0x39000000)
- return false;
- // Check the value of the sf bit.
- if (!(ldrInstr >> 31))
- return false;
- uint32_t adrpDestReg = adrpInstr & 0x1f;
- uint32_t ldrDestReg = ldrInstr & 0x1f;
- uint32_t ldrSrcReg = (ldrInstr >> 5) & 0x1f;
- // Check if ADPR and LDR use the same register.
- if (adrpDestReg != ldrDestReg || adrpDestReg != ldrSrcReg)
- return false;
-
- Symbol &sym = *adrpRel.sym;
- // GOT references to absolute symbols can't be relaxed to use ADRP/ADD in
- // position-independent code because these instructions produce a relative
- // address.
- if (ctx.arg.isPic && !cast<Defined>(sym).section)
- return false;
- // Check if the address difference is within 4GB range.
- int64_t val =
- getAArch64Page(sym.getVA(ctx)) - getAArch64Page(secAddr + adrpRel.offset);
- if (val != llvm::SignExtend64(val, 33))
- return false;
-
- return true;
-}
-
bool AArch64Relaxer::tryRelaxAdrpLdr(const Relocation &adrpRel,
const Relocation &ldrRel, uint64_t secAddr,
uint8_t *buf) const {
@@ -1031,17 +1053,10 @@ bool AArch64Relaxer::tryRelaxAdrpLdr(const Relocation &adrpRel,
// ADRP xn, sym
// ADD xn, xn, :lo_12: sym
- if (!ctx.arg.relax || adrpRel.type != R_AARCH64_ADR_GOT_PAGE ||
- ldrRel.type != R_AARCH64_LD64_GOT_LO12_NC)
+ if (adrpRel.expr != RE_AARCH64_RELAX_GOT_PAGE_PC)
return false;
Symbol *sym = adrpRel.sym;
- if (unsafeToRelaxAdrpLdr.contains(sym))
- return false;
-
- assert(isLegalAdrpLdrRelaxationCandidate(adrpRel, ldrRel, secAddr, buf) &&
- "Should have been marked as unsafe");
-
uint32_t adrpInstr = read32le(buf + adrpRel.offset);
uint32_t adrpDestReg = adrpInstr & 0x1f;
Relocation adrpSymRel = {RE_AARCH64_PAGE_PC, R_AARCH64_ADR_PREL_PG_HI21,
@@ -1074,7 +1089,7 @@ static bool needsGotForMemtag(const Relocation &rel) {
void AArch64::relocateAlloc(InputSection &sec, uint8_t *buf) const {
uint64_t secAddr = sec.getOutputSection()->addr + sec.outSecOff;
const ArrayRef<Relocation> relocs = sec.relocs();
- AArch64Relaxer relaxer(ctx, relocs, secAddr, buf);
+ AArch64Relaxer relaxer(ctx);
for (size_t i = 0, size = relocs.size(); i != size; ++i) {
const Relocation &rel = relocs[i];
if (rel.expr == R_NONE) // See finalizeAddressDependentContent()
@@ -1185,6 +1200,39 @@ void AArch64::applyBranchToBranchOpt() const {
redirectControlTransferRelocations);
}
+bool AArch64::relaxOnce(int pass) const {
+ if (!ctx.arg.relax)
+ return false;
+
+ SmallVector<InputSection *, 0> storage;
+ bool changed = false;
+ for (OutputSection *osec : ctx.outputSections) {
+ if (!(osec->flags & SHF_EXECINSTR))
+ continue;
+ for (InputSection *sec : getInputSections(*osec, storage)) {
+ for (size_t i = 0, e = sec->relocs().size(); i != e; ++i) {
+ Relocation &rel = sec->relocs()[i];
+ if (rel.expr != RE_AARCH64_RELAX_GOT_PAGE_PC)
+ continue;
+ int64_t val = getAArch64Page(rel.sym->getVA(ctx, rel.addend)) -
+ getAArch64Page(sec->getOutputSection()->addr +
+ sec->outSecOff + rel.offset);
+ if (val == llvm::SignExtend64(val, 33))
+ continue;
+ if (rel.sym->auxIdx == 0) {
+ rel.sym->allocateAux(ctx);
+ addGotEntry(ctx, *rel.sym);
+ changed = true;
+ }
+ rel.expr = RE_AARCH64_GOT_PAGE_PC;
+ if (i + 1 < e && sec->relocs()[i + 1].expr == RE_AARCH64_RELAX_GOT_LO12)
+ sec->relocs()[i + 1].expr = R_GOT;
+ }
+ }
+ }
+ return changed;
+}
+
// 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/InputSection.cpp b/lld/ELF/InputSection.cpp
index 13baaa5b8c2ad..c9cae89ce195d 100644
--- a/lld/ELF/InputSection.cpp
+++ b/lld/ELF/InputSection.cpp
@@ -813,6 +813,7 @@ uint64_t InputSectionBase::getRelocTargetVA(Ctx &ctx, const Relocation &r,
case R_DTPREL:
case R_RELAX_GOT_PC_NOPIC:
case RE_AARCH64_AUTH:
+ case RE_AARCH64_RELAX_GOT_LO12:
case RE_RISCV_ADD:
case RE_RISCV_LEB128:
return r.sym->getVA(ctx, a);
@@ -908,7 +909,8 @@ uint64_t InputSectionBase::getRelocTargetVA(Ctx &ctx, const Relocation &r,
case RE_MIPS_TLSLD:
return ctx.in.mipsGot->getVA() + ctx.in.mipsGot->getTlsIndexOffset(file) -
ctx.in.mipsGot->getGp(file);
- case RE_AARCH64_PAGE_PC: {
+ case RE_AARCH64_PAGE_PC:
+ case RE_AARCH64_RELAX_GOT_PAGE_PC: {
uint64_t val = r.sym->isUndefWeak() ? p + a : r.sym->getVA(ctx, a);
return getAArch64Page(val) - getAArch64Page(p);
}
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index 7aaae802f4d7c..be0215d85c734 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -146,8 +146,8 @@ bool lld::elf::needsGot(RelExpr expr) {
// file (PC, or GOT for example).
static bool isRelExpr(RelExpr expr) {
return oneof<R_PC, R_GOTREL, R_GOTPLTREL, RE_ARM_PCA, RE_MIPS_GOTREL,
- RE_PPC64_CALL, RE_AARCH64_PAGE_PC, R_RELAX_GOT_PC,
- RE_RISCV_PC_INDIRECT, RE_LOONGARCH_PAGE_PC,
+ RE_PPC64_CALL, RE_AARCH64_PAGE_PC, RE_AARCH64_RELAX_GOT_PAGE_PC,
+ R_RELAX_GOT_PC, RE_RISCV_PC_INDIRECT, RE_LOONGARCH_PAGE_PC,
RE_LOONGARCH_PC_INDIRECT>(expr);
}
diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h
index 5ae053d3af793..2719e48f69e12 100644
--- a/lld/ELF/Relocations.h
+++ b/lld/ELF/Relocations.h
@@ -85,6 +85,8 @@ enum RelExpr {
RE_AARCH64_GOT_PAGE_PC,
RE_AARCH64_GOT_PAGE,
RE_AARCH64_PAGE_PC,
+ RE_AARCH64_RELAX_GOT_PAGE_PC,
+ RE_AARCH64_RELAX_GOT_LO12,
RE_AARCH64_TLSDESC_PAGE,
RE_AARCH64_AUTH,
RE_ARM_PCA,
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 8cf88cd99cf8a..6fb4427c1cc04 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -1523,9 +1523,9 @@ template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
// the final addresses are unavailable.
uint32_t pass = 0, assignPasses = 0;
while (!ctx.arg.relocatable) {
- bool changed = ctx.target->needsThunks
- ? tc.createThunks(pass, ctx.outputSections)
- : ctx.target->relaxOnce(pass);
+ bool changed = ctx.target->relaxOnce(pass);
+ if (ctx.target->needsThunks)
+ changed |= tc.createThunks(pass, ctx.outputSections);
bool spilled = ctx.script->spillSections();
changed |= spilled;
++pass;
diff --git a/lld/test/ELF/aarch64-adrp-ldr-got-symbols.s b/lld/test/ELF/aarch64-adrp-ldr-got-symbols.s
index 3f2f4e953d7d2..8d066ac0b5d45 100644
--- a/lld/test/ELF/aarch64-adrp-ldr-got-symbols.s
+++ b/lld/test/ELF/aarch64-adrp-ldr-got-symbols.s
@@ -10,6 +10,7 @@
# RUN: ld.lld -shared -T %t/linker.t %t/symbols.o %t/abs.o -o %t/symbols.so
# RUN: llvm-objdump --no-show-raw-insn -d %t/symbols.so | \
# RUN: FileCheck --check-prefix=LIB %s
+# RUN: llvm-readelf -r %t/symbols.so | FileCheck --check-prefix=LIB-RELOC %s
## Symbol 'hidden_sym' is nonpreemptible, the relaxation should be applied.
LIB: adrp x0
@@ -31,9 +32,17 @@ LIB-NEXT: ldr x3
LIB-NEXT: adrp x4
LIB-NEXT: ldr x4
+## hidden_sym is relaxed and does not produce a dynamic relocation.
+LIB-RELOC: Relocation section '.rela.dyn' at offset 0x{{[0-9a-f]+}} contains 3 entries:
+LIB-RELOC-NOT: R_AARCH64_RELATIVE
+LIB-RELOC: R_AARCH64_GLOB_DAT {{.*}} undefined_sym + 0
+LIB-RELOC-NEXT: R_AARCH64_GLOB_DAT {{.*}} global_sym + 0
+LIB-RELOC-NEXT: R_AARCH64_IRELATIVE
+
# RUN: ld.lld -T %t/linker.t -z undefs %t/symbols.o %t/abs.o -o %t/symbols
# RUN: llvm-objdump --no-show-raw-insn -d %t/symbols | \
# RUN: FileCheck --check-prefix=EXE %s
+# RUN: llvm-readelf -r %t/symbols | FileCheck --check-prefix=EXE-RELOC %s
## Symbol 'global_sym' is nonpreemptible, the relaxation should be applied.
EXE: adrp x1
@@ -43,6 +52,12 @@ EXE-NEXT: add x1
EXE: adrp x4
EXE-NEXT: add x4
+## In the executable, relaxed symbols do not produce dynamic relocations.
+EXE-RELOC: Relocation section '.rela.dyn' at offset 0x{{[0-9a-f]+}} contains 1 entries:
+EXE-RELOC-NOT: R_AARCH64_GLOB_DAT
+EXE-RELOC-NOT: R_AARCH64_RELATIVE
+EXE-RELOC: R_AARCH64_IRELATIVE
+
## The linker script ensures that .rodata and .text are sufficiently (>1MB)
## far apart so that the adrp + ldr pair cannot be relaxed to adr + nop.
#--- linker.t
diff --git a/lld/test/ELF/aarch64-adrp-ldr-got.s b/lld/test/ELF/aarch64-adrp-ldr-got.s
index a8216da9f20c3..93c935fa038f3 100644
--- a/lld/test/ELF/aarch64-adrp-ldr-got.s
+++ b/lld/test/ELF/aarch64-adrp-ldr-got.s
@@ -32,11 +32,18 @@
# RUN: ld.lld %t/a.o -T %t/within-adr-range.t -o %t/a
# RUN: llvm-objdump --no-show-raw-insn -d %t/a | FileCheck --check-prefix=ADR %s
+# RUN: llvm-readelf -x .got %t/a | FileCheck --check-prefix=GOT-RELAX %s
## Symbol 'x' is nonpreemptible, the relaxation should be applied.
# ADR: nop
# ADR-NEXT: adr x1
+## Symbol 'x' does not have a GOT entry when relaxed.
+# GOT-RELAX: Hex dump of section '.got':
+# GOT-RELAX-NEXT: 0x{{[0-9a-f]+}} 04100000 00000000 08100000 00000000
+# GOT-RELAX-NEXT: 0x{{[0-9a-f]+}} 0c100000 00000000 10100000 00000000
+# GOT-RELAX-NOT: 00100000
+
## Symbol 'x' is nonpreemptible, but --no-relax surpresses relaxations.
# RUN: ld.lld %t/a.o -T %t/out-of-adr-range.t --no-relax -o %t/no-relax
# RUN: llvm-objdump --no-show-raw-insn -d %t/no-relax | \
@@ -49,6 +56,25 @@
# RUN: ld.lld %t/a.o -T %t/out-of-range.t -o %t/out-of-range
# RUN: llvm-objdump --no-show-raw-insn -d %t/out-of-range | \
# RUN: FileCheck --check-prefix=X1-NO-RELAX %s
+# RUN: llvm-readelf -x .got %t/out-of-range | FileCheck --check-prefix=GOT-NO-RELAX %s
+
+## Symbol 'x' has a GOT entry restored by relaxOnce when out of range.
+# GOT-NO-RELAX: Hex dump of section '.got':
+# GOT-NO-RELAX-NEXT: 0x{{[0-9a-f]+}} 04100000 00000000 08100000 00000000
+# GOT-NO-RELAX-NEXT: 0x{{[0-9a-f]+}} 0c100000 00000000 10100000 00000000
+# GOT-NO-RELAX-NEXT: 0x{{[0-9a-f]+}} 00100000 00000000
+
+## Symbol 'x' has upper bits set (e.g. HWASAN tag), relaxOnce restores the GOT entry
+## even when output section VA is small.
+# RUN: ld.lld %t/a.o -T %t/within-adr-range.t --defsym=x=0x4b00000000001000 -o %t/tagged
+# RUN: llvm-objdump --no-show-raw-insn -d %t/tagged | \
+# RUN: FileCheck --check-prefix=X1-NO-RELAX %s
+# RUN: llvm-readelf -x .got %t/tagged | FileCheck --check-prefix=GOT-TAGGED %s
+
+# GOT-TAGGED: Hex dump of section '.got':
+# GOT-TAGGED-NEXT: 0x{{[0-9a-f]+}} 04100000 00000000 08100000 00000000
+# GOT-TAGGED-NEXT: 0x{{[0-9a-f]+}} 0c100000 00000000 10100000 00000000
+# GOT-TAGGED-NEXT: 0x{{[0-9a-f]+}} 00100000 0000004b
## Relocations do not appear in pairs, no relaxations should be applied for
## that symbol. We can still relax other symbols.
@@ -117,6 +143,7 @@ SECTIONS {
#--- a.s
.rodata
+.globl x
.hidden x
x:
.word 10
@@ -148,6 +175,7 @@ _start:
#--- unpaired.s
.text
+.globl x
.hidden x
x:
nop
@@ -166,6 +194,7 @@ L:
#--- lone-ldr.s
.text
+.globl x
.hidden x
x:
nop
@@ -175,6 +204,7 @@ _start:
#--- all-or-nothing.s
.rodata
+.globl x
.hidden x
x:
.word 10
diff --git a/lld/test/ELF/aarch64-got-relocations.s b/lld/test/ELF/aarch64-got-relocations.s
index a7745b05904db..bc6a30f1ffcb5 100644
--- a/lld/test/ELF/aarch64-got-relocations.s
+++ b/lld/test/ELF/aarch64-got-relocations.s
@@ -1,15 +1,22 @@
# REQUIRES: aarch64
# RUN: llvm-mc -filetype=obj -triple=aarch64-unknown-cloudabi %s -o %t.o
-# RUN: ld.lld --hash-style=sysv -pie %t.o -o %t
-# RUN: llvm-readobj -r %t | FileCheck %s
+# RUN: ld.lld --hash-style=sysv -pie --no-relax %t.o -o %t.norelax
+# RUN: llvm-readobj -r %t.norelax | FileCheck --check-prefix=NORELAX %s
+# RUN: ld.lld --hash-style=sysv -pie %t.o -o %t.relax
+# RUN: llvm-readobj -r %t.relax | FileCheck --check-prefix=RELAX %s
-# If we're addressing a global relatively through the GOT, we still need to
-# emit a relocation for the entry in the GOT itself.
-# CHECK: Relocations [
-# CHECK: Section (4) .rela.dyn {
-# CHECK: 0x{{[0-9A-F]+}} R_AARCH64_RELATIVE - 0x{{[0-9A-F]+}}
-# CHECK: }
-# CHECK: ]
+# If we're addressing a global relatively through the GOT and relaxations are
+# disabled, we still need to emit a relocation for the entry in the GOT itself.
+# NORELAX: Relocations [
+# NORELAX: Section (4) .rela.dyn {
+# NORELAX: 0x{{[0-9A-F]+}} R_AARCH64_RELATIVE - 0x{{[0-9A-F]+}}
+# NORELAX: }
+# NORELAX: ]
+
+# When relaxation is enabled, the pair is relaxed and no GOT entry or dynamic
+# relocation is emitted.
+# RELAX: Relocations [
+# RELAX-NEXT: ]
.globl _start
.type _start, at function
More information about the llvm-commits
mailing list