234 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8///9/// \file10/// Lower @llvm.global_dtors.11///12/// Implement @llvm.global_dtors by creating wrapper functions that are13/// registered in @llvm.global_ctors and which contain a call to14/// `__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 30using namespace llvm;31 32#define DEBUG_TYPE "lower-global-dtors"33 34namespace {35class 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 47public:48 static char ID;49 LowerGlobalDtorsLegacyPass() : ModulePass(ID) {50 initializeLowerGlobalDtorsLegacyPassPass(*PassRegistry::getPassRegistry());51 }52};53} // End anonymous namespace54 55char LowerGlobalDtorsLegacyPass::ID = 0;56INITIALIZE_PASS(LowerGlobalDtorsLegacyPass, DEBUG_TYPE,57 "Lower @llvm.global_dtors via `__cxa_atexit`", false, false)58 59ModulePass *llvm::createLowerGlobalDtorsLegacyPass() {60 return new LowerGlobalDtorsLegacyPass();61}62 63static bool runImpl(Module &M);64bool LowerGlobalDtorsLegacyPass::runOnModule(Module &M) { return runImpl(M); }65 66PreservedAnalyses 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 77static 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 a95 // priority, sequences of destructors with the same associated object are96 // 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(C, 0), VoidStar, VoidStar},142 /*isVarArg=*/false));143 144 // If __cxa_atexit is defined (e.g. in the case of LTO) and arg0 is not145 // actually used (i.e. it's dummy/stub function as used in emscripten when146 // the program never exits) we can simply return early and clear out147 // @llvm.global_dtors.148 if (auto F = dyn_cast<Function>(AtExit.getCallee())) {149 if (F && F->hasExactDefinition() && F->getArg(0)->use_empty()) {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 function166 // to call all the destructors at that level, and a function to register the167 // 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 of217 // memory before main has even started, something is wrong.218 CallInst::Create(Intrinsic::getOrInsertDeclaration(&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