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