LLVM 23.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"
46#include <cassert>
47#include <optional>
48#include <utility>
49#include <vector>
50
51using namespace llvm;
52
53#define DEBUG_TYPE "shadow-stack-gc-lowering"
54
55namespace {
56
57class ShadowStackGCLoweringImpl {
58 /// RootChain - This is the global linked-list that contains the chain of GC
59 /// roots.
60 GlobalVariable *Head = nullptr;
61
62 StructType *FrameMapTy = nullptr;
63
64 /// Roots - GC roots in the current function. Each is a pair of the
65 /// intrinsic call and its corresponding alloca.
66 std::vector<std::pair<CallInst *, AllocaInst *>> Roots;
67
68 /// RootOffsets - Byte offsets and sizes of each root within the frame.
69 /// Each element is a pair of (offset, size).
70 std::vector<std::pair<uint64_t, uint64_t>> RootOffsets;
71
72public:
73 ShadowStackGCLoweringImpl() = default;
74
75 bool doInitialization(Module &M);
77
78private:
79 bool IsNullValue(Value *V);
80 Constant *GetFrameMap(Function &F, uint64_t FrameSizeInPtrs);
81 std::pair<uint64_t, Align> ComputeFrameLayout(Function &F);
82 void CollectRoots(Function &F);
83};
84
85class ShadowStackGCLowering : public FunctionPass {
86 ShadowStackGCLoweringImpl Impl;
87
88public:
89 static char ID;
90
91 ShadowStackGCLowering();
92
93 bool doInitialization(Module &M) override { return Impl.doInitialization(M); }
94 void getAnalysisUsage(AnalysisUsage &AU) const override {
96 }
97 bool runOnFunction(Function &F) override {
98 std::optional<DomTreeUpdater> DTU;
99 if (auto *DTWP = getAnalysisIfAvailable<DominatorTreeWrapperPass>())
100 DTU.emplace(DTWP->getDomTree(), DomTreeUpdater::UpdateStrategy::Lazy);
101 return Impl.runOnFunction(F, DTU ? &*DTU : nullptr);
102 }
103};
104
105} // end anonymous namespace
106
109 auto &Map = MAM.getResult<CollectorMetadataAnalysis>(M);
110 if (!Map.contains("shadow-stack"))
111 return PreservedAnalyses::all();
112
113 ShadowStackGCLoweringImpl Impl;
114 bool Changed = Impl.doInitialization(M);
115 for (auto &F : M) {
116 auto &FAM =
117 MAM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
118 auto *DT = FAM.getCachedResult<DominatorTreeAnalysis>(F);
119 DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Lazy);
120 Changed |= Impl.runOnFunction(F, DT ? &DTU : nullptr);
121 }
122
123 if (!Changed)
124 return PreservedAnalyses::all();
127 return PA;
128}
129
130char ShadowStackGCLowering::ID = 0;
131char &llvm::ShadowStackGCLoweringID = ShadowStackGCLowering::ID;
132
133INITIALIZE_PASS_BEGIN(ShadowStackGCLowering, DEBUG_TYPE,
134 "Shadow Stack GC Lowering", false, false)
137INITIALIZE_PASS_END(ShadowStackGCLowering, DEBUG_TYPE,
138 "Shadow Stack GC Lowering", false, false)
139
140FunctionPass *llvm::createShadowStackGCLoweringPass() { return new ShadowStackGCLowering(); }
141
142ShadowStackGCLowering::ShadowStackGCLowering() : FunctionPass(ID) {}
143
144Constant *ShadowStackGCLoweringImpl::GetFrameMap(Function &F,
145 uint64_t FrameSizeInPtrs) {
146 // doInitialization creates the abstract type of this value.
147 Type *VoidPtr = PointerType::getUnqual(F.getContext());
148
149 // Truncate the ShadowStackDescriptor if some metadata is null.
150 unsigned NumMeta = 0;
152 for (unsigned I = 0; I != Roots.size(); ++I) {
153 Constant *C = cast<Constant>(Roots[I].first->getArgOperand(1));
154 if (!C->isNullValue())
155 NumMeta = I + 1;
156 Metadata.push_back(C);
157 }
158 Metadata.resize(NumMeta);
159
160 Type *Int32Ty = Type::getInt32Ty(F.getContext());
161
162 Constant *BaseElts[] = {
163 ConstantInt::get(Int32Ty, FrameSizeInPtrs, false),
164 ConstantInt::get(Int32Ty, NumMeta, false),
165 };
166
167 Constant *DescriptorElts[] = {
168 ConstantStruct::get(FrameMapTy, BaseElts),
169 ConstantArray::get(ArrayType::get(VoidPtr, NumMeta), Metadata)};
170
171 Type *EltTys[] = {DescriptorElts[0]->getType(), DescriptorElts[1]->getType()};
172 StructType *STy = StructType::create(EltTys, "gc_map." + utostr(NumMeta));
173
174 Constant *FrameMap = ConstantStruct::get(STy, DescriptorElts);
175
176 // FIXME: Is this actually dangerous as WritingAnLLVMPass.html claims? Seems
177 // that, short of multithreaded LLVM, it should be safe; all that is
178 // necessary is that a simple Module::iterator loop not be invalidated.
179 // Appending to the GlobalVariable list is safe in that sense.
180 //
181 // All of the output passes emit globals last. The ExecutionEngine
182 // explicitly supports adding globals to the module after
183 // initialization.
184 //
185 // Still, if it isn't deemed acceptable, then this transformation needs
186 // to be a ModulePass (which means it cannot be in the 'llc' pipeline
187 // (which uses a FunctionPassManager (which segfaults (not asserts) if
188 // provided a ModulePass))).
189 return new GlobalVariable(*F.getParent(), FrameMap->getType(), true,
190 GlobalVariable::InternalLinkage, FrameMap,
191 "__gc_" + F.getName());
192}
193
194std::pair<uint64_t, Align>
195ShadowStackGCLoweringImpl::ComputeFrameLayout(Function &F) {
196 // Compute the layout of the shadow stack frame using byte offsets.
197 // Layout: [Next ptr | Map ptr | Root 0 | Root 1 | ... | Root N]
198
199 const DataLayout &DL = F.getParent()->getDataLayout();
200 uint64_t PtrSize = DL.getPointerSize(0);
201 Align PtrAlign = DL.getPointerABIAlignment(0);
202
203 RootOffsets.clear();
204 Align MaxAlign = PtrAlign;
205
206 // Offset 0: Next pointer
207 // Offset PtrSize: Map pointer
208 uint64_t Offset = 2 * PtrSize;
209
210 // Compute offsets and sizes for each root
211 for (const std::pair<CallInst *, AllocaInst *> &Root : Roots) {
212 AllocaInst *AI = Root.second;
213 std::optional<TypeSize> RootSize = AI->getAllocationSize(DL);
214 if (!RootSize || !RootSize->isFixed())
216 "Intrinsic::gcroot requires a fixed size stack object");
217 uint64_t Size = RootSize->getFixedValue();
218 Align RootAlign = AI->getAlign();
219 MaxAlign = std::max(MaxAlign, RootAlign);
220
221 // Align the offset for this root
222 uint64_t AlignedOffset = alignTo(Offset, RootAlign);
223
224 // Store both offset and size as a pair
225 RootOffsets.push_back({AlignedOffset, Size});
226 Offset = AlignedOffset + Size;
227 }
228
229 // Final frame size, aligned to maximum alignment
230 uint64_t FrameSize = alignTo(Offset, MaxAlign);
231 return {FrameSize, MaxAlign};
232}
233
234/// doInitialization - If this module uses the GC intrinsics, find them now. If
235/// not, exit fast.
236bool ShadowStackGCLoweringImpl::doInitialization(Module &M) {
237 bool Active = false;
238 for (Function &F : M) {
239 if (F.hasGC() && F.getGC() == "shadow-stack") {
240 Active = true;
241 break;
242 }
243 }
244 if (!Active)
245 return false;
246
247 // struct FrameMap {
248 // int32_t NumRoots; // Number of roots in stack frame.
249 // int32_t NumMeta; // Number of metadata descriptors. May be < NumRoots.
250 // void *Meta[]; // May be absent for roots without metadata.
251 // };
252 std::vector<Type *> EltTys;
253 // 32 bits is ok up to a 32GB stack frame. :)
254 EltTys.push_back(Type::getInt32Ty(M.getContext()));
255 // Specifies length of variable length array.
256 EltTys.push_back(Type::getInt32Ty(M.getContext()));
257 FrameMapTy = StructType::create(EltTys, "gc_map");
258
259 // The shadow stack linked list uses opaque pointers.
260 // Each frame is a byte array with: [Next ptr | Map ptr | Roots...]
261 PointerType *StackEntryPtrTy = PointerType::getUnqual(M.getContext());
262
263 // Get the root chain if it already exists.
264 Head = M.getGlobalVariable("llvm_gc_root_chain");
265 if (!Head) {
266 // If the root chain does not exist, insert a new one with linkonce
267 // linkage!
268 Head = new GlobalVariable(
269 M, StackEntryPtrTy, false, GlobalValue::LinkOnceAnyLinkage,
270 Constant::getNullValue(StackEntryPtrTy), "llvm_gc_root_chain");
271 } else if (Head->hasExternalLinkage() && Head->isDeclaration()) {
272 Head->setInitializer(Constant::getNullValue(StackEntryPtrTy));
274 }
275
276 return true;
277}
278
279bool ShadowStackGCLoweringImpl::IsNullValue(Value *V) {
280 if (Constant *C = dyn_cast<Constant>(V))
281 return C->isNullValue();
282 return false;
283}
284
285void ShadowStackGCLoweringImpl::CollectRoots(Function &F) {
286 assert(Roots.empty() && "Not cleaned up?");
287
289
290 for (BasicBlock &BB : F)
291 for (Instruction &I : BB)
292 if (IntrinsicInst *CI = dyn_cast<IntrinsicInst>(&I))
293 if (Function *F = CI->getCalledFunction())
294 if (F->getIntrinsicID() == Intrinsic::gcroot) {
295 std::pair<CallInst *, AllocaInst *> Pair = std::make_pair(
296 CI,
297 cast<AllocaInst>(CI->getArgOperand(0)->stripPointerCasts()));
298 if (IsNullValue(CI->getArgOperand(1)))
299 Roots.push_back(Pair);
300 else
301 MetaRoots.push_back(Pair);
302 }
303
304 // Number roots with metadata (usually empty) at the beginning, so that the
305 // FrameMap::Meta array can be elided.
306 Roots.insert(Roots.begin(), MetaRoots.begin(), MetaRoots.end());
307}
308
309/// runOnFunction - Insert code to maintain the shadow stack.
310bool ShadowStackGCLoweringImpl::runOnFunction(Function &F,
311 DomTreeUpdater *DTU) {
312 // Quick exit for functions that do not use the shadow stack GC.
313 if (!F.hasGC() || F.getGC() != "shadow-stack")
314 return false;
315
316 LLVMContext &Context = F.getContext();
317 const DataLayout &DL = F.getParent()->getDataLayout();
318
319 // Find calls to llvm.gcroot.
320 CollectRoots(F);
321
322 // If there are no roots in this function, then there is no need to add a
323 // stack map entry for it.
324 if (Roots.empty())
325 return false;
326
327 // Compute frame layout using byte offsets first.
328 auto [FrameSize, FrameAlign] = ComputeFrameLayout(F);
329
330 // Build the constant map with frame size in pointer-sized units.
331 uint64_t PtrSize = DL.getPointerSize();
332 Value *FrameMap = GetFrameMap(F, FrameSize / PtrSize - 2);
333
334 // Build the shadow stack entry at the very start of the function.
335 BasicBlock::iterator IP = F.getEntryBlock().begin();
336 IRBuilder<> AtEntry(IP->getParent(), IP);
337 Type *Int8Ty = Type::getInt8Ty(Context);
338 AllocaInst *StackEntry = AtEntry.CreateAlloca(
339 ArrayType::get(Int8Ty, FrameSize), nullptr, "gc_frame");
340 StackEntry->setAlignment(FrameAlign);
341
342 AtEntry.SetInsertPointPastAllocas(&F);
343 IP = AtEntry.GetInsertPoint();
344
345 // Initialize the map pointer and load the current head of the shadow stack.
346 Instruction *CurrentHead =
347 AtEntry.CreateLoad(AtEntry.getPtrTy(), Head, "gc_currhead");
348
349 // Map pointer is at offset PtrSize (after the Next pointer)
350 Value *EntryMapPtr = AtEntry.CreatePtrAdd(
351 StackEntry, AtEntry.getInt64(PtrSize), "gc_frame.map");
352 AtEntry.CreateStore(FrameMap, EntryMapPtr);
353
354 // Zero out any padding between roots to ensure deterministic frame contents.
355 // This includes the region after the map pointer up to the first root.
356 uint64_t LastEnd = 2 * PtrSize; // End of Map pointer field
357 assert(RootOffsets.size() == Roots.size());
358 for (unsigned I = 0, E = Roots.size(); I != E; ++I) {
359 auto [RootOffset, RootSize] = RootOffsets[I];
360
361 // Zero any padding before this root
362 if (RootOffset > LastEnd) {
363 Value *PaddingPtr =
364 AtEntry.CreatePtrAdd(StackEntry, AtEntry.getInt64(LastEnd));
365 AtEntry.CreateMemSet(PaddingPtr, AtEntry.getInt8(0), RootOffset - LastEnd,
366 Align(1));
367 }
368
369 // For each root, compute pointer using precomputed offset
370 Value *SlotPtr = AtEntry.CreatePtrAdd(
371 StackEntry, AtEntry.getInt64(RootOffset), "gc_root");
372
373 // And use it in lieu of the alloca.
374 AllocaInst *OriginalAlloca = Roots[I].second;
375 SlotPtr->takeName(OriginalAlloca);
376 OriginalAlloca->replaceAllUsesWith(SlotPtr);
377
378 LastEnd = RootOffset + RootSize;
379 }
380
381 // Zero any padding at the end of the frame
382 if (FrameSize > LastEnd) {
383 Value *PaddingPtr =
384 AtEntry.CreatePtrAdd(StackEntry, AtEntry.getInt64(LastEnd));
385 AtEntry.CreateMemSet(PaddingPtr, AtEntry.getInt8(0), FrameSize - LastEnd,
386 Align(1));
387 }
388
389 // Move past the original stores inserted by GCStrategy::InitRoots. This isn't
390 // really necessary (the collector would never see the intermediate state at
391 // runtime), but it's nicer not to push the half-initialized entry onto the
392 // shadow stack.
393 while (isa<StoreInst>(IP))
394 ++IP;
395 AtEntry.SetInsertPoint(IP->getParent(), IP);
396
397 // Push the entry onto the shadow stack.
398 // Next pointer is at offset 0, so it's just the frame pointer
399 AtEntry.CreateStore(CurrentHead, StackEntry);
400 // The new head value is also the frame pointer (the linked list links to
401 // frame base)
402 AtEntry.CreateStore(StackEntry, Head);
403
404 // For each instruction that escapes...
405 EscapeEnumerator EE(F, "gc_cleanup", /*HandleExceptions=*/true, DTU);
406 while (IRBuilder<> *AtExit = EE.Next()) {
407 // Pop the entry from the shadow stack. Don't reuse CurrentHead from
408 // AtEntry, since that would make the value live for the entire function.
409 // Next pointer is at offset 0, so load from the frame base
410 Value *SavedHead =
411 AtExit->CreateLoad(AtExit->getPtrTy(), StackEntry, "gc_savedhead");
412 AtExit->CreateStore(SavedHead, Head);
413 }
414
415 // Delete the original allocas (which are no longer used) and the intrinsic
416 // calls (which are no longer valid). Doing this last avoids invalidating
417 // iterators.
418 for (std::pair<CallInst *, AllocaInst *> &Root : Roots) {
419 Root.first->eraseFromParent();
420 Root.second->eraseFromParent();
421 }
422
423 Roots.clear();
424 RootOffsets.clear();
425 return true;
426}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
This file contains the declarations for the subclasses of Constant, which represent the different fla...
dxil translate DXIL Translate Metadata
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
Machine Check Debug Module
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.
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.
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.
Analysis pass which computes a DominatorTree.
Definition Dominators.h:278
Legacy analysis pass which computes a DominatorTree.
Definition Dominators.h:314
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
bool hasExternalLinkage() const
LLVM_ABI bool isDeclaration() const
Return true if the primary definition of this global value is outside of the current translation unit...
Definition Globals.cpp:337
void setLinkage(LinkageTypes LT)
@ LinkOnceAnyLinkage
Keep one copy of function when linking (inline)
Definition GlobalValue.h:55
LLVM_ABI void setInitializer(Constant *InitVal)
setInitializer - Sets the initializer for this global variable, removing any existing initializer if ...
Definition Globals.cpp:542
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.
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:67
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
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM)
void push_back(const T &Elt)
Class to represent struct types.
static LLVM_ABI StructType * create(LLVMContext &Context, StringRef Name)
This creates an identified struct.
Definition Type.cpp:689
LLVM Value Representation.
Definition Value.h:75
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:255
LLVM_ABI void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
Definition Value.cpp:549
LLVM_ABI void takeName(Value *V)
Transfer the name from V to this value.
Definition Value.cpp:399
Changed
constexpr char Align[]
Key for Kernel::Arg::Metadata::mAlign.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition CallingConv.h:24
@ C
The default llvm calling convention, compatible with C.
Definition CallingConv.h:34
friend class Instruction
Iterator for Instructions in a `BasicBlock.
Definition BasicBlock.h:73
This is an optimization pass for GlobalISel generic memory operations.
@ Offset
Definition DWP.cpp:557
FunctionAddr VTableAddr Value
Definition InstrProf.h:137
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
FunctionAddr VTableAddr uintptr_t uintptr_t Int32Ty
Definition InstrProf.h:328
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
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
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
IRBuilder(LLVMContext &, FolderTy, InserterTy, MDNode *, ArrayRef< OperandBundleDef >) -> IRBuilder< FolderTy, InserterTy >
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
LLVM_ABI FunctionPass * createShadowStackGCLoweringPass()
ShadowStackGCLowering - Implements the custom lowering mechanism used by the shadow stack GC.
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