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 Type *DsoHandleTy = Type::getInt8Ty(C); 145 Constant *DsoHandle = M.getOrInsertGlobal("__dso_handle", DsoHandleTy, [&] { 146 auto *GV = new GlobalVariable(M, DsoHandleTy, /*isConstant=*/true, 147 GlobalVariable::ExternalWeakLinkage, nullptr, 148 "__dso_handle"); 149 GV->setVisibility(GlobalVariable::HiddenVisibility); 150 return GV; 151 }); 152 153 // For each unique priority level and associated symbol, generate a function 154 // to call all the destructors at that level, and a function to register the 155 // first function with __cxa_atexit. 156 for (auto &PriorityAndMore : DtorFuncs) { 157 uint16_t Priority = PriorityAndMore.first; 158 uint64_t Id = 0; 159 auto &AtThisPriority = PriorityAndMore.second; 160 for (auto &AssociatedAndMore : AtThisPriority) { 161 Constant *Associated = AssociatedAndMore.first; 162 auto ThisId = Id++; 163 164 Function *CallDtors = Function::Create( 165 AtExitFuncTy, Function::PrivateLinkage, 166 "call_dtors" + 167 (Priority != UINT16_MAX ? (Twine(".") + Twine(Priority)) 168 : Twine()) + 169 (AtThisPriority.size() > 1 ? Twine("$") + Twine(ThisId) 170 : Twine()) + 171 (!Associated->isNullValue() ? (Twine(".") + Associated->getName()) 172 : Twine()), 173 &M); 174 BasicBlock *BB = BasicBlock::Create(C, "body", CallDtors); 175 FunctionType *VoidVoid = FunctionType::get(Type::getVoidTy(C), 176 /*isVarArg=*/false); 177 178 for (auto *Dtor : reverse(AssociatedAndMore.second)) 179 CallInst::Create(VoidVoid, Dtor, "", BB); 180 ReturnInst::Create(C, BB); 181 182 Function *RegisterCallDtors = Function::Create( 183 VoidVoid, Function::PrivateLinkage, 184 "register_call_dtors" + 185 (Priority != UINT16_MAX ? (Twine(".") + Twine(Priority)) 186 : Twine()) + 187 (AtThisPriority.size() > 1 ? Twine("$") + Twine(ThisId) 188 : Twine()) + 189 (!Associated->isNullValue() ? (Twine(".") + Associated->getName()) 190 : Twine()), 191 &M); 192 BasicBlock *EntryBB = BasicBlock::Create(C, "entry", RegisterCallDtors); 193 BasicBlock *FailBB = BasicBlock::Create(C, "fail", RegisterCallDtors); 194 BasicBlock *RetBB = BasicBlock::Create(C, "return", RegisterCallDtors); 195 196 Value *Null = ConstantPointerNull::get(VoidStar); 197 Value *Args[] = {CallDtors, Null, DsoHandle}; 198 Value *Res = CallInst::Create(AtExit, Args, "call", EntryBB); 199 Value *Cmp = new ICmpInst(*EntryBB, ICmpInst::ICMP_NE, Res, 200 Constant::getNullValue(Res->getType())); 201 BranchInst::Create(FailBB, RetBB, Cmp, EntryBB); 202 203 // If `__cxa_atexit` hits out-of-memory, trap, so that we don't misbehave. 204 // This should be very rare, because if the process is running out of 205 // memory before main has even started, something is wrong. 206 CallInst::Create(Intrinsic::getDeclaration(&M, Intrinsic::trap), "", 207 FailBB); 208 new UnreachableInst(C, FailBB); 209 210 ReturnInst::Create(C, RetBB); 211 212 // Now register the registration function with @llvm.global_ctors. 213 appendToGlobalCtors(M, RegisterCallDtors, Priority, Associated); 214 } 215 } 216 217 // Now that we've lowered everything, remove @llvm.global_dtors. 218 GV->eraseFromParent(); 219 220 return true; 221 } 222