[LLD][ELF][AArch64] Add support for SHF_AARCH64_PURECODE ELF section flag (3/3) (#125689)

Add support for the new SHF_AARCH64_PURECODE ELF section flag:
https://github.com/ARM-software/abi-aa/pull/304

The general implementation follows the existing one for ARM targets. The
output section only has the `SHF_AARCH64_PURECODE` flag set if all input
sections have it set.

Related PRs:
* LLVM: https://github.com/llvm/llvm-project/pull/125687
* Clang: https://github.com/llvm/llvm-project/pull/125688
This commit is contained in:
Csanád Hajdú 2025-02-21 18:01:38 +01:00 committed by GitHub
parent cf50936b23
commit 6e457c2001
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 187 additions and 6 deletions

View File

@ -42,7 +42,10 @@ using namespace lld::elf;
uint32_t OutputSection::getPhdrFlags() const {
uint32_t ret = 0;
if (ctx.arg.emachine != EM_ARM || !(flags & SHF_ARM_PURECODE))
bool purecode =
(ctx.arg.emachine == EM_ARM && (flags & SHF_ARM_PURECODE)) ||
(ctx.arg.emachine == EM_AARCH64 && (flags & SHF_AARCH64_PURECODE));
if (!purecode)
ret |= PF_R;
if (flags & SHF_WRITE)
ret |= PF_W;
@ -161,8 +164,11 @@ void OutputSection::commitSection(InputSection *isec) {
}
isec->parent = this;
uint64_t andMask =
ctx.arg.emachine == EM_ARM ? (uint64_t)SHF_ARM_PURECODE : 0;
uint64_t andMask = 0;
if (ctx.arg.emachine == EM_ARM)
andMask |= (uint64_t)SHF_ARM_PURECODE;
if (ctx.arg.emachine == EM_AARCH64)
andMask |= (uint64_t)SHF_AARCH64_PURECODE;
uint64_t orMask = ~andMask;
uint64_t andFlags = (flags & isec->flags) & andMask;
uint64_t orFlags = (flags | isec->flags) & orMask;

View File

@ -1411,6 +1411,7 @@ static std::optional<uint64_t> parseFlag(StringRef tok) {
.Case(CASE_ENT(SHF_COMPRESSED))
.Case(CASE_ENT(SHF_EXCLUDE))
.Case(CASE_ENT(SHF_ARM_PURECODE))
.Case(CASE_ENT(SHF_AARCH64_PURECODE))
.Default(std::nullopt);
#undef CASE_ENT
}

View File

@ -91,6 +91,19 @@ private:
ThunkSection *tsec = nullptr;
};
// AArch64 long range Thunks compatible with execute-only code.
class AArch64ABSXOLongThunk final : public AArch64Thunk {
public:
AArch64ABSXOLongThunk(Ctx &ctx, Symbol &dest, int64_t addend,
bool mayNeedLandingPad)
: AArch64Thunk(ctx, dest, addend, mayNeedLandingPad) {}
uint32_t size() override { return getMayUseShortThunk() ? 4 : 20; }
void addSymbols(ThunkSection &sec) override;
private:
void writeLong(uint8_t *buf) override;
};
class AArch64ADRPThunk final : public AArch64Thunk {
public:
AArch64ADRPThunk(Ctx &ctx, Symbol &dest, int64_t addend,
@ -663,6 +676,33 @@ void AArch64ABSLongThunk::addLongMapSyms() {
addSymbol("$d", STT_NOTYPE, 8, *tsec);
}
void AArch64ABSXOLongThunk::writeLong(uint8_t *buf) {
const uint8_t data[] = {
0x10, 0x00, 0x80, 0xd2, // movz x16, :abs_g0_nc:S, lsl #0
0x10, 0x00, 0xa0, 0xf2, // movk x16, :abs_g1_nc:S, lsl #16
0x10, 0x00, 0xc0, 0xf2, // movk x16, :abs_g2_nc:S, lsl #32
0x10, 0x00, 0xe0, 0xf2, // movk x16, :abs_g3:S, lsl #48
0x00, 0x02, 0x1f, 0xd6, // br x16
};
// If mayNeedLandingPad is true then destination is an
// AArch64BTILandingPadThunk that defines landingPad.
assert(!mayNeedLandingPad || landingPad != nullptr);
uint64_t s = mayNeedLandingPad
? landingPad->getVA(ctx, 0)
: getAArch64ThunkDestVA(ctx, destination, addend);
memcpy(buf, data, sizeof(data));
ctx.target->relocateNoSym(buf + 0, R_AARCH64_MOVW_UABS_G0_NC, s);
ctx.target->relocateNoSym(buf + 4, R_AARCH64_MOVW_UABS_G1_NC, s);
ctx.target->relocateNoSym(buf + 8, R_AARCH64_MOVW_UABS_G2_NC, s);
ctx.target->relocateNoSym(buf + 12, R_AARCH64_MOVW_UABS_G3, s);
}
void AArch64ABSXOLongThunk::addSymbols(ThunkSection &sec) {
addSymbol(ctx.saver.save("__AArch64AbsXOLongThunk_" + destination.getName()),
STT_FUNC, 0, sec);
addSymbol("$x", STT_NOTYPE, 0, sec);
}
// This Thunk has a maximum range of 4Gb, this is sufficient for all programs
// using the small code model, including pc-relative ones. At time of writing
// clang and gcc do not support the large code model for position independent
@ -1482,7 +1522,8 @@ Thunk::Thunk(Ctx &ctx, Symbol &d, int64_t a)
Thunk::~Thunk() = default;
static std::unique_ptr<Thunk> addThunkAArch64(Ctx &ctx, RelType type, Symbol &s,
static std::unique_ptr<Thunk> addThunkAArch64(Ctx &ctx, const InputSection &sec,
RelType type, Symbol &s,
int64_t a) {
assert(is_contained({R_AARCH64_CALL26, R_AARCH64_JUMP26, R_AARCH64_PLT32},
type));
@ -1491,6 +1532,9 @@ static std::unique_ptr<Thunk> addThunkAArch64(Ctx &ctx, RelType type, Symbol &s,
!isAArch64BTILandingPad(ctx, s, a);
if (ctx.arg.picThunk)
return std::make_unique<AArch64ADRPThunk>(ctx, s, a, mayNeedLandingPad);
if (sec.getParent()->flags & SHF_AARCH64_PURECODE)
return std::make_unique<AArch64ABSXOLongThunk>(ctx, s, a,
mayNeedLandingPad);
return std::make_unique<AArch64ABSLongThunk>(ctx, s, a, mayNeedLandingPad);
}
@ -1702,7 +1746,7 @@ std::unique_ptr<Thunk> elf::addThunk(Ctx &ctx, const InputSection &isec,
switch (ctx.arg.emachine) {
case EM_AARCH64:
return addThunkAArch64(ctx, rel.type, s, a);
return addThunkAArch64(ctx, isec, rel.type, s, a);
case EM_ARM:
return addThunkArm(ctx, isec, rel.type, s, a);
case EM_AVR:

View File

@ -0,0 +1,32 @@
// REQUIRES: aarch64
// RUN: llvm-mc -filetype=obj -triple=aarch64 %s -o %t.o
// RUN: ld.lld %t.o -o %t.so -shared
// RUN: llvm-readelf -l %t.so | FileCheck --implicit-check-not=LOAD %s
// RUN: echo ".section .foo,\"ax\"; ret" > %t.s
// RUN: llvm-mc -filetype=obj -triple=aarch64 %t.s -o %t2.o
// RUN: ld.lld %t.o %t2.o -o %t.so -shared
// RUN: llvm-readelf -l %t.so | FileCheck --check-prefix=DIFF --implicit-check-not=LOAD %s
// CHECK: LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x000245 0x000245 R 0x10000
// CHECK-NEXT: LOAD 0x000248 0x0000000000010248 0x0000000000010248 0x{{.*}} 0x{{.*}} R E 0x10000
// CHECK-NEXT: LOAD 0x00024c 0x000000000002024c 0x000000000002024c 0x{{.*}} 0x{{.*}} E 0x10000
// CHECK-NEXT: LOAD 0x000250 0x0000000000030250 0x0000000000030250 0x000070 0x000db0 RW 0x10000
// CHECK: 01 .dynsym .gnu.hash .hash .dynstr
// CHECK-NEXT: 02 .text
// CHECK-NEXT: 03 .foo
// CHECK-NEXT: 04 .dynamic
// DIFF: LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x00020d 0x00020d R 0x10000
// DIFF-NEXT: LOAD 0x000210 0x0000000000010210 0x0000000000010210 0x00000c 0x00000c R E 0x10000
// DIFF-NEXT: LOAD 0x000220 0x0000000000020220 0x0000000000020220 0x000070 0x000de0 RW 0x10000
// DIFF: 01 .dynsym .gnu.hash .hash .dynstr
// DIFF-NEXT: 02 .text .foo
// DIFF-NEXT: 03 .dynamic
ret
.section .foo,"axy"
ret

View File

@ -0,0 +1,75 @@
// REQUIRES: aarch64
// RUN: rm -rf %t && split-file %s %t && cd %t
// RUN: llvm-mc -filetype=obj -triple=aarch64 asm -o a.o
// RUN: ld.lld --script=lds a.o -o exe --defsym absolute=0xf0000000
// RUN: llvm-objdump -d --no-show-raw-insn exe | FileCheck %s
//--- asm
.section ".note.gnu.property", "a"
.p2align 3
.long 4
.long 0x10
.long 0x5
.asciz "GNU"
/// Enable BTI.
.long 0xc0000000 // GNU_PROPERTY_AARCH64_FEATURE_1_AND.
.long 4
.long 1 // GNU_PROPERTY_AARCH64_FEATURE_1_BTI.
.long 0
.section .text.0,"axy",@progbits
.global _start
.type _start,@function
_start:
/// Expect thunk to target a linker generated entry point with BTI landing pad.
/// Two calls to make sure only one landing pad is created.
bl fn1
b fn1
/// No BTI landing pad is added for absolute symbols.
bl absolute
/// This function does not have a BTI compatible landing pad. Expect a linker
/// generated landing pad for indirect branch thunks.
.section .text.1,"axy",@progbits
.hidden fn1
.type fn1,@function
fn1:
ret
// CHECK-LABEL: <_start>:
// CHECK-NEXT: 18001000: bl 0x1800100c <__AArch64AbsXOLongThunk_>
// CHECK-NEXT: b 0x1800100c <__AArch64AbsXOLongThunk_>
// CHECK-NEXT: bl 0x18001020 <__AArch64AbsXOLongThunk_absolute>
// CHECK-LABEL: <__AArch64AbsXOLongThunk_>:
// CHECK-NEXT: 1800100c: mov x16, #0x0
// CHECK-NEXT: movk x16, #0x3000, lsl #16
// CHECK-NEXT: movk x16, #0x0, lsl #32
// CHECK-NEXT: movk x16, #0x0, lsl #48
// CHECK-NEXT: br x16
// CHECK-LABEL: <__AArch64AbsXOLongThunk_absolute>:
// CHECK-NEXT: 18001020: mov x16, #0x0
// CHECK-NEXT: movk x16, #0xf000, lsl #16
// CHECK-NEXT: movk x16, #0x0, lsl #32
// CHECK-NEXT: movk x16, #0x0, lsl #48
// CHECK-NEXT: br x16
// CHECK-LABEL: <__AArch64BTIThunk_>:
// CHECK-NEXT: 30000000: bti c
// CHECK-LABEL: <fn1>:
// CHECK-NEXT: 30000004: ret
//--- lds
PHDRS {
low PT_LOAD FLAGS(0x1 | 0x4);
mid PT_LOAD FLAGS(0x1 | 0x4);
high PT_LOAD FLAGS(0x1 | 0x4);
}
SECTIONS {
.rodata 0x10000000 : { *(.note.gnu.property) } :low
.text 0x18001000 : { *(.text.0) } :mid
.text_high 0x30000000 : { *(.text.*) } :high
}

View File

@ -0,0 +1,22 @@
// REQUIRES: aarch64
// RUN: llvm-mc -filetype=obj -triple=aarch64 %s -o %t.o
// RUN: ld.lld %t.o --defsym big=0x1111222233334444 -o %t
// RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck %s
.section .text,"axy",@progbits,unique,0
.globl _start
_start:
bl big
b big
// CHECK: Disassembly of section .text:
// CHECK-EMPTY:
// CHECK-LABEL: <_start>:
// CHECK-NEXT: bl {{.*}} <__AArch64AbsXOLongThunk_big>
// CHECK-NEXT: b {{.*}} <__AArch64AbsXOLongThunk_big>
// CHECK-LABEL: <__AArch64AbsXOLongThunk_big>:
// CHECK-NEXT: mov x16, #0x4444
// CHECK-NEXT: movk x16, #0x3333, lsl #16
// CHECK-NEXT: movk x16, #0x2222, lsl #32
// CHECK-NEXT: movk x16, #0x1111, lsl #48
// CHECK-NEXT: br x16

View File

@ -15,7 +15,8 @@
# RUN: .outsec3 : { INPUT_SECTION_FLAGS(SHF_WRITE) *(.sec.*) } \
# RUN: .outsec4 : { INPUT_SECTION_FLAGS(SHF_MERGE & !SHF_STRINGS) *(.sec.*) } \
# RUN: .outsec5 : { INPUT_SECTION_FLAGS(SHF_STRINGS) *(.sec.*) } \
# RUN: .outsec6 : { INPUT_SECTION_FLAGS(!SHF_TLS & !SHF_EXCLUDE & !SHF_COMPRESSED & !SHF_ARM_PURECODE) *(.sec.*) } \
# RUN: .outsec6 : { INPUT_SECTION_FLAGS(!SHF_TLS & !SHF_EXCLUDE & !SHF_COMPRESSED & \
# RUN: !SHF_ARM_PURECODE & !SHF_AARCH64_PURECODE) *(.sec.*) } \
# RUN: } " > %t.script
# RUN: ld.lld -o %t1 --script %t.script %t.o
# RUN: llvm-readobj --symbols %t1 | FileCheck %s