Skip to content
Merged
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
44 changes: 21 additions & 23 deletions lib/Target/AArch64/AArch64Relocator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -699,7 +699,6 @@ Relocator::Result abs(Relocation &pReloc, AArch64Relocator &pParent) {
// Create GOT slot for rsym with the fragment reference of P
} else
S = pParent.getSymValue(&pReloc);
Relocator::Address targetVal = S + A;

bool isAuthAbs = pReloc.type() == llvm::ELF::R_AARCH64_AUTH_ABS64;
ELFSection *target_sect = pReloc.targetRef()->getOutputELFSection();
Expand All @@ -712,46 +711,42 @@ Relocator::Result abs(Relocation &pReloc, AArch64Relocator &pParent) {
return Relocator::OK;
}

if (isAuthAbs) {
if (rsym && (rsym->reserved() & Relocator::ReserveRel)) {
// Handle authenticated pointer relocations:
// - Undefined weak reference: the relocation is 0 regardless of the schema
// - AUTH_RELATIVE: write only schema (dynamic linker adds address)
// - AUTH_ABS64 (preemptible): skip writing (dynamic linker handles)
// - AUTH_ABS64 (non-preemptible): write address + schema

// PAuth spec 8.1.1:
// "if the target symbol is an undefined weak reference, the result of
// the relocation is 0 (nullptr) regardless of the signing schema"
if (rsym && rsym->isWeak() && rsym->isUndef()) {
pReloc.target() = 0;
return Relocator::OK;
}
Relocator::DWord signingSchema = getSigningSchema(pReloc);
if (rsym && (rsym->reserved() & Relocator::ReserveRel)) {
if (isAuthAbs) {
Relocation *dynrel = pParent.getTarget().findRelativeReloc(&pReloc);
// Only store the signing schema in the place for authenticated RELA
if (dynrel && (dynrel->type() == llvm::ELF::R_AARCH64_AUTH_RELATIVE)) {
pReloc.target() = signingSchema;
pReloc.target() = getSigningSchema(pReloc);
return Relocator::OK;
}
}
targetVal = static_cast<uint32_t>(targetVal) | signingSchema;
if (pParent.getTarget().isSymbolPreemptible(*rsym))
return Relocator::OK;
}

if (rsym && (rsym->reserved() & Relocator::ReserveRel) &&
(pParent.getTarget().isSymbolPreemptible(*rsym)))
return Relocator::OK;

if (rsym && rsym->reserved() & Relocator::ReservePLT)
S = pParent.getTarget().findEntryInPLT(rsym)->getAddr(
pParent.config().getDiagEngine());

if (rsym && rsym->isWeakUndef() &&
(pParent.config().codeGenType() == LinkerConfig::Exec))
S = 0;
if (rsym && rsym->isWeakUndef()) {
// PAuth spec 8.1.1:
// "if the target symbol is an undefined weak reference, the result of
// the relocation is 0 (nullptr) regardless of the signing schema"
if (isAuthAbs) {
pReloc.target() = 0;
return Relocator::OK;
}
if (pParent.config().codeGenType() == LinkerConfig::Exec)
S = 0;
}

switch (pReloc.type()) {
case llvm::ELF::R_AARCH64_ABS32:
case llvm::ELF::R_AARCH64_AUTH_ABS64:
if (!llvm::isUInt<32>(S + A) && !llvm::isInt<32>(S + A))
return emitSignedOrUnsignedRangeOverflow(pReloc, pParent, S + A, 32);
break;
Expand All @@ -765,7 +760,10 @@ Relocator::Result abs(Relocation &pReloc, AArch64Relocator &pParent) {

// A local symbol may need RELATIVE Type dynamic relocation
// perform static relocation
pReloc.target() = targetVal;
if (isAuthAbs)
pReloc.target() = static_cast<uint32_t>(S + A) | getSigningSchema(pReloc);
else
pReloc.target() = S + A;
return Relocator::OK;
}

Expand Down
108 changes: 108 additions & 0 deletions test/AArch64/standalone/Relocations/ABS64PLTAddr.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#---ABS64PLTAddr.s----------------- Executable ------------------#
#BEGIN_COMMENT
# Regression test for R_AARCH64_ABS64 to a preemptible function.
#
# When a non-PIE executable references a function defined in a shared
# library through R_AARCH64_ABS64 stored in a writable section (e.g. a
# function pointer in .data), the linker sets ReservePLT for the symbol
# but does NOT reserve a dynamic relocation (see
# GNULDBackend::symbolNeedsDynRel, "pSymHasPLT && Function -> false").
# The dynamic linker therefore never revisits that field, and the value
# baked in at link time must be the PLT entry address, not 0 and not
# the raw addend.
#
#END_COMMENT
#START_TEST
# RUN: rm -rf %t && split-file %s %t && cd %t

# RUN: %llvm-mc -filetype=obj -triple=aarch64 a.s -o a.o
# RUN: %link %linkopts -shared a.o -soname=a.so -o a.so
# RUN: %llvm-mc -filetype=obj -triple=aarch64 main.s -o main.o
# RUN: %llvm-mc -filetype=obj -triple=aarch64 weak.s -o weak.o

## Non-PIE dynamic executable: R_AARCH64_ABS64 to preemptible function `foo`
## must be resolved at link time to `foo`'s PLT entry address. Pin the
## section VAs so the expected PLT VA (0x40020 for the first PLTN entry
## sitting after the 32-byte PLT0 header) is a fixed literal below.
# RUN: %link %linkopts main.o a.so -no-pie \
# RUN: --section-start .text=0x10000 \
# RUN: --section-start .data=0x20000 \
# RUN: --section-start .plt=0x40000 \
# RUN: -o main.nopie
# RUN: %readelf --elf-output-style LLVM -r main.nopie 2>&1 \
# RUN: | %filecheck %s --check-prefix=NOPIE-RELOCS --allow-empty
# RUN: %readelf -x .data main.nopie \
# RUN: | %filecheck %s --check-prefix=NOPIE-DATA

## There must be NO dynamic relocation that would fix up the .data field
## for `foo` at runtime (i.e. no R_AARCH64_ABS64 / R_AARCH64_GLOB_DAT
## naming foo). The JUMP_SLOT in .rela.plt is expected, it populates
## .got.plt so the PLT stub can resolve foo lazily. The regression this
## test guards against is the linker forgetting to bake foo's PLT
## address into the .data slot at link time, since without a matching
## dyn reloc there is no runtime fixup for that word.
# NOPIE-RELOCS: .rela.plt
# NOPIE-RELOCS: R_AARCH64_JUMP_SLOT foo
# NOPIE-RELOCS-NOT: R_AARCH64_ABS64 foo
# NOPIE-RELOCS-NOT: R_AARCH64_GLOB_DAT foo

## Both slots must hold `foo`'s PLT entry VA (0x40020), byte-swapped
## for little endian. Slot 1: `.quad foo` -> 0x0000000000040020
## Slot 2: `.quad foo + 0x1000` -> 0x0000000000041020
# NOPIE-DATA: Hex dump of section '.data':
# NOPIE-DATA-NEXT: {{0x[0-9a-f]+}} 20000400 00000000 20100400 00000000

## Plain-ABS64 to a weak undefined symbol in a non-PIE exec: exercises the
## `isWeakUndef() && Exec` S=0 branch in abs(). Both eight-byte slots must
## be zero.
# RUN: %link %linkopts weak.o -no-pie -o weak.nopie
# RUN: %readelf --elf-output-style LLVM -r weak.nopie 2>&1 \
# RUN: | %filecheck %s --check-prefix=WEAK-RELOCS --allow-empty
# RUN: %readelf -x .data weak.nopie \
# RUN: | %filecheck %s --check-prefix=WEAK-DATA

# WEAK-RELOCS-NOT: weak_undef

# WEAK-DATA: Hex dump of section '.data':
# WEAK-DATA-NEXT: {{0x[0-9a-f]+}} 00000000 00000000 00010000 00000000
## ^^^^^^^^ ^^^^^^^^ weak_undef = 0 (S=0, A=0)
## ^^^^^^^^ ^^^^^^^^ weak_undef + 0x100 = 0x100 (S=0, A=0x100)

#END_TEST

#--- a.s
.global foo
.type foo, @function
foo:
ret

#--- main.s
.global _start
.type _start, @function
_start:
ret

.section .data, "aw"
.p2align 3
## R_AARCH64_ABS64 against a preemptible function. The linker reserves a
## PLT entry for `foo` but no dyn reloc for this field, so the field must
## be baked to the PLT entry address at link time.
.quad foo
.quad foo + 0x1000

#--- weak.s
.global _start
.type _start, @function
_start:
ret

.weak weak_undef
.type weak_undef, @function

.section .data, "aw"
.p2align 3
## Plain R_AARCH64_ABS64 to a weak undefined symbol. Non-PIE exec: the
## `isWeakUndef() && Exec` branch in abs() zeroes S, and the field must
## be written as zero.
.quad weak_undef
.quad weak_undef + 0x100
20 changes: 20 additions & 0 deletions test/AArch64/standalone/Relocations/PAuthReloc.s
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,26 @@

# STATIC_NOPIE-COUNT-14: {{0x[0-9A-F]+}} R_AARCH64_AUTH_RELATIVE - {{0x[0-9A-F]+}}

# The static-nopie build has no shared libraries, so every AUTH_ABS64
# in a.s becomes non-preemptible and gets an AUTH_RELATIVE dynamic
# relocation. In that case abs() must write only the signing schema in
# the top 32 bits and zero in the low 32 bits at each slot. The byte
# layout is identical to the NOPIE HEX block above, mirror it here.
# RUN: %readelf -x .test %t/main.static.nopie | %filecheck %s --check-prefix=STATIC_NOPIE_HEX
# RUN: %readelf -x .rodata %t/main.static.nopie | %filecheck %s --check-prefix=STATIC_NOPIE_RODATA

# STATIC_NOPIE_HEX: Hex dump of section '.test':
# STATIC_NOPIE_HEX-NEXT: {{0x[0-9a-f]+}} 00000000 2a000020 00000000 2b000000
# STATIC_NOPIE_HEX-NEXT: {{0x[0-9a-f]+}} 00000000 2c000080 00000000 2d000020
# STATIC_NOPIE_HEX-NEXT: {{0x[0-9a-f]+}} 00000000 2e000020 00000000 2f000020
# STATIC_NOPIE_HEX-NEXT: {{0x[0-9a-f]+}} 00000000 30000020 00000000 31000020
# STATIC_NOPIE_HEX-NEXT: {{0x[0-9a-f]+}} 00000000 32000000 77000000 00330000
# STATIC_NOPIE_HEX-NEXT: {{0x[0-9a-f]+}} 20770000 00003400 0020

# STATIC_NOPIE_RODATA: Hex dump of section '.rodata':
# STATIC_NOPIE_RODATA-NEXT: {{0x[0-9a-f]+}} 00000000 35000020 00000000 36000000
# STATIC_NOPIE_RODATA-NEXT: {{0x[0-9a-f]+}} 00000000 37000020

#END_TEST

#--- a.s
Expand Down
Loading