Skip to content

Commit 40e0181

Browse files
committed
[AArch64] Lower ptrauth.sign of constant as PtrAuthGlobalAddress.
This lets us guarantee the emission of one of our hardened constant signed pointer materialization sequences, rather than emitting the sign intrinsic as a naked sign, and exposing the raw pointer. Do that in the builder to help guarantee that through the backend. At the IR level, there aren't great ways to enforce that the constant operand to the intrinsic stays there, but we might be able to do that by generalizing immarg to be applicable to callsites rather than just Intrinsic definitions. That's all theoretical either way, and there's rarely a good reason to hoist a constant op from an intrinsic anyway. This is pretty tolerant of weird discriminators, which should cover the interesting cases.
1 parent b739a3c commit 40e0181

File tree

2 files changed

+300
-0
lines changed

2 files changed

+300
-0
lines changed

llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8005,6 +8005,65 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I,
80058005
setValue(&I, DAG.getNode(ISD::AND, sdl, PtrVT, Ptr, Mask));
80068006
return;
80078007
}
8008+
case Intrinsic::ptrauth_sign: {
8009+
Value *Ptr = I.getOperand(0);
8010+
Value *Disc = I.getOperand(2);
8011+
8012+
if (auto *PtrToInt = dyn_cast<PtrToIntOperator>(Ptr))
8013+
Ptr = PtrToInt->getOperand(0);
8014+
8015+
APInt PtrOff(DAG.getDataLayout().getPointerSizeInBits(), 0);
8016+
Ptr = Ptr->stripAndAccumulateConstantOffsets(DAG.getDataLayout(), PtrOff,
8017+
/*AllowNonInbounds=*/true);
8018+
8019+
// We found that the raw pointer is a pointer constant + constant offset:
8020+
// we can turn it into the (hardened) signed constant pointer
8021+
// materialization sequence, via PtrAuthGlobalAddress.
8022+
// Otherwise, let it be handled as a plain raw intrinsic.
8023+
if (!isa<GlobalValue>(Ptr)) {
8024+
visitTargetIntrinsic(I, Intrinsic);
8025+
return;
8026+
}
8027+
8028+
auto *DiscC = dyn_cast<ConstantInt>(Disc);
8029+
Value *AddrDisc = nullptr;
8030+
8031+
auto *BlendInt = dyn_cast<IntrinsicInst>(Disc);
8032+
if (BlendInt && BlendInt->getIntrinsicID() == Intrinsic::ptrauth_blend) {
8033+
DiscC = dyn_cast<ConstantInt>(BlendInt->getOperand(1));
8034+
AddrDisc = BlendInt->getOperand(0);
8035+
}
8036+
// If we can't isolate the constant discriminator, treat the whole thing
8037+
// as a variable address discriminator.
8038+
if (!DiscC)
8039+
AddrDisc = Disc;
8040+
8041+
// We're extracting the addr-disc from a blend intrinsic, which could be
8042+
// in another block: make sure it isn't, because it might not have gotten
8043+
// exported to this block via a vreg.
8044+
// The blends should be sunk into the same block by CGP already.
8045+
// Ideally, to guarantee this, we should make ptrauth.sign have both
8046+
// address-discriminator and integer discriminator, and do the blend itself.
8047+
if (auto *AddrDiscInst = dyn_cast_or_null<Instruction>(AddrDisc)) {
8048+
if (AddrDiscInst->getParent() != I.getParent()) {
8049+
visitTargetIntrinsic(I, Intrinsic);
8050+
return;
8051+
}
8052+
}
8053+
8054+
SDValue NullPtr = DAG.getIntPtrConstant(0, getCurSDLoc());
8055+
SDValue DiscVal = DiscC ? getValue(DiscC) : NullPtr;
8056+
SDValue AddrDiscVal = AddrDisc ? getValue(AddrDisc) : NullPtr;
8057+
8058+
setValue(&I, DAG.getNode(ISD::PtrAuthGlobalAddress, getCurSDLoc(),
8059+
NullPtr.getValueType(),
8060+
DAG.getGlobalAddress(
8061+
cast<GlobalValue>(Ptr), getCurSDLoc(),
8062+
NullPtr.getValueType(), PtrOff.getSExtValue()),
8063+
getValue(I.getOperand(1)), AddrDiscVal, DiscVal));
8064+
return;
8065+
}
8066+
80088067
case Intrinsic::threadlocal_address: {
80098068
setValue(&I, getValue(I.getOperand(0)));
80108069
return;
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
2+
; RUN: llc < %s -mtriple arm64e-apple-darwin | FileCheck %s
3+
4+
target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
5+
6+
define i8* @test_intrinsic() {
7+
; CHECK-LABEL: test_intrinsic:
8+
; CHECK: ; %bb.0:
9+
; CHECK-NEXT: adrp x16, _g@GOTPAGE
10+
; CHECK-NEXT: ldr x16, [x16, _g@GOTPAGEOFF]
11+
; CHECK-NEXT: mov x17, #56 ; =0x38
12+
; CHECK-NEXT: pacda x16, x17
13+
; CHECK-NEXT: mov x0, x16
14+
; CHECK-NEXT: ret
15+
%tmp0 = ptrtoint i32* @g to i64
16+
%tmp1 = call i64 @llvm.ptrauth.sign(i64 %tmp0, i32 2, i64 56)
17+
%tmp2 = inttoptr i64 %tmp1 to i8*
18+
ret i8* %tmp2
19+
}
20+
21+
define i8* @test_intrinsic_weak() {
22+
; CHECK-LABEL: test_intrinsic_weak:
23+
; CHECK: ; %bb.0:
24+
; CHECK-NEXT: adrp x0, l_g_weak$auth_ptr$da$56@PAGE
25+
; CHECK-NEXT: ldr x0, [x0, l_g_weak$auth_ptr$da$56@PAGEOFF]
26+
; CHECK-NEXT: ret
27+
%tmp0 = ptrtoint i32* @g_weak to i64
28+
%tmp1 = call i64 @llvm.ptrauth.sign(i64 %tmp0, i32 2, i64 56)
29+
%tmp2 = inttoptr i64 %tmp1 to i8*
30+
ret i8* %tmp2
31+
}
32+
33+
; Non-external symbols don't need to be accessed through the GOT: always prefer
34+
; the dynamic materialization sequence, with adrp+add rather than a GOT load.
35+
36+
define i8* @test_intrinsic_strong_def() {
37+
; CHECK-LABEL: test_intrinsic_strong_def:
38+
; CHECK: ; %bb.0:
39+
; CHECK-NEXT: adrp x16, _g_strong_def@PAGE
40+
; CHECK-NEXT: add x16, x16, _g_strong_def@PAGEOFF
41+
; CHECK-NEXT: pacdza x16
42+
; CHECK-NEXT: mov x0, x16
43+
; CHECK-NEXT: ret
44+
%tmp0 = ptrtoint i32* @g_strong_def to i64
45+
%tmp1 = call i64 @llvm.ptrauth.sign(i64 %tmp0, i32 2, i64 0)
46+
%tmp2 = inttoptr i64 %tmp1 to i8*
47+
ret i8* %tmp2
48+
}
49+
50+
define i8* @test_intrinsic_bkey() {
51+
; CHECK-LABEL: test_intrinsic_bkey:
52+
; CHECK: ; %bb.0:
53+
; CHECK-NEXT: adrp x16, _g@GOTPAGE
54+
; CHECK-NEXT: ldr x16, [x16, _g@GOTPAGEOFF]
55+
; CHECK-NEXT: mov x17, #78 ; =0x4e
56+
; CHECK-NEXT: pacib x16, x17
57+
; CHECK-NEXT: mov x0, x16
58+
; CHECK-NEXT: ret
59+
%tmp0 = ptrtoint i32* @g to i64
60+
%tmp1 = call i64 @llvm.ptrauth.sign(i64 %tmp0, i32 1, i64 78)
61+
%tmp2 = inttoptr i64 %tmp1 to i8*
62+
ret i8* %tmp2
63+
}
64+
65+
define i8* @test_intrinsic_constantexpr() {
66+
; CHECK-LABEL: test_intrinsic_constantexpr:
67+
; CHECK: ; %bb.0:
68+
; CHECK-NEXT: adrp x16, _g@GOTPAGE
69+
; CHECK-NEXT: ldr x16, [x16, _g@GOTPAGEOFF]
70+
; CHECK-NEXT: mov x17, #56 ; =0x38
71+
; CHECK-NEXT: pacda x16, x17
72+
; CHECK-NEXT: mov x0, x16
73+
; CHECK-NEXT: ret
74+
%tmp0 = call i64 @llvm.ptrauth.sign(i64 ptrtoint (i32* @g to i64), i32 2, i64 56)
75+
%tmp1 = inttoptr i64 %tmp0 to i8*
76+
ret i8* %tmp1
77+
}
78+
79+
define i8* @test_intrinsic_constantexpr_offset() {
80+
; CHECK-LABEL: test_intrinsic_constantexpr_offset:
81+
; CHECK: ; %bb.0:
82+
; CHECK-NEXT: adrp x16, _g@GOTPAGE
83+
; CHECK-NEXT: ldr x16, [x16, _g@GOTPAGEOFF]
84+
; CHECK-NEXT: add x16, x16, #16
85+
; CHECK-NEXT: mov x17, #56 ; =0x38
86+
; CHECK-NEXT: pacda x16, x17
87+
; CHECK-NEXT: mov x0, x16
88+
; CHECK-NEXT: ret
89+
%tmp0 = call i64 @llvm.ptrauth.sign(i64 ptrtoint (i8* getelementptr (i8, i8* bitcast (i32* @g to i8*), i64 16) to i64), i32 2, i64 56)
90+
%tmp1 = inttoptr i64 %tmp0 to i8*
91+
ret i8* %tmp1
92+
}
93+
94+
define i8* @test_intrinsic_constantexpr_offset_neg() {
95+
; CHECK-LABEL: test_intrinsic_constantexpr_offset_neg:
96+
; CHECK: ; %bb.0:
97+
; CHECK-NEXT: adrp x16, _g@GOTPAGE
98+
; CHECK-NEXT: ldr x16, [x16, _g@GOTPAGEOFF]
99+
; CHECK-NEXT: mov x17, #1 ; =0x1
100+
; CHECK-NEXT: movk x17, #32769, lsl #16
101+
; CHECK-NEXT: add x16, x16, x17
102+
; CHECK-NEXT: mov x17, #56 ; =0x38
103+
; CHECK-NEXT: pacda x16, x17
104+
; CHECK-NEXT: mov x0, x16
105+
; CHECK-NEXT: ret
106+
%tmp0 = call i64 @llvm.ptrauth.sign(i64 ptrtoint (i8* getelementptr (i8, i8* bitcast (i32* @g to i8*), i64 add (i64 2147483648, i64 65537)) to i64), i32 2, i64 56)
107+
%tmp1 = inttoptr i64 %tmp0 to i8*
108+
ret i8* %tmp1
109+
}
110+
111+
define i8* @test_intrinsic_non_constant(i8* %arg0) {
112+
; CHECK-LABEL: test_intrinsic_non_constant:
113+
; CHECK: ; %bb.0:
114+
; CHECK-NEXT: mov w8, #56 ; =0x38
115+
; CHECK-NEXT: pacda x0, x8
116+
; CHECK-NEXT: ret
117+
%tmp0 = ptrtoint i8* %arg0 to i64
118+
%tmp1 = call i64 @llvm.ptrauth.sign(i64 %tmp0, i32 2, i64 56)
119+
%tmp2 = inttoptr i64 %tmp1 to i8*
120+
ret i8* %tmp2
121+
}
122+
123+
define i8* @test_intrinsic_blend_addr_disc(i8* %arg0) {
124+
; CHECK-LABEL: test_intrinsic_blend_addr_disc:
125+
; CHECK: ; %bb.0:
126+
; CHECK-NEXT: adrp x16, _g@GOTPAGE
127+
; CHECK-NEXT: ldr x16, [x16, _g@GOTPAGEOFF]
128+
; CHECK-NEXT: mov x17, x0
129+
; CHECK-NEXT: movk x17, #23, lsl #48
130+
; CHECK-NEXT: pacda x16, x17
131+
; CHECK-NEXT: mov x0, x16
132+
; CHECK-NEXT: ret
133+
%tmp0 = ptrtoint i32* @g to i64
134+
%tmp1 = ptrtoint i8* %arg0 to i64
135+
%tmp2 = call i64 @llvm.ptrauth.blend(i64 %tmp1, i64 23)
136+
%tmp3 = call i64 @llvm.ptrauth.sign(i64 %tmp0, i32 2, i64 %tmp2)
137+
%tmp4 = inttoptr i64 %tmp3 to i8*
138+
ret i8* %tmp4
139+
}
140+
141+
define i8* @test_intrinsic_addr_disc(i8* %arg0) {
142+
; CHECK-LABEL: test_intrinsic_addr_disc:
143+
; CHECK: ; %bb.0:
144+
; CHECK-NEXT: adrp x16, _g@GOTPAGE
145+
; CHECK-NEXT: ldr x16, [x16, _g@GOTPAGEOFF]
146+
; CHECK-NEXT: pacda x16, x0
147+
; CHECK-NEXT: mov x0, x16
148+
; CHECK-NEXT: ret
149+
%tmp0 = ptrtoint i32* @g to i64
150+
%tmp1 = ptrtoint i8* %arg0 to i64
151+
%tmp2 = call i64 @llvm.ptrauth.sign(i64 %tmp0, i32 2, i64 %tmp1)
152+
%tmp3 = inttoptr i64 %tmp2 to i8*
153+
ret i8* %tmp3
154+
}
155+
156+
define i8* @test_intrinsic_blend_addr_disc_cross_bb(i8* %arg0, i8* %arg1, i1 %arg2) {
157+
; CHECK-LABEL: test_intrinsic_blend_addr_disc_cross_bb:
158+
; CHECK: ; %bb.0: ; %common.ret
159+
; CHECK-NEXT: mov x8, x0
160+
; CHECK-NEXT: movk x8, #23, lsl #48
161+
; CHECK-NEXT: Lloh0:
162+
; CHECK-NEXT: adrp x9, _g2@GOTPAGE
163+
; CHECK-NEXT: Lloh1:
164+
; CHECK-NEXT: ldr x9, [x9, _g2@GOTPAGEOFF]
165+
; CHECK-NEXT: Lloh2:
166+
; CHECK-NEXT: adrp x10, _g@GOTPAGE
167+
; CHECK-NEXT: Lloh3:
168+
; CHECK-NEXT: ldr x10, [x10, _g@GOTPAGEOFF]
169+
; CHECK-NEXT: tst w2, #0x1
170+
; CHECK-NEXT: csel x0, x10, x9, ne
171+
; CHECK-NEXT: pacda x0, x8
172+
; CHECK-NEXT: ret
173+
; CHECK-NEXT: .loh AdrpLdrGot Lloh2, Lloh3
174+
; CHECK-NEXT: .loh AdrpLdrGot Lloh0, Lloh1
175+
%tmp0 = ptrtoint i8* %arg0 to i64
176+
%tmp1 = call i64 @llvm.ptrauth.blend(i64 %tmp0, i64 23)
177+
br i1 %arg2, label %bb1, label %bb2
178+
179+
bb1:
180+
%tmp2 = ptrtoint i32* @g to i64
181+
%tmp3 = call i64 @llvm.ptrauth.sign(i64 %tmp2, i32 2, i64 %tmp1)
182+
%tmp4 = inttoptr i64 %tmp3 to i8*
183+
ret i8* %tmp4
184+
185+
bb2:
186+
%tmp5 = ptrtoint i32* @g2 to i64
187+
%tmp6 = call i64 @llvm.ptrauth.sign(i64 %tmp5, i32 2, i64 %tmp1)
188+
%tmp7 = inttoptr i64 %tmp6 to i8*
189+
ret i8* %tmp7
190+
}
191+
192+
define i8* @test_intrinsic_function() {
193+
; CHECK-LABEL: test_intrinsic_function:
194+
; CHECK: ; %bb.0:
195+
; CHECK-NEXT: adrp x16, _test_intrinsic_function@PAGE
196+
; CHECK-NEXT: add x16, x16, _test_intrinsic_function@PAGEOFF
197+
; CHECK-NEXT: mov x17, #56 ; =0x38
198+
; CHECK-NEXT: pacib x16, x17
199+
; CHECK-NEXT: mov x0, x16
200+
; CHECK-NEXT: ret
201+
%tmp0 = call i64 @llvm.ptrauth.sign(i64 ptrtoint (i8* ()* @test_intrinsic_function to i64), i32 1, i64 56)
202+
%tmp1 = inttoptr i64 %tmp0 to i8*
203+
ret i8* %tmp1
204+
}
205+
206+
define i8* @test_intrinsic_constant_int() {
207+
; CHECK-LABEL: test_intrinsic_constant_int:
208+
; CHECK: ; %bb.0:
209+
; CHECK-NEXT: mov x0, #0 ; =0x0
210+
; CHECK-NEXT: mov w8, #56 ; =0x38
211+
; CHECK-NEXT: pacda x0, x8
212+
; CHECK-NEXT: ret
213+
%tmp0 = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 56)
214+
%tmp1 = inttoptr i64 %tmp0 to i8*
215+
ret i8* %tmp1
216+
}
217+
218+
define i8* @test_intrinsic_constantexpr_offset_strong_def() {
219+
; CHECK-LABEL: test_intrinsic_constantexpr_offset_strong_def:
220+
; CHECK: ; %bb.0:
221+
; CHECK-NEXT: adrp x16, _g_strong_def@PAGE
222+
; CHECK-NEXT: add x16, x16, _g_strong_def@PAGEOFF
223+
; CHECK-NEXT: add x16, x16, #2
224+
; CHECK-NEXT: mov x17, #56 ; =0x38
225+
; CHECK-NEXT: pacda x16, x17
226+
; CHECK-NEXT: mov x0, x16
227+
; CHECK-NEXT: ret
228+
%tmp0 = call i64 @llvm.ptrauth.sign(i64 ptrtoint (i8* getelementptr (i8, i8* bitcast (i32* @g_strong_def to i8*), i64 2) to i64), i32 2, i64 56)
229+
%tmp1 = inttoptr i64 %tmp0 to i8*
230+
ret i8* %tmp1
231+
}
232+
233+
declare i64 @llvm.ptrauth.sign(i64, i32, i64)
234+
declare i64 @llvm.ptrauth.blend(i64, i64)
235+
236+
@g = external global i32
237+
@g2 = external global i32
238+
239+
@g_weak = extern_weak global i32
240+
241+
@g_strong_def = constant i32 42

0 commit comments

Comments
 (0)