[llvm-branch-commits] [lld] [PAC][lld] Do not emit AUTH relocs against undef weak non-preemptible symbols (PR #194636)

Daniil Kovalev via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Mon May 25 02:36:47 PDT 2026


https://github.com/kovdan01 updated https://github.com/llvm/llvm-project/pull/194636

>From 4bd87749d2236a469499ee6e6c56a78d864707f9 Mon Sep 17 00:00:00 2001
From: Daniil Kovalev <dkovalev at accesssoftek.com>
Date: Tue, 28 Apr 2026 15:54:49 +0300
Subject: [PATCH 1/2] [PAC][lld] Do not emit AUTH relocs against undef weak
 non-preemptible symbols

Undefined weak non-preemptible symbols should be statically resolved to
the addend value and not signed. Previously, a dynamic relocation
against such symbols was emitted, which is not a correct behavior.

See also docs: https://github.com/ARM-software/abi-aa/pull/391

Resolves #173296
---
 lld/ELF/Arch/AArch64.cpp                      | 135 +++++++++++++++---
 lld/ELF/RelocScan.h                           |   4 +-
 lld/ELF/Relocations.cpp                       |  19 ++-
 lld/ELF/SyntheticSections.cpp                 |  20 ++-
 lld/ELF/SyntheticSections.h                   |   3 +-
 .../ELF/aarch64-reloc-pauth-undef-weak-dso.s  |  48 +++++++
 .../ELF/aarch64-reloc-pauth-undef-weak-pie.s  |  47 ++++++
 lld/test/ELF/aarch64-reloc-pauth-undef-weak.s |  48 +++++++
 lld/test/ELF/aarch64-tlsdesc-pauth.s          |  12 +-
 9 files changed, 300 insertions(+), 36 deletions(-)
 create mode 100644 lld/test/ELF/aarch64-reloc-pauth-undef-weak-dso.s
 create mode 100644 lld/test/ELF/aarch64-reloc-pauth-undef-weak-pie.s
 create mode 100644 lld/test/ELF/aarch64-reloc-pauth-undef-weak.s

diff --git a/lld/ELF/Arch/AArch64.cpp b/lld/ELF/Arch/AArch64.cpp
index 99b3085852df7..9f72ce14c80ec 100644
--- a/lld/ELF/Arch/AArch64.cpp
+++ b/lld/ELF/Arch/AArch64.cpp
@@ -102,6 +102,8 @@ class AArch64 : public TargetInfo {
   void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
   void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
   void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
+  void relaxAuthTlsDescForNonPreemptibleUndefWeak(uint8_t *loc,
+                                                  const Relocation &rel) const;
 };
 
 struct AArch64Relaxer {
@@ -191,6 +193,7 @@ template <class ELFT, class RelTy>
 void AArch64::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
   RelocScan rs(ctx, &sec);
   sec.relocations.reserve(rels.size());
+  SmallVector<Relocation, 0> tlsdescCallRels;
 
   for (auto it = rels.begin(); it != rels.end(); ++it) {
     const RelTy &rel = *it;
@@ -206,6 +209,20 @@ void AArch64::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
     // 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`.
+
+    auto handleTlsDescAuth = [&sym, &sec, type, offset,
+                              addend](RelExpr tlsdescExpr) {
+      sym.setFlags(NEEDS_TLSDESC_AUTH);
+      if (sym.isUndefWeak() && !sym.isPreemptible) {
+        // Resolves statically to null. Handle in
+        // relaxAuthTlsDescForNonPreemptibleUndefWeak
+        sec.addReloc({R_TPREL, type, offset, addend, &sym});
+      } else {
+        sym.setFlags(NEEDS_TLSDESC);
+        sec.addReloc({tlsdescExpr, type, offset, addend, &sym});
+      }
+    };
+
     switch (type) {
     case R_AARCH64_NONE:
       continue;
@@ -340,30 +357,28 @@ void AArch64::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
 
     // TLSDESC relocations:
     case R_AARCH64_TLSDESC_ADR_PAGE21:
+      sym.setFlags(NEEDS_TLSDESC_NONAUTH);
       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:
+      sym.setFlags(NEEDS_TLSDESC_NONAUTH);
       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});
+      tlsdescCallRels.push_back({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});
+      handleTlsDescAuth(RE_AARCH64_TLSDESC_PAGE);
       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});
+      handleTlsDescAuth(R_TLSDESC);
       continue;
 
     default:
@@ -375,6 +390,27 @@ void AArch64::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
     rs.process(expr, type, offset, sym, addend);
   }
 
+  for (const auto &tlsdescCallRel : tlsdescCallRels) {
+    const Symbol *sym = tlsdescCallRel.sym;
+    auto flags = sym->flags.load(std::memory_order_relaxed);
+
+    if (flags & NEEDS_TLSDESC_AUTH) {
+      if (sym->isUndefWeak() && !sym->isPreemptible)
+        sec.addReloc(tlsdescCallRel);
+      continue;
+    }
+    if (flags & NEEDS_TLSDESC_NONAUTH) {
+      if (!ctx.arg.shared)
+        sec.addReloc(tlsdescCallRel);
+      continue;
+    }
+
+    Err(ctx) << getErrorLoc(ctx, sec.content().data() + tlsdescCallRel.offset)
+             << "relocation R_AARCH64_TLSDESC_CALL against '" << sym
+             << "' requires other TLSDESC or AUTH TLSDESC relocations present "
+                "against this symbol";
+  }
+
   if (ctx.arg.branchToBranch)
     llvm::stable_sort(sec.relocs(),
                       [](auto &l, auto &r) { return l.offset < r.offset; });
