1 //===-- LowerGlobalDtors.cpp - Lower @llvm.global_dtors -------------------===// 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 /// \file 10 /// Lower @llvm.global_dtors. 11 /// 12 /// Implement @llvm.global_dtors by creating wrapper functions that are 13 /// registered in @llvm.global_ctors and which contain a call to 14 /// `__cxa_atexit` to register their destructor functions. 15 /// 16 //===----------------------------------------------------------------------===// 17 18 #include "llvm/Transforms/Utils/LowerGlobalDtors.h" 19 20 #include "llvm/IR/Constants.h" 21 #include "llvm/IR/Instructions.h" 22 #include "llvm/IR/Intrinsics.h" 23 #include "llvm/InitializePasses.h" 24 #include "llvm/Pass.h" 25 #include "llvm/Transforms/Utils.h" 26 #include "llvm/Transforms/Utils/ModuleUtils.h" 27 #include <map> 28 29 using namespace llvm; 30 31 #define DEBUG_TYPE "lower-global-dtors" 32 33 namespace { 34 class LowerGlobalDtorsLegacyPass final : public ModulePass { 35 StringRef getPassName() const override { 36 return "Lower @llvm.global_dtors via `__cxa_atexit`"; 37 } 38 39 void getAnalysisUsage(AnalysisUsage &AU) const override { 40 AU.setPreservesCFG(); 41 ModulePass::getAnalysisUsage(AU); 42 } 43 44 bool runOnModule(Module &M) override; 45 46 public: 47 static char ID; 48 LowerGlobalDtorsLegacyPass() : ModulePass(ID) { 49 initializeLowerGlobalDtorsLegacyPassPass(*PassRegistry::getPassRegistry()); 50 } 51 }; 52 } // End anonymous namespace 53 54 char LowerGlobalDtorsLegacyPass::ID = 0; 55 INITIALIZE_PASS(LowerGlobalDtorsLegacyPass, DEBUG_TYPE, 56 "Lower @llvm.global_dtors via `__cxa_atexit`", false, false) 57 58 ModulePass *llvm::createLowerGlobalDtorsLegacyPass() { 59 return new LowerGlobalDtorsLegacyPass(); 60 } 61 62 static bool runImpl(Module &M); 63 bool LowerGlobalDtorsLegacyPass::runOnModule(Module &M) { return runImpl(M); } 64 65 PreservedAnalyses LowerGlobalDtorsPass::run(Module &M, 66 ModuleAnalysisManager &AM) { 67 bool Changed = runImpl(M); 68 if (!Changed) 69 return PreservedAnalyses::all(); 70 71 PreservedAnalyses PA; 72 PA.preserveSet<CFGAnalyses>(); 73 return PA; 74 } 75 76 static bool runImpl(Module &M) { 77 GlobalVariable *GV = M.getGlobalVariable("llvm.global_dtors"); 78 if (!GV || !GV->hasInitializer()) 79 return false; 80 81 const ConstantArray *InitList = dyn_cast<ConstantArray>(GV->getInitializer()); 82 if (!InitList) 83 return false; 84 85 // Validate @llvm.global_dtor's type. 86 auto *ETy = dyn_cast<StructType>(InitList->getType()->getElementType()); 87 if (!ETy || ETy->getNumElements() != 3 || 88 !ETy->getTypeAtIndex(0U)->isIntegerTy() || 89 !ETy->getTypeAtIndex(1U)->isPointerTy() || 90 !ETy->getTypeAtIndex(2U)->isPointerTy()) 91 return false; // Not (int, ptr, ptr). 92 93 // Collect the contents of @llvm.global_dtors, ordered by priority. Within a 94 // priority, sequences of destructors with the same associated object are 95 // recorded so that we can register them as a group. 96 std::map< 97 uint16_t, 98 std::vector<std::pair<Constant *, std::vector<Constant *>>> 99 > DtorFuncs; 100 for (Value *O : InitList->operands()) { 101 auto *CS = dyn_cast<ConstantStruct>(O); 102 if (!CS) 103 continue; // Malformed. 104 105 auto *Priority = dyn_cast<ConstantInt>(CS->getOperand(0)); 106 if (!Priority) 107 continue; // Malformed. 108 uint16_t PriorityValue = Priority->getLimitedValue(UINT16_MAX); 109 110 Constant *DtorFunc = CS->getOperand(1); 111 if (DtorFunc->isNullValue()) 112 break; // Found a null terminator, skip the rest. 113 114 Constant *Associated = CS->getOperand(2); 115 Associated = cast<Constant>(Associated->stripPointerCasts()); 116 117 auto &AtThisPriority = DtorFuncs[PriorityValue]; 118 if (AtThisPriority.empty() || AtThisPriority.back().first != Associated) { 119 std::vector<Constant *> NewList; 120 NewList.push_back(DtorFunc); 121 AtThisPriority.push_back(std::make_pair(Associated, NewList)); 122 } else { 123 AtThisPriority.back().second.push_back(DtorFunc); 124 } 125 } 126 if (DtorFuncs.empty()) 127 return false; 128 129 // extern "C" int __cxa_atexit(void (*f)(void *), void *p, void *d); 130 LLVMContext &C = M.getContext(); 131 PointerType *VoidStar = Type::getInt8PtrTy(C); 132 Type *AtExitFuncArgs[] = {VoidStar}; 133 FunctionType *AtExitFuncTy = 134 FunctionType::get(Type::getVoidTy(C), AtExitFuncArgs, 135 /*isVarArg=*/false); 136 137 FunctionCallee AtExit = M.getOrInsertFunction( 138 "__cxa_atexit", 139 FunctionType::get(Type::getInt32Ty(C), 140 {PointerType::get(AtExitFuncTy, 0), VoidStar, VoidStar}, 141 /*isVarArg=*/false)); 142 143 // Declare __dso_local. 144 Constant *DsoHandle = M.getNamedValue("__dso_handle"); 145 if (!DsoHandle) { 146 Type *DsoHandleTy = Type::getInt8Ty(C); 147 GlobalVariable *Handle = new GlobalVariable( 148 M, DsoHandleTy, /*isConstant=*/true, 149 GlobalVariable::ExternalWeakLinkage, nullptr, "__dso_handle"); 150 Handle->setVisibility(GlobalVariable::HiddenVisibility); 151 DsoHandle = Handle; 152 } 153 154 // For each unique priority level and associated symbol, generate a function 155 // to call all the destructors at that level, and a function to register the 156 // first function with __cxa_atexit. 157 for (auto &PriorityAndMore : DtorFuncs) { 158 uint16_t Priority = PriorityAndMore.first; 159 uint64_t Id = 0; 160 auto &AtThisPriority = PriorityAndMore.second; 161 for (auto &AssociatedAndMore : AtThisPriority) { 162 Constant *Associated = AssociatedAndMore.first; 163 auto ThisId = Id++; 164 165 Function *CallDtors = Function::Create( 166 AtExitFuncTy, Function::PrivateLinkage, 167 "call_dtors" + 168 (Priority != UINT16_MAX ? (Twine(".") + Twine(Priority)) 169 : Twine()) + 170 (AtThisPriority.size() > 1 ? Twine("$") + Twine(ThisId) 171 : Twine()) + 172 (!Associated->isNullValue() ? (Twine(".") + Associated->getName()) 173 : Twine()), 174 &M); 175 BasicBlock *BB = BasicBlock::Create(C, "body", CallDtors); 176 FunctionType *VoidVoid = FunctionType::get(Type::getVoidTy(C), 177 /*isVarArg=*/false); 178 179 for (auto Dtor : reverse(AssociatedAndMore.second)) 180 CallInst::Create(VoidVoid, Dtor, "", BB); 181 ReturnInst::Create(C, BB); 182 183 Function *RegisterCallDtors = Function::Create( 184 VoidVoid, Function::PrivateLinkage, 185 "register_call_dtors" + 186 (Priority != UINT16_MAX ? (Twine(".") + Twine(Priority)) 187 : Twine()) + 188 (AtThisPriority.size() > 1 ? Twine("$") + Twine(ThisId) 189 : Twine()) + 190 (!Associated->isNullValue() ? (Twine(".") + Associated->getName()) 191 : Twine()), 192 &M); 193 BasicBlock *EntryBB = BasicBlock::Create(C, "entry", RegisterCallDtors); 194 BasicBlock *FailBB = BasicBlock::Create(C, "fail", RegisterCallDtors); 195 BasicBlock *RetBB = BasicBlock::Create(C, "return", RegisterCallDtors); 196 197 Value *Null = ConstantPointerNull::get(VoidStar); 198 Value *Args[] = {CallDtors, Null, DsoHandle}; 199 Value *Res = CallInst::Create(AtExit, Args, "call", EntryBB); 200 Value *Cmp = new ICmpInst(*EntryBB, ICmpInst::ICMP_NE, Res, 201 Constant::getNullValue(Res->getType())); 202 BranchInst::Create(FailBB, RetBB, Cmp, EntryBB); 203 204 // If `__cxa_atexit` hits out-of-memory, trap, so that we don't misbehave. 205 // This should be very rare, because if the process is running out of 206 // memory before main has even started, something is wrong. 207 CallInst::Create(Intrinsic::getDeclaration(&M, Intrinsic::trap), "", 208 FailBB); 209 new UnreachableInst(C, FailBB); 210 211 ReturnInst::Create(C, RetBB); 212 213 // Now register the registration function with @llvm.global_ctors. 214 appendToGlobalCtors(M, RegisterCallDtors, Priority, Associated); 215 } 216 } 217 218 // Now that we've lowered everything, remove @llvm.global_dtors. 219 GV->eraseFromParent(); 220 221 return true; 222 } 223