[lld] [PAC][ELF] Remove NEEDS_{GOT,TLSDESC}_NONAUTH (PR #213452)
Jessica Clarke via llvm-commits
llvm-commits at lists.llvm.org
Sat Aug 1 07:56:25 PDT 2026
https://github.com/jrtc27 created https://github.com/llvm/llvm-project/pull/213452
Currently we have all of NEEDS_GOT, NEEDS_GOT_AUTH and NEEDS_GOT_NONAUTH
(and similarly for TLSDESC), which takes up three bits, but is only
being used to encode four states (no GOT entry, a normal GOT entry, an
AUTH GOT entry, and an unsupported attempt to have both). This is a
waste of precious bits in the atomic uint16_t flags. Re-encode this in
the more natural mannerl effectively NEEDS_GOT is now NEEDS_GOT_NONAUTH
and what was NEEDS_GOT is now implied by having either GOT type.
Note that this does change the behaviour of replaceWithDefined (which
previously just threw away both NEEDS_GOT_AUTH and NEEDS_GOT_NONAUTH,
which only "worked" because we only used NEEDS_GOT_NONAUTH to check if
both it and NEEDS_GOT_AUTH were present, but presumably gave you the
wrong GOT entry type for AUTH GOT entries). This also moves the assert
in handleNonPreemptibleIfunc, which previously silently ignored which
type of GOT entry was requested in the non-direct case and would always
give you a normal non-AUTH R_AARCH64_IRELATIVE, but now will assert if
it should ever be giving an R_AARCH64_AUTH_IRELATIVE. If this breaks
linking real software using PAuth, I consider that a feature, as it's
better to fail at link time than silently do the wrong thing (though
surely this would be better in the form of an actual error rather than
assert that may be compiled out and gives a big scary message and stack
trace), and since there is no test coverage for this case I can only
conclude any current "working" behaviour is accidental and unsupported.
Those who care about PAuth should take it upon themselves to fix such
deficiencies, but that should not get in the way of cleaning up
technical debt.
This also changes the error checking for TLSDESC. Previously we'd set
NEEDS_TLSDESC_NONAUTH for all non-AUTH TLSDESC relocations, regardless
of whether they're being relaxed, and even for TLSDESC_CALL, despite
that never otherwise affecting symbol flags. Now that doesn't exist we
end up only having NEEDS_TLSDESC if (a) not relaxing and (b) this isn't
a TLSDESC_CALL. As a result, if you mix and match relocations within a
single TLSDESC sequence, such as in aarch64-tlsdesc-pauth.s, you can get
nonsense behaviour. Given this is totally nonsense input, rather than
the more real and legitimate case of linking code that has different
sequences for the same symbol, detecting this is not useful, and it can
just be added to the long list of "garbage in, garbage out" behaviour of
linkers. Since err1 already covers the sensible error case, remove err3,
the case we no longer catch, but is garbage input. Whilst here, add the
missing .tlsdesccall to err1 so it's actually a legitimate TLSDESC code
sequence, remove err2 since it's just err1 with the two sequences
swapped and provides zero additional coverage, and rename err1 to err
since it's the only one left.
Fixes: 9178708c3bf9 ("[PAC][lld][AArch64][ELF] Support signed TLSDESC (#113817)")
Fixes: 417d2d7ce694 ("[PAC][lld][AArch64][ELF] Support signed GOT (#113815)")
>From 8cd852e925f6b2ea2cb2e635b4256353483b3e46 Mon Sep 17 00:00:00 2001
From: Jessica Clarke <jrtc27 at jrtc27.com>
Date: Sat, 1 Aug 2026 15:54:53 +0100
Subject: [PATCH] [PAC][ELF] Remove NEEDS_{GOT,TLSDESC}_NONAUTH
Currently we have all of NEEDS_GOT, NEEDS_GOT_AUTH and NEEDS_GOT_NONAUTH
(and similarly for TLSDESC), which takes up three bits, but is only
being used to encode four states (no GOT entry, a normal GOT entry, an
AUTH GOT entry, and an unsupported attempt to have both). This is a
waste of precious bits in the atomic uint16_t flags. Re-encode this in
the more natural mannerl effectively NEEDS_GOT is now NEEDS_GOT_NONAUTH
and what was NEEDS_GOT is now implied by having either GOT type.
Note that this does change the behaviour of replaceWithDefined (which
previously just threw away both NEEDS_GOT_AUTH and NEEDS_GOT_NONAUTH,
which only "worked" because we only used NEEDS_GOT_NONAUTH to check if
both it and NEEDS_GOT_AUTH were present, but presumably gave you the
wrong GOT entry type for AUTH GOT entries). This also moves the assert
in handleNonPreemptibleIfunc, which previously silently ignored which
type of GOT entry was requested in the non-direct case and would always
give you a normal non-AUTH R_AARCH64_IRELATIVE, but now will assert if
it should ever be giving an R_AARCH64_AUTH_IRELATIVE. If this breaks
linking real software using PAuth, I consider that a feature, as it's
better to fail at link time than silently do the wrong thing (though
surely this would be better in the form of an actual error rather than
assert that may be compiled out and gives a big scary message and stack
trace), and since there is no test coverage for this case I can only
conclude any current "working" behaviour is accidental and unsupported.
Those who care about PAuth should take it upon themselves to fix such
deficiencies, but that should not get in the way of cleaning up
technical debt.
This also changes the error checking for TLSDESC. Previously we'd set
NEEDS_TLSDESC_NONAUTH for all non-AUTH TLSDESC relocations, regardless
of whether they're being relaxed, and even for TLSDESC_CALL, despite
that never otherwise affecting symbol flags. Now that doesn't exist we
end up only having NEEDS_TLSDESC if (a) not relaxing and (b) this isn't
a TLSDESC_CALL. As a result, if you mix and match relocations within a
single TLSDESC sequence, such as in aarch64-tlsdesc-pauth.s, you can get
nonsense behaviour. Given this is totally nonsense input, rather than
the more real and legitimate case of linking code that has different
sequences for the same symbol, detecting this is not useful, and it can
just be added to the long list of "garbage in, garbage out" behaviour of
linkers. Since err1 already covers the sensible error case, remove err3,
the case we no longer catch, but is garbage input. Whilst here, add the
missing .tlsdesccall to err1 so it's actually a legitimate TLSDESC code
sequence, remove err2 since it's just err1 with the two sequences
swapped and provides zero additional coverage, and rename err1 to err
since it's the only one left.
Fixes: 9178708c3bf9 ("[PAC][lld][AArch64][ELF] Support signed TLSDESC (#113817)")
Fixes: 417d2d7ce694 ("[PAC][lld][AArch64][ELF] Support signed GOT (#113815)")
---
lld/ELF/Arch/AArch64.cpp | 15 ++++++------
lld/ELF/RelocScan.h | 4 +---
lld/ELF/Relocations.cpp | 22 ++++++++---------
lld/ELF/Symbols.h | 7 +++---
lld/test/ELF/aarch64-tlsdesc-pauth.s | 35 ++++------------------------
5 files changed, 28 insertions(+), 55 deletions(-)
diff --git a/lld/ELF/Arch/AArch64.cpp b/lld/ELF/Arch/AArch64.cpp
index 4d24c49382559..223f0ecdd3ccd 100644
--- a/lld/ELF/Arch/AArch64.cpp
+++ b/lld/ELF/Arch/AArch64.cpp
@@ -301,20 +301,20 @@ void AArch64::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
expr = R_GOT_PC;
break;
- // AUTH GOT relocations. Set NEEDS_GOT_AUTH to detect incompatibility with
- // NEEDS_GOT_NONAUTH. rs.process does not set the flag.
+ // AUTH GOT relocations. Handle flags here, as rs.process assumes R_GOT is a
+ // normal non-AUTH GOT entry.
case R_AARCH64_AUTH_LD64_GOT_LO12_NC:
case R_AARCH64_AUTH_GOT_ADD_LO12_NC:
- sym.setFlags(NEEDS_GOT | NEEDS_GOT_AUTH);
+ sym.setFlags(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);
+ sym.setFlags(NEEDS_GOT_AUTH);
rs.processAux(R_GOT_PC, type, offset, sym, addend);
continue;
case R_AARCH64_AUTH_ADR_GOT_PAGE:
- sym.setFlags(NEEDS_GOT | NEEDS_GOT_AUTH);
+ sym.setFlags(NEEDS_GOT_AUTH);
rs.processAux(RE_AARCH64_GOT_PAGE_PC, type, offset, sym, addend);
continue;
@@ -354,7 +354,6 @@ void AArch64::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
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;
@@ -363,12 +362,12 @@ void AArch64::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
// 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);
+ sym.setFlags(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);
+ sym.setFlags(NEEDS_TLSDESC_AUTH);
sec.addReloc({R_TLSDESC, type, offset, addend, &sym});
continue;
diff --git a/lld/ELF/RelocScan.h b/lld/ELF/RelocScan.h
index c39089840d2fb..ca55577e9ee71 100644
--- a/lld/ELF/RelocScan.h
+++ b/lld/ELF/RelocScan.h
@@ -163,9 +163,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 4addf28bbffac..4ee25781e3eaf 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -251,7 +251,8 @@ static void replaceWithDefined(Ctx &ctx, Symbol &sym, SectionBase &sec,
sym.versionId = old.versionId;
sym.isUsedInRegularObj = true;
// A copy relocated alias may need a GOT entry.
- sym.flags.store(old.flags.load(std::memory_order_relaxed) & NEEDS_GOT,
+ sym.flags.store(old.flags.load(std::memory_order_relaxed) &
+ (NEEDS_GOT | NEEDS_GOT_AUTH),
std::memory_order_relaxed);
}
@@ -959,7 +960,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.
- sym.setFlags(NEEDS_GOT | NEEDS_GOT_NONAUTH);
+ sym.setFlags(NEEDS_GOT);
}
} else if (needsPlt(expr)) {
sym.setFlags(NEEDS_PLT);
@@ -1244,7 +1245,7 @@ static bool handleNonPreemptibleIfunc(Ctx &ctx, Symbol &sym, uint16_t flags) {
if (!sym.isGnuIFunc() || sym.isPreemptible || ctx.arg.zIfuncNoplt)
return false;
// Skip unreferenced non-preemptible ifunc.
- if (!(flags & (NEEDS_GOT | NEEDS_PLT | HAS_DIRECT_RELOC)))
+ if (!(flags & (NEEDS_GOT | NEEDS_GOT_AUTH | NEEDS_PLT | HAS_DIRECT_RELOC)))
return true;
sym.isInIplt = true;
@@ -1258,6 +1259,8 @@ static bool handleNonPreemptibleIfunc(Ctx &ctx, Symbol &sym, uint16_t flags) {
sym.allocateAux(ctx);
ctx.symAux.back().pltIdx = ctx.symAux[irelativeSym->auxIdx].pltIdx;
+ assert(!(flags & NEEDS_GOT_AUTH) &&
+ "R_AARCH64_AUTH_IRELATIVE is not supported yet");
if (flags & HAS_DIRECT_RELOC) {
// Change the value to the IPLT and redirect all references to it.
auto &d = cast<Defined>(sym);
@@ -1268,11 +1271,8 @@ static bool handleNonPreemptibleIfunc(Ctx &ctx, Symbol &sym, uint16_t flags) {
// don't try to call the PLT as if it were an ifunc resolver.
d.type = STT_FUNC;
- if (flags & NEEDS_GOT) {
- assert(!(flags & NEEDS_GOT_AUTH) &&
- "R_AARCH64_AUTH_IRELATIVE is not supported yet");
+ if (flags & NEEDS_GOT)
addGotEntry(ctx, sym);
- }
} else if (flags & NEEDS_GOT) {
// Redirect GOT accesses to point to the Igot.
sym.gotInIgot = true;
@@ -1294,8 +1294,8 @@ void elf::postScanRelocations(Ctx &ctx) {
return;
sym.allocateAux(ctx);
- if (flags & NEEDS_GOT) {
- if ((flags & NEEDS_GOT_AUTH) && (flags & NEEDS_GOT_NONAUTH)) {
+ if (flags & (NEEDS_GOT | NEEDS_GOT_AUTH)) {
+ if ((flags & NEEDS_GOT) && (flags & NEEDS_GOT_AUTH)) {
auto diag = Err(ctx);
diag << "both AUTH and non-AUTH GOT entries for '" << sym.getName()
<< "' requested, but only one type of GOT entry per symbol is "
@@ -1338,8 +1338,8 @@ void elf::postScanRelocations(Ctx &ctx) {
return;
GotSection *got = ctx.in.got.get();
- if (flags & NEEDS_TLSDESC) {
- if ((flags & NEEDS_TLSDESC_AUTH) && (flags & NEEDS_TLSDESC_NONAUTH)) {
+ if (flags & (NEEDS_TLSDESC | NEEDS_TLSDESC_AUTH)) {
+ if ((flags & NEEDS_TLSDESC) && (flags & NEEDS_TLSDESC_AUTH)) {
Err(ctx)
<< "both AUTH and non-AUTH TLSDESC entries for '" << sym.getName()
<< "' requested, but only one type of TLSDESC entry per symbol is "
diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h
index 0893776882dc6..18558ec8eee76 100644
--- a/lld/ELF/Symbols.h
+++ b/lld/ELF/Symbols.h
@@ -56,9 +56,9 @@ enum {
NEEDS_GOT_DTPREL = 1 << 7,
NEEDS_TLSIE = 1 << 8,
NEEDS_GOT_AUTH = 1 << 9,
- NEEDS_GOT_NONAUTH = 1 << 10,
+ // 1 << 10 unused
NEEDS_TLSDESC_AUTH = 1 << 11,
- NEEDS_TLSDESC_NONAUTH = 1 << 12,
+ // 1 << 12 unused
};
// The base class for real symbol classes.
@@ -352,7 +352,8 @@ class Symbol {
bool needsDynReloc() const {
return flags.load(std::memory_order_relaxed) &
(NEEDS_COPY | NEEDS_GOT | NEEDS_PLT | NEEDS_TLSDESC | NEEDS_TLSGD |
- NEEDS_GOT_DTPREL | NEEDS_TLSIE);
+ NEEDS_GOT_DTPREL | NEEDS_TLSIE | NEEDS_GOT_AUTH |
+ NEEDS_TLSDESC_AUTH);
}
void allocateAux(Ctx &ctx) {
assert(auxIdx == 0);
diff --git a/lld/test/ELF/aarch64-tlsdesc-pauth.s b/lld/test/ELF/aarch64-tlsdesc-pauth.s
index bf0ae4a87f322..453a59f4af1d6 100644
--- a/lld/test/ELF/aarch64-tlsdesc-pauth.s
+++ b/lld/test/ELF/aarch64-tlsdesc-pauth.s
@@ -92,10 +92,10 @@ local2:
/// ^^
/// 0b10100000 bit 63 address diversity = true, bits 61..60 key = DA
-//--- err1.s
-// RUN: llvm-mc -filetype=obj -triple=aarch64-pc-linux -mattr=+pauth err1.s -o err1.o
-// RUN: not ld.lld -shared err1.o 2>&1 | FileCheck --check-prefix=ERR1 --implicit-check-not=error: %s
-// ERR1: error: both AUTH and non-AUTH TLSDESC entries for 'a' requested, but only one type of TLSDESC entry per symbol is supported
+//--- err.s
+// RUN: llvm-mc -filetype=obj -triple=aarch64-pc-linux -mattr=+pauth err.s -o err.o
+// RUN: not ld.lld -shared err.o 2>&1 | FileCheck --check-prefix=ERR --implicit-check-not=error: %s
+// ERR: 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]
@@ -105,30 +105,5 @@ local2:
adrp x0, :tlsdesc:a
ldr x1, [x0, :tlsdesc_lo12:a]
add x0, x0, :tlsdesc_lo12:a
- blr x1
-
-//--- err2.s
-// RUN: llvm-mc -filetype=obj -triple=aarch64-pc-linux -mattr=+pauth err2.s -o err2.o
-// RUN: not ld.lld -shared err2.o 2>&1 | FileCheck --check-prefix=ERR2 --implicit-check-not=error: %s
-// ERR2: 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:a
- ldr x1, [x0, :tlsdesc_lo12:a]
- add x0, x0, :tlsdesc_lo12:a
- blr x1
-
- adrp x0, :tlsdesc_auth:a
- ldr x16, [x0, :tlsdesc_auth_lo12:a]
- add x0, x0, :tlsdesc_auth_lo12: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
- .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
+ blr x1
More information about the llvm-commits
mailing list