[lld] [ELF] Add target-specific relocation scanning for AArch64 (PR #181099)

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Thu Feb 12 22:11:28 PST 2026


https://github.com/MaskRay updated https://github.com/llvm/llvm-project/pull/181099

>From e8c2b8e364ff4b6b67acc567e124f5b7e2717fe4 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Sat, 7 Feb 2026 22:52:13 -0800
Subject: [PATCH 1/3] [ELF] Add target-specific relocation scanning for AArch64

Implement AArch64::scanSectionImpl, following the pattern established
for x86 `#178846` and MIPS/PPC64 (`#163138`). This merges the getRelExpr
for SHF_ALLOC sections into the target-specific scanner, eliminating
abstraction overhead.

- Remove 7 AArch64-specific RelExpr members (RE_AARCH64_AUTH_GOT,
  RE_AARCH64_AUTH_GOT_PC, RE_AARCH64_AUTH_GOT_PAGE_PC, RE_AARCH64_AUTH,
  RE_AARCH64_AUTH_TLSDESC_PAGE, RE_AARCH64_AUTH_TLSDESC,
  RE_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC) by using regular RelExpr
  members with flag-based dispatch (NEEDS_GOT_AUTH, NEEDS_TLSDESC_AUTH).
  AUTH GOT relocations now call `sym.setFlags(NEEDS_GOT | NEEDS_GOT_AUTH)`
  and `rs.processAux` directly.
- Remove adjustTlsExpr and handleAArch64PAuthTlsRelocation by inlining
  their logic into scanSectionImpl and relocateAlloc.
- Simplify getRelExpr to only handle relocations needed by
  relocateNonAlloc and relocateEh.
---
 lld/ELF/AArch64ErrataFix.cpp |  22 +--
 lld/ELF/Arch/AArch64.cpp     | 339 +++++++++++++++++++++++------------
 lld/ELF/InputSection.cpp     |   6 -
 lld/ELF/RelocScan.h          |  10 +-
 lld/ELF/Relocations.cpp      |  75 ++------
 lld/ELF/Relocations.h        |   6 -
 6 files changed, 262 insertions(+), 196 deletions(-)

diff --git a/lld/ELF/AArch64ErrataFix.cpp b/lld/ELF/AArch64ErrataFix.cpp
index a2ab58f96b2b5..adaaf0c750b8f 100644
--- a/lld/ELF/AArch64ErrataFix.cpp
+++ b/lld/ELF/AArch64ErrataFix.cpp
@@ -539,20 +539,22 @@ static void implementPatch(Ctx &ctx, uint64_t adrpAddr, uint64_t patcheeOffset,
   // Case 1: R_AARCH64_JUMP26 branch relocation. We have already patched this
   // instance of the erratum on a previous patch and altered the relocation. We
   // have nothing more to do.
-  // Case 2: A TLS Relaxation R_RELAX_TLS_IE_TO_LE. In this case the ADRP that
-  // we read will be transformed into a MOVZ later so we actually don't match
-  // the sequence and have nothing more to do.
-  // Case 3: A load/store register (unsigned immediate) class relocation. There
-  // are two of these R_AARCH_LD64_ABS_LO12_NC and R_AARCH_LD64_GOT_LO12_NC and
-  // they are both absolute. We need to add the same relocation to the patch,
-  // and replace the relocation with a R_AARCH_JUMP26 branch relocation.
-  // Case 4: No relocation. We must create a new R_AARCH64_JUMP26 branch
-  // relocation at the offset.
+  // Case 2: A TLS IE to LE optimization. In this case the ADRP that we read
+  // will be transformed into a MOVZ later so we actually don't match the
+  // sequence and have nothing more to do. Case 3: A load/store register
+  // (unsigned immediate) class relocation. There are two of these
+  // R_AARCH_LD64_ABS_LO12_NC and R_AARCH_LD64_GOT_LO12_NC and they are both
+  // absolute. We need to add the same relocation to the patch, and replace the
+  // relocation with a R_AARCH_JUMP26 branch relocation. Case 4: No relocation.
+  // We must create a new R_AARCH64_JUMP26 branch relocation at the offset.
   auto relIt = llvm::find_if(isec->relocs(), [=](const Relocation &r) {
     return r.offset == patcheeOffset;
   });
   if (relIt != isec->relocs().end() &&
-      (relIt->type == R_AARCH64_JUMP26 || relIt->expr == R_RELAX_TLS_IE_TO_LE))
+      (relIt->type == R_AARCH64_JUMP26 ||
+       ((relIt->type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 ||
+         relIt->type == R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC) &&
+        relIt->expr == R_TPREL)))
     return;
 
   Log(ctx) << "detected cortex-a53-843419 erratum sequence starting at " <<
diff --git a/lld/ELF/Arch/AArch64.cpp b/lld/ELF/Arch/AArch64.cpp
index 3c0d4ca645556..0c2d3d356becc 100644
--- a/lld/ELF/Arch/AArch64.cpp
+++ b/lld/ELF/Arch/AArch64.cpp
@@ -8,6 +8,7 @@
 
 #include "InputFiles.h"
 #include "OutputSections.h"
+#include "RelocScan.h"
 #include "Symbols.h"
 #include "SyntheticSections.h"
 #include "Target.h"
@@ -86,10 +87,13 @@ class AArch64 : public TargetInfo {
   bool usesOnlyLowPageBits(RelType type) const override;
   void relocate(uint8_t *loc, const Relocation &rel,
                 uint64_t val) const override;
-  RelExpr adjustTlsExpr(RelType type, RelExpr expr) const override;
   void relocateAlloc(InputSection &sec, uint8_t *buf) const override;
   void applyBranchToBranchOpt() const override;
 
+  template <class ELFT, class RelTy>
+  void scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels);
+  void scanSection(InputSectionBase &sec) override;
+
 private:
   void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
   void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
@@ -137,106 +141,16 @@ AArch64::AArch64(Ctx &ctx) : TargetInfo(ctx) {
   needsThunks = true;
 }
 
+// Only needed to support relocations used by relocateNonAlloc and relocateEh.
 RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
                             const uint8_t *loc) const {
   switch (type) {
-  case R_AARCH64_ABS16:
   case R_AARCH64_ABS32:
   case R_AARCH64_ABS64:
-  case R_AARCH64_FUNCINIT64:
-  case R_AARCH64_ADD_ABS_LO12_NC:
-  case R_AARCH64_LDST128_ABS_LO12_NC:
-  case R_AARCH64_LDST16_ABS_LO12_NC:
-  case R_AARCH64_LDST32_ABS_LO12_NC:
-  case R_AARCH64_LDST64_ABS_LO12_NC:
-  case R_AARCH64_LDST8_ABS_LO12_NC:
-  case R_AARCH64_MOVW_SABS_G0:
-  case R_AARCH64_MOVW_SABS_G1:
-  case R_AARCH64_MOVW_SABS_G2:
-  case R_AARCH64_MOVW_UABS_G0:
-  case R_AARCH64_MOVW_UABS_G0_NC:
-  case R_AARCH64_MOVW_UABS_G1:
-  case R_AARCH64_MOVW_UABS_G1_NC:
-  case R_AARCH64_MOVW_UABS_G2:
-  case R_AARCH64_MOVW_UABS_G2_NC:
-  case R_AARCH64_MOVW_UABS_G3:
-    return R_ABS;
-  case R_AARCH64_PATCHINST:
-    if (!isAbsolute(s))
-      Err(ctx) << getErrorLoc(ctx, loc)
-               << "R_AARCH64_PATCHINST relocation against non-absolute symbol "
-               << &s;
     return R_ABS;
-  case R_AARCH64_AUTH_ABS64:
-    return RE_AARCH64_AUTH;
-  case R_AARCH64_TLSDESC_ADR_PAGE21:
-    return RE_AARCH64_TLSDESC_PAGE;
-  case R_AARCH64_AUTH_TLSDESC_ADR_PAGE21:
-    return RE_AARCH64_AUTH_TLSDESC_PAGE;
-  case R_AARCH64_TLSDESC_LD64_LO12:
-  case R_AARCH64_TLSDESC_ADD_LO12:
-    return R_TLSDESC;
-  case R_AARCH64_AUTH_TLSDESC_LD64_LO12:
-  case R_AARCH64_AUTH_TLSDESC_ADD_LO12:
-    return RE_AARCH64_AUTH_TLSDESC;
-  case R_AARCH64_TLSDESC_CALL:
-    return R_TLSDESC_CALL;
-  case R_AARCH64_TLSLE_ADD_TPREL_HI12:
-  case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
-  case R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC:
-  case R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC:
-  case R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC:
-  case R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC:
-  case R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC:
-  case R_AARCH64_TLSLE_MOVW_TPREL_G0:
-  case R_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
-  case R_AARCH64_TLSLE_MOVW_TPREL_G1:
-  case R_AARCH64_TLSLE_MOVW_TPREL_G1_NC:
-  case R_AARCH64_TLSLE_MOVW_TPREL_G2:
-    return R_TPREL;
-  case R_AARCH64_CALL26:
-  case R_AARCH64_CONDBR19:
-  case R_AARCH64_JUMP26:
-  case R_AARCH64_TSTBR14:
-    return R_PLT_PC;
-  case R_AARCH64_PLT32:
-    const_cast<Symbol &>(s).thunkAccessed = true;
-    return R_PLT_PC;
-  case R_AARCH64_PREL16:
   case R_AARCH64_PREL32:
   case R_AARCH64_PREL64:
-  case R_AARCH64_ADR_PREL_LO21:
-  case R_AARCH64_LD_PREL_LO19:
-  case R_AARCH64_MOVW_PREL_G0:
-  case R_AARCH64_MOVW_PREL_G0_NC:
-  case R_AARCH64_MOVW_PREL_G1:
-  case R_AARCH64_MOVW_PREL_G1_NC:
-  case R_AARCH64_MOVW_PREL_G2:
-  case R_AARCH64_MOVW_PREL_G2_NC:
-  case R_AARCH64_MOVW_PREL_G3:
     return R_PC;
-  case R_AARCH64_ADR_PREL_PG_HI21:
-  case R_AARCH64_ADR_PREL_PG_HI21_NC:
-    return RE_AARCH64_PAGE_PC;
-  case R_AARCH64_LD64_GOT_LO12_NC:
-  case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
-    return R_GOT;
-  case R_AARCH64_AUTH_LD64_GOT_LO12_NC:
-  case R_AARCH64_AUTH_GOT_ADD_LO12_NC:
-    return RE_AARCH64_AUTH_GOT;
-  case R_AARCH64_AUTH_GOT_LD_PREL19:
-  case R_AARCH64_AUTH_GOT_ADR_PREL_LO21:
-    return RE_AARCH64_AUTH_GOT_PC;
-  case R_AARCH64_LD64_GOTPAGE_LO15:
-    return RE_AARCH64_GOT_PAGE;
-  case R_AARCH64_ADR_GOT_PAGE:
-  case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
-    return RE_AARCH64_GOT_PAGE_PC;
-  case R_AARCH64_AUTH_ADR_GOT_PAGE:
-    return RE_AARCH64_AUTH_GOT_PAGE_PC;
-  case R_AARCH64_GOTPCREL32:
-  case R_AARCH64_GOT_LD_PREL19:
-    return R_GOT_PC;
   case R_AARCH64_NONE:
     return R_NONE;
   default:
@@ -246,21 +160,14 @@ RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
   }
 }
 
