LLDB mainline
InjectPointerSigningFixups.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// On arm64e, Clang emits ConstantPtrAuth expressions in global initializers
10// to represent signed pointers. These are normally resolved by the dynamic
11// linker, but LLDB's JIT does not run the linker, so they must be resolved
12// manually. This pass replaces each ConstantPtrAuth in a global initializer
13// with the unsigned pointer and emits a constructor function that signs the
14// pointer at runtime using the ptrauth intrinsics.
15//
16// Example: given "static int (*fp)(int, int) = &mul;", Clang emits:
17//
18// @fp = internal global ptr ptrauth (ptr @mul, i32 0)
19//
20// This pass transforms it into:
21//
22// @fp = internal global ptr @mul
23// @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }]
24// [{ i32, ptr, ptr } { i32 0, ptr @ptrauth.sign, ptr null }]
25//
26// define internal void @ptrauth.sign() {
27// %1 = load ptr, ptr @fp, align 8
28// %2 = ptrtoint ptr %1 to i64
29// %3 = call i64 @llvm.ptrauth.sign(i64 %2, i32 0, i64 0)
30// %4 = inttoptr i64 %3 to ptr
31// store ptr %4, ptr @fp, align 8
32// ret void
33// }
34//
35//===----------------------------------------------------------------------===//
36
38#include "llvm/IR/Constants.h"
39#include "llvm/IR/Function.h"
40#include "llvm/IR/IRBuilder.h"
41#include "llvm/IR/Instructions.h"
42#include "llvm/IR/Module.h"
43#include "llvm/Support/raw_ostream.h"
44#include "llvm/TargetParser/Triple.h"
45
46using namespace llvm;
47
48namespace {
49struct ExprStep {
50 ConstantExpr *CE;
51 unsigned OperandIdx;
52};
53
54/// Used to keep track of ConstantPtrAuth expressions in global initializers.
55struct GlobalInitPtrAuthFixup {
56 GlobalVariable *GV;
57 ConstantPtrAuth *CPA;
58 /// ConstantAggregate types are walekd via GEP indices.
59 SmallVector<unsigned> GEPPath;
60 /// ConstantExpr types are traversed via ExprStep (ConstantExpr + Operand
61 /// index).
62 SmallVector<ExprStep> ExprPath;
63 GlobalInitPtrAuthFixup(GlobalVariable *GV, ConstantPtrAuth *CPA,
64 const SmallVectorImpl<unsigned> &GEPPath,
65 const SmallVectorImpl<ExprStep> &ExprPath)
66 : GV(GV), CPA(CPA), GEPPath(GEPPath.begin(), GEPPath.end()),
67 ExprPath(ExprPath.begin(), ExprPath.end()) {}
68};
69
70/// Used to keep track of extern_weak ConstantPtrAuth expressions inlined in
71/// instructions.
72struct WeakInlinePtrAuthFixup {
73 Instruction *Inst;
74 unsigned OperandIdx;
75 ConstantPtrAuth *CPA;
76};
77} // namespace
78
79/// Recursively walk a constant looking for ConstantPtrAuth expressions in
80/// global initializers.
81static void
82findGlobalInitPtrAuth(Constant *C, GlobalVariable &GV,
86 if (auto *CPA = dyn_cast<ConstantPtrAuth>(C)) {
87 Fixups.emplace_back(&GV, CPA, GEPPath, ExprPath);
88 return;
89 }
90 if (isa<ConstantAggregate>(C)) {
91 for (unsigned I = 0, E = C->getNumOperands(); I != E; ++I) {
92 if (auto *COp = dyn_cast<Constant>(C->getOperand(I))) {
93 GEPPath.push_back(I);
94 findGlobalInitPtrAuth(COp, GV, GEPPath, ExprPath, Fixups);
95 GEPPath.pop_back();
96 }
97 }
98 return;
99 }
100
101 if (auto *CE = dyn_cast<ConstantExpr>(C)) {
102 for (unsigned I = 0, E = C->getNumOperands(); I != E; ++I) {
103 if (auto *COp = dyn_cast<Constant>(C->getOperand(I))) {
104 ExprPath.push_back({CE, I});
105 findGlobalInitPtrAuth(COp, GV, GEPPath, ExprPath, Fixups);
106 ExprPath.pop_back();
107 }
108 }
109 }
110}
111
112namespace lldb_private {
113
114Error InjectPointerSigningFixupCode(llvm::Module &M,
115 ExecutionPolicy execution_policy) {
116 // If we cannot execute fixups, don't insert them.
117 if (execution_policy == eExecutionPolicyNever)
118 return Error::success();
119
120 llvm::Triple T(M.getTargetTriple());
121
122 // Bail out if we don't need pointer signing fixups.
123 if (!T.isArm64e())
124 return Error::success();
125
126 // Collect all ConstantPtrAuth expressions in global initializers.
127 SmallVector<GlobalInitPtrAuthFixup> GlobalInitFixups;
128 for (auto &G : M.globals()) {
129 if (!G.hasInitializer())
130 continue;
131 SmallVector<unsigned> GEPPath;
132 SmallVector<ExprStep> ExprPath;
133 findGlobalInitPtrAuth(G.getInitializer(), G, GEPPath, ExprPath,
134 GlobalInitFixups);
135 }
136
137 // Collect all inline ConstantPtrAuth expressions for extern_weak globals in
138 // functions.
139 SmallVector<WeakInlinePtrAuthFixup> WeakInlineFixups;
140 for (auto &F : M.functions()) {
141 for (auto &BB : F) {
142 for (auto &Inst : BB) {
143 for (unsigned OpIdx = 0, E = Inst.getNumOperands(); OpIdx != E;
144 OpIdx++) {
145 auto *CPA = dyn_cast<ConstantPtrAuth>(Inst.getOperand(OpIdx));
146 if (!CPA)
147 continue;
148 auto *GV = dyn_cast<GlobalValue>(CPA->getPointer());
149 if (!GV || !GV->hasExternalWeakLinkage())
150 continue;
151 WeakInlineFixups.push_back({&Inst, OpIdx, CPA});
152 }
153 }
154 }
155 }
156
157 if (GlobalInitFixups.empty() && WeakInlineFixups.empty())
158 return Error::success();
159
160 // Set up types and intrinsics.
161 auto &Ctx = M.getContext();
162 Type *Int32Ty = Type::getInt32Ty(Ctx);
163 Type *IntPtrTy = Type::getInt64Ty(Ctx);
164 Function *BlendIntrinsic =
165 Intrinsic::getOrInsertDeclaration(&M, Intrinsic::ptrauth_blend);
166 Function *SignIntrinsic =
167 Intrinsic::getOrInsertDeclaration(&M, Intrinsic::ptrauth_sign);
168
169 // Create the fixup function.
170 Function *FixupFn =
171 Function::Create(FunctionType::get(Type::getVoidTy(Ctx), false),
172 GlobalValue::InternalLinkage, "ptrauth.sign", &M);
173 FixupFn->insert(FixupFn->end(), BasicBlock::Create(Ctx));
174 IRBuilder<> B(&FixupFn->back());
175
176 for (auto &Fixup : GlobalInitFixups) {
177 GlobalVariable *GV = Fixup.GV;
178 ConstantPtrAuth *CPA = Fixup.CPA;
179
180 // Null pointers must remain zero.
181 if (isa<ConstantPointerNull>(CPA->getPointer())) {
182 CPA->replaceAllUsesWith(CPA->getPointer());
183 continue;
184 }
185
186 // Build a GEP to the location of the ConstantPtrAuth (or the expression
187 // path to the ConstantPtrAuth) within the global.
188 Value *Loc;
189 if (Fixup.GEPPath.empty()) {
190 Loc = GV;
191 } else {
192 SmallVector<Value *> GEPValues;
193 GEPValues.push_back(ConstantInt::get(Int32Ty, 0));
194 for (unsigned Idx : Fixup.GEPPath)
195 GEPValues.push_back(ConstantInt::get(Int32Ty, Idx));
196 Loc = B.CreateGEP(GV->getValueType(), GV, GEPValues);
197 }
198
199 Type *PtrTy = CPA->getType();
200
201 // Compute the discriminator, blending with the address if needed.
202 Value *Disc = CPA->getDiscriminator();
203 if (CPA->hasAddressDiscriminator())
204 Disc = B.CreateCall(BlendIntrinsic,
205 {B.CreatePointerCast(Loc, IntPtrTy), Disc});
206
207 if (!Fixup.ExprPath.empty()) {
208 // The CPA is wrapped in a ConstantExpr chain. Sign the CPA's pointer
209 // directly and re-evaluate the expr chain.
210 Value *SignedPtr = B.CreateCall(
211 SignIntrinsic, {B.CreatePointerCast(CPA->getPointer(), IntPtrTy),
212 CPA->getKey(), Disc});
213 Value *Result = B.CreateIntToPtr(SignedPtr, PtrTy);
214
215 for (auto &Step : llvm::reverse(Fixup.ExprPath)) {
216 Instruction *I = Step.CE->getAsInstruction();
217 I->setOperand(Step.OperandIdx, Result);
218 B.Insert(I);
219 Result = I;
220 }
221 B.CreateStore(Result, Loc);
222 } else {
223 // There is no expression chain. Load and sign the pointer directly.
224 Value *RawPtr = B.CreateLoad(PtrTy, Loc);
225 Value *SignedPtr =
226 B.CreateCall(SignIntrinsic, {B.CreatePointerCast(RawPtr, IntPtrTy),
227 CPA->getKey(), Disc});
228 B.CreateStore(B.CreateBitOrPointerCast(SignedPtr, PtrTy), Loc);
229 }
230 // Replace the ConstantPtrAuth in the initializer with the unsigned pointer.
231 CPA->replaceAllUsesWith(CPA->getPointer());
232 }
233
234 // Close off the fixup function.
235 B.CreateRetVoid();
236
237 // Rewrite extern_weak inline CPA operands.
238 for (auto &Fixup : WeakInlineFixups) {
239 IRBuilder<> B(Fixup.Inst);
240 ConstantPtrAuth *CPA = Fixup.CPA;
241 Type *PtrTy = CPA->getType();
242
243 Value *Disc = CPA->getDiscriminator();
244 if (CPA->hasAddressDiscriminator()) {
245 Value *AddrDisc =
246 B.CreatePointerCast(CPA->getAddrDiscriminator(), IntPtrTy);
247 Disc = B.CreateCall(BlendIntrinsic, {AddrDisc, Disc});
248 }
249
250 // Signing a pointer value of `0x0` yields a non-null but invalid pointer.
251 // We'll emit a runtime guard around signing the pointer.
252 Value *RawPtr = B.CreatePtrToInt(CPA->getPointer(), IntPtrTy);
253 Value *NullCheck = B.CreateIsNull(RawPtr);
254 Value *SignedPtr =
255 B.CreateCall(SignIntrinsic, {RawPtr, CPA->getKey(), Disc});
256 Value *Result =
257 B.CreateSelect(NullCheck, Constant::getNullValue(IntPtrTy), SignedPtr);
258 Fixup.Inst->setOperand(Fixup.OperandIdx, B.CreateIntToPtr(Result, PtrTy));
259 }
260
261 // Update the global ctors list to call the pointer fixup function first.
262 auto *UInt8PtrTy = PointerType::getUnqual(Ctx);
263 StructType *CtorType =
264 StructType::get(Ctx, {Int32Ty, FixupFn->getType(), UInt8PtrTy});
265 Constant *PtrFixupCtor =
266 ConstantStruct::get(CtorType, {ConstantInt::get(Int32Ty, 0), FixupFn,
267 Constant::getNullValue(UInt8PtrTy)});
268
269 const char *LLVMGlobalCtorsName = "llvm.global_ctors";
270 GlobalVariable *OldCtorList = M.getNamedGlobal(LLVMGlobalCtorsName);
271 SmallVector<Constant *> CtorListArgs;
272 CtorListArgs.push_back(PtrFixupCtor);
273
274 if (OldCtorList) {
275 // If the old ctors list has any uses then bail out: we do not know how to
276 // rewrite them.
277 if (OldCtorList->getNumUses() != 0) {
278 std::string ErrStr;
279 raw_string_ostream S(ErrStr);
280 S << "Global ctors variable has users, so can not be rewritten to "
281 "include pointer fixups: '"
282 << *OldCtorList << "'";
283 return make_error<StringError>(S.str(), inconvertibleErrorCode());
284 }
285
286 for (auto &Op : OldCtorList->getInitializer()->operands())
287 CtorListArgs.push_back(cast<Constant>(Op.get()));
288 }
289
290 ArrayType *CtorListType = ArrayType::get(CtorType, CtorListArgs.size());
291 Constant *CtorListInit = ConstantArray::get(CtorListType, CtorListArgs);
292
293 GlobalVariable *NewCtorList = new GlobalVariable(
294 M, CtorListType, false, GlobalValue::AppendingLinkage, CtorListInit);
295
296 if (OldCtorList) {
297 NewCtorList->takeName(OldCtorList);
298 OldCtorList->eraseFromParent();
299 } else
300 NewCtorList->setName(LLVMGlobalCtorsName);
301
302 return Error::success();
303}
304
305} // namespace lldb_private
static void findGlobalInitPtrAuth(Constant *C, GlobalVariable &GV, SmallVectorImpl< unsigned > &GEPPath, SmallVectorImpl< ExprStep > &ExprPath, SmallVectorImpl< GlobalInitPtrAuthFixup > &Fixups)
Recursively walk a constant looking for ConstantPtrAuth expressions in global initializers.
A class that describes a function.
Definition Function.h:392
A class that represents a running process on the host machine.
Error InjectPointerSigningFixupCode(llvm::Module &M, ExecutionPolicy execution_policy)
ExecutionPolicy
Expression execution policies.