164902d33SJulian Lettner //===-- LowerGlobalDtors.cpp - Lower @llvm.global_dtors -------------------===// 264902d33SJulian Lettner // 364902d33SJulian Lettner // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 464902d33SJulian Lettner // See https://llvm.org/LICENSE.txt for license information. 564902d33SJulian Lettner // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 664902d33SJulian Lettner // 764902d33SJulian Lettner //===----------------------------------------------------------------------===// 864902d33SJulian Lettner /// 964902d33SJulian Lettner /// \file 1064902d33SJulian Lettner /// Lower @llvm.global_dtors. 1164902d33SJulian Lettner /// 1264902d33SJulian Lettner /// Implement @llvm.global_dtors by creating wrapper functions that are 1364902d33SJulian Lettner /// registered in @llvm.global_ctors and which contain a call to 1464902d33SJulian Lettner /// `__cxa_atexit` to register their destructor functions. 1564902d33SJulian Lettner /// 1664902d33SJulian Lettner //===----------------------------------------------------------------------===// 1764902d33SJulian Lettner 1864902d33SJulian Lettner #include "llvm/Transforms/Utils/LowerGlobalDtors.h" 1964902d33SJulian Lettner 2064902d33SJulian Lettner #include "llvm/IR/Constants.h" 2164902d33SJulian Lettner #include "llvm/IR/Instructions.h" 2264902d33SJulian Lettner #include "llvm/IR/Intrinsics.h" 234169338eSNikita Popov #include "llvm/IR/Module.h" 2464902d33SJulian Lettner #include "llvm/InitializePasses.h" 2564902d33SJulian Lettner #include "llvm/Pass.h" 2664902d33SJulian Lettner #include "llvm/Transforms/Utils.h" 2764902d33SJulian Lettner #include "llvm/Transforms/Utils/ModuleUtils.h" 2864902d33SJulian Lettner #include <map> 2964902d33SJulian Lettner 3064902d33SJulian Lettner using namespace llvm; 3164902d33SJulian Lettner 3264902d33SJulian Lettner #define DEBUG_TYPE "lower-global-dtors" 3364902d33SJulian Lettner 3464902d33SJulian Lettner namespace { 3564902d33SJulian Lettner class LowerGlobalDtorsLegacyPass final : public ModulePass { 3664902d33SJulian Lettner StringRef getPassName() const override { 3764902d33SJulian Lettner return "Lower @llvm.global_dtors via `__cxa_atexit`"; 3864902d33SJulian Lettner } 3964902d33SJulian Lettner 4064902d33SJulian Lettner void getAnalysisUsage(AnalysisUsage &AU) const override { 4164902d33SJulian Lettner AU.setPreservesCFG(); 4264902d33SJulian Lettner ModulePass::getAnalysisUsage(AU); 4364902d33SJulian Lettner } 4464902d33SJulian Lettner 4564902d33SJulian Lettner bool runOnModule(Module &M) override; 4664902d33SJulian Lettner 4764902d33SJulian Lettner public: 4864902d33SJulian Lettner static char ID; 4964902d33SJulian Lettner LowerGlobalDtorsLegacyPass() : ModulePass(ID) { 5064902d33SJulian Lettner initializeLowerGlobalDtorsLegacyPassPass(*PassRegistry::getPassRegistry()); 5164902d33SJulian Lettner } 5264902d33SJulian Lettner }; 5364902d33SJulian Lettner } // End anonymous namespace 5464902d33SJulian Lettner 5564902d33SJulian Lettner char LowerGlobalDtorsLegacyPass::ID = 0; 5664902d33SJulian Lettner INITIALIZE_PASS(LowerGlobalDtorsLegacyPass, DEBUG_TYPE, 5764902d33SJulian Lettner "Lower @llvm.global_dtors via `__cxa_atexit`", false, false) 5864902d33SJulian Lettner 5964902d33SJulian Lettner ModulePass *llvm::createLowerGlobalDtorsLegacyPass() { 6064902d33SJulian Lettner return new LowerGlobalDtorsLegacyPass(); 6164902d33SJulian Lettner } 6264902d33SJulian Lettner 6364902d33SJulian Lettner static bool runImpl(Module &M); 6464902d33SJulian Lettner bool LowerGlobalDtorsLegacyPass::runOnModule(Module &M) { return runImpl(M); } 6564902d33SJulian Lettner 6664902d33SJulian Lettner PreservedAnalyses LowerGlobalDtorsPass::run(Module &M, 6764902d33SJulian Lettner ModuleAnalysisManager &AM) { 6864902d33SJulian Lettner bool Changed = runImpl(M); 6964902d33SJulian Lettner if (!Changed) 7064902d33SJulian Lettner return PreservedAnalyses::all(); 7164902d33SJulian Lettner 7264902d33SJulian Lettner PreservedAnalyses PA; 7364902d33SJulian Lettner PA.preserveSet<CFGAnalyses>(); 7464902d33SJulian Lettner return PA; 7564902d33SJulian Lettner } 7664902d33SJulian Lettner 7764902d33SJulian Lettner static bool runImpl(Module &M) { 7864902d33SJulian Lettner GlobalVariable *GV = M.getGlobalVariable("llvm.global_dtors"); 7964902d33SJulian Lettner if (!GV || !GV->hasInitializer()) 8064902d33SJulian Lettner return false; 8164902d33SJulian Lettner 8264902d33SJulian Lettner const ConstantArray *InitList = dyn_cast<ConstantArray>(GV->getInitializer()); 8364902d33SJulian Lettner if (!InitList) 8464902d33SJulian Lettner return false; 8564902d33SJulian Lettner 8664902d33SJulian Lettner // Validate @llvm.global_dtor's type. 8764902d33SJulian Lettner auto *ETy = dyn_cast<StructType>(InitList->getType()->getElementType()); 8864902d33SJulian Lettner if (!ETy || ETy->getNumElements() != 3 || 8964902d33SJulian Lettner !ETy->getTypeAtIndex(0U)->isIntegerTy() || 9064902d33SJulian Lettner !ETy->getTypeAtIndex(1U)->isPointerTy() || 9164902d33SJulian Lettner !ETy->getTypeAtIndex(2U)->isPointerTy()) 9264902d33SJulian Lettner return false; // Not (int, ptr, ptr). 9364902d33SJulian Lettner 9464902d33SJulian Lettner // Collect the contents of @llvm.global_dtors, ordered by priority. Within a 9564902d33SJulian Lettner // priority, sequences of destructors with the same associated object are 9664902d33SJulian Lettner // recorded so that we can register them as a group. 9764902d33SJulian Lettner std::map< 9864902d33SJulian Lettner uint16_t, 9964902d33SJulian Lettner std::vector<std::pair<Constant *, std::vector<Constant *>>> 10064902d33SJulian Lettner > DtorFuncs; 10164902d33SJulian Lettner for (Value *O : InitList->operands()) { 10264902d33SJulian Lettner auto *CS = dyn_cast<ConstantStruct>(O); 10364902d33SJulian Lettner if (!CS) 10464902d33SJulian Lettner continue; // Malformed. 10564902d33SJulian Lettner 10664902d33SJulian Lettner auto *Priority = dyn_cast<ConstantInt>(CS->getOperand(0)); 10764902d33SJulian Lettner if (!Priority) 10864902d33SJulian Lettner continue; // Malformed. 10964902d33SJulian Lettner uint16_t PriorityValue = Priority->getLimitedValue(UINT16_MAX); 11064902d33SJulian Lettner 11164902d33SJulian Lettner Constant *DtorFunc = CS->getOperand(1); 11264902d33SJulian Lettner if (DtorFunc->isNullValue()) 11364902d33SJulian Lettner break; // Found a null terminator, skip the rest. 11464902d33SJulian Lettner 11564902d33SJulian Lettner Constant *Associated = CS->getOperand(2); 11664902d33SJulian Lettner Associated = cast<Constant>(Associated->stripPointerCasts()); 11764902d33SJulian Lettner 11864902d33SJulian Lettner auto &AtThisPriority = DtorFuncs[PriorityValue]; 11964902d33SJulian Lettner if (AtThisPriority.empty() || AtThisPriority.back().first != Associated) { 12064902d33SJulian Lettner std::vector<Constant *> NewList; 12164902d33SJulian Lettner NewList.push_back(DtorFunc); 12264902d33SJulian Lettner AtThisPriority.push_back(std::make_pair(Associated, NewList)); 12364902d33SJulian Lettner } else { 12464902d33SJulian Lettner AtThisPriority.back().second.push_back(DtorFunc); 12564902d33SJulian Lettner } 12664902d33SJulian Lettner } 12764902d33SJulian Lettner if (DtorFuncs.empty()) 12864902d33SJulian Lettner return false; 12964902d33SJulian Lettner 13064902d33SJulian Lettner // extern "C" int __cxa_atexit(void (*f)(void *), void *p, void *d); 13164902d33SJulian Lettner LLVMContext &C = M.getContext(); 1327b9d73c2SPaulo Matos PointerType *VoidStar = PointerType::getUnqual(C); 13364902d33SJulian Lettner Type *AtExitFuncArgs[] = {VoidStar}; 13464902d33SJulian Lettner FunctionType *AtExitFuncTy = 13564902d33SJulian Lettner FunctionType::get(Type::getVoidTy(C), AtExitFuncArgs, 13664902d33SJulian Lettner /*isVarArg=*/false); 13764902d33SJulian Lettner 13864902d33SJulian Lettner FunctionCallee AtExit = M.getOrInsertFunction( 13964902d33SJulian Lettner "__cxa_atexit", 14064902d33SJulian Lettner FunctionType::get(Type::getInt32Ty(C), 141*416f1c46SMats Jun Larsen {PointerType::get(C, 0), VoidStar, VoidStar}, 14264902d33SJulian Lettner /*isVarArg=*/false)); 14364902d33SJulian Lettner 144e01c7d54SSam Clegg // If __cxa_atexit is defined (e.g. in the case of LTO) and arg0 is not 145e01c7d54SSam Clegg // actually used (i.e. it's dummy/stub function as used in emscripten when 146e01c7d54SSam Clegg // the program never exits) we can simply return early and clear out 147e01c7d54SSam Clegg // @llvm.global_dtors. 148e01c7d54SSam Clegg if (auto F = dyn_cast<Function>(AtExit.getCallee())) { 149e01c7d54SSam Clegg if (F && F->hasExactDefinition() && F->getArg(0)->getNumUses() == 0) { 150e01c7d54SSam Clegg GV->eraseFromParent(); 151e01c7d54SSam Clegg return true; 152e01c7d54SSam Clegg } 153e01c7d54SSam Clegg } 154e01c7d54SSam Clegg 15564902d33SJulian Lettner // Declare __dso_local. 15664902d33SJulian Lettner Type *DsoHandleTy = Type::getInt8Ty(C); 15764902d33SJulian Lettner Constant *DsoHandle = M.getOrInsertGlobal("__dso_handle", DsoHandleTy, [&] { 15864902d33SJulian Lettner auto *GV = new GlobalVariable(M, DsoHandleTy, /*isConstant=*/true, 15964902d33SJulian Lettner GlobalVariable::ExternalWeakLinkage, nullptr, 16064902d33SJulian Lettner "__dso_handle"); 16164902d33SJulian Lettner GV->setVisibility(GlobalVariable::HiddenVisibility); 16264902d33SJulian Lettner return GV; 16364902d33SJulian Lettner }); 16464902d33SJulian Lettner 16564902d33SJulian Lettner // For each unique priority level and associated symbol, generate a function 16664902d33SJulian Lettner // to call all the destructors at that level, and a function to register the 16764902d33SJulian Lettner // first function with __cxa_atexit. 16864902d33SJulian Lettner for (auto &PriorityAndMore : DtorFuncs) { 16964902d33SJulian Lettner uint16_t Priority = PriorityAndMore.first; 17064902d33SJulian Lettner uint64_t Id = 0; 17164902d33SJulian Lettner auto &AtThisPriority = PriorityAndMore.second; 17264902d33SJulian Lettner for (auto &AssociatedAndMore : AtThisPriority) { 17364902d33SJulian Lettner Constant *Associated = AssociatedAndMore.first; 17464902d33SJulian Lettner auto ThisId = Id++; 17564902d33SJulian Lettner 17664902d33SJulian Lettner Function *CallDtors = Function::Create( 17764902d33SJulian Lettner AtExitFuncTy, Function::PrivateLinkage, 17864902d33SJulian Lettner "call_dtors" + 17964902d33SJulian Lettner (Priority != UINT16_MAX ? (Twine(".") + Twine(Priority)) 18064902d33SJulian Lettner : Twine()) + 18164902d33SJulian Lettner (AtThisPriority.size() > 1 ? Twine("$") + Twine(ThisId) 18264902d33SJulian Lettner : Twine()) + 18364902d33SJulian Lettner (!Associated->isNullValue() ? (Twine(".") + Associated->getName()) 18464902d33SJulian Lettner : Twine()), 18564902d33SJulian Lettner &M); 18664902d33SJulian Lettner BasicBlock *BB = BasicBlock::Create(C, "body", CallDtors); 18764902d33SJulian Lettner FunctionType *VoidVoid = FunctionType::get(Type::getVoidTy(C), 18864902d33SJulian Lettner /*isVarArg=*/false); 18964902d33SJulian Lettner 19056ea4f9bSKazu Hirata for (auto *Dtor : reverse(AssociatedAndMore.second)) 19164902d33SJulian Lettner CallInst::Create(VoidVoid, Dtor, "", BB); 19264902d33SJulian Lettner ReturnInst::Create(C, BB); 19364902d33SJulian Lettner 19464902d33SJulian Lettner Function *RegisterCallDtors = Function::Create( 19564902d33SJulian Lettner VoidVoid, Function::PrivateLinkage, 19664902d33SJulian Lettner "register_call_dtors" + 19764902d33SJulian Lettner (Priority != UINT16_MAX ? (Twine(".") + Twine(Priority)) 19864902d33SJulian Lettner : Twine()) + 19964902d33SJulian Lettner (AtThisPriority.size() > 1 ? Twine("$") + Twine(ThisId) 20064902d33SJulian Lettner : Twine()) + 20164902d33SJulian Lettner (!Associated->isNullValue() ? (Twine(".") + Associated->getName()) 20264902d33SJulian Lettner : Twine()), 20364902d33SJulian Lettner &M); 20464902d33SJulian Lettner BasicBlock *EntryBB = BasicBlock::Create(C, "entry", RegisterCallDtors); 20564902d33SJulian Lettner BasicBlock *FailBB = BasicBlock::Create(C, "fail", RegisterCallDtors); 20664902d33SJulian Lettner BasicBlock *RetBB = BasicBlock::Create(C, "return", RegisterCallDtors); 20764902d33SJulian Lettner 20864902d33SJulian Lettner Value *Null = ConstantPointerNull::get(VoidStar); 20964902d33SJulian Lettner Value *Args[] = {CallDtors, Null, DsoHandle}; 21064902d33SJulian Lettner Value *Res = CallInst::Create(AtExit, Args, "call", EntryBB); 2116b62a913SJeremy Morse Value *Cmp = new ICmpInst(EntryBB, ICmpInst::ICMP_NE, Res, 21264902d33SJulian Lettner Constant::getNullValue(Res->getType())); 21364902d33SJulian Lettner BranchInst::Create(FailBB, RetBB, Cmp, EntryBB); 21464902d33SJulian Lettner 21564902d33SJulian Lettner // If `__cxa_atexit` hits out-of-memory, trap, so that we don't misbehave. 21664902d33SJulian Lettner // This should be very rare, because if the process is running out of 21764902d33SJulian Lettner // memory before main has even started, something is wrong. 218fa789dffSRahul Joshi CallInst::Create(Intrinsic::getOrInsertDeclaration(&M, Intrinsic::trap), 219fa789dffSRahul Joshi "", FailBB); 22064902d33SJulian Lettner new UnreachableInst(C, FailBB); 22164902d33SJulian Lettner 22264902d33SJulian Lettner ReturnInst::Create(C, RetBB); 22364902d33SJulian Lettner 22464902d33SJulian Lettner // Now register the registration function with @llvm.global_ctors. 22564902d33SJulian Lettner appendToGlobalCtors(M, RegisterCallDtors, Priority, Associated); 22664902d33SJulian Lettner } 22764902d33SJulian Lettner } 22864902d33SJulian Lettner 22964902d33SJulian Lettner // Now that we've lowered everything, remove @llvm.global_dtors. 23064902d33SJulian Lettner GV->eraseFromParent(); 23164902d33SJulian Lettner 23264902d33SJulian Lettner return true; 23364902d33SJulian Lettner } 234