@@ -645,11 +681,22 @@ void AArch64::relocate(uint8_t *loc, const Relocation &rel,
     write64(ctx, loc, val);
     break;
   case R_AARCH64_AUTH_ABS64:
-    // This is used for the addend of a .relr.auth.dyn entry,
-    // which is a 32-bit value; the upper 32 bits are used to
-    // encode the schema.
-    checkInt(ctx, loc, val, 32, rel);
-    write32(ctx, loc, val);
+    if (rel.sym->isUndefined() && !rel.sym->isPreemptible) {
+      // Undefined weak non-preemptible symbols are statically resolved to the
+      // addend. No dynamic relocation and corresponding signing schema encoding
+      // is needed.
+      //
+      // Note: at this point, binding of undefined weak non-preemptible symbols
+      // has already been changed from weak to local by computeBinding call, so
+      // just check against isUndefined().
+      write64(ctx, loc, val);
+    } else {
+      // This is used for the addend of a .relr.auth.dyn entry,
+      // which is a 32-bit value; the upper 32 bits are used to
+      // encode the schema.
+      checkInt(ctx, loc, val, 32, rel);
+      write32(ctx, loc, val);
+    }
     break;
   case R_AARCH64_TLS_DTPREL64:
     write64(ctx, loc, val);
@@ -803,6 +850,35 @@ void AArch64::relocate(uint8_t *loc, const Relocation &rel,
   }
 }
 