-RelExpr AArch64::adjustTlsExpr(RelType type, RelExpr expr) const {
-  if (expr == R_RELAX_TLS_GD_TO_IE) {
-    if (type == R_AARCH64_TLSDESC_ADR_PAGE21)
-      return RE_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC;
-    return R_RELAX_TLS_GD_TO_IE_ABS;
-  }
-  return expr;
-}
-
 bool AArch64::usesOnlyLowPageBits(RelType type) const {
   switch (type) {
   default:
     return false;
   case R_AARCH64_ADD_ABS_LO12_NC:
   case R_AARCH64_LD64_GOT_LO12_NC:
+  case R_AARCH64_AUTH_LD64_GOT_LO12_NC:
+  case R_AARCH64_AUTH_GOT_ADD_LO12_NC:
   case R_AARCH64_LDST128_ABS_LO12_NC:
   case R_AARCH64_LDST16_ABS_LO12_NC:
   case R_AARCH64_LDST32_ABS_LO12_NC:
@@ -273,6 +180,201 @@ bool AArch64::usesOnlyLowPageBits(RelType type) const {
   }
 }
 
+template <class ELFT, class RelTy>
+void AArch64::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
+  RelocScan rs(ctx, &sec);
+  sec.relocations.reserve(rels.size());
+
+  for (auto it = rels.begin(); it != rels.end(); ++it) {
+    const RelTy &rel = *it;
+    uint32_t symIdx = rel.getSymbol(false);
+    Symbol &sym = sec.getFile<ELFT>()->getSymbol(symIdx);
+    uint64_t offset = rel.r_offset;
+    RelType type = rel.getType(false);
+    if (sym.isUndefined() && symIdx != 0 &&
+        rs.maybeReportUndefined(cast<Undefined>(sym), offset))
+      continue;
+    int64_t addend = rs.getAddend<ELFT>(rel, type);
+    RelExpr expr;
+    switch (type) {
+    case R_AARCH64_NONE:
+      continue;
+
+      // Absolute relocations:
+    case R_AARCH64_ABS16:
+    case R_AARCH64_ABS32:
+    case R_AARCH64_ABS64:
+    case R_AARCH64_FUNCINIT64:
+    case R_AARCH64_ADD_ABS_LO12_NC:
+    case R_AARCH64_LDST128_ABS_LO12_NC:
+    case R_AARCH64_LDST16_ABS_LO12_NC:
+    case R_AARCH64_LDST32_ABS_LO12_NC:
+    case R_AARCH64_LDST64_ABS_LO12_NC:
+    case R_AARCH64_LDST8_ABS_LO12_NC:
+    case R_AARCH64_MOVW_SABS_G0:
+    case R_AARCH64_MOVW_SABS_G1:
+    case R_AARCH64_MOVW_SABS_G2:
+    case R_AARCH64_MOVW_UABS_G0:
+    case R_AARCH64_MOVW_UABS_G0_NC:
+    case R_AARCH64_MOVW_UABS_G1:
+    case R_AARCH64_MOVW_UABS_G1_NC:
+    case R_AARCH64_MOVW_UABS_G2:
+    case R_AARCH64_MOVW_UABS_G2_NC:
+    case R_AARCH64_MOVW_UABS_G3:
+      expr = R_ABS;
+      break;
+
+    case R_AARCH64_AUTH_ABS64:
+      expr = RE_AARCH64_AUTH;
+      break;
+
+    case R_AARCH64_PATCHINST:
+      if (!isAbsolute(sym))
+        Err(ctx) << getErrorLoc(ctx, sec.content().data() + offset)
+                 << "R_AARCH64_PATCHINST relocation against non-absolute "
+                    "symbol "
+                 << &sym;
+      expr = R_ABS;
+      break;
+
+      // PC-relative relocations:
+    case R_AARCH64_PREL16:
+    case R_AARCH64_PREL32:
+    case R_AARCH64_PREL64:
+    case R_AARCH64_ADR_PREL_LO21:
+    case R_AARCH64_LD_PREL_LO19:
+    case R_AARCH64_MOVW_PREL_G0:
+    case R_AARCH64_MOVW_PREL_G0_NC:
+    case R_AARCH64_MOVW_PREL_G1:
+    case R_AARCH64_MOVW_PREL_G1_NC:
+    case R_AARCH64_MOVW_PREL_G2:
+    case R_AARCH64_MOVW_PREL_G2_NC:
+    case R_AARCH64_MOVW_PREL_G3:
+      rs.processR_PC(type, offset, addend, sym);
+      continue;
+
+      // Page-PC relocations:
+    case R_AARCH64_ADR_PREL_PG_HI21:
+    case R_AARCH64_ADR_PREL_PG_HI21_NC:
+      expr = RE_AARCH64_PAGE_PC;
+      break;
+
+      // PLT-generating relocations:
+    case R_AARCH64_CALL26:
+    case R_AARCH64_CONDBR19:
+    case R_AARCH64_JUMP26:
+    case R_AARCH64_TSTBR14:
+      rs.processR_PLT_PC(type, offset, addend, sym);
+      continue;
+    case R_AARCH64_PLT32:
+      sym.thunkAccessed = true;
+      rs.processR_PLT_PC(type, offset, addend, sym);
+      continue;
+
+      // GOT relocations:
+    case R_AARCH64_LD64_GOT_LO12_NC:
+      expr = R_GOT;
+      break;
+    case R_AARCH64_AUTH_LD64_GOT_LO12_NC:
+    case R_AARCH64_AUTH_GOT_ADD_LO12_NC:
+      sym.setFlags(NEEDS_GOT | NEEDS_GOT_AUTH);
+      rs.processAux(R_GOT, type, offset, sym, addend);
+      continue;
+    case R_AARCH64_AUTH_GOT_LD_PREL19:
+    case R_AARCH64_AUTH_GOT_ADR_PREL_LO21:
+      sym.setFlags(NEEDS_GOT | NEEDS_GOT_AUTH);
+      rs.processAux(R_GOT_PC, type, offset, sym, addend);
+      continue;
+    case R_AARCH64_ADR_GOT_PAGE:
+      expr = RE_AARCH64_GOT_PAGE_PC;
+      break;
+    case R_AARCH64_AUTH_ADR_GOT_PAGE:
+      sym.setFlags(NEEDS_GOT | NEEDS_GOT_AUTH);
+      rs.processAux(RE_AARCH64_GOT_PAGE_PC, type, offset, sym, addend);
+      continue;
+    case R_AARCH64_LD64_GOTPAGE_LO15:
+      expr = RE_AARCH64_GOT_PAGE;
+      break;
+    case R_AARCH64_GOTPCREL32:
+    case R_AARCH64_GOT_LD_PREL19:
+      expr = R_GOT_PC;
+      break;
+
+      // TLS LE relocations:
+    case R_AARCH64_TLSLE_ADD_TPREL_HI12:
+    case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
+    case R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC:
+    case R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC:
+    case R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC:
+    case R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC:
+    case R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC:
+    case R_AARCH64_TLSLE_MOVW_TPREL_G0:
+    case R_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
+    case R_AARCH64_TLSLE_MOVW_TPREL_G1:
+    case R_AARCH64_TLSLE_MOVW_TPREL_G1_NC:
+    case R_AARCH64_TLSLE_MOVW_TPREL_G2:
+      if (rs.checkTlsLe(offset, sym, type))
+        continue;
+      expr = R_TPREL;
+      break;
+
+      // TLS IE relocations:
+    case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
+      rs.handleTlsIe(RE_AARCH64_GOT_PAGE_PC, type, offset, addend, sym);
+      continue;
+    case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
+      rs.handleTlsIe(R_GOT, type, offset, addend, sym);
+      continue;
+
+      // TLSDESC relocations:
+    case R_AARCH64_TLSDESC_ADR_PAGE21:
+      rs.handleTlsDesc(RE_AARCH64_TLSDESC_PAGE, RE_AARCH64_GOT_PAGE_PC, type,
+                       offset, addend, sym);
+      continue;
+    case R_AARCH64_TLSDESC_LD64_LO12:
+    case R_AARCH64_TLSDESC_ADD_LO12:
+      rs.handleTlsDesc(R_TLSDESC, R_GOT, type, offset, addend, sym);
+      continue;
+    case R_AARCH64_TLSDESC_CALL:
+      sym.setFlags(NEEDS_TLSDESC_NONAUTH);
+      if (!ctx.arg.shared)
+        sec.addReloc({R_TPREL, type, offset, addend, &sym});
+      continue;
+
+      // AUTH TLSDESC relocations. Do not optimize to LE/IE because PAUTHELF64
+      // only supports the descriptor based TLS (TLSDESC).
+      // https://github.com/ARM-software/abi-aa/blob/main/pauthabielf64/pauthabielf64.rst#general-restrictions
+    case R_AARCH64_AUTH_TLSDESC_ADR_PAGE21:
+      sym.setFlags(NEEDS_TLSDESC | NEEDS_TLSDESC_AUTH);
+      sec.addReloc({RE_AARCH64_TLSDESC_PAGE, type, offset, addend, &sym});
+      continue;
+    case R_AARCH64_AUTH_TLSDESC_LD64_LO12:
+    case R_AARCH64_AUTH_TLSDESC_ADD_LO12:
+      sym.setFlags(NEEDS_TLSDESC | NEEDS_TLSDESC_AUTH);
+      sec.addReloc({R_TLSDESC, type, offset, addend, &sym});
+      continue;
+
+    default:
+      Err(ctx) << getErrorLoc(ctx, sec.content().data() + offset)
+               << "unknown relocation (" << type.v << ") against symbol "
+               << &sym;
+      continue;
+    }
+    rs.process(expr, type, offset, sym, addend);
+  }
+
+  if (ctx.arg.branchToBranch)
+    llvm::stable_sort(sec.relocs(),
+                      [](auto &l, auto &r) { return l.offset < r.offset; });
+}
+
+void AArch64::scanSection(InputSectionBase &sec) {
+  if (ctx.arg.ekind == ELF64BEKind)
+    elf::scanSection1<AArch64, ELF64BE>(*this, sec);
+  else
+    elf::scanSection1<AArch64, ELF64LE>(*this, sec);
+}
+
 RelType AArch64::getDynRel(RelType type) const {
   if (type == R_AARCH64_ABS64 || type == R_AARCH64_AUTH_ABS64 ||
       type == R_AARCH64_FUNCINIT64)
@@ -807,8 +909,7 @@ bool AArch64Relaxer::tryRelaxAdrpAdd(const Relocation &adrpRel,
   // to
   // NOP
   // ADR xn, sym
-  if (!ctx.arg.relax || adrpRel.type != R_AARCH64_ADR_PREL_PG_HI21 ||
-      addRel.type != R_AARCH64_ADD_ABS_LO12_NC)
+  if (!ctx.arg.relax || addRel.type != R_AARCH64_ADD_ABS_LO12_NC)
     return false;
   // Check if the relocations apply to consecutive instructions.
   if (adrpRel.offset + 4 != addRel.offset)
@@ -945,34 +1046,44 @@ void AArch64::relocateAlloc(InputSection &sec, uint8_t *buf) const {
       continue;
     }
 
-    switch (rel.expr) {
-    case RE_AARCH64_GOT_PAGE_PC:
+    switch (rel.type) {
+    case R_AARCH64_ADR_GOT_PAGE:
       if (i + 1 < size &&
           relaxer.tryRelaxAdrpLdr(rel, relocs[i + 1], secAddr, buf)) {
         ++i;
         continue;
       }
       break;
-    case RE_AARCH64_PAGE_PC:
+    case R_AARCH64_ADR_PREL_PG_HI21:
       if (i + 1 < size &&
           relaxer.tryRelaxAdrpAdd(rel, relocs[i + 1], secAddr, buf)) {
         ++i;
         continue;
       }
       break;
-    case RE_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC:
-    case R_RELAX_TLS_GD_TO_IE_ABS:
-      relaxTlsGdToIe(loc, rel, val);
-      continue;
-    case R_RELAX_TLS_GD_TO_LE:
-      relaxTlsGdToLe(loc, rel, val);
+
+    case R_AARCH64_TLSDESC_ADR_PAGE21:
+    case R_AARCH64_TLSDESC_LD64_LO12:
+    case R_AARCH64_TLSDESC_ADD_LO12:
+    case R_AARCH64_TLSDESC_CALL:
+      if (rel.expr == R_TPREL)
+        relaxTlsGdToLe(loc, rel, val);
+      else if (rel.expr == RE_AARCH64_GOT_PAGE_PC || rel.expr == R_GOT)
+        relaxTlsGdToIe(loc, rel, val);
+      else
+        relocate(loc, rel, val);
       continue;
-    case R_RELAX_TLS_IE_TO_LE:
-      relaxTlsIeToLe(loc, rel, val);
+    case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
+    case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
+      if (rel.expr == R_TPREL)
+        relaxTlsIeToLe(loc, rel, val);
+      else
+        relocate(loc, rel, val);
       continue;
     default:
       break;
     }
+
     relocate(loc, rel, val);
   }
 }
diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp
index 7402c8454c023..dd83a06ca6d43 100644
--- a/lld/ELF/InputSection.cpp
+++ b/lld/ELF/InputSection.cpp
@@ -821,7 +821,6 @@ uint64_t InputSectionBase::getRelocTargetVA(Ctx &ctx, const Relocation &r,
   case RE_ARM_SBREL:
     return r.sym->getVA(ctx, a) - getARMStaticBase(*r.sym);
   case R_GOT:
-  case RE_AARCH64_AUTH_GOT:
   case R_RELAX_TLS_GD_TO_IE_ABS:
     return r.sym->getGotVA(ctx) + a;
   case RE_LOONGARCH_GOT:
@@ -849,13 +848,10 @@ uint64_t InputSectionBase::getRelocTargetVA(Ctx &ctx, const Relocation &r,
   case R_RELAX_TLS_GD_TO_IE_GOT_OFF:
     return r.sym->getGotOffset(ctx) + a;
   case RE_AARCH64_GOT_PAGE_PC:
-  case RE_AARCH64_AUTH_GOT_PAGE_PC:
-  case RE_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC:
     return getAArch64Page(r.sym->getGotVA(ctx) + a) - getAArch64Page(p);
   case RE_AARCH64_GOT_PAGE:
     return r.sym->getGotVA(ctx) + a - getAArch64Page(ctx.in.got->getVA());
   case R_GOT_PC:
-  case RE_AARCH64_AUTH_GOT_PC:
   case R_RELAX_TLS_GD_TO_IE:
     return r.sym->getGotVA(ctx) + a - p;
   case R_GOTPLT_GOTREL:
@@ -1015,14 +1011,12 @@ uint64_t InputSectionBase::getRelocTargetVA(Ctx &ctx, const Relocation &r,
   case R_SIZE:
     return r.sym->getSize() + a;
   case R_TLSDESC:
-  case RE_AARCH64_AUTH_TLSDESC:
     return ctx.in.got->getTlsDescAddr(*r.sym) + a;
   case R_TLSDESC_PC:
     return ctx.in.got->getTlsDescAddr(*r.sym) + a - p;
   case R_TLSDESC_GOTPLT:
     return ctx.in.got->getTlsDescAddr(*r.sym) + a - ctx.in.gotPlt->getVA();
   case RE_AARCH64_TLSDESC_PAGE:
-  case RE_AARCH64_AUTH_TLSDESC_PAGE:
     return getAArch64Page(ctx.in.got->getTlsDescAddr(*r.sym) + a) -
            getAArch64Page(p);
   case RE_LOONGARCH_TLSDESC_PAGE_PC:
diff --git a/lld/ELF/RelocScan.h b/lld/ELF/RelocScan.h
index d52afda26effb..64c1f992a0ef5 100644
--- a/lld/ELF/RelocScan.h
+++ b/lld/ELF/RelocScan.h
@@ -112,8 +112,9 @@ class RelocScan {
       ctx.hasTlsIe.store(true, std::memory_order_relaxed);
       sym.setFlags(NEEDS_TLSIE);
       // R_GOT (absolute GOT address) needs a RELATIVE dynamic relocation in
-      // PIC. This is used by R_386_TLS_IE.
-      if (ieExpr == R_GOT && ctx.arg.isPic)
+      // PIC when the relocation uses the full address (not just low page bits).
+      if (ieExpr == R_GOT && ctx.arg.isPic &&
+          !ctx.target->usesOnlyLowPageBits(type))
         sec->getPartition(ctx).relaDyn->addRelativeReloc(
             ctx.target->relativeRel, *sec, offset, sym, addend, type, ieExpr);
       else
@@ -156,11 +157,12 @@ class RelocScan {
     return true;
   }
 
-  // Handle TLSDESC relocation.
+  // Handle TLSDESC relocation. NEEDS_TLSDESC_NONAUTH is a no-op for
+  // non-AArch64 targets and detects incompatibility with NEEDS_TLSDESC_AUTH.
   void handleTlsDesc(RelExpr sharedExpr, RelExpr ieExpr, RelType type,
                      uint64_t offset, int64_t addend, Symbol &sym) {
     if (ctx.arg.shared) {
-      sym.setFlags(NEEDS_TLSDESC);
+      sym.setFlags(NEEDS_TLSDESC | NEEDS_TLSDESC_NONAUTH);
       sec->addReloc({sharedExpr, type, offset, addend, &sym});
     } else if (sym.isPreemptible) {
       // Optimize to Initial Exec.
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index 797acb08b0506..09625c2673c84 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -134,10 +134,8 @@ static bool needsPlt(RelExpr expr) {
 }
 
 bool lld::elf::needsGot(RelExpr expr) {
-  return oneof<R_GOT, RE_AARCH64_AUTH_GOT, RE_AARCH64_AUTH_GOT_PC, R_GOT_OFF,
-               RE_MIPS_GOT_LOCAL_PAGE, RE_MIPS_GOT_OFF, RE_MIPS_GOT_OFF32,
-               RE_AARCH64_GOT_PAGE_PC, RE_AARCH64_AUTH_GOT_PAGE_PC,
-               RE_AARCH64_AUTH_GOT_PAGE_PC, R_GOT_PC, R_GOTPLT,
+  return oneof<R_GOT, R_GOT_OFF, RE_MIPS_GOT_LOCAL_PAGE, RE_MIPS_GOT_OFF,
+               RE_MIPS_GOT_OFF32, RE_AARCH64_GOT_PAGE_PC, R_GOT_PC, R_GOTPLT,
                RE_AARCH64_GOT_PAGE, RE_LOONGARCH_GOT, RE_LOONGARCH_GOT_PAGE_PC>(
       expr);
 }
@@ -841,15 +839,14 @@ bool RelocScan::isStaticLinkTimeConstant(RelExpr e, RelType type,
                                          const Symbol &sym,
                                          uint64_t relOff) const {
   // These expressions always compute a constant
-  if (oneof<
-          R_GOTPLT, R_GOT_OFF, R_RELAX_HINT, RE_MIPS_GOT_LOCAL_PAGE,
-          RE_MIPS_GOTREL, RE_MIPS_GOT_OFF, RE_MIPS_GOT_OFF32, RE_MIPS_GOT_GP_PC,
-          RE_AARCH64_GOT_PAGE_PC, RE_AARCH64_AUTH_GOT_PAGE_PC, R_GOT_PC,
-          R_GOTONLY_PC, R_GOTPLTONLY_PC, R_PLT_PC, R_PLT_GOTREL, R_PLT_GOTPLT,
-          R_GOTPLT_GOTREL, R_GOTPLT_PC, RE_PPC32_PLTREL, RE_PPC64_CALL_PLT,
-          RE_PPC64_RELAX_TOC, RE_RISCV_ADD, RE_AARCH64_GOT_PAGE,
-          RE_AARCH64_AUTH_GOT, RE_AARCH64_AUTH_GOT_PC, RE_LOONGARCH_PLT_PAGE_PC,
-          RE_LOONGARCH_GOT, RE_LOONGARCH_GOT_PAGE_PC>(e))
+  if (oneof<R_GOTPLT, R_GOT_OFF, R_RELAX_HINT, RE_MIPS_GOT_LOCAL_PAGE,
+            RE_MIPS_GOTREL, RE_MIPS_GOT_OFF, RE_MIPS_GOT_OFF32,
+            RE_MIPS_GOT_GP_PC, RE_AARCH64_GOT_PAGE_PC, R_GOT_PC, R_GOTONLY_PC,
+            R_GOTPLTONLY_PC, R_PLT_PC, R_PLT_GOTREL, R_PLT_GOTPLT,
+            R_GOTPLT_GOTREL, R_GOTPLT_PC, RE_PPC32_PLTREL, RE_PPC64_CALL_PLT,
+            RE_PPC64_RELAX_TOC, RE_RISCV_ADD, RE_AARCH64_GOT_PAGE,
+            RE_LOONGARCH_PLT_PAGE_PC, RE_LOONGARCH_GOT,
+            RE_LOONGARCH_GOT_PAGE_PC>(e))
     return true;
 
   // These never do, except if the entire file is position dependent or if
@@ -971,11 +968,7 @@ void RelocScan::process(RelExpr expr, RelType type, uint64_t offset,
     } else if (!sym.isTls() || ctx.arg.emachine != EM_LOONGARCH) {
       // Many LoongArch TLS relocs reuse the RE_LOONGARCH_GOT type, in which
       // case the NEEDS_GOT flag shouldn't get set.
-      if (expr == RE_AARCH64_AUTH_GOT || expr == RE_AARCH64_AUTH_GOT_PAGE_PC ||
-          expr == RE_AARCH64_AUTH_GOT_PC)
-        sym.setFlags(NEEDS_GOT | NEEDS_GOT_AUTH);
-      else
-        sym.setFlags(NEEDS_GOT | NEEDS_GOT_NONAUTH);
+      sym.setFlags(NEEDS_GOT | NEEDS_GOT_NONAUTH);
     }
   } else if (needsPlt(expr)) {
     sym.setFlags(NEEDS_PLT);
@@ -1142,27 +1135,6 @@ void RelocScan::processAux(RelExpr expr, RelType type, uint64_t offset,
   printLocation(diag, *sec, sym, offset);
 }
 
-static unsigned handleAArch64PAuthTlsRelocation(InputSectionBase *sec,
-                                                RelExpr expr, RelType type,
-                                                uint64_t offset, Symbol &sym,
-                                                int64_t addend) {
-  // Do not optimize signed TLSDESC to LE/IE (as described in pauthabielf64).
-  // https://github.com/ARM-software/abi-aa/blob/main/pauthabielf64/pauthabielf64.rst#general-restrictions
-  // > PAUTHELF64 only supports the descriptor based TLS (TLSDESC).
-  if (oneof<RE_AARCH64_AUTH_TLSDESC_PAGE, RE_AARCH64_AUTH_TLSDESC>(expr)) {
-    sym.setFlags(NEEDS_TLSDESC | NEEDS_TLSDESC_AUTH);
-    sec->addReloc({expr, type, offset, addend, &sym});
-    return 1;
-  }
-
-  // TLSDESC_CALL hint relocation should not be emitted by compiler with signed
-  // TLSDESC enabled.
-  if (expr == R_TLSDESC_CALL)
-    sym.setFlags(NEEDS_TLSDESC_NONAUTH);
-
-  return 0;
-}
-
 // Notes about General Dynamic and Local Dynamic TLS models below. They may
 // require the generation of a pair of GOT entries that have associated dynamic
 // relocations. The pair of GOT entries created are of the form GOT[e0] Module
@@ -1173,27 +1145,18 @@ static unsigned handleAArch64PAuthTlsRelocation(InputSectionBase *sec,
 unsigned RelocScan::handleTlsRelocation(RelExpr expr, RelType type,
                                         uint64_t offset, Symbol &sym,
                                         int64_t addend) {
-  bool isAArch64 = ctx.arg.emachine == EM_AARCH64;
-
-  if (isAArch64)
-    if (unsigned processed = handleAArch64PAuthTlsRelocation(
-            sec, expr, type, offset, sym, addend))
-      return processed;
-
   if (expr == R_TPREL || expr == R_TPREL_NEG)
     return checkTlsLe(offset, sym, type) ? 1 : 0;
 
   bool isRISCV = ctx.arg.emachine == EM_RISCV;
 
-  if (oneof<RE_AARCH64_TLSDESC_PAGE, R_TLSDESC, R_TLSDESC_CALL, R_TLSDESC_PC,
-            R_TLSDESC_GOTPLT, RE_LOONGARCH_TLSDESC_PAGE_PC>(expr) &&
+  if (oneof<R_TLSDESC, R_TLSDESC_CALL, R_TLSDESC_PC, R_TLSDESC_GOTPLT,
+            RE_LOONGARCH_TLSDESC_PAGE_PC>(expr) &&
       ctx.arg.shared) {
     // R_RISCV_TLSDESC_{LOAD_LO12,ADD_LO12_I,CALL} reference a label. Do not
     // set NEEDS_TLSDESC on the label.
     if (expr != R_TLSDESC_CALL) {
-      if (isAArch64)
-        sym.setFlags(NEEDS_TLSDESC | NEEDS_TLSDESC_NONAUTH);
-      else if (!isRISCV || type == R_RISCV_TLSDESC_HI20)
+      if (!isRISCV || type == R_RISCV_TLSDESC_HI20)
         sym.setFlags(NEEDS_TLSDESC);
       sec->addReloc({expr, type, offset, addend, &sym});
     }
@@ -1279,9 +1242,9 @@ unsigned RelocScan::handleTlsRelocation(RelExpr expr, RelType type,
     return 1;
   }
 
-  if (oneof<RE_AARCH64_TLSDESC_PAGE, R_TLSDESC, R_TLSDESC_CALL, R_TLSDESC_PC,
-            R_TLSDESC_GOTPLT, R_TLSGD_GOT, R_TLSGD_GOTPLT, R_TLSGD_PC,
-            RE_LOONGARCH_TLSGD_PAGE_PC, RE_LOONGARCH_TLSDESC_PAGE_PC>(expr)) {
+  if (oneof<R_TLSDESC, R_TLSDESC_CALL, R_TLSDESC_PC, R_TLSDESC_GOTPLT,
+            R_TLSGD_GOT, R_TLSGD_GOTPLT, R_TLSGD_PC, RE_LOONGARCH_TLSGD_PAGE_PC,
+            RE_LOONGARCH_TLSDESC_PAGE_PC>(expr)) {
     if (!execOptimize) {
       sym.setFlags(NEEDS_TLSGD);
       sec->addReloc({expr, type, offset, addend, &sym});
@@ -1305,8 +1268,8 @@ unsigned RelocScan::handleTlsRelocation(RelExpr expr, RelType type,
     return ctx.target->getTlsGdRelaxSkip(type);
   }
 
-  if (oneof<R_GOT, R_GOTPLT, R_GOT_PC, RE_AARCH64_GOT_PAGE_PC,
-            RE_LOONGARCH_GOT_PAGE_PC, R_GOT_OFF, R_TLSIE_HINT>(expr)) {
+  if (oneof<R_GOT, R_GOTPLT, R_GOT_PC, RE_LOONGARCH_GOT_PAGE_PC, R_GOT_OFF,
+            R_TLSIE_HINT>(expr)) {
     ctx.hasTlsIe.store(true, std::memory_order_relaxed);
     // Initial-Exec relocs can be optimized to Local-Exec if the symbol is
     // locally defined.  This is not supported on SystemZ.
diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h
index 680eb66e3356a..43a3dcb09b007 100644
--- a/lld/ELF/Relocations.h
+++ b/lld/ELF/Relocations.h
@@ -95,15 +95,9 @@ enum RelExpr {
   // of a relocation type, there are some relocations whose semantics are
   // unique to a target. Such relocation are marked with RE_<TARGET_NAME>.
   RE_AARCH64_GOT_PAGE_PC,
-  RE_AARCH64_AUTH_GOT_PAGE_PC,
   RE_AARCH64_GOT_PAGE,
-  RE_AARCH64_AUTH_GOT,
-  RE_AARCH64_AUTH_GOT_PC,
   RE_AARCH64_PAGE_PC,
-  RE_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC,
   RE_AARCH64_TLSDESC_PAGE,
-  RE_AARCH64_AUTH_TLSDESC_PAGE,
-  RE_AARCH64_AUTH_TLSDESC,
   RE_AARCH64_AUTH,
   RE_ARM_PCA,
   RE_ARM_SBREL,

>From 8a3a70cc17f6d558f8e6d1527e6c736a20279e76 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Thu, 12 Feb 2026 10:15:28 -0800
Subject: [PATCH 2/3] comments

---
 lld/ELF/AArch64ErrataFix.cpp | 14 ++++++++------
 lld/ELF/Arch/AArch64.cpp     |  3 +++
 lld/ELF/RelocScan.h          |  5 +++--
 3 files changed, 14 insertions(+), 8 deletions(-)

diff --git a/lld/ELF/AArch64ErrataFix.cpp b/lld/ELF/AArch64ErrataFix.cpp
index adaaf0c750b8f..6e1169c6d90f0 100644
--- a/lld/ELF/AArch64ErrataFix.cpp
+++ b/lld/ELF/AArch64ErrataFix.cpp
@@ -541,15 +541,17 @@ static void implementPatch(Ctx &ctx, uint64_t adrpAddr, uint64_t patcheeOffset,
   // have nothing more to do.
   // Case 2: A TLS IE to LE optimization. In this case the ADRP that we read
   // will be transformed into a MOVZ later so we actually don't match the
-  // sequence and have nothing more to do. Case 3: A load/store register
-  // (unsigned immediate) class relocation. There are two of these
-  // R_AARCH_LD64_ABS_LO12_NC and R_AARCH_LD64_GOT_LO12_NC and they are both
-  // absolute. We need to add the same relocation to the patch, and replace the
-  // relocation with a R_AARCH_JUMP26 branch relocation. Case 4: No relocation.
-  // We must create a new R_AARCH64_JUMP26 branch relocation at the offset.
+  // sequence and have nothing more to do.
+  // Case 3: A load/store register (unsigned immediate) class relocation. There
+  // are two of these R_AARCH_LD64_ABS_LO12_NC and R_AARCH_LD64_GOT_LO12_NC and
+  // they are both absolute. We need to add the same relocation to the patch,
+  // and replace the relocation with a R_AARCH_JUMP26 branch relocation.
+  // Case 4: No relocation. We must create a new R_AARCH64_JUMP26 branch
+  // relocation at the offset.
   auto relIt = llvm::find_if(isec->relocs(), [=](const Relocation &r) {
     return r.offset == patcheeOffset;
   });
+  // Detect and skip Case 1 and Case 2 above.
   if (relIt != isec->relocs().end() &&
       (relIt->type == R_AARCH64_JUMP26 ||
        ((relIt->type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 ||
diff --git a/lld/ELF/Arch/AArch64.cpp b/lld/ELF/Arch/AArch64.cpp
index 0c2d3d356becc..d672932e2e5ba 100644
--- a/lld/ELF/Arch/AArch64.cpp
+++ b/lld/ELF/Arch/AArch64.cpp
@@ -196,6 +196,9 @@ void AArch64::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
       continue;
     int64_t addend = rs.getAddend<ELFT>(rel, type);
     RelExpr expr;
+    // Relocation types that only need a RelExpr set `expr` and break out of
+    // the switch to reach rs.process(). Types that need special handling
+    // (fast-path helpers, TLS) call a handler and use `continue`.
     switch (type) {
     case R_AARCH64_NONE:
       continue;
diff --git a/lld/ELF/RelocScan.h b/lld/ELF/RelocScan.h
index 64c1f992a0ef5..dae075c8d43e4 100644
--- a/lld/ELF/RelocScan.h
+++ b/lld/ELF/RelocScan.h
@@ -157,11 +157,12 @@ class RelocScan {
     return true;
   }
 
-  // Handle TLSDESC relocation. NEEDS_TLSDESC_NONAUTH is a no-op for
-  // non-AArch64 targets and detects incompatibility with NEEDS_TLSDESC_AUTH.
+  // Handle TLSDESC relocation.
   void handleTlsDesc(RelExpr sharedExpr, RelExpr ieExpr, RelType type,
                      uint64_t offset, int64_t addend, Symbol &sym) {
     if (ctx.arg.shared) {
+      // NEEDS_TLSDESC_NONAUTH is a no-op for non-AArch64 targets and detects
+      // incompatibility with NEEDS_TLSDESC_AUTH.
       sym.setFlags(NEEDS_TLSDESC | NEEDS_TLSDESC_NONAUTH);
       sec->addReloc({sharedExpr, type, offset, addend, &sym});
     } else if (sym.isPreemptible) {

>From b9c52323aadf1322ad32f320aac7755b1ec15615 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Thu, 12 Feb 2026 10:17:23 -0800
Subject: [PATCH 3/3] PLT32

---
 lld/ELF/Arch/AArch64.cpp | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/lld/ELF/Arch/AArch64.cpp b/lld/ELF/Arch/AArch64.cpp
index d672932e2e5ba..c0abfe019b786 100644
--- a/lld/ELF/Arch/AArch64.cpp
+++ b/lld/ELF/Arch/AArch64.cpp
@@ -263,16 +263,15 @@ void AArch64::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
       break;
 
       // PLT-generating relocations:
+    case R_AARCH64_PLT32:
+      sym.thunkAccessed = true;
+      [[fallthrough]];
     case R_AARCH64_CALL26:
     case R_AARCH64_CONDBR19:
     case R_AARCH64_JUMP26:
     case R_AARCH64_TSTBR14:
       rs.processR_PLT_PC(type, offset, addend, sym);
       continue;
-    case R_AARCH64_PLT32:
-      sym.thunkAccessed = true;
-      rs.processR_PLT_PC(type, offset, addend, sym);
-      continue;
 
       // GOT relocations:
     case R_AARCH64_LD64_GOT_LO12_NC:



More information about the llvm-commits mailing list