Skip to content

Commit 0d30bb2

Browse files
committed
[𝘀𝗽𝗿] initial version
Created using spr 1.3.6-beta.1
2 parents 5ca53a2 + 902c43a commit 0d30bb2

File tree

4 files changed

+164
-7
lines changed

4 files changed

+164
-7
lines changed

llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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");

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: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,9 @@
9393
; CHECK-NEXT: .section .rodata
9494
; CHECK-NEXT: .xword [[FUNC]]@FUNCINIT
9595
@globalref8 = constant ptr ptrauth (ptr getelementptr (i8, ptr @global, i64 8), i32 2, i64 5, ptr null), align 8
96+
97+
$comdat = comdat any
98+
@comdat = constant ptr ptrauth (ptr null, i32 2, i64 1, ptr null), align 8, comdat
99+
; CHECK: comdat:
100+
; CHECK-NEXT: [[PLACE:.*]]:
101+
; CHECK-NEXT: .section .text.startup,"axG",@progbits,comdat,comdat
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)