LLVM 24.0.0git
ShadowStackGCLowering.cpp
Go to the documentation of this file.
1//===- ShadowStackGCLowering.cpp - Custom lowering for shadow-stack gc ----===//
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// This file contains the custom lowering code required by the shadow-stack GC
10// strategy.
11//
12// This pass implements the code transformation described in this paper:
13// "Accurate Garbage Collection in an Uncooperative Environment"
14// Fergus Henderson, ISMM, 2002
15//
16//===----------------------------------------------------------------------===//
17
23#include "llvm/CodeGen/Passes.h"
24#include "llvm/IR/BasicBlock.h"
25#include "llvm/IR/Constant.h"
26#include "llvm/IR/Constants.h"
27#include "llvm/IR/DataLayout.h"
29#include "llvm/IR/Dominators.h"
30#include "llvm/IR/Function.h"
31#include "llvm/IR/GlobalValue.h"
33#include "llvm/IR/IRBuilder.h"
36#include "llvm/IR/Intrinsics.h"
37#include "llvm/IR/Module.h"
38#include "llvm/IR/Type.h"
39#include "llvm/IR/Value.h"
41#include "llvm/Pass.h"
45#include <cassert>
46#include <optional>
47#include <utility>
48#include <vector>
49
50using namespace llvm;
51
52#define DEBUG_TYPE "shadow-stack-gc-lowering"
53
54namespace {
55
56class ShadowStackGCLoweringImpl {
57 /// RootChain - This is the global linked-list that contains the chain of GC
58 /// roots.
59 GlobalVariable *Head = nullptr;
60
61 StructType *FrameMapTy = nullptr;
62
63 /// Roots - GC roots in the current function. Each is a pair of the
64 /// intrinsic call and its corresponding alloca.
65 std::vector<std::pair<CallInst *, AllocaInst *>> Roots;
66
67 /// RootOffsets - Byte offsets and sizes of each root within the frame.
68 /// Each element is a pair of (offset, size).
69 std::vector<std::pair<uint64_t, uint64_t>> RootOffsets;
70
71public:
72 ShadowStackGCLoweringImpl() = default;
73
74 bool doInitialization(Module &M);
76
77private:
78 bool IsNullValue(Value *V);
79 Constant *GetFrameMap(Function &F, uint64_t FrameSizeInPtrs);
80 std::pair<uint64_t, Align> ComputeFrameLayout(Function &F);
81 void CollectRoots(Function &F);
82};
83
84class ShadowStackGCLowering : public FunctionPass {
85 ShadowStackGCLoweringImpl Impl;
86
87public:
88 static char ID;
89
90 ShadowStackGCLowering();
91
92 bool doInitialization(Module &M) override { return Impl.doInitialization(M); }
93 void getAnalysisUsage(AnalysisUsage &AU) const override {
95 }
96 bool runOnFunction(Function &F) override {
97 std::optional<DomTreeUpdater> DTU;
98 if (auto *DTWP = getAnalysisIfAvailable<DominatorTreeWrapperPass>())
99 DTU.emplace(DTWP->getDomTree(), DomTreeUpdater::UpdateStrategy::Lazy);
100 return Impl.runOnFunction(F, DTU ? &*DTU : nullptr);
101 }
102};
103
104} // end anonymous namespace
105
108 auto &Map = MAM.getResult<CollectorMetadataAnalysis>(M);
109 if (!Map.contains("shadow-stack"))
110 return PreservedAnalyses::all();
111
112 ShadowStackGCLoweringImpl Impl;
113 bool Changed = Impl.doInitialization(M);
114 for (auto &F : M) {
115 auto &FAM =
116 MAM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
117 auto *DT = FAM.getCachedResult<DominatorTreeAnalysis>(F);
118 DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Lazy);
119 Changed |= Impl.runOnFunction(F, DT ? &DTU : nullptr);
120 }
121
122 if (!Changed)
123 return PreservedAnalyses::all();
126 return PA;
127}
128
129char ShadowStackGCLowering::ID = 0;
130char &llvm::ShadowStackGCLoweringID = ShadowStackGCLowering::ID;
131
132INITIALIZE_PASS_BEGIN(ShadowStackGCLowering, DEBUG_TYPE,
133 "Shadow Stack GC Lowering", false, false)
136INITIALIZE_PASS_END(ShadowStackGCLowering, DEBUG_TYPE,
137 "Shadow Stack GC Lowering", false, false)
138
139ShadowStackGCLowering::ShadowStackGCLowering() : FunctionPass(ID) {}
140
141Constant *ShadowStackGCLoweringImpl::GetFrameMap(Function &F,
142 uint64_t FrameSizeInPtrs) {
143 // doInitialization creates the abstract type of this value.
144 Type *VoidPtr = PointerType::getUnqual(F.getContext());
145
146 // Truncate the ShadowStackDescriptor if some metadata is null.
147 unsigned NumMeta = 0;
149 for (unsigned I = 0; I != Roots.size(); ++I) {
150 Constant *C = cast<Constant>(Roots[I].first->getArgOperand(1));
151 if (!C->isNullValue())
152 NumMeta = I + 1;
153 Metadata.push_back(C);
154 }
155 Metadata.resize(NumMeta);
156
157 Type *Int32Ty = Type::getInt32Ty(F.getContext());
158
159 Constant *BaseElts[] = {
160 ConstantInt::get(Int32Ty, FrameSizeInPtrs, false),
161 ConstantInt::get(Int32Ty, NumMeta, false),
162 };
163
164 Constant *DescriptorElts[] = {
165 ConstantStruct::get(FrameMapTy, BaseElts),
166 ConstantArray::get(ArrayType::get(VoidPtr, NumMeta), Metadata)};
167
168 Type *EltTys[] = {DescriptorElts[0]->getType(), DescriptorElts[1]->getType()};
169 StructType *STy = StructType::create(EltTys, "gc_map." + utostr(NumMeta));
170
171 Constant *FrameMap = ConstantStruct::get(STy, DescriptorElts);
172
173 // FIXME: Is this actually dangerous as WritingAnLLVMPass.html claims? Seems
174 // that, short of multithreaded LLVM, it should be safe; all that is
175 // necessary is that a simple Module::iterator loop not be invalidated.
176 // Appending to the GlobalVariable list is safe in that sense.
177 //
178 // All of the output passes emit globals last. The ExecutionEngine
179 // explicitly supports adding globals to the module after
180 // initialization.
181 //
182 // Still, if it isn't deemed acceptable, then this transformation needs
183 // to be a ModulePass (which means it cannot be in the 'llc' pipeline
184 // (which uses a FunctionPassManager (which segfaults (not asserts) if
185 // provided a ModulePass))).
186 return new GlobalVariable(*F.getParent(), FrameMap->getType(), true,
188 "__gc_" + F.getName());
189}
190
191std::pair<uint64_t, Align>
192ShadowStackGCLoweringImpl::ComputeFrameLayout(Function &F) {
193 // Compute the layout of the shadow stack frame using byte offsets.
194 // Layout: [Next ptr | Map ptr | Root 0 | Root 1 | ... | Root N]
195
196 const DataLayout &DL = F.getParent()->getDataLayout();
197 uint64_t PtrSize = DL.getPointerSize(0);
198 Align PtrAlign = DL.getPointerABIAlignment(0);
199
200 RootOffsets.clear();
201 Align MaxAlign = PtrAlign;
202
203 // Offset 0: Next pointer
204 // Offset PtrSize: Map pointer
205 uint64_t Offset = 2 * PtrSize;
206
207 // Compute offsets and sizes for each root
208 for (const std::pair<CallInst *, AllocaInst *> &Root : Roots) {
209 AllocaInst *AI = Root.second;
210 std::optional<TypeSize> RootSize = AI->getAllocationSize(DL);
211 if (!RootSize || !RootSize->isFixed())
213 "Intrinsic::gcroot requires a fixed size stack object");
214 uint64_t Size = RootSize->getFixedValue();
215 Align RootAlign = AI->getAlign();
216 MaxAlign = std::max(MaxAlign, RootAlign);
217
218 // Align the offset for this root
219 uint64_t AlignedOffset = alignTo(Offset, RootAlign);
220
221 // Store both offset and size as a pair
222 RootOffsets.push_back({AlignedOffset, Size});
223 Offset = AlignedOffset + Size;
224 }
225
226 // Final frame size, aligned to maximum alignment
227 uint64_t FrameSize = alignTo(Offset, MaxAlign);
228 return {FrameSize, MaxAlign};
229}
230
231/// doInitialization - If this module uses the GC intrinsics, find them now. If
232/// not, exit fast.
233bool ShadowStackGCLoweringImpl::doInitialization(Module &M) {
234 bool Active = false;
235 for (Function &F : M) {
236 if (F.hasGC() && F.getGC() == "shadow-stack") {
237 Active = true;
238 break;
239 }
240 }
241 if (!Active)
242 return false;
243
244 // struct FrameMap {
245 // int32_t NumRoots; // Number of roots in stack frame.
246 // int32_t NumMeta; // Number of metadata descriptors. May be < NumRoots.
247 // void *Meta[]; // May be absent for roots without metadata.
248 // };
249 std::vector<Type *> EltTys;
250 // 32 bits is ok up to a 32GB stack frame. :)
251 EltTys.push_back(Type::getInt32Ty(M.getContext()));
252 // Specifies length of variable length array.
253 EltTys.push_back(Type::getInt32Ty(M.getContext()));
254 FrameMapTy = StructType::create(EltTys, "gc_map");
255
256 // The shadow stack linked list uses opaque pointers.
257 // Each frame is a byte array with: [Next ptr | Map ptr | Roots...]
258 PointerType *StackEntryPtrTy = PointerType::getUnqual(M.getContext());
259
260 // Get the root chain if it already exists.
261 Head = M.getGlobalVariable("llvm_gc_root_chain");
262 if (!Head) {
263 // If the root chain does not exist, insert a new one with linkonce
264 // linkage!
265 Head = new GlobalVariable(
266 M, StackEntryPtrTy, false, GlobalValue::LinkOnceAnyLinkage,
267 Constant::getNullValue(StackEntryPtrTy), "llvm_gc_root_chain");
268 } else if (Head->hasExternalLinkage() && Head->isDeclaration()) {
269 Head->setInitializer(Constant::getNullValue(StackEntryPtrTy));
270 Head->setLinkage(GlobalValue::LinkOnceAnyLinkage);
271 }
272
273 return true;
274}
275
276bool ShadowStackGCLoweringImpl::IsNullValue(Value *V) {
277 if (Constant *C = dyn_cast<Constant>(V))
278 return C->isNullValue();
279 return false;
280}
281
282void ShadowStackGCLoweringImpl::CollectRoots(Function &F) {
283 assert(Roots.empty() && "Not cleaned up?");
284
286
287 for (BasicBlock &BB : F)
288 for (Instruction &I : BB)
290 if (Function *F = CI->getCalledFunction())
291 if (F->getIntrinsicID() == Intrinsic::gcroot) {
292 std::pair<CallInst *, AllocaInst *> Pair = std::make_pair(
293 CI,
294 cast<AllocaInst>(CI->getArgOperand(0)->stripPointerCasts()));
295 if (IsNullValue(CI->getArgOperand(1)))
296 Roots.push_back(Pair);
297 else
298 MetaRoots.push_back(Pair);
299 }
300
301 // Number roots with metadata (usually empty) at the beginning, so that the
302 // FrameMap::Meta array can be elided.
303 Roots.insert(Roots.begin(), MetaRoots.begin(), MetaRoots.end());
304}
305
306/// runOnFunction - Insert code to maintain the shadow stack.
307bool ShadowStackGCLoweringImpl::runOnFunction(Function &F,
308 DomTreeUpdater *DTU) {
309 // Quick exit for functions that do not use the shadow stack GC.
310 if (!F.hasGC() || F.getGC() != "shadow-stack")
311 return false;
312
313 LLVMContext &Context = F.getContext();
314 const DataLayout &DL = F.getParent()->getDataLayout();
315
316 // Find calls to llvm.gcroot.
317 CollectRoots(F);
318
319 // If there are no roots in this function, then there is no need to add a
320 // stack map entry for it.
321 if (Roots.empty())
322 return false;
323
324 // Compute frame layout using byte offsets first.
325 auto [FrameSize, FrameAlign] = ComputeFrameLayout(F);
326
327 // Build the constant map with frame size in pointer-sized units.
328 uint64_t PtrSize = DL.getPointerSize();
329 Value *FrameMap = GetFrameMap(F, FrameSize / PtrSize - 2);
330
331 // Build the shadow stack entry at the very start of the function.
332 BasicBlock::iterator IP = F.getEntryBlock().begin();
333 IRBuilder<> AtEntry(IP->getParent(), IP);
334 Type *Int8Ty = Type::getInt8Ty(Context);
335 AllocaInst *StackEntry = AtEntry.CreateAlloca(
336 ArrayType::get(Int8Ty, FrameSize), nullptr, "gc_frame");
337 StackEntry->setAlignment(FrameAlign);
338
339 AtEntry.SetInsertPointPastAllocas(&F);
340 IP = AtEntry.GetInsertPoint();
341
342 // Initialize the map pointer and load the current head of the shadow stack.
343 Instruction *CurrentHead =
344 AtEntry.CreateLoad(AtEntry.getPtrTy(), Head, "gc_currhead");
345
346 // Map pointer is at offset PtrSize (after the Next pointer)
347 Value *EntryMapPtr = AtEntry.CreatePtrAdd(
348 StackEntry, AtEntry.getInt64(PtrSize), "gc_frame.map");
349 AtEntry.CreateStore(FrameMap, EntryMapPtr);
350
351 // Zero out any padding between roots to ensure deterministic frame contents.
352 // This includes the region after the map pointer up to the first root.
353 uint64_t LastEnd = 2 * PtrSize; // End of Map pointer field
354 assert(RootOffsets.size() == Roots.size());
355 for (unsigned I = 0, E = Roots.size(); I != E; ++I) {
356 auto [RootOffset, RootSize] = RootOffsets[I];
357
358 // Zero any padding before this root
359 if (RootOffset > LastEnd) {
360 Value *PaddingPtr =
361 AtEntry.CreatePtrAdd(StackEntry, AtEntry.getInt64(LastEnd));
362 AtEntry.CreateMemSet(PaddingPtr, AtEntry.getInt8(0), RootOffset - LastEnd,
363 Align(1));
364 }
365
366 // For each root, compute pointer using precomputed offset
367 Value *SlotPtr = AtEntry.CreatePtrAdd(
368 StackEntry, AtEntry.getInt64(RootOffset), "gc_root");
369
370 // And use it in lieu of the alloca.
371 AllocaInst *OriginalAlloca = Roots[I].second;
372 SlotPtr->takeName(OriginalAlloca);
373 OriginalAlloca->replaceAllUsesWith(SlotPtr);
374
375 LastEnd = RootOffset + RootSize;
376 }
377
378 // Zero any padding at the end of the frame
379 if (FrameSize > LastEnd) {
380 Value *PaddingPtr =
381 AtEntry.CreatePtrAdd(StackEntry, AtEntry.getInt64(LastEnd));
382 AtEntry.CreateMemSet(PaddingPtr, AtEntry.getInt8(0), FrameSize - LastEnd,
383 Align(1));
384 }
385
386 // Move past the original stores inserted by GCStrategy::InitRoots. This isn't
387 // really necessary (the collector would never see the intermediate state at
388 // runtime), but it's nicer not to push the half-initialized entry onto the
389 // shadow stack.
390 while (isa<StoreInst>(IP))
391 ++IP;
392 AtEntry.SetInsertPoint(IP->getParent(), IP);
393
394 // Push the entry onto the shadow stack.
395 // Next pointer is at offset 0, so it's just the frame pointer
396 AtEntry.CreateStore(CurrentHead, StackEntry);
397 // The new head value is also the frame pointer (the linked list links to
398 // frame base)
399 AtEntry.CreateStore(StackEntry, Head);
400
401 // For each instruction that escapes...
402 EscapeEnumerator EE(F, "gc_cleanup", /*HandleExceptions=*/true, DTU);
403 while (IRBuilder<> *AtExit = EE.Next()) {
404 // Pop the entry from the shadow stack. Don't reuse CurrentHead from
405 // AtEntry, since that would make the value live for the entire function.
406 // Next pointer is at offset 0, so load from the frame base
407 Value *SavedHead =
408 AtExit->CreateLoad(AtExit->getPtrTy(), StackEntry, "gc_savedhead");
409 AtExit->CreateStore(SavedHead, Head);
410 }
411
412 // Delete the original allocas (which are no longer used) and the intrinsic
413 // calls (which are no longer valid). Doing this last avoids invalidating
414 // iterators.
415 for (std::pair<CallInst *, AllocaInst *> &Root : Roots) {
416 Root.first->eraseFromParent();
417 Root.second->eraseFromParent();
418 }
419
420 Roots.clear();
421 RootOffsets.clear();
422 return true;
423}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
unsigned uint64_t
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
This file contains the declarations for the subclasses of Constant, which represent the different fla...
static bool runOnFunction(Function &F, bool PostInlining)
#define DEBUG_TYPE
Module.h This file contains the declarations for the Module class.
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
FunctionAnalysisManager FAM
ModuleAnalysisManager MAM
#define INITIALIZE_PASS_DEPENDENCY(depName)
Definition PassSupport.h:42
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
Definition PassSupport.h:44
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
Definition PassSupport.h:39
This file defines the SmallVector class.
This file contains some functions that are useful when dealing with strings.
an instruction to allocate memory on the stack
Align getAlign() const
Return the alignment of the memory that is being allocated by the instruction.
LLVM_ABI std::optional< TypeSize > getAllocationSize(const DataLayout &DL) const
Get allocation size in bytes.
void setAlignment(Align Align)
Represent the analysis usage information of a pass.
AnalysisUsage & addPreserved()
Add the specified Pass class to the set of analyses preserved by this pass.
static LLVM_ABI ArrayType * get(Type *ElementType, uint64_t NumElements)
This static method is the primary way to construct an ArrayType.
LLVM Basic Block Representation.
Definition BasicBlock.h:62
InstListType::iterator iterator
Instruction iterators...
Definition BasicBlock.h:170
An analysis pass which caches information about the entire Module.
Definition GCMetadata.h:202
static LLVM_ABI Constant * get(ArrayType *T, ArrayRef< Constant * > V)
static LLVM_ABI Constant * get(StructType *T, ArrayRef< Constant * > V)
This is an important base class in LLVM.
Definition Constant.h:43
static LLVM_ABI Constant * getNullValue(Type *Ty)
Constructor to create a '0' constant of arbitrary type.
A parsed version of the target data layout string in and methods for querying it.
Definition DataLayout.h:64
Analysis pass which computes a DominatorTree.
Definition Dominators.h:241
Legacy analysis pass which computes a DominatorTree.
Definition Dominators.h:277
EscapeEnumerator - This is a little algorithm to find all escape points from a function so that "fina...
FunctionPass class - This class is used to implement most global optimizations.
Definition Pass.h:314
An analysis pass which caches information about the entire Module.
Definition GCMetadata.h:237
@ InternalLinkage
Rename collisions when linking (static functions).
Definition GlobalValue.h:60
@ LinkOnceAnyLinkage
Keep one copy of function when linking (inline)
Definition GlobalValue.h:55
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition IRBuilder.h:2908
A wrapper class for inspecting calls to intrinsic functions.
This is an important class for using LLVM in a threaded context.
Definition LLVMContext.h:68
const MachineFunction * getParent() const
Return the MachineFunction containing this basic block.
const DataLayout & getDataLayout() const
Return the DataLayout attached to the Module associated to this MF.
Root of the metadata hierarchy.
Definition Metadata.h:64
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:68
Class to represent pointers.
static PointerType * getUnqual(LLVMContext &C)
This constructs an opaque pointer to an object in the default address space (address space zero).
A set of analyses that are preserved following a run of a transformation pass.
Definition Analysis.h:112
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition Analysis.h:118
PreservedAnalyses & preserve()
Mark an analysis as preserved.
Definition Analysis.h:132
LLVM_ABI PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM)
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Class to represent struct types.
static LLVM_ABI StructType * create(LLVMContext &Context, StringRef Name)
This creates an identified struct.
Definition Type.cpp:662
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:46
static LLVM_ABI IntegerType * getInt32Ty(LLVMContext &C)
Definition Type.cpp:299
static LLVM_ABI IntegerType * getInt8Ty(LLVMContext &C)
Definition Type.cpp:297
LLVM Value Representation.
Definition Value.h:75
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:257
LLVM_ABI void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
Definition Value.cpp:553
LLVM_ABI void takeName(Value *V)
Transfer the name from V to this value.
Definition Value.cpp:400
Changed
This is an optimization pass for GlobalISel generic memory operations.
@ Offset
Definition DWP.cpp:577
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
InnerAnalysisManagerProxy< FunctionAnalysisManager, Module > FunctionAnalysisManagerModuleProxy
Provide the FunctionAnalysisManager to Module proxy.
std::string utostr(uint64_t X, bool isNeg=false)
LLVM_ABI char & ShadowStackGCLoweringID
ShadowStackGCLowering - Implements the custom lowering mechanism used by the shadow stack GC.
constexpr uint64_t alignTo(uint64_t Size, Align A)
Returns a multiple of A needed to store Size bytes.
Definition Alignment.h:144
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:547
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
AnalysisManager< Module > ModuleAnalysisManager
Convenience typedef for the Module analysis manager.
Definition MIRParser.h:39
LLVM_ABI void reportFatalUsageError(Error Err)
Report a fatal error that does not indicate a bug in LLVM.
Definition Error.cpp:177
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition Alignment.h:39