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