[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