Skip to content

Commit 5aa16f8

Browse files
committed
[𝘀𝗽𝗿] initial version
Created using spr 1.3.6-beta.1
2 parents 5ca53a2 + 168c35a commit 5aa16f8

File tree

5 files changed

+208
-16
lines changed

5 files changed

+208
-16
lines changed

llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ class AArch64AsmPrinter : public AsmPrinter {
220220
void LowerLOADgotAUTH(const MachineInstr &MI);
221221

222222
const MCExpr *emitPAuthRelocationAsIRelative(
223-
const MCExpr *Target, uint16_t Disc, AArch64PACKey::ID KeyID,
223+
const MCExpr *Target, uint64_t Disc, AArch64PACKey::ID KeyID,
224224
bool HasAddressDiversity, bool IsDSOLocal, const MCExpr *DSExpr);
225225

226226
/// tblgen'erated driver function for lowering simple MI->MC
@@ -2461,7 +2461,7 @@ static bool targetSupportsIRelativeRelocation(const Triple &TT) {
24612461
// ret
24622462
// .popsection
24632463
const MCExpr *AArch64AsmPrinter::emitPAuthRelocationAsIRelative(
2464-
const MCExpr *Target, uint16_t Disc, AArch64PACKey::ID KeyID,
2464+
const MCExpr *Target, uint64_t Disc, AArch64PACKey::ID KeyID,
24652465
bool HasAddressDiversity, bool IsDSOLocal, const MCExpr *DSExpr) {
24662466
const Triple &TT = TM.getTargetTriple();
24672467

@@ -2484,9 +2484,14 @@ const MCExpr *AArch64AsmPrinter::emitPAuthRelocationAsIRelative(
24842484
OutStreamer->emitLabel(Place);
24852485
OutStreamer->pushSection();
24862486

2487+
const MCSymbolELF *Group =
2488+
static_cast<MCSectionELF *>(OutStreamer->getCurrentSectionOnly())
2489+
->getGroup();
24872490
OutStreamer->switchSection(OutStreamer->getContext().getELFSection(
2488-
".text.startup", ELF::SHT_PROGBITS, ELF::SHF_ALLOC | ELF::SHF_EXECINSTR,
2489-
0, "", true, PAuthIFuncNextUniqueID++, nullptr));
2491+
".text.startup", ELF::SHT_PROGBITS,
2492+
ELF::SHF_ALLOC | ELF::SHF_EXECINSTR | (Group ? ELF::SHF_GROUP : 0), 0,
2493+
Group, true, Group ? MCSection::NonUniqueID : PAuthIFuncNextUniqueID++,
2494+
nullptr));
24902495

24912496
MCSymbol *IRelativeSym =
24922497
OutStreamer->getContext().createLinkerPrivateSymbol("pauth_ifunc");
@@ -2503,12 +2508,16 @@ const MCExpr *AArch64AsmPrinter::emitPAuthRelocationAsIRelative(
25032508
if (HasAddressDiversity) {
25042509
auto *PlacePlusDisc = MCBinaryExpr::createAdd(
25052510
MCSymbolRefExpr::create(Place, OutStreamer->getContext()),
2506-
MCConstantExpr::create(static_cast<int16_t>(Disc),
2507-
OutStreamer->getContext()),
2511+
MCConstantExpr::create(Disc, OutStreamer->getContext()),
25082512
OutStreamer->getContext());
25092513
emitAddress(*OutStreamer, AArch64::X1, PlacePlusDisc, /*IsDSOLocal=*/true,
25102514
*STI);
25112515
} else {
2516+
if (!isUInt<16>(Disc)) {
2517+
OutContext.reportError(SMLoc(), "AArch64 PAC Discriminator '" +
2518+
Twine(Disc) +
2519+
"' out of range [0, 0xFFFF]");
2520+
}
25122521
emitMOVZ(AArch64::X1, Disc, 0);
25132522
}
25142523

@@ -2587,18 +2596,19 @@ AArch64AsmPrinter::lowerConstantPtrAuth(const ConstantPtrAuth &CPA) {
25872596
}
25882597

25892598
uint64_t Disc = CPA.getDiscriminator()->getZExtValue();
2590-
if (!isUInt<16>(Disc)) {
2591-
CPA.getContext().emitError("AArch64 PAC Discriminator '" + Twine(Disc) +
2592-
"' out of range [0, 0xFFFF]");
2593-
Disc = 0;
2594-
}
25952599

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

2606+
if (!isUInt<16>(Disc)) {
2607+
CPA.getContext().emitError("AArch64 PAC Discriminator '" + Twine(Disc) +
2608+
"' out of range [0, 0xFFFF]");
2609+
Disc = 0;
2610+
}
2611+
26022612
if (DSExpr)
26032613
report_fatal_error("deactivation symbols unsupported in constant "
26042614
"expressions on this target");

llvm/lib/Transforms/Scalar/SROA.cpp

Lines changed: 78 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
#include "llvm/IR/Instruction.h"
6363
#include "llvm/IR/Instructions.h"
6464
#include "llvm/IR/IntrinsicInst.h"
65+
#include "llvm/IR/Intrinsics.h"
6566
#include "llvm/IR/LLVMContext.h"
6667
#include "llvm/IR/Metadata.h"
6768
#include "llvm/IR/Module.h"
@@ -535,9 +536,11 @@ class Slice {
535536
public:
536537
Slice() = default;
537538

538-
Slice(uint64_t BeginOffset, uint64_t EndOffset, Use *U, bool IsSplittable)
539+
Slice(uint64_t BeginOffset, uint64_t EndOffset, Use *U, bool IsSplittable,
540+
Value *ProtectedFieldDisc)
539541
: BeginOffset(BeginOffset), EndOffset(EndOffset),
540-
UseAndIsSplittable(U, IsSplittable) {}
542+
UseAndIsSplittable(U, IsSplittable),
543+
ProtectedFieldDisc(ProtectedFieldDisc) {}
541544

542545
uint64_t beginOffset() const { return BeginOffset; }
543546
uint64_t endOffset() const { return EndOffset; }
@@ -550,6 +553,10 @@ class Slice {
550553
bool isDead() const { return getUse() == nullptr; }
551554
void kill() { UseAndIsSplittable.setPointer(nullptr); }
552555

556+
// When this access is via an llvm.protected.field.ptr intrinsic, contains
557+
// the second argument to the intrinsic, the discriminator.
558+
Value *ProtectedFieldDisc;
559+
553560
/// Support for ordering ranges.
554561
///
555562
/// This provides an ordering over ranges such that start offsets are
@@ -641,6 +648,10 @@ class AllocaSlices {
641648
/// Access the dead users for this alloca.
642649
ArrayRef<Instruction *> getDeadUsers() const { return DeadUsers; }
643650

651+
/// Access the users for this alloca that are llvm.protected.field.ptr
652+
/// intrinsics.
653+
ArrayRef<IntrinsicInst *> getPFPUsers() const { return PFPUsers; }
654+
644655
/// Access Uses that should be dropped if the alloca is promotable.
645656
ArrayRef<Use *> getDeadUsesIfPromotable() const {
646657
return DeadUseIfPromotable;
@@ -701,6 +712,10 @@ class AllocaSlices {
701712
/// they come from outside of the allocated space.
702713
SmallVector<Instruction *, 8> DeadUsers;
703714

715+
/// Users that are llvm.protected.field.ptr intrinsics. These will be RAUW'd
716+
/// to their first argument if we rewrite the alloca.
717+
SmallVector<IntrinsicInst *, 0> PFPUsers;
718+
704719
/// Uses which will become dead if can promote the alloca.
705720
SmallVector<Use *, 8> DeadUseIfPromotable;
706721

@@ -1029,6 +1044,10 @@ class AllocaSlices::SliceBuilder : public PtrUseVisitor<SliceBuilder> {
10291044
/// Set to de-duplicate dead instructions found in the use walk.
10301045
SmallPtrSet<Instruction *, 4> VisitedDeadInsts;
10311046

1047+
// When this access is via an llvm.protected.field.ptr intrinsic, contains
1048+
// the second argument to the intrinsic, the discriminator.
1049+
Value *ProtectedFieldDisc = nullptr;
1050+
10321051
public:
10331052
SliceBuilder(const DataLayout &DL, AllocaInst &AI, AllocaSlices &AS)
10341053
: PtrUseVisitor<SliceBuilder>(DL),
@@ -1074,7 +1093,8 @@ class AllocaSlices::SliceBuilder : public PtrUseVisitor<SliceBuilder> {
10741093
EndOffset = AllocSize;
10751094
}
10761095

1077-
AS.Slices.push_back(Slice(BeginOffset, EndOffset, U, IsSplittable));
1096+
AS.Slices.push_back(
1097+
Slice(BeginOffset, EndOffset, U, IsSplittable, ProtectedFieldDisc));
10781098
}
10791099

10801100
void visitBitCastInst(BitCastInst &BC) {
@@ -1274,6 +1294,27 @@ class AllocaSlices::SliceBuilder : public PtrUseVisitor<SliceBuilder> {
12741294
return;
12751295
}
12761296

1297+
if (II.getIntrinsicID() == Intrinsic::protected_field_ptr) {
1298+
// We only handle loads and stores as users of llvm.protected.field.ptr.
1299+
// Other uses may add items to the worklist, which will cause
1300+
// ProtectedFieldDisc to be tracked incorrectly.
1301+
AS.PFPUsers.push_back(&II);
1302+
ProtectedFieldDisc = II.getArgOperand(1);
1303+
for (Use &U : II.uses()) {
1304+
this->U = &U;
1305+
if (auto *LI = dyn_cast<LoadInst>(U.getUser()))
1306+
visitLoadInst(*LI);
1307+
else if (auto *SI = dyn_cast<StoreInst>(U.getUser()))
1308+
visitStoreInst(*SI);
1309+
else
1310+
PI.setAborted(&II);
1311+
if (PI.isAborted())
1312+
break;
1313+
}
1314+
ProtectedFieldDisc = nullptr;
1315+
return;
1316+
}
1317+
12771318
Base::visitIntrinsicInst(II);
12781319
}
12791320

@@ -4948,7 +4989,7 @@ bool SROA::presplitLoadsAndStores(AllocaInst &AI, AllocaSlices &AS) {
49484989
NewSlices.push_back(
49494990
Slice(BaseOffset + PartOffset, BaseOffset + PartOffset + PartSize,
49504991
&PLoad->getOperandUse(PLoad->getPointerOperandIndex()),
4951-
/*IsSplittable*/ false));
4992+
/*IsSplittable*/ false, nullptr));
49524993
LLVM_DEBUG(dbgs() << " new slice [" << NewSlices.back().beginOffset()
49534994
<< ", " << NewSlices.back().endOffset()
49544995
<< "): " << *PLoad << "\n");
@@ -5104,10 +5145,12 @@ bool SROA::presplitLoadsAndStores(AllocaInst &AI, AllocaSlices &AS) {
51045145
LLVMContext::MD_access_group});
51055146

51065147
// Now build a new slice for the alloca.
5148+
// ProtectedFieldDisc==nullptr is a lie, but it doesn't matter because we
5149+
// already determined that all accesses are consistent.
51075150
NewSlices.push_back(
51085151
Slice(BaseOffset + PartOffset, BaseOffset + PartOffset + PartSize,
51095152
&PStore->getOperandUse(PStore->getPointerOperandIndex()),
5110-
/*IsSplittable*/ false));
5153+
/*IsSplittable*/ false, nullptr));
51115154
LLVM_DEBUG(dbgs() << " new slice [" << NewSlices.back().beginOffset()
51125155
<< ", " << NewSlices.back().endOffset()
51135156
<< "): " << *PStore << "\n");
@@ -5875,6 +5918,30 @@ SROA::runOnAlloca(AllocaInst &AI) {
58755918
return {Changed, CFGChanged};
58765919
}
58775920

5921+
for (auto &P : AS.partitions()) {
5922+
// For now, we can't split if a field is accessed both via protected field
5923+
// and not, because that would mean that we would need to introduce sign and
5924+
// auth operations to convert between the protected and non-protected uses,
5925+
// and this pass doesn't know how to do that. Also, this case is unlikely to
5926+
// occur in normal code.
5927+
std::optional<Value *> ProtectedFieldDisc;
5928+
auto SliceHasMismatch = [&](Slice &S) {
5929+
if (auto *II = dyn_cast<IntrinsicInst>(S.getUse()->getUser()))
5930+
if (II->getIntrinsicID() == Intrinsic::lifetime_start ||
5931+
II->getIntrinsicID() == Intrinsic::lifetime_end)
5932+
return false;
5933+
if (!ProtectedFieldDisc)
5934+
ProtectedFieldDisc = S.ProtectedFieldDisc;
5935+
return *ProtectedFieldDisc != S.ProtectedFieldDisc;
5936+
};
5937+
for (Slice &S : P)
5938+
if (SliceHasMismatch(S))
5939+
return {Changed, CFGChanged};
5940+
for (Slice *S : P.splitSliceTails())
5941+
if (SliceHasMismatch(*S))
5942+
return {Changed, CFGChanged};
5943+
}
5944+
58785945
// Delete all the dead users of this alloca before splitting and rewriting it.
58795946
for (Instruction *DeadUser : AS.getDeadUsers()) {
58805947
// Free up everything used by this instruction.
@@ -5892,6 +5959,12 @@ SROA::runOnAlloca(AllocaInst &AI) {
58925959
clobberUse(*DeadOp);
58935960
Changed = true;
58945961
}
5962+
for (IntrinsicInst *PFPUser : AS.getPFPUsers()) {
5963+
PFPUser->replaceAllUsesWith(PFPUser->getArgOperand(0));
5964+
5965+
DeadInsts.push_back(PFPUser);
5966+
Changed = true;
5967+
}
58955968

58965969
// No slices to split. Leave the dead alloca for a later pass to clean up.
58975970
if (AS.begin() == AS.end())

llvm/test/CodeGen/AArch64/ptrauth-irelative.ll

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,19 @@
6767
; CHECK-NEXT: .xword [[FUNC]]@FUNCINIT
6868
@disc = constant ptr ptrauth (ptr @dsolocal, i32 2, i64 0, ptr @disc), align 8
6969

70+
; CHECK: disc65536:
71+
; CHECK-NEXT: [[PLACE:.*]]:
72+
; CHECK-NEXT: .section .text.startup
73+
; CHECK-NEXT: [[FUNC:.*]]:
74+
; CHECK-NEXT: adrp x0, dsolocal
75+
; CHECK-NEXT: add x0, x0, :lo12:dsolocal
76+
; CHECK-NEXT: adrp x1, [[PLACE]]+65536
77+
; CHECK-NEXT: add x1, x1, :lo12:[[PLACE]]+65536
78+
; CHECK-NEXT: b __emupac_pacda
79+
; CHECK-NEXT: .section .rodata
80+
; CHECK-NEXT: .xword [[FUNC]]@FUNCINIT
81+
@disc65536 = constant ptr ptrauth (ptr @dsolocal, i32 2, i64 65536, ptr @disc), align 8
82+
7083
@global = external global i8
7184

7285
; CHECK: globalref:
@@ -93,3 +106,9 @@
93106
; CHECK-NEXT: .section .rodata
94107
; CHECK-NEXT: .xword [[FUNC]]@FUNCINIT
95108
@globalref8 = constant ptr ptrauth (ptr getelementptr (i8, ptr @global, i64 8), i32 2, i64 5, ptr null), align 8
109+
110+
$comdat = comdat any
111+
@comdat = constant ptr ptrauth (ptr null, i32 2, i64 1, ptr null), align 8, comdat
112+
; CHECK: comdat:
113+
; CHECK-NEXT: [[PLACE:.*]]:
114+
; CHECK-NEXT: .section .text.startup,"axG",@progbits,comdat,comdat

llvm/test/CodeGen/AArch64/ptrauth-reloc.ll

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,3 +175,20 @@
175175

176176
@g = external global i32
177177
@g.ref.ia.65536 = constant ptr ptrauth (ptr @g, i32 0, i64 65536)
178+
179+
;--- err-disc-elf.ll
180+
181+
; RUN: not llc < err-disc-elf.ll -mtriple aarch64-elf -mattr=+pauth 2>&1 \
182+
; RUN: | FileCheck %s --check-prefix=CHECK-ERR-DISC-ELF
183+
; RUN: not llc < err-disc-elf.ll -mtriple aarch64-elf -mattr=+pauth \
184+
; RUN: -global-isel -verify-machineinstrs -global-isel-abort=1 2>&1 \
185+
; RUN: | FileCheck %s --check-prefix=CHECK-ERR-DISC-ELF
186+
187+
@g = external global i32
188+
189+
@ds = external global i8
190+
; CHECK-ERR-DISC-ELF-NOT: error: AArch64 PAC Discriminator '65537' out of range [0, 0xFFFF]
191+
@g.ref.da.65537 = constant ptr ptrauth (ptr @g, i32 2, i64 65537, ptr @g.ref.da.65537, ptr @ds)
192+
193+
; CHECK-ERR-DISC-ELF: error: AArch64 PAC Discriminator '65538' out of range [0, 0xFFFF]
194+
@g.ref.da.65538 = constant ptr ptrauth (ptr @g, i32 2, i64 65538, ptr null, ptr @ds)
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
2+
; RUN: opt -passes=sroa -S < %s | FileCheck %s
3+
4+
define void @slice(ptr %ptr1, ptr %ptr2, ptr %out1, ptr %out2) {
5+
; CHECK-LABEL: define void @slice(
6+
; CHECK-SAME: ptr [[PTR1:%.*]], ptr [[PTR2:%.*]], ptr [[OUT1:%.*]], ptr [[OUT2:%.*]]) {
7+
; CHECK-NEXT: store ptr [[PTR1]], ptr [[OUT1]], align 8
8+
; CHECK-NEXT: store ptr [[PTR2]], ptr [[OUT2]], align 8
9+
; CHECK-NEXT: ret void
10+
;
11+
%alloca = alloca { ptr, ptr }
12+
13+
%protptrptr1.1 = call ptr @llvm.protected.field.ptr.p0(ptr %alloca, i64 1, i1 true)
14+
store ptr %ptr1, ptr %protptrptr1.1
15+
%protptrptr1.2 = call ptr @llvm.protected.field.ptr.p0(ptr %alloca, i64 1, i1 true)
16+
%ptr1a = load ptr, ptr %protptrptr1.2
17+
18+
%gep = getelementptr { ptr, ptr }, ptr %alloca, i64 0, i32 1
19+
%protptrptr2.1 = call ptr @llvm.protected.field.ptr.p0(ptr %gep, i64 2, i1 true)
20+
store ptr %ptr2, ptr %protptrptr2.1
21+
%protptrptr2.2 = call ptr @llvm.protected.field.ptr.p0(ptr %gep, i64 2, i1 true)
22+
%ptr2a = load ptr, ptr %protptrptr2.2
23+
24+
store ptr %ptr1a, ptr %out1
25+
store ptr %ptr2a, ptr %out2
26+
ret void
27+
}
28+
29+
define ptr @mixed(ptr %ptr) {
30+
; CHECK-LABEL: define ptr @mixed(
31+
; CHECK-SAME: ptr [[PTR:%.*]]) {
32+
; CHECK-NEXT: [[ALLOCA:%.*]] = alloca ptr, align 8
33+
; CHECK-NEXT: store ptr [[PTR]], ptr [[ALLOCA]], align 8
34+
; CHECK-NEXT: [[PROTPTRPTR1_2:%.*]] = call ptr @llvm.protected.field.ptr.p0(ptr [[ALLOCA]], i64 1, i1 true)
35+
; CHECK-NEXT: [[PTR1A:%.*]] = load ptr, ptr [[PROTPTRPTR1_2]], align 8
36+
; CHECK-NEXT: ret ptr [[PTR1A]]
37+
;
38+
%alloca = alloca ptr
39+
40+
store ptr %ptr, ptr %alloca
41+
%protptrptr1.2 = call ptr @llvm.protected.field.ptr.p0(ptr %alloca, i64 1, i1 true)
42+
%ptr1a = load ptr, ptr %protptrptr1.2
43+
44+
ret ptr %ptr1a
45+
}
46+
47+
define void @split_non_promotable(ptr %ptr1, ptr %ptr2, ptr %out1, ptr %out2) {
48+
; CHECK-LABEL: define void @split_non_promotable(
49+
; CHECK-SAME: ptr [[PTR1:%.*]], ptr [[PTR2:%.*]], ptr [[OUT1:%.*]], ptr [[OUT2:%.*]]) {
50+
; CHECK-NEXT: [[ALLOCA_SROA_2:%.*]] = alloca ptr, align 8
51+
; CHECK-NEXT: store volatile ptr [[PTR2]], ptr [[ALLOCA_SROA_2]], align 8
52+
; CHECK-NEXT: [[PTR2A:%.*]] = load volatile ptr, ptr [[ALLOCA_SROA_2]], align 8
53+
; CHECK-NEXT: store ptr [[PTR1]], ptr [[OUT1]], align 8
54+
; CHECK-NEXT: store ptr [[PTR2A]], ptr [[OUT2]], align 8
55+
; CHECK-NEXT: ret void
56+
;
57+
%alloca = alloca { ptr, ptr }
58+
59+
%protptrptr1.1 = call ptr @llvm.protected.field.ptr.p0(ptr %alloca, i64 1, i1 true)
60+
store ptr %ptr1, ptr %protptrptr1.1
61+
%protptrptr1.2 = call ptr @llvm.protected.field.ptr.p0(ptr %alloca, i64 1, i1 true)
62+
%ptr1a = load ptr, ptr %protptrptr1.2
63+
64+
%gep = getelementptr { ptr, ptr }, ptr %alloca, i64 0, i32 1
65+
%protptrptr2.1 = call ptr @llvm.protected.field.ptr.p0(ptr %gep, i64 2, i1 true)
66+
store volatile ptr %ptr2, ptr %protptrptr2.1
67+
%protptrptr2.2 = call ptr @llvm.protected.field.ptr.p0(ptr %gep, i64 2, i1 true)
68+
%ptr2a = load volatile ptr, ptr %protptrptr2.2
69+
70+
store ptr %ptr1a, ptr %out1
71+
store ptr %ptr2a, ptr %out2
72+
ret void
73+
}

0 commit comments

Comments
 (0)