+void AArch64::relaxAuthTlsDescForNonPreemptibleUndefWeak(
+    uint8_t *loc, const Relocation &rel) const {
+  // AUTH TLSDESC relocations are in the form:
+  //   adrp x0, :tlsdesc_auth:v             [R_AARCH64_AUTH_TLSDESC_ADR_PAGE21]
+  //   ldr  x16, [x0, :tlsdesc_auth_lo12:v] [R_AARCH64_AUTH_TLSDESC_LD64_LO12]
+  //   add  x0, x0, :tlsdesc_auth_lo12:v    [R_AARCH64_AUTH_TLSDESC_ADD_LO12]
+  //   .tlsdesccall v                       [R_AARCH64_TLSDESC_CALL]
+  //   blraa x16, x0
+  // And it can optimized to:
+  //   mov     x0, #0x0
+  //   nop
+  //   nop
+  //   nop
+
+  switch (rel.type) {
+  case R_AARCH64_AUTH_TLSDESC_ADD_LO12:
+  case R_AARCH64_AUTH_TLSDESC_LD64_LO12:
+  case R_AARCH64_TLSDESC_CALL:
+    write32le(loc, 0xd503201f); // nop
+    return;
+  case R_AARCH64_AUTH_TLSDESC_ADR_PAGE21:
+    write32le(loc, 0xd2800000); // mov x0, #0x0
+    return;
+  default:
+    llvm_unreachable("unsupported relocation for non-preemptible undefined "
+                     "weak AUTH TLSDESC relaxation");
+  }
+}
+
 void AArch64::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
                              uint64_t val) const {
   // TLSDESC Global-Dynamic relocation are in the form:
@@ -1054,6 +1130,26 @@ void AArch64::relocateAlloc(InputSection &sec, uint8_t *buf) const {
       continue;
     }
 
+    auto tryRelaxTlsDesc = [this, &rel, loc, val]() {
+      assert(rel.sym->hasFlag(NEEDS_TLSDESC_NONAUTH));
+      assert(!rel.sym->hasFlag(NEEDS_TLSDESC_AUTH));
+      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);
+    };
+
+    auto tryRelaxTlsDescAuth = [this, &rel, loc, val]() {
+      assert(!rel.sym->hasFlag(NEEDS_TLSDESC_NONAUTH));
+      assert(rel.sym->hasFlag(NEEDS_TLSDESC_AUTH));
+      if (rel.expr == R_TPREL)
+        relaxAuthTlsDescForNonPreemptibleUndefWeak(loc, rel);
+      else
+        relocate(loc, rel, val);
+    };
+
     switch (rel.type) {
     case R_AARCH64_ADR_GOT_PAGE:
       if (i + 1 < size &&
@@ -1073,13 +1169,18 @@ void AArch64::relocateAlloc(InputSection &sec, uint8_t *buf) const {
     case R_AARCH64_TLSDESC_ADR_PAGE21:
     case R_AARCH64_TLSDESC_LD64_LO12:
     case R_AARCH64_TLSDESC_ADD_LO12:
+      tryRelaxTlsDesc();
+      continue;
     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);
+      if (rel.sym->hasFlag(NEEDS_TLSDESC_AUTH))
+        tryRelaxTlsDescAuth();
       else
-        relocate(loc, rel, val);
+        tryRelaxTlsDesc();
+      continue;
+    case R_AARCH64_AUTH_TLSDESC_ADR_PAGE21:
+    case R_AARCH64_AUTH_TLSDESC_LD64_LO12:
+    case R_AARCH64_AUTH_TLSDESC_ADD_LO12:
+      tryRelaxTlsDescAuth();
       continue;
     case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
     case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
diff --git a/lld/ELF/RelocScan.h b/lld/ELF/RelocScan.h
index 526482a156ccc..9829cec203261 100644
--- a/lld/ELF/RelocScan.h
+++ b/lld/ELF/RelocScan.h
@@ -159,9 +159,7 @@ class RelocScan {
   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);
+      sym.setFlags(NEEDS_TLSDESC);
       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 ac7752a423440..abac5d9ea4c22 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -789,9 +789,12 @@ static void addGotAuthEntry(Ctx &ctx, Symbol &sym) {
     return;
   }
 
-  // Signed GOT requires dynamic relocation.
-  ctx.in.relaDyn->addReloc(
-      {R_AARCH64_AUTH_RELATIVE, ctx.in.got.get(), off, false, sym, 0, R_ABS});
+  // Signed GOT requires dynamic relocation unless the symbol is
+  // non-preemptible and undefined weak.
+  if (!sym.isUndefWeak()) {
+    ctx.in.relaDyn->addReloc(
+        {R_AARCH64_AUTH_RELATIVE, ctx.in.got.get(), off, false, sym, 0, R_ABS});
+  }
 }
 
 static void addTpOffsetGotEntry(Ctx &ctx, Symbol &sym) {
@@ -851,8 +854,12 @@ bool RelocScan::isStaticLinkTimeConstant(RelExpr e, RelType type,
   // only the low bits are used.
   if (e == R_GOT || e == R_PLT)
     return ctx.target->usesOnlyLowPageBits(type) || !ctx.arg.isPic;
-  // R_AARCH64_AUTH_ABS64 and iRelSymbolicRel require a dynamic relocation.
-  if (e == RE_AARCH64_AUTH || type == ctx.target->iRelSymbolicRel)
+  // R_AARCH64_AUTH_ABS64 requires a dynamic relocation unless the symbol is
+  // non-preemptible and undefined weak.
+  if (e == RE_AARCH64_AUTH && (!sym.isUndefWeak() || sym.isPreemptible))
+    return false;
+  // iRelSymbolicRel requires a dynamic relocation.
+  if (type == ctx.target->iRelSymbolicRel)
     return false;
 
   // The behavior of an undefined weak reference is implementation defined.
@@ -1354,7 +1361,7 @@ void elf::postScanRelocations(Ctx &ctx) {
       got->addTlsDescEntry(sym);
       RelType tlsDescRel = ctx.target->tlsDescRel;
       if (flags & NEEDS_TLSDESC_AUTH) {
-        got->addTlsDescAuthEntry();
+        got->addTlsDescAuthEntry(sym);
         tlsDescRel = ELF::R_AARCH64_AUTH_TLSDESC;
       }
       ctx.in.relaDyn->addAddendOnlyRelocIfNonPreemptible(
diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index b9a42590dc8e7..476797d4f5f0c 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -507,7 +507,9 @@ void GotSection::addEntry(const Symbol &sym) {
 
 void GotSection::addAuthEntry(const Symbol &sym) {
   authEntries.push_back(
-      {(numEntries - 1) * ctx.target->gotEntrySize, sym.isFunc()});
+      {/*offset=*/(numEntries - 1) * ctx.target->gotEntrySize,
+       /*isSymbolFunc=*/sym.isFunc(),
+       /*isUndefWeakNonPreemptible=*/sym.isUndefWeak() && !sym.isPreemptible});
 }
 
 bool GotSection::addTlsDescEntry(const Symbol &sym) {
@@ -517,9 +519,12 @@ bool GotSection::addTlsDescEntry(const Symbol &sym) {
   return true;
 }
 
-void GotSection::addTlsDescAuthEntry() {
-  authEntries.push_back({(numEntries - 2) * ctx.target->gotEntrySize, true});
-  authEntries.push_back({(numEntries - 1) * ctx.target->gotEntrySize, false});
+void GotSection::addTlsDescAuthEntry(const Symbol &sym) {
+  authEntries.push_back({/*offset=*/(numEntries - 2) * ctx.target->gotEntrySize,
+                         /*isSymbolFunc=*/true,
+                         /*isUndefWeakNonPreemptible=*/false});
+  assert(!sym.isFunc());
+  addAuthEntry(sym);
 }
 
 bool GotSection::addDynTlsEntry(const Symbol &sym) {
@@ -578,6 +583,12 @@ void GotSection::writeTo(uint8_t *buf) {
   ctx.target->writeGotHeader(buf);
   ctx.target->relocateAlloc(*this, buf);
   for (const AuthEntryInfo &authEntry : authEntries) {
+    uint8_t *dest = buf + authEntry.offset;
+
+    if (authEntry.isUndefWeakNonPreemptible) {
+      write64(ctx, dest, 0);
+      continue;
+    }
     // https://github.com/ARM-software/abi-aa/blob/2024Q3/pauthabielf64/pauthabielf64.rst#default-signing-schema
     //   Signed GOT entries use the IA key for symbols of type STT_FUNC and the
     //   DA key for all other symbol types, with the address of the GOT entry as
@@ -587,7 +598,6 @@ void GotSection::writeTo(uint8_t *buf) {
     // https://github.com/ARM-software/abi-aa/blob/2024Q3/pauthabielf64/pauthabielf64.rst#encoding-the-signing-schema
     //   If address diversity is set and the discriminator
     //   is 0 then modifier = Place
-    uint8_t *dest = buf + authEntry.offset;
     uint64_t key = authEntry.isSymbolFunc ? /*IA=*/0b00 : /*DA=*/0b10;
     uint64_t addrDiversity = 1;
     write64(ctx, dest, (addrDiversity << 63) | (key << 60));
diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h
index 38a8bb9b320f4..e9dcd8f1c7a30 100644
--- a/lld/ELF/SyntheticSections.h
+++ b/lld/ELF/SyntheticSections.h
@@ -123,7 +123,7 @@ class GotSection final : public SyntheticSection {
   void addEntry(const Symbol &sym);
   void addAuthEntry(const Symbol &sym);
   bool addTlsDescEntry(const Symbol &sym);
-  void addTlsDescAuthEntry();
+  void addTlsDescAuthEntry(const Symbol &sym);
   bool addDynTlsEntry(const Symbol &sym);
   bool addTlsIndex();
   uint32_t getTlsDescOffset(const Symbol &sym) const;
@@ -144,6 +144,7 @@ class GotSection final : public SyntheticSection {
   struct AuthEntryInfo {
     size_t offset;
     bool isSymbolFunc;
+    bool isUndefWeakNonPreemptible;
   };
   SmallVector<AuthEntryInfo, 0> authEntries;
 };
diff --git a/lld/test/ELF/aarch64-reloc-pauth-undef-weak-dso.s b/lld/test/ELF/aarch64-reloc-pauth-undef-weak-dso.s
new file mode 100644
index 0000000000000..3080d5cef9eca
--- /dev/null
+++ b/lld/test/ELF/aarch64-reloc-pauth-undef-weak-dso.s
@@ -0,0 +1,48 @@
+# REQUIRES: aarch64
+# RUN: llvm-mc -filetype=obj -triple=aarch64 -mattr=+pauth %s -o %t.o
+# RUN: ld.lld -shared %t.o -o %t
+# RUN: llvm-readobj -r %t | FileCheck %s --check-prefix=RELA
+# RUN: llvm-readelf -x.data %t | FileCheck %s --check-prefix=DATA
+# RUN: llvm-readelf -x.got  %t | FileCheck %s --check-prefix=GOT
+# RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck %s --check-prefix=DIS
+
+## Verify that R_AARCH64_AUTH_ABS64 against a weak undefined symbol is resolved
+## to NULL (plus addend).
+
+# RELA-LABEL: Relocations [
+# RELA-NEXT:  ]
+
+# DATA-LABEL: Hex dump of section '.data':
+# DATA-NEXT:  0x00030300 00000000 00000000 25000000 00000000
+# DATA-NEXT:  0x00030310 00000000 00000000 25000000 00000000
+
+# GOT-LABEL:  Hex dump of section '.got':
+# GOT-NEXT:   0x000202f8 00000000 00000000
+
+# DIS-LABEL:  <_start>:
+# DIS-NEXT:     adrp x0, 0x20000
+# DIS-NEXT:     ldr  x0, [x0, #0x2f8]
+# DIS-NEXT:     mov  x0, #0x0
+# DIS-NEXT:     nop
+# DIS-NEXT:     nop
+# DIS-NEXT:     nop
+
+.weak undef
+.hidden undef
+
+.globl _start
+_start:
+  adrp x0, :got_auth:undef
+  ldr x0, [x0, :got_auth_lo12:undef]
+  adrp  x0,  :tlsdesc_auth:undef
+  ldr   x16, [x0, :tlsdesc_auth_lo12:undef]
+  add   x0,  x0, :tlsdesc_auth_lo12:undef
+  .tlsdesccall undef
+  blraa x16, x0
+
+.data
+foo:
+.quad undef at AUTH(da,42)
+.quad (undef + 37)@AUTH(da,42)
+.quad undef
+.quad (undef + 37)
diff --git a/lld/test/ELF/aarch64-reloc-pauth-undef-weak-pie.s b/lld/test/ELF/aarch64-reloc-pauth-undef-weak-pie.s
new file mode 100644
index 0000000000000..bec858d2ea7c9
--- /dev/null
+++ b/lld/test/ELF/aarch64-reloc-pauth-undef-weak-pie.s
@@ -0,0 +1,47 @@
+# REQUIRES: aarch64
+# RUN: llvm-mc -filetype=obj -triple=aarch64 -mattr=+pauth %s -o %t.o
+# RUN: ld.lld -pie %t.o -o %t
+# RUN: llvm-readobj -r %t | FileCheck %s --check-prefix=RELA
+# RUN: llvm-readelf -x.data %t | FileCheck %s --check-prefix=DATA
+# RUN: llvm-readelf -x.got  %t | FileCheck %s --check-prefix=GOT
+# RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck %s --check-prefix=DIS
+
+## Verify that R_AARCH64_AUTH_ABS64 against a weak undefined symbol is resolved
+## to NULL (plus addend).
+
+# RELA-LABEL: Relocations [
+# RELA-NEXT:  ]
+
+# DATA-LABEL: Hex dump of section '.data':
+# DATA-NEXT:  0x000302f8 00000000 00000000 25000000 00000000
+# DATA-NEXT:  0x00030308 00000000 00000000 25000000 00000000
+
+# GOT-LABEL:  Hex dump of section '.got':
+# GOT-NEXT:   0x000202f0 00000000 00000000
+
+# DIS-LABEL:  <_start>:
+# DIS-NEXT:     adrp x0, 0x20000
+# DIS-NEXT:     ldr  x0, [x0, #0x2f0]
+# DIS-NEXT:     mov  x0, #0x0
+# DIS-NEXT:     nop
+# DIS-NEXT:     nop
+# DIS-NEXT:     nop
+
+.weak undef
+
+.globl _start
+_start:
+  adrp x0, :got_auth:undef
+  ldr x0, [x0, :got_auth_lo12:undef]
+  adrp  x0,  :tlsdesc_auth:undef
+  ldr   x16, [x0, :tlsdesc_auth_lo12:undef]
+  add   x0,  x0, :tlsdesc_auth_lo12:undef
+  .tlsdesccall undef
+  blraa x16, x0
+
+.data
+foo:
+.quad undef at AUTH(da,42)
+.quad (undef + 37)@AUTH(da,42)
+.quad undef
+.quad (undef + 37)
diff --git a/lld/test/ELF/aarch64-reloc-pauth-undef-weak.s b/lld/test/ELF/aarch64-reloc-pauth-undef-weak.s
new file mode 100644
index 0000000000000..44995654909bf
--- /dev/null
+++ b/lld/test/ELF/aarch64-reloc-pauth-undef-weak.s
@@ -0,0 +1,48 @@
+# REQUIRES: aarch64
+# RUN: llvm-mc -filetype=obj -triple=aarch64 -mattr=+pauth %s -o %t.o
+# RUN: ld.lld --static %t.o -o %t
+# RUN: llvm-readobj -r %t | FileCheck %s --check-prefix=RELA
+# RUN: llvm-readelf -x.data %t | FileCheck %s --check-prefix=DATA
+# RUN: llvm-readelf -x.got  %t | FileCheck %s --check-prefix=GOT
+# RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck %s --check-prefix=DIS
+
+## Verify that R_AARCH64_AUTH_ABS64 against a weak undefined symbol is resolved
+## to NULL (plus addend).
+
+# RELA-LABEL: Relocations [
+# RELA-NEXT:  ]
+
+# DATA-LABEL: Hex dump of section '.data':
+# DATA-NEXT:  0x002301e8 00000000 00000000 25000000 00000000
+# DATA-NEXT:  0x002301f8 00000000 00000000 25000000 00000000
+
+# GOT-LABEL:  Hex dump of section '.got':
+# GOT-NEXT:   0x002201e0 00000000 00000000
+
+# DIS-LABEL:  <_start>:
+
+# DIS-NEXT:     adrp  x0,  0x220000
+# DIS-NEXT:     ldr   x0,  [x0, #0x1e0]
+# DIS-NEXT:     mov   x0,  #0x0
+# DIS-NEXT:     nop
+# DIS-NEXT:     nop
+# DIS-NEXT:     nop
+
+.weak undef
+
+.globl _start
+_start:
+  adrp  x0,  :got_auth:undef
+  ldr   x0,  [x0, :got_auth_lo12:undef]
+  adrp  x0,  :tlsdesc_auth:undef
+  ldr   x16, [x0, :tlsdesc_auth_lo12:undef]
+  add   x0,  x0, :tlsdesc_auth_lo12:undef
+  .tlsdesccall undef
+  blraa x16, x0
+
+.data
+foo:
+.quad undef at AUTH(da,42)
+.quad (undef + 37)@AUTH(da,42)
+.quad undef
+.quad undef + 37
diff --git a/lld/test/ELF/aarch64-tlsdesc-pauth.s b/lld/test/ELF/aarch64-tlsdesc-pauth.s
index bf0ae4a87f322..b5f818cf60567 100644
--- a/lld/test/ELF/aarch64-tlsdesc-pauth.s
+++ b/lld/test/ELF/aarch64-tlsdesc-pauth.s
@@ -27,6 +27,7 @@ a:
         adrp    x0, :tlsdesc_auth:a
         ldr     x16, [x0, :tlsdesc_auth_lo12:a]
         add     x0, x0, :tlsdesc_auth_lo12:a
+        .tlsdesccall a
         blraa   x16, x0
 
 // CHECK:      adrp    x0, 0x[[P]]000
@@ -41,6 +42,7 @@ a:
         adrp    x0, :tlsdesc_auth:local1
         ldr     x16, [x0, :tlsdesc_auth_lo12:local1]
         add     x0, x0, :tlsdesc_auth_lo12:local1
+        .tlsdesccall local1
         blraa   x16, x0
 
 // CHECK:      adrp    x0, 0x[[P]]000
@@ -51,6 +53,7 @@ a:
         adrp    x0, :tlsdesc_auth:local2
         ldr     x16, [x0, :tlsdesc_auth_lo12:local2]
         add     x0, x0, :tlsdesc_auth_lo12:local2
+        .tlsdesccall local2
         blraa   x16, x0
 
 // CHECK:      adrp    x0, 0x[[P]]000
@@ -100,11 +103,13 @@ local2:
         adrp    x0, :tlsdesc_auth:a
         ldr     x16, [x0, :tlsdesc_auth_lo12:a]
         add     x0, x0, :tlsdesc_auth_lo12:a
+        .tlsdesccall a
         blraa   x16, x0
 
         adrp    x0, :tlsdesc:a
         ldr     x1, [x0, :tlsdesc_lo12:a]
         add     x0, x0, :tlsdesc_lo12:a
+        .tlsdesccall a
         blr     x1
 
 //--- err2.s
@@ -115,20 +120,19 @@ local2:
         adrp    x0, :tlsdesc:a
         ldr     x1, [x0, :tlsdesc_lo12:a]
         add     x0, x0, :tlsdesc_lo12:a
+        .tlsdesccall a
         blr     x1
 
         adrp    x0, :tlsdesc_auth:a
         ldr     x16, [x0, :tlsdesc_auth_lo12:a]
         add     x0, x0, :tlsdesc_auth_lo12:a
+        .tlsdesccall a
         blraa   x16, x0
 
 //--- err3.s
 // RUN: llvm-mc -filetype=obj -triple=aarch64-pc-linux -mattr=+pauth err3.s -o err3.o
 // RUN: not ld.lld -shared err3.o 2>&1 | FileCheck --check-prefix=ERR3 --implicit-check-not=error: %s
-// ERR3: error: both AUTH and non-AUTH TLSDESC entries for 'a' requested, but only one type of TLSDESC entry per symbol is supported
+// ERR3: error: err3.o:(.text+0x0): relocation R_AARCH64_TLSDESC_CALL against 'a' requires other TLSDESC or AUTH TLSDESC relocations present against this symbol
         .text
-        adrp    x0, :tlsdesc_auth:a
-        ldr     x16, [x0, :tlsdesc_auth_lo12:a]
-        add     x0, x0, :tlsdesc_auth_lo12:a
         .tlsdesccall a
         blraa   x16, x0

>From 727f78810bf1e38c633d9d35a290cd6291f8cb9e Mon Sep 17 00:00:00 2001
From: Daniil Kovalev <dkovalev at accesssoftek.com>
Date: Mon, 18 May 2026 18:40:51 +0300
Subject: [PATCH 2/2] Support R_AARCH64_AUTH_TLSDESC_CALL

---
 lld/ELF/Arch/AArch64.cpp                      | 73 +++++--------------
 lld/ELF/RelocScan.h                           |  4 +-
 .../ELF/aarch64-reloc-pauth-undef-weak-dso.s  |  2 +-
 .../ELF/aarch64-reloc-pauth-undef-weak-pie.s  |  2 +-
 lld/test/ELF/aarch64-reloc-pauth-undef-weak.s |  2 +-
 lld/test/ELF/aarch64-tlsdesc-pauth.s          | 15 ++--
 6 files changed, 35 insertions(+), 63 deletions(-)

diff --git a/lld/ELF/Arch/AArch64.cpp b/lld/ELF/Arch/AArch64.cpp
index 9f72ce14c80ec..0eeba27eefcdb 100644
--- a/lld/ELF/Arch/AArch64.cpp
+++ b/lld/ELF/Arch/AArch64.cpp
@@ -193,7 +193,6 @@ template <class ELFT, class RelTy>
 void AArch64::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
   RelocScan rs(ctx, &sec);
   sec.relocations.reserve(rels.size());
-  SmallVector<Relocation, 0> tlsdescCallRels;
 
   for (auto it = rels.begin(); it != rels.end(); ++it) {
     const RelTy &rel = *it;
@@ -357,17 +356,17 @@ void AArch64::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
 
     // TLSDESC relocations:
     case R_AARCH64_TLSDESC_ADR_PAGE21:
-      sym.setFlags(NEEDS_TLSDESC_NONAUTH);
       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:
-      sym.setFlags(NEEDS_TLSDESC_NONAUTH);
       rs.handleTlsDesc(R_TLSDESC, R_GOT, type, offset, addend, sym);
       continue;
     case R_AARCH64_TLSDESC_CALL:
-      tlsdescCallRels.push_back({R_TPREL, type, offset, addend, &sym});
+      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
@@ -380,6 +379,11 @@ void AArch64::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
     case R_AARCH64_AUTH_TLSDESC_ADD_LO12:
       handleTlsDescAuth(R_TLSDESC);
       continue;
+    case R_AARCH64_AUTH_TLSDESC_CALL:
+      sym.setFlags(NEEDS_TLSDESC_AUTH);
+      if (sym.isUndefWeak() && !sym.isPreemptible)
+        sec.addReloc({R_TPREL, type, offset, addend, &sym});
+      continue;
 
     default:
       Err(ctx) << getErrorLoc(ctx, sec.content().data() + offset)
@@ -390,27 +394,6 @@ void AArch64::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
     rs.process(expr, type, offset, sym, addend);
   }
 
-  for (const auto &tlsdescCallRel : tlsdescCallRels) {
-    const Symbol *sym = tlsdescCallRel.sym;
-    auto flags = sym->flags.load(std::memory_order_relaxed);
-
-    if (flags & NEEDS_TLSDESC_AUTH) {
-      if (sym->isUndefWeak() && !sym->isPreemptible)
-        sec.addReloc(tlsdescCallRel);
-      continue;
-    }
-    if (flags & NEEDS_TLSDESC_NONAUTH) {
-      if (!ctx.arg.shared)
-        sec.addReloc(tlsdescCallRel);
-      continue;
-    }
-
-    Err(ctx) << getErrorLoc(ctx, sec.content().data() + tlsdescCallRel.offset)
-             << "relocation R_AARCH64_TLSDESC_CALL against '" << sym
-             << "' requires other TLSDESC or AUTH TLSDESC relocations present "
-                "against this symbol";
-  }
-
   if (ctx.arg.branchToBranch)
     llvm::stable_sort(sec.relocs(),
                       [](auto &l, auto &r) { return l.offset < r.offset; });
@@ -856,7 +839,7 @@ void AArch64::relaxAuthTlsDescForNonPreemptibleUndefWeak(
   //   adrp x0, :tlsdesc_auth:v             [R_AARCH64_AUTH_TLSDESC_ADR_PAGE21]
   //   ldr  x16, [x0, :tlsdesc_auth_lo12:v] [R_AARCH64_AUTH_TLSDESC_LD64_LO12]
   //   add  x0, x0, :tlsdesc_auth_lo12:v    [R_AARCH64_AUTH_TLSDESC_ADD_LO12]
-  //   .tlsdesccall v                       [R_AARCH64_TLSDESC_CALL]
+  //   .tlsdescauthcall v                   [R_AARCH64_AUTH_TLSDESC_CALL]
   //   blraa x16, x0
   // And it can optimized to:
   //   mov     x0, #0x0
@@ -867,7 +850,7 @@ void AArch64::relaxAuthTlsDescForNonPreemptibleUndefWeak(
   switch (rel.type) {
   case R_AARCH64_AUTH_TLSDESC_ADD_LO12:
   case R_AARCH64_AUTH_TLSDESC_LD64_LO12:
-  case R_AARCH64_TLSDESC_CALL:
+  case R_AARCH64_AUTH_TLSDESC_CALL:
     write32le(loc, 0xd503201f); // nop
     return;
   case R_AARCH64_AUTH_TLSDESC_ADR_PAGE21:
@@ -1130,26 +1113,6 @@ void AArch64::relocateAlloc(InputSection &sec, uint8_t *buf) const {
       continue;
     }
 
-    auto tryRelaxTlsDesc = [this, &rel, loc, val]() {
-      assert(rel.sym->hasFlag(NEEDS_TLSDESC_NONAUTH));
-      assert(!rel.sym->hasFlag(NEEDS_TLSDESC_AUTH));
-      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);
-    };
-
-    auto tryRelaxTlsDescAuth = [this, &rel, loc, val]() {
-      assert(!rel.sym->hasFlag(NEEDS_TLSDESC_NONAUTH));
-      assert(rel.sym->hasFlag(NEEDS_TLSDESC_AUTH));
-      if (rel.expr == R_TPREL)
-        relaxAuthTlsDescForNonPreemptibleUndefWeak(loc, rel);
-      else
-        relocate(loc, rel, val);
-    };
-
     switch (rel.type) {
     case R_AARCH64_ADR_GOT_PAGE:
       if (i + 1 < size &&
@@ -1169,18 +1132,22 @@ void AArch64::relocateAlloc(InputSection &sec, uint8_t *buf) const {
     case R_AARCH64_TLSDESC_ADR_PAGE21:
     case R_AARCH64_TLSDESC_LD64_LO12:
     case R_AARCH64_TLSDESC_ADD_LO12:
-      tryRelaxTlsDesc();
-      continue;
     case R_AARCH64_TLSDESC_CALL:
-      if (rel.sym->hasFlag(NEEDS_TLSDESC_AUTH))
-        tryRelaxTlsDescAuth();
+      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
-        tryRelaxTlsDesc();
+        relocate(loc, rel, val);
       continue;
     case R_AARCH64_AUTH_TLSDESC_ADR_PAGE21:
     case R_AARCH64_AUTH_TLSDESC_LD64_LO12:
     case R_AARCH64_AUTH_TLSDESC_ADD_LO12:
-      tryRelaxTlsDescAuth();
+    case R_AARCH64_AUTH_TLSDESC_CALL:
+      if (rel.expr == R_TPREL)
+        relaxAuthTlsDescForNonPreemptibleUndefWeak(loc, rel);
+      else
+        relocate(loc, rel, val);
       continue;
     case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
     case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
diff --git a/lld/ELF/RelocScan.h b/lld/ELF/RelocScan.h
index 9829cec203261..526482a156ccc 100644
--- a/lld/ELF/RelocScan.h
+++ b/lld/ELF/RelocScan.h
@@ -159,7 +159,9 @@ class RelocScan {
   void handleTlsDesc(RelExpr sharedExpr, RelExpr ieExpr, RelType type,
                      uint64_t offset, int64_t addend, Symbol &sym) {
     if (ctx.arg.shared) {
-      sym.setFlags(NEEDS_TLSDESC);
+      // 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) {
       // Optimize to Initial Exec.
diff --git a/lld/test/ELF/aarch64-reloc-pauth-undef-weak-dso.s b/lld/test/ELF/aarch64-reloc-pauth-undef-weak-dso.s
index 3080d5cef9eca..0346f45992381 100644
--- a/lld/test/ELF/aarch64-reloc-pauth-undef-weak-dso.s
+++ b/lld/test/ELF/aarch64-reloc-pauth-undef-weak-dso.s
@@ -37,7 +37,7 @@ _start:
   adrp  x0,  :tlsdesc_auth:undef
   ldr   x16, [x0, :tlsdesc_auth_lo12:undef]
   add   x0,  x0, :tlsdesc_auth_lo12:undef
-  .tlsdesccall undef
+  .tlsdescauthcall undef
   blraa x16, x0
 
 .data
diff --git a/lld/test/ELF/aarch64-reloc-pauth-undef-weak-pie.s b/lld/test/ELF/aarch64-reloc-pauth-undef-weak-pie.s
index bec858d2ea7c9..fa168317a50de 100644
--- a/lld/test/ELF/aarch64-reloc-pauth-undef-weak-pie.s
+++ b/lld/test/ELF/aarch64-reloc-pauth-undef-weak-pie.s
@@ -36,7 +36,7 @@ _start:
   adrp  x0,  :tlsdesc_auth:undef
   ldr   x16, [x0, :tlsdesc_auth_lo12:undef]
   add   x0,  x0, :tlsdesc_auth_lo12:undef
-  .tlsdesccall undef
+  .tlsdescauthcall undef
   blraa x16, x0
 
 .data
diff --git a/lld/test/ELF/aarch64-reloc-pauth-undef-weak.s b/lld/test/ELF/aarch64-reloc-pauth-undef-weak.s
index 44995654909bf..a5ae1e919e992 100644
--- a/lld/test/ELF/aarch64-reloc-pauth-undef-weak.s
+++ b/lld/test/ELF/aarch64-reloc-pauth-undef-weak.s
@@ -37,7 +37,7 @@ _start:
   adrp  x0,  :tlsdesc_auth:undef
   ldr   x16, [x0, :tlsdesc_auth_lo12:undef]
   add   x0,  x0, :tlsdesc_auth_lo12:undef
-  .tlsdesccall undef
+  .tlsdescauthcall undef
   blraa x16, x0
 
 .data
diff --git a/lld/test/ELF/aarch64-tlsdesc-pauth.s b/lld/test/ELF/aarch64-tlsdesc-pauth.s
index b5f818cf60567..41c7b31b5a7e6 100644
--- a/lld/test/ELF/aarch64-tlsdesc-pauth.s
+++ b/lld/test/ELF/aarch64-tlsdesc-pauth.s
@@ -27,7 +27,7 @@ a:
         adrp    x0, :tlsdesc_auth:a
         ldr     x16, [x0, :tlsdesc_auth_lo12:a]
         add     x0, x0, :tlsdesc_auth_lo12:a
-        .tlsdesccall a
+        .tlsdescauthcall a
         blraa   x16, x0
 
 // CHECK:      adrp    x0, 0x[[P]]000
@@ -42,7 +42,7 @@ a:
         adrp    x0, :tlsdesc_auth:local1
         ldr     x16, [x0, :tlsdesc_auth_lo12:local1]
         add     x0, x0, :tlsdesc_auth_lo12:local1
-        .tlsdesccall local1
+        .tlsdescauthcall local1
         blraa   x16, x0
 
 // CHECK:      adrp    x0, 0x[[P]]000
@@ -53,7 +53,7 @@ a:
         adrp    x0, :tlsdesc_auth:local2
         ldr     x16, [x0, :tlsdesc_auth_lo12:local2]
         add     x0, x0, :tlsdesc_auth_lo12:local2
-        .tlsdesccall local2
+        .tlsdescauthcall local2
         blraa   x16, x0
 
 // CHECK:      adrp    x0, 0x[[P]]000
@@ -109,7 +109,7 @@ local2:
         adrp    x0, :tlsdesc:a
         ldr     x1, [x0, :tlsdesc_lo12:a]
         add     x0, x0, :tlsdesc_lo12:a
-        .tlsdesccall a
+        .tlsdescauthcall a
         blr     x1
 
 //--- err2.s
@@ -126,13 +126,16 @@ local2:
         adrp    x0, :tlsdesc_auth:a
         ldr     x16, [x0, :tlsdesc_auth_lo12:a]
         add     x0, x0, :tlsdesc_auth_lo12:a
-        .tlsdesccall a
+        .tlsdescauthcall a
         blraa   x16, x0
 
 //--- err3.s
 // RUN: llvm-mc -filetype=obj -triple=aarch64-pc-linux -mattr=+pauth err3.s -o err3.o
 // RUN: not ld.lld -shared err3.o 2>&1 | FileCheck --check-prefix=ERR3 --implicit-check-not=error: %s
-// ERR3: error: err3.o:(.text+0x0): relocation R_AARCH64_TLSDESC_CALL against 'a' requires other TLSDESC or AUTH TLSDESC relocations present against this symbol
+// ERR3: error: both AUTH and non-AUTH TLSDESC entries for 'a' requested, but only one type of TLSDESC entry per symbol is supported
         .text
+        adrp    x0, :tlsdesc_auth:a
+        ldr     x16, [x0, :tlsdesc_auth_lo12:a]
+        add     x0, x0, :tlsdesc_auth_lo12:a
         .tlsdesccall a
         blraa   x16, x0



More information about the llvm-branch-commits mailing list