Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
46a1dbc
[𝘀𝗽𝗿] changes to main this commit is based on
pcc Mar 28, 2025
005ee2d
[𝘀𝗽𝗿] initial version
pcc Mar 28, 2025
f0582ee
[𝘀𝗽𝗿] changes introduced through rebase
pcc May 13, 2025
f63460a
Update to use R_AARCH64_FUNCINIT64
pcc May 13, 2025
a51d67b
[𝘀𝗽𝗿] changes introduced through rebase
pcc May 24, 2025
b2a4e53
Rebase
pcc May 24, 2025
fb8ed1e
[𝘀𝗽𝗿] changes introduced through rebase
pcc Jul 9, 2025
eb29698
Rebase
pcc Jul 9, 2025
99accf2
[𝘀𝗽𝗿] changes introduced through rebase
pcc Jul 10, 2025
5757a4b
Rebase
pcc Jul 10, 2025
7e9ff5f
[𝘀𝗽𝗿] changes introduced through rebase
pcc Jul 11, 2025
d9112ca
Rebase
pcc Jul 11, 2025
c83ac0d
[𝘀𝗽𝗿] changes introduced through rebase
pcc Jul 19, 2025
96634a7
Add and update tests
pcc Jul 19, 2025
e3d45ba
[𝘀𝗽𝗿] changes introduced through rebase
pcc Jul 30, 2025
dd4ef4e
Rebase
pcc Jul 30, 2025
2d46b4d
[𝘀𝗽𝗿] changes introduced through rebase
pcc Aug 1, 2025
6a1c243
Rebase
pcc Aug 1, 2025
f00843f
[𝘀𝗽𝗿] changes introduced through rebase
pcc Aug 1, 2025
cfb68af
Rebase
pcc Aug 1, 2025
ccf433b
[𝘀𝗽𝗿] changes introduced through rebase
pcc Sep 3, 2025
566b335
Rebase
pcc Sep 3, 2025
e559f05
[𝘀𝗽𝗿] changes introduced through rebase
pcc Sep 5, 2025
691a8b1
Rebase
pcc Sep 5, 2025
5d04950
[𝘀𝗽𝗿] changes introduced through rebase
pcc Oct 9, 2025
03bf1a1
Rebase
pcc Oct 9, 2025
7ed12ae
[𝘀𝗽𝗿] changes introduced through rebase
Varnike Nov 26, 2025
e4e4b96
Rebase, address review comments
pcc Nov 26, 2025
725889f
Format
pcc Nov 26, 2025
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
218 changes: 203 additions & 15 deletions llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,14 @@
#include "llvm/IR/Module.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstBuilder.h"
#include "llvm/MC/MCSectionELF.h"
#include "llvm/MC/MCSectionMachO.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/MC/MCValue.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
Expand Down Expand Up @@ -95,6 +97,7 @@ class AArch64AsmPrinter : public AsmPrinter {
bool EnableImportCallOptimization = false;
DenseMap<MCSection *, std::vector<std::pair<MCSymbol *, MCSymbol *>>>
SectionToImportedFunctionCalls;
unsigned PAuthIFuncNextUniqueID = 1;

public:
static char ID;
Expand Down Expand Up @@ -211,6 +214,12 @@ class AArch64AsmPrinter : public AsmPrinter {
// authenticating)
void LowerLOADgotAUTH(const MachineInstr &MI);

const MCExpr *emitPAuthRelocationAsIRelative(const MCExpr *Target,
uint16_t Disc,
AArch64PACKey::ID KeyID,
bool HasAddressDiversity,
bool IsDSOLocal);

/// tblgen'erated driver function for lowering simple MI->MC
/// pseudo instructions.
bool lowerPseudoInstExpansion(const MachineInstr *MI, MCInst &Inst);
Expand Down Expand Up @@ -2299,6 +2308,182 @@ void AArch64AsmPrinter::emitPtrauthBranch(const MachineInstr *MI) {
EmitToStreamer(*OutStreamer, BRInst);
}

static void emitAddress(MCStreamer &Streamer, MCRegister Reg,
const MCExpr *Expr, bool DSOLocal,
const MCSubtargetInfo &STI) {
MCValue Val;
if (!Expr->evaluateAsRelocatable(Val, nullptr))
report_fatal_error("emitAddress could not evaluate");
if (DSOLocal) {
Streamer.emitInstruction(
MCInstBuilder(AArch64::ADRP)
.addReg(Reg)
.addExpr(MCSpecifierExpr::create(Expr, AArch64::S_ABS_PAGE,
Streamer.getContext())),
STI);
Streamer.emitInstruction(
MCInstBuilder(AArch64::ADDXri)
.addReg(Reg)
.addReg(Reg)
.addExpr(MCSpecifierExpr::create(Expr, AArch64::S_LO12,
Streamer.getContext()))
.addImm(0),
STI);
} else {
auto *SymRef =
MCSymbolRefExpr::create(Val.getAddSym(), Streamer.getContext());
Streamer.emitInstruction(
MCInstBuilder(AArch64::ADRP)
.addReg(Reg)
.addExpr(MCSpecifierExpr::create(SymRef, AArch64::S_GOT_PAGE,
Streamer.getContext())),
STI);
Streamer.emitInstruction(
MCInstBuilder(AArch64::LDRXui)
.addReg(Reg)
.addReg(Reg)
.addExpr(MCSpecifierExpr::create(SymRef, AArch64::S_GOT_LO12,
Streamer.getContext())),
STI);
if (Val.getConstant())
Streamer.emitInstruction(MCInstBuilder(AArch64::ADDXri)
.addReg(Reg)
.addReg(Reg)
.addImm(Val.getConstant())
.addImm(0),
STI);
}
}

static bool targetSupportsPAuthRelocation(const Triple &TT,
const MCExpr *Target) {
// No released version of glibc supports PAuth relocations.
if (TT.isOSGlibc())
return false;

// We emit PAuth constants as IRELATIVE relocations in cases where the
// constant cannot be represented as a PAuth relocation:
// 1) The signed value is not a symbol.
return !isa<MCConstantExpr>(Target);
}

static bool targetSupportsIRelativeRelocation(const Triple &TT) {
// IFUNCs are ELF-only.
if (!TT.isOSBinFormatELF())
return false;

// musl doesn't support IFUNCs.
if (TT.isMusl())
return false;

return true;
}

// Emit an ifunc resolver that returns a signed pointer to the specified target,
// and return a FUNCINIT reference to the resolver. In the linked binary, this
// function becomes the target of an IRELATIVE relocation. This resolver is used
// to relocate signed pointers in global variable initializers in special cases
// where the standard R_AARCH64_AUTH_ABS64 relocation would not work.
//
// Example (signed null pointer, not address discriminated):
//
// .8byte .Lpauth_ifunc0
// .pushsection .text.startup,"ax",@progbits
// .Lpauth_ifunc0:
// mov x0, #0
// mov x1, #12345
// b __emupac_pacda
//
// Example (signed null pointer, address discriminated):
//
// .Ltmp:
// .8byte .Lpauth_ifunc0
// .pushsection .text.startup,"ax",@progbits
// .Lpauth_ifunc0:
// mov x0, #0
// adrp x1, .Ltmp
// add x1, x1, :lo12:.Ltmp
// b __emupac_pacda
// .popsection
//
// Example (signed pointer to symbol, not address discriminated):
//
// .Ltmp:
// .8byte .Lpauth_ifunc0
// .pushsection .text.startup,"ax",@progbits
// .Lpauth_ifunc0:
// adrp x0, symbol
// add x0, x0, :lo12:symbol
// mov x1, #12345
// b __emupac_pacda
// .popsection
const MCExpr *AArch64AsmPrinter::emitPAuthRelocationAsIRelative(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe for posterity add a comment of the way codegen in the different cases?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

const MCExpr *Target, uint16_t Disc, AArch64PACKey::ID KeyID,
bool HasAddressDiversity, bool IsDSOLocal) {
const Triple &TT = TM.getTargetTriple();

// We only emit an IRELATIVE relocation if the target supports IRELATIVE and
// does not support the kind of PAuth relocation that we are trying to emit.
if (targetSupportsPAuthRelocation(TT, Target) ||
!targetSupportsIRelativeRelocation(TT))
return nullptr;

// For now, only the DA key is supported.
if (KeyID != AArch64PACKey::DA)
return nullptr;

std::unique_ptr<MCSubtargetInfo> STI(
TM.getTarget().createMCSubtargetInfo(TT, "", ""));
assert(STI && "Unable to create subtarget info");

MCSymbol *Place = OutStreamer->getContext().createTempSymbol();
OutStreamer->emitLabel(Place);
OutStreamer->pushSection();

OutStreamer->switchSection(OutStreamer->getContext().getELFSection(
".text.startup", ELF::SHT_PROGBITS, ELF::SHF_ALLOC | ELF::SHF_EXECINSTR,
0, "", true, PAuthIFuncNextUniqueID++, nullptr));

MCSymbol *IRelativeSym =
OutStreamer->getContext().createLinkerPrivateSymbol("pauth_ifunc");
OutStreamer->emitLabel(IRelativeSym);
if (isa<MCConstantExpr>(Target)) {
OutStreamer->emitInstruction(MCInstBuilder(AArch64::MOVZXi)
.addReg(AArch64::X0)
.addExpr(Target)
.addImm(0),
*STI);
} else {
emitAddress(*OutStreamer, AArch64::X0, Target, IsDSOLocal, *STI);
}
if (HasAddressDiversity) {
auto *PlacePlusDisc = MCBinaryExpr::createAdd(
MCSymbolRefExpr::create(Place, OutStreamer->getContext()),
MCConstantExpr::create(static_cast<int16_t>(Disc),
OutStreamer->getContext()),
OutStreamer->getContext());
emitAddress(*OutStreamer, AArch64::X1, PlacePlusDisc, /*IsDSOLocal=*/true,
*STI);
} else {
emitMOVZ(AArch64::X1, Disc, 0);
}

// We don't know the subtarget because this is being emitted for a global
// initializer. Because the performance of IFUNC resolvers is unimportant, we
// always call the EmuPAC runtime, which will end up using the PAC instruction
// if the target supports PAC.
MCSymbol *EmuPAC =
OutStreamer->getContext().getOrCreateSymbol("__emupac_pacda");
const MCSymbolRefExpr *EmuPACRef =
MCSymbolRefExpr::create(EmuPAC, OutStreamer->getContext());
OutStreamer->emitInstruction(MCInstBuilder(AArch64::B).addExpr(EmuPACRef),
*STI);
OutStreamer->popSection();

return MCSymbolRefExpr::create(IRelativeSym, AArch64::S_FUNCINIT,
OutStreamer->getContext());
}

const MCExpr *
AArch64AsmPrinter::lowerConstantPtrAuth(const ConstantPtrAuth &CPA) {
MCContext &Ctx = OutContext;
Expand All @@ -2310,23 +2495,20 @@ AArch64AsmPrinter::lowerConstantPtrAuth(const ConstantPtrAuth &CPA) {

auto *BaseGVB = dyn_cast<GlobalValue>(BaseGV);

// If we can't understand the referenced ConstantExpr, there's nothing
// else we can do: emit an error.
if (!BaseGVB) {
BaseGV->getContext().emitError(
"cannot resolve target base/addend of ptrauth constant");
return nullptr;
const MCExpr *Sym;
if (BaseGVB) {
// If there is an addend, turn that into the appropriate MCExpr.
Sym = MCSymbolRefExpr::create(getSymbol(BaseGVB), Ctx);
if (Offset.sgt(0))
Sym = MCBinaryExpr::createAdd(
Sym, MCConstantExpr::create(Offset.getSExtValue(), Ctx), Ctx);
else if (Offset.slt(0))
Sym = MCBinaryExpr::createSub(
Sym, MCConstantExpr::create((-Offset).getSExtValue(), Ctx), Ctx);
} else {
Sym = MCConstantExpr::create(Offset.getSExtValue(), Ctx);
}

// If there is an addend, turn that into the appropriate MCExpr.
const MCExpr *Sym = MCSymbolRefExpr::create(getSymbol(BaseGVB), Ctx);
if (Offset.sgt(0))
Sym = MCBinaryExpr::createAdd(
Sym, MCConstantExpr::create(Offset.getSExtValue(), Ctx), Ctx);
else if (Offset.slt(0))
Sym = MCBinaryExpr::createSub(
Sym, MCConstantExpr::create((-Offset).getSExtValue(), Ctx), Ctx);

uint64_t KeyID = CPA.getKey()->getZExtValue();
// We later rely on valid KeyID value in AArch64PACKeyIDToString call from
// AArch64AuthMCExpr::printImpl, so fail fast.
Expand All @@ -2344,6 +2526,12 @@ AArch64AsmPrinter::lowerConstantPtrAuth(const ConstantPtrAuth &CPA) {
Disc = 0;
}

// Check if we need to represent this with an IRELATIVE and emit it if so.
if (auto *IFuncSym = emitPAuthRelocationAsIRelative(
Sym, Disc, AArch64PACKey::ID(KeyID), CPA.hasAddressDiscriminator(),
BaseGVB && BaseGVB->isDSOLocal()))
return IFuncSym;

// Finally build the complete @AUTH expr.
return AArch64AuthMCExpr::create(Sym, Disc, AArch64PACKey::ID(KeyID),
CPA.hasAddressDiscriminator(), Ctx);
Expand Down
78 changes: 78 additions & 0 deletions llvm/test/CodeGen/AArch64/ptrauth-irelative.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
; RUN: llc -mtriple aarch64-linux-gnu -mattr=+pauth -filetype=asm -o - %s | FileCheck %s

; CHECK: nullref:
; CHECK-NEXT: [[PLACE:.*]]:
; CHECK-NEXT: .section .text.startup
; CHECK-NEXT: [[FUNC:.*]]:
; CHECK-NEXT: movz x0, #0
; CHECK-NEXT: mov x1, #1
; CHECK-NEXT: b __emupac_pacda
; CHECK-NEXT: .section .rodata
; CHECK-NEXT: .xword [[FUNC]]@FUNCINIT
@nullref = constant ptr ptrauth (ptr null, i32 2, i64 1, ptr null), align 8

@dsolocal = external dso_local global i8

; CHECK: dsolocalref:
; CHECK-NEXT: [[PLACE:.*]]:
; CHECK-NEXT: .section .text.startup
; CHECK-NEXT: [[FUNC:.*]]:
; CHECK-NEXT: adrp x0, dsolocal
; CHECK-NEXT: add x0, x0, :lo12:dsolocal
; CHECK-NEXT: mov x1, #2
; CHECK-NEXT: b __emupac_pacda
; CHECK-NEXT: .section .rodata
; CHECK-NEXT: .xword [[FUNC]]@FUNCINIT
@dsolocalref = constant ptr ptrauth (ptr @dsolocal, i32 2, i64 2, ptr null), align 8

; CHECK: dsolocalref8:
; CHECK-NEXT: [[PLACE:.*]]:
; CHECK-NEXT: .section .text.startup
; CHECK-NEXT: [[FUNC:.*]]:
; CHECK-NEXT: adrp x0, dsolocal+8
; CHECK-NEXT: add x0, x0, :lo12:dsolocal+8
; CHECK-NEXT: mov x1, #3
; CHECK-NEXT: b __emupac_pacda
; CHECK-NEXT: .section .rodata
; CHECK-NEXT: .xword [[FUNC]]@FUNCINIT
@dsolocalref8 = constant ptr ptrauth (ptr getelementptr (i8, ptr @dsolocal, i64 8), i32 2, i64 3, ptr null), align 8

; CHECK: disc:
; CHECK-NEXT: [[PLACE:.*]]:
; CHECK-NEXT: .section .text.startup
; CHECK-NEXT: [[FUNC:.*]]:
; CHECK-NEXT: adrp x0, dsolocal
; CHECK-NEXT: add x0, x0, :lo12:dsolocal
; CHECK-NEXT: adrp x1, [[PLACE]]
; CHECK-NEXT: add x1, x1, :lo12:[[PLACE]]
; CHECK-NEXT: b __emupac_pacda
; CHECK-NEXT: .section .rodata
; CHECK-NEXT: .xword [[FUNC]]@FUNCINIT
@disc = constant ptr ptrauth (ptr @dsolocal, i32 2, i64 0, ptr @disc), align 8

@global = external global i8

; CHECK: globalref:
; CHECK-NEXT: [[PLACE:.*]]:
; CHECK-NEXT: .section .text.startup
; CHECK-NEXT: [[FUNC:.*]]:
; CHECK-NEXT: adrp x0, :got:global
; CHECK-NEXT: ldr x0, [x0, :got_lo12:global]
; CHECK-NEXT: mov x1, #4
; CHECK-NEXT: b __emupac_pacda
; CHECK-NEXT: .section .rodata
; CHECK-NEXT: .xword [[FUNC]]@FUNCINIT
@globalref = constant ptr ptrauth (ptr @global, i32 2, i64 4, ptr null), align 8

; CHECK: globalref8:
; CHECK-NEXT: [[PLACE:.*]]:
; CHECK-NEXT: .section .text.startup
; CHECK-NEXT: [[FUNC:.*]]:
; CHECK-NEXT: adrp x0, :got:global
; CHECK-NEXT: ldr x0, [x0, :got_lo12:global]
; CHECK-NEXT: add x0, x0, #8
; CHECK-NEXT: mov x1, #5
; CHECK-NEXT: b __emupac_pacda
; CHECK-NEXT: .section .rodata
; CHECK-NEXT: .xword [[FUNC]]@FUNCINIT
@globalref8 = constant ptr ptrauth (ptr getelementptr (i8, ptr @global, i64 8), i32 2, i64 5, ptr null), align 8
2 changes: 1 addition & 1 deletion llvm/test/CodeGen/AArch64/ptrauth-type-info-vptr-discr.ll
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
; RUN: llc -mtriple aarch64-linux-gnu -mattr=+pauth -filetype=asm -o - %s | FileCheck --check-prefix=ELF %s
; RUN: llc -mtriple aarch64-linux-musl -mattr=+pauth -filetype=asm -o - %s | FileCheck --check-prefix=ELF %s
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this needed? shouldn't the codegen for things that didn't need IRELATIVE before stay the same?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's needed because glibc doesn't support the PAuth relocations (so it uses IRELATIVE after this change), so I needed to switch to another triple that does.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clarified with Peter: before, this generated PAuth relocations for glibc, even though that wouldn't actually work.

; RUN: llc -mtriple aarch64-apple-darwin -mattr=+pauth -filetype=asm -o - %s | FileCheck --check-prefix=MACHO %s

; ELF-LABEL: _ZTI10Disc:
Expand Down
Loading