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/Module.h"
42#include "llvm/Support/raw_ostream.h"
43#include "llvm/TargetParser/Triple.h"
44
45using namespace llvm;
46
47namespace {
48struct PtrAuthFixup {
49 GlobalVariable *GV;
50 ConstantPtrAuth *CPA;
51 SmallVector<unsigned> Indices;
52 PtrAuthFixup(GlobalVariable *GV, ConstantPtrAuth *CPA,
53 const SmallVectorImpl<unsigned> &Indices)
54 : GV(GV), CPA(CPA), Indices(Indices.begin(), Indices.end()) {}
55};
56} // namespace
57
58/// Recursively walk a constant looking for ConstantPtrAuth expressions.
59/// When found, record the global variable containing the ConstantPtrAuth and
60/// the index path to reach it within the initializer.
61static void findPtrAuth(Constant *C, GlobalVariable &GV,
64 if (auto *CPA = dyn_cast<ConstantPtrAuth>(C)) {
65 Fixups.emplace_back(&GV, CPA, Indices);
66 return;
67 }
68 for (unsigned I = 0, E = C->getNumOperands(); I != E; ++I) {
69 if (auto *COp = dyn_cast<Constant>(C->getOperand(I))) {
70 Indices.push_back(I);
71 findPtrAuth(COp, GV, Indices, Fixups);
72 Indices.pop_back();
73 }
74 }
75}
76
77namespace lldb_private {
78
79Error InjectPointerSigningFixupCode(llvm::Module &M,
80 ExecutionPolicy execution_policy) {
81 // If we cannot execute fixups, don't insert them.
82 if (execution_policy == eExecutionPolicyNever)
83 return Error::success();
84
85 llvm::Triple T(M.getTargetTriple());
86
87 // Bail out if we don't need pointer signing fixups.
88 if (!T.isArm64e())
89 return Error::success();
90
91 // Collect all ConstantPtrAuth expressions in global initializers.
92 SmallVector<PtrAuthFixup> Fixups;
93 for (auto &G : M.globals()) {
94 if (!G.hasInitializer())
95 continue;
96 SmallVector<unsigned> Indices;
97 findPtrAuth(G.getInitializer(), G, Indices, Fixups);
98 }
99
100 if (Fixups.empty())
101 return Error::success();
102
103 // Set up types and intrinsics.
104 auto &Ctx = M.getContext();
105 Type *Int32Ty = Type::getInt32Ty(Ctx);
106 Type *IntPtrTy = Type::getInt64Ty(Ctx);
107 Function *BlendIntrinsic =
108 Intrinsic::getOrInsertDeclaration(&M, Intrinsic::ptrauth_blend);
109 Function *SignIntrinsic =
110 Intrinsic::getOrInsertDeclaration(&M, Intrinsic::ptrauth_sign);
111
112 // Create the fixup function.
113 Function *FixupFn =
114 Function::Create(FunctionType::get(Type::getVoidTy(Ctx), false),
115 GlobalValue::InternalLinkage, "ptrauth.sign", &M);
116 FixupFn->insert(FixupFn->end(), BasicBlock::Create(Ctx));
117 IRBuilder<> B(&FixupFn->back());
118
119 for (auto &Fixup : Fixups) {
120 GlobalVariable *GV = Fixup.GV;
121 ConstantPtrAuth *CPA = Fixup.CPA;
122
123 // Null pointers must remain zero.
124 if (isa<ConstantPointerNull>(CPA->getPointer())) {
125 CPA->replaceAllUsesWith(CPA->getPointer());
126 continue;
127 }
128
129 // Build a GEP to the location of the ConstantPtrAuth within the global.
130 Value *Loc;
131 if (Fixup.Indices.empty()) {
132 Loc = GV;
133 } else {
134 SmallVector<Value *> GEPIndices;
135 GEPIndices.push_back(ConstantInt::get(Int32Ty, 0));
136 for (unsigned Idx : Fixup.Indices)
137 GEPIndices.push_back(ConstantInt::get(Int32Ty, Idx));
138 Loc = B.CreateGEP(GV->getValueType(), GV, GEPIndices);
139 }
140
141 Type *PtrTy = CPA->getType();
142
143 // Load the raw (unsigned) pointer.
144 Value *RawPtr = B.CreateLoad(PtrTy, Loc);
145
146 // Compute the discriminator, blending with the address if needed.
147 Value *Disc = CPA->getDiscriminator();
148 if (CPA->hasAddressDiscriminator())
149 Disc = B.CreateCall(BlendIntrinsic,
150 {B.CreatePointerCast(Loc, IntPtrTy), Disc});
151
152 // Sign the pointer.
153 Value *SignedPtr =
154 B.CreateCall(SignIntrinsic, {B.CreatePointerCast(RawPtr, IntPtrTy),
155 CPA->getKey(), Disc});
156
157 // Store the signed pointer back.
158 B.CreateStore(B.CreateBitOrPointerCast(SignedPtr, PtrTy), Loc);
159
160 // Replace the ConstantPtrAuth in the initializer with the unsigned pointer.
161 CPA->replaceAllUsesWith(CPA->getPointer());
162 }
163
164 // Close off the fixup function.
165 B.CreateRetVoid();
166
167 // Update the global ctors list to call the pointer fixup function first.
168 auto *UInt8PtrTy = PointerType::getUnqual(Ctx);
169 StructType *CtorType =
170 StructType::get(Ctx, {Int32Ty, FixupFn->getType(), UInt8PtrTy});
171 Constant *PtrFixupCtor =
172 ConstantStruct::get(CtorType, {ConstantInt::get(Int32Ty, 0), FixupFn,
173 Constant::getNullValue(UInt8PtrTy)});
174
175 const char *LLVMGlobalCtorsName = "llvm.global_ctors";
176 GlobalVariable *OldCtorList = M.getNamedGlobal(LLVMGlobalCtorsName);
177 SmallVector<Constant *> CtorListArgs;
178 CtorListArgs.push_back(PtrFixupCtor);
179
180 if (OldCtorList) {
181 // If the old ctors list has any uses then bail out: we do not know how to
182 // rewrite them.
183 if (OldCtorList->getNumUses() != 0) {
184 std::string ErrStr;
185 raw_string_ostream S(ErrStr);
186 S << "Global ctors variable has users, so can not be rewritten to "
187 "include pointer fixups: '"
188 << *OldCtorList << "'";
189 return make_error<StringError>(S.str(), inconvertibleErrorCode());
190 }
191
192 for (auto &Op : OldCtorList->getInitializer()->operands())
193 CtorListArgs.push_back(cast<Constant>(Op.get()));
194 }
195
196 ArrayType *CtorListType = ArrayType::get(CtorType, CtorListArgs.size());
197 Constant *CtorListInit = ConstantArray::get(CtorListType, CtorListArgs);
198
199 GlobalVariable *NewCtorList = new GlobalVariable(
200 M, CtorListType, false, GlobalValue::AppendingLinkage, CtorListInit);
201
202 if (OldCtorList) {
203 NewCtorList->takeName(OldCtorList);
204 OldCtorList->eraseFromParent();
205 } else
206 NewCtorList->setName(LLVMGlobalCtorsName);
207
208 return Error::success();
209}
210
211} // namespace lldb_private
static void findPtrAuth(Constant *C, GlobalVariable &GV, SmallVectorImpl< unsigned > &Indices, SmallVectorImpl< PtrAuthFixup > &Fixups)
Recursively walk a constant looking for ConstantPtrAuth expressions.
A class that describes a function.
Definition Function.h:400
A class that represents a running process on the host machine.
Error InjectPointerSigningFixupCode(llvm::Module &M, ExecutionPolicy execution_policy)
ExecutionPolicy
Expression execution policies.