Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 77 additions & 9 deletions lld/ELF/Arch/AArch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -206,6 +208,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;
Expand Down Expand Up @@ -357,13 +373,16 @@ 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);
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;
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:
Expand Down Expand Up @@ -645,11 +664,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);
Expand Down Expand Up @@ -803,6 +833,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]
// .tlsdescauthcall v [R_AARCH64_AUTH_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_AUTH_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:
Expand Down Expand Up @@ -1081,6 +1140,15 @@ void AArch64::relocateAlloc(InputSection &sec, uint8_t *buf) const {
else
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:
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:
if (rel.expr == R_TPREL)
Expand Down
19 changes: 13 additions & 6 deletions lld/ELF/Relocations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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(
Expand Down
20 changes: 15 additions & 5 deletions lld/ELF/SyntheticSections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down Expand Up @@ -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
Expand All @@ -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));
Expand Down
3 changes: 2 additions & 1 deletion lld/ELF/SyntheticSections.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -144,6 +144,7 @@ class GotSection final : public SyntheticSection {
struct AuthEntryInfo {
size_t offset;
bool isSymbolFunc;
bool isUndefWeakNonPreemptible;
};
SmallVector<AuthEntryInfo, 0> authEntries;
};
Expand Down
48 changes: 48 additions & 0 deletions lld/test/ELF/aarch64-reloc-pauth-undef-weak-dso.s
Original file line number Diff line number Diff line change
@@ -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
.tlsdescauthcall undef
blraa x16, x0

.data
foo:
.quad undef@AUTH(da,42)
.quad (undef + 37)@AUTH(da,42)
.quad undef
.quad (undef + 37)
47 changes: 47 additions & 0 deletions lld/test/ELF/aarch64-reloc-pauth-undef-weak-pie.s
Original file line number Diff line number Diff line change
@@ -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
.tlsdescauthcall undef
blraa x16, x0

.data
foo:
.quad undef@AUTH(da,42)
.quad (undef + 37)@AUTH(da,42)
.quad undef
.quad (undef + 37)
48 changes: 48 additions & 0 deletions lld/test/ELF/aarch64-reloc-pauth-undef-weak.s
Original file line number Diff line number Diff line change
@@ -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
.tlsdescauthcall undef
blraa x16, x0

.data
foo:
.quad undef@AUTH(da,42)
.quad (undef + 37)@AUTH(da,42)
.quad undef
.quad undef + 37
Loading