brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.6 KiB · df3a2a9 Raw
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