xref: /llvm-project/llvm/lib/Transforms/Utils/LowerGlobalDtors.cpp (revision 416f1c465db62d829283f6902ef35e027e127aa7)
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