| 1 | //===-- ModuleUtils.cpp - Functions to manipulate Modules -----------------===// |
| 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 | // This family of functions perform manipulations on Modules. |
| 10 | // |
| 11 | //===----------------------------------------------------------------------===// |
| 12 | |
| 13 | #include "llvm/Transforms/Utils/ModuleUtils.h" |
| 14 | #include "llvm/Analysis/VectorUtils.h" |
| 15 | #include "llvm/ADT/SmallString.h" |
| 16 | #include "llvm/IR/DerivedTypes.h" |
| 17 | #include "llvm/IR/Function.h" |
| 18 | #include "llvm/IR/IRBuilder.h" |
| 19 | #include "llvm/IR/MDBuilder.h" |
| 20 | #include "llvm/IR/Module.h" |
| 21 | #include "llvm/Support/Casting.h" |
| 22 | #include "llvm/Support/MD5.h" |
| 23 | #include "llvm/Support/raw_ostream.h" |
| 24 | #include "llvm/Support/xxhash.h" |
| 25 | |
| 26 | using namespace llvm; |
| 27 | |
| 28 | #define DEBUG_TYPE "moduleutils" |
| 29 | |
| 30 | static void appendToGlobalArray(StringRef ArrayName, Module &M, Function *F, |
| 31 | int Priority, Constant *Data) { |
| 32 | IRBuilder<> IRB(M.getContext()); |
| 33 | |
| 34 | // Get the current set of static global constructors and add the new ctor |
| 35 | // to the list. |
| 36 | SmallVector<Constant *, 16> CurrentCtors; |
| 37 | StructType *EltTy; |
| 38 | if (GlobalVariable *GVCtor = M.getNamedGlobal(Name: ArrayName)) { |
| 39 | EltTy = cast<StructType>(Val: GVCtor->getValueType()->getArrayElementType()); |
| 40 | if (Constant *Init = GVCtor->getInitializer()) { |
| 41 | unsigned n = Init->getNumOperands(); |
| 42 | CurrentCtors.reserve(N: n + 1); |
| 43 | for (unsigned i = 0; i != n; ++i) |
| 44 | CurrentCtors.push_back(Elt: cast<Constant>(Val: Init->getOperand(i))); |
| 45 | } |
| 46 | GVCtor->eraseFromParent(); |
| 47 | } else { |
| 48 | EltTy = StructType::get( |
| 49 | elt1: IRB.getInt32Ty(), |
| 50 | elts: PointerType::get(C&: M.getContext(), AddressSpace: F->getAddressSpace()), elts: IRB.getPtrTy()); |
| 51 | } |
| 52 | |
| 53 | // Build a 3 field global_ctor entry. We don't take a comdat key. |
| 54 | Constant *CSVals[3]; |
| 55 | CSVals[0] = IRB.getInt32(C: Priority); |
| 56 | CSVals[1] = F; |
| 57 | CSVals[2] = Data ? ConstantExpr::getPointerCast(C: Data, Ty: IRB.getPtrTy()) |
| 58 | : Constant::getNullValue(Ty: IRB.getPtrTy()); |
| 59 | Constant *RuntimeCtorInit = |
| 60 | ConstantStruct::get(T: EltTy, V: ArrayRef(CSVals, EltTy->getNumElements())); |
| 61 | |
| 62 | CurrentCtors.push_back(Elt: RuntimeCtorInit); |
| 63 | |
| 64 | // Create a new initializer. |
| 65 | ArrayType *AT = ArrayType::get(ElementType: EltTy, NumElements: CurrentCtors.size()); |
| 66 | Constant *NewInit = ConstantArray::get(T: AT, V: CurrentCtors); |
| 67 | |
| 68 | // Create the new global variable and replace all uses of |
| 69 | // the old global variable with the new one. |
| 70 | (void)new GlobalVariable(M, NewInit->getType(), false, |
| 71 | GlobalValue::AppendingLinkage, NewInit, ArrayName); |
| 72 | } |
| 73 | |
| 74 | void llvm::appendToGlobalCtors(Module &M, Function *F, int Priority, Constant *Data) { |
| 75 | appendToGlobalArray(ArrayName: "llvm.global_ctors" , M, F, Priority, Data); |
| 76 | } |
| 77 | |
| 78 | void llvm::appendToGlobalDtors(Module &M, Function *F, int Priority, Constant *Data) { |
| 79 | appendToGlobalArray(ArrayName: "llvm.global_dtors" , M, F, Priority, Data); |
| 80 | } |
| 81 | |
| 82 | static void transformGlobalArray(StringRef ArrayName, Module &M, |
| 83 | const GlobalCtorTransformFn &Fn) { |
| 84 | GlobalVariable *GVCtor = M.getNamedGlobal(Name: ArrayName); |
| 85 | if (!GVCtor) |
| 86 | return; |
| 87 | |
| 88 | IRBuilder<> IRB(M.getContext()); |
| 89 | SmallVector<Constant *, 16> CurrentCtors; |
| 90 | bool Changed = false; |
| 91 | StructType *EltTy = |
| 92 | cast<StructType>(Val: GVCtor->getValueType()->getArrayElementType()); |
| 93 | if (Constant *Init = GVCtor->getInitializer()) { |
| 94 | CurrentCtors.reserve(N: Init->getNumOperands()); |
| 95 | for (Value *OP : Init->operands()) { |
| 96 | Constant *C = cast<Constant>(Val: OP); |
| 97 | Constant *NewC = Fn(C); |
| 98 | Changed |= (!NewC || NewC != C); |
| 99 | if (NewC) |
| 100 | CurrentCtors.push_back(Elt: NewC); |
| 101 | } |
| 102 | } |
| 103 | if (!Changed) |
| 104 | return; |
| 105 | |
| 106 | GVCtor->eraseFromParent(); |
| 107 | |
| 108 | // Create a new initializer. |
| 109 | ArrayType *AT = ArrayType::get(ElementType: EltTy, NumElements: CurrentCtors.size()); |
| 110 | Constant *NewInit = ConstantArray::get(T: AT, V: CurrentCtors); |
| 111 | |
| 112 | // Create the new global variable and replace all uses of |
| 113 | // the old global variable with the new one. |
| 114 | (void)new GlobalVariable(M, NewInit->getType(), false, |
| 115 | GlobalValue::AppendingLinkage, NewInit, ArrayName); |
| 116 | } |
| 117 | |
| 118 | void llvm::transformGlobalCtors(Module &M, const GlobalCtorTransformFn &Fn) { |
| 119 | transformGlobalArray(ArrayName: "llvm.global_ctors" , M, Fn); |
| 120 | } |
| 121 | |
| 122 | void llvm::transformGlobalDtors(Module &M, const GlobalCtorTransformFn &Fn) { |
| 123 | transformGlobalArray(ArrayName: "llvm.global_dtors" , M, Fn); |
| 124 | } |
| 125 | |
| 126 | static void collectUsedGlobals(GlobalVariable *GV, |
| 127 | SmallSetVector<Constant *, 16> &Init) { |
| 128 | if (!GV || !GV->hasInitializer()) |
| 129 | return; |
| 130 | |
| 131 | auto *CA = cast<ConstantArray>(Val: GV->getInitializer()); |
| 132 | for (Use &Op : CA->operands()) |
| 133 | Init.insert(X: cast<Constant>(Val&: Op)); |
| 134 | } |
| 135 | |
| 136 | static void appendToUsedList(Module &M, StringRef Name, ArrayRef<GlobalValue *> Values) { |
| 137 | GlobalVariable *GV = M.getGlobalVariable(Name); |
| 138 | |
| 139 | SmallSetVector<Constant *, 16> Init; |
| 140 | collectUsedGlobals(GV, Init); |
| 141 | if (GV) |
| 142 | GV->eraseFromParent(); |
| 143 | |
| 144 | Type *ArrayEltTy = llvm::PointerType::getUnqual(C&: M.getContext()); |
| 145 | for (auto *V : Values) |
| 146 | Init.insert(X: ConstantExpr::getPointerBitCastOrAddrSpaceCast(C: V, Ty: ArrayEltTy)); |
| 147 | |
| 148 | if (Init.empty()) |
| 149 | return; |
| 150 | |
| 151 | ArrayType *ATy = ArrayType::get(ElementType: ArrayEltTy, NumElements: Init.size()); |
| 152 | GV = new llvm::GlobalVariable(M, ATy, false, GlobalValue::AppendingLinkage, |
| 153 | ConstantArray::get(T: ATy, V: Init.getArrayRef()), |
| 154 | Name); |
| 155 | GV->setSection("llvm.metadata" ); |
| 156 | } |
| 157 | |
| 158 | void llvm::appendToUsed(Module &M, ArrayRef<GlobalValue *> Values) { |
| 159 | appendToUsedList(M, Name: "llvm.used" , Values); |
| 160 | } |
| 161 | |
| 162 | void llvm::appendToCompilerUsed(Module &M, ArrayRef<GlobalValue *> Values) { |
| 163 | appendToUsedList(M, Name: "llvm.compiler.used" , Values); |
| 164 | } |
| 165 | |
| 166 | static void removeFromUsedList(Module &M, StringRef Name, |
| 167 | function_ref<bool(Constant *)> ShouldRemove) { |
| 168 | GlobalVariable *GV = M.getNamedGlobal(Name); |
| 169 | if (!GV) |
| 170 | return; |
| 171 | |
| 172 | SmallSetVector<Constant *, 16> Init; |
| 173 | collectUsedGlobals(GV, Init); |
| 174 | |
| 175 | Type *ArrayEltTy = cast<ArrayType>(Val: GV->getValueType())->getElementType(); |
| 176 | |
| 177 | SmallVector<Constant *, 16> NewInit; |
| 178 | for (Constant *MaybeRemoved : Init) { |
| 179 | if (!ShouldRemove(MaybeRemoved->stripPointerCasts())) |
| 180 | NewInit.push_back(Elt: MaybeRemoved); |
| 181 | } |
| 182 | |
| 183 | if (!NewInit.empty()) { |
| 184 | ArrayType *ATy = ArrayType::get(ElementType: ArrayEltTy, NumElements: NewInit.size()); |
| 185 | GlobalVariable *NewGV = |
| 186 | new GlobalVariable(M, ATy, false, GlobalValue::AppendingLinkage, |
| 187 | ConstantArray::get(T: ATy, V: NewInit), "" , GV, |
| 188 | GV->getThreadLocalMode(), GV->getAddressSpace()); |
| 189 | NewGV->setSection(GV->getSection()); |
| 190 | NewGV->takeName(V: GV); |
| 191 | } |
| 192 | |
| 193 | GV->eraseFromParent(); |
| 194 | } |
| 195 | |
| 196 | void llvm::removeFromUsedLists(Module &M, |
| 197 | function_ref<bool(Constant *)> ShouldRemove) { |
| 198 | removeFromUsedList(M, Name: "llvm.used" , ShouldRemove); |
| 199 | removeFromUsedList(M, Name: "llvm.compiler.used" , ShouldRemove); |
| 200 | } |
| 201 | |
| 202 | void llvm::setKCFIType(Module &M, Function &F, StringRef MangledType) { |
| 203 | if (!M.getModuleFlag(Key: "kcfi" )) |
| 204 | return; |
| 205 | // Matches CodeGenModule::CreateKCFITypeId in Clang. |
| 206 | LLVMContext &Ctx = M.getContext(); |
| 207 | MDBuilder MDB(Ctx); |
| 208 | std::string Type = MangledType.str(); |
| 209 | if (M.getModuleFlag(Key: "cfi-normalize-integers" )) |
| 210 | Type += ".normalized" ; |
| 211 | F.setMetadata(KindID: LLVMContext::MD_kcfi_type, |
| 212 | Node: MDNode::get(Context&: Ctx, MDs: MDB.createConstant(C: ConstantInt::get( |
| 213 | Ty: Type::getInt32Ty(C&: Ctx), |
| 214 | V: static_cast<uint32_t>(xxHash64(Data: Type)))))); |
| 215 | // If the module was compiled with -fpatchable-function-entry, ensure |
| 216 | // we use the same patchable-function-prefix. |
| 217 | if (auto *MD = mdconst::extract_or_null<ConstantInt>( |
| 218 | MD: M.getModuleFlag(Key: "kcfi-offset" ))) { |
| 219 | if (unsigned Offset = MD->getZExtValue()) |
| 220 | F.addFnAttr(Kind: "patchable-function-prefix" , Val: std::to_string(val: Offset)); |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | FunctionCallee llvm::declareSanitizerInitFunction(Module &M, StringRef InitName, |
| 225 | ArrayRef<Type *> InitArgTypes, |
| 226 | bool Weak) { |
| 227 | assert(!InitName.empty() && "Expected init function name" ); |
| 228 | auto *VoidTy = Type::getVoidTy(C&: M.getContext()); |
| 229 | auto *FnTy = FunctionType::get(Result: VoidTy, Params: InitArgTypes, isVarArg: false); |
| 230 | auto FnCallee = M.getOrInsertFunction(Name: InitName, T: FnTy); |
| 231 | auto *Fn = cast<Function>(Val: FnCallee.getCallee()); |
| 232 | if (Weak && Fn->isDeclaration()) |
| 233 | Fn->setLinkage(Function::ExternalWeakLinkage); |
| 234 | return FnCallee; |
| 235 | } |
| 236 | |
| 237 | Function *llvm::createSanitizerCtor(Module &M, StringRef CtorName) { |
| 238 | Function *Ctor = Function::createWithDefaultAttr( |
| 239 | Ty: FunctionType::get(Result: Type::getVoidTy(C&: M.getContext()), isVarArg: false), |
| 240 | Linkage: GlobalValue::InternalLinkage, AddrSpace: M.getDataLayout().getProgramAddressSpace(), |
| 241 | N: CtorName, M: &M); |
| 242 | Ctor->addFnAttr(Kind: Attribute::NoUnwind); |
| 243 | setKCFIType(M, F&: *Ctor, MangledType: "_ZTSFvvE" ); // void (*)(void) |
| 244 | BasicBlock *CtorBB = BasicBlock::Create(Context&: M.getContext(), Name: "" , Parent: Ctor); |
| 245 | ReturnInst::Create(C&: M.getContext(), InsertAtEnd: CtorBB); |
| 246 | // Ensure Ctor cannot be discarded, even if in a comdat. |
| 247 | appendToUsed(M, Values: {Ctor}); |
| 248 | return Ctor; |
| 249 | } |
| 250 | |
| 251 | std::pair<Function *, FunctionCallee> llvm::createSanitizerCtorAndInitFunctions( |
| 252 | Module &M, StringRef CtorName, StringRef InitName, |
| 253 | ArrayRef<Type *> InitArgTypes, ArrayRef<Value *> InitArgs, |
| 254 | StringRef VersionCheckName, bool Weak) { |
| 255 | assert(!InitName.empty() && "Expected init function name" ); |
| 256 | assert(InitArgs.size() == InitArgTypes.size() && |
| 257 | "Sanitizer's init function expects different number of arguments" ); |
| 258 | FunctionCallee InitFunction = |
| 259 | declareSanitizerInitFunction(M, InitName, InitArgTypes, Weak); |
| 260 | Function *Ctor = createSanitizerCtor(M, CtorName); |
| 261 | IRBuilder<> IRB(M.getContext()); |
| 262 | |
| 263 | BasicBlock *RetBB = &Ctor->getEntryBlock(); |
| 264 | if (Weak) { |
| 265 | RetBB->setName("ret" ); |
| 266 | auto *EntryBB = BasicBlock::Create(Context&: M.getContext(), Name: "entry" , Parent: Ctor, InsertBefore: RetBB); |
| 267 | auto *CallInitBB = |
| 268 | BasicBlock::Create(Context&: M.getContext(), Name: "callfunc" , Parent: Ctor, InsertBefore: RetBB); |
| 269 | auto *InitFn = cast<Function>(Val: InitFunction.getCallee()); |
| 270 | auto *InitFnPtr = |
| 271 | PointerType::get(C&: M.getContext(), AddressSpace: InitFn->getAddressSpace()); |
| 272 | IRB.SetInsertPoint(EntryBB); |
| 273 | Value *InitNotNull = |
| 274 | IRB.CreateICmpNE(LHS: InitFn, RHS: ConstantPointerNull::get(T: InitFnPtr)); |
| 275 | IRB.CreateCondBr(Cond: InitNotNull, True: CallInitBB, False: RetBB); |
| 276 | IRB.SetInsertPoint(CallInitBB); |
| 277 | } else { |
| 278 | IRB.SetInsertPoint(RetBB->getTerminator()); |
| 279 | } |
| 280 | |
| 281 | IRB.CreateCall(Callee: InitFunction, Args: InitArgs); |
| 282 | if (!VersionCheckName.empty()) { |
| 283 | FunctionCallee VersionCheckFunction = M.getOrInsertFunction( |
| 284 | Name: VersionCheckName, T: FunctionType::get(Result: IRB.getVoidTy(), Params: {}, isVarArg: false), |
| 285 | AttributeList: AttributeList()); |
| 286 | IRB.CreateCall(Callee: VersionCheckFunction, Args: {}); |
| 287 | } |
| 288 | |
| 289 | if (Weak) |
| 290 | IRB.CreateBr(Dest: RetBB); |
| 291 | |
| 292 | return std::make_pair(x&: Ctor, y&: InitFunction); |
| 293 | } |
| 294 | |
| 295 | std::pair<Function *, FunctionCallee> |
| 296 | llvm::getOrCreateSanitizerCtorAndInitFunctions( |
| 297 | Module &M, StringRef CtorName, StringRef InitName, |
| 298 | ArrayRef<Type *> InitArgTypes, ArrayRef<Value *> InitArgs, |
| 299 | function_ref<void(Function *, FunctionCallee)> FunctionsCreatedCallback, |
| 300 | StringRef VersionCheckName, bool Weak) { |
| 301 | assert(!CtorName.empty() && "Expected ctor function name" ); |
| 302 | |
| 303 | if (Function *Ctor = M.getFunction(Name: CtorName)) |
| 304 | // FIXME: Sink this logic into the module, similar to the handling of |
| 305 | // globals. This will make moving to a concurrent model much easier. |
| 306 | if (Ctor->arg_empty() || |
| 307 | Ctor->getReturnType() == Type::getVoidTy(C&: M.getContext())) |
| 308 | return {Ctor, |
| 309 | declareSanitizerInitFunction(M, InitName, InitArgTypes, Weak)}; |
| 310 | |
| 311 | Function *Ctor; |
| 312 | FunctionCallee InitFunction; |
| 313 | std::tie(args&: Ctor, args&: InitFunction) = llvm::createSanitizerCtorAndInitFunctions( |
| 314 | M, CtorName, InitName, InitArgTypes, InitArgs, VersionCheckName, Weak); |
| 315 | FunctionsCreatedCallback(Ctor, InitFunction); |
| 316 | return std::make_pair(x&: Ctor, y&: InitFunction); |
| 317 | } |
| 318 | |
| 319 | void llvm::filterDeadComdatFunctions( |
| 320 | SmallVectorImpl<Function *> &DeadComdatFunctions) { |
| 321 | SmallPtrSet<Function *, 32> MaybeDeadFunctions; |
| 322 | SmallPtrSet<Comdat *, 32> MaybeDeadComdats; |
| 323 | for (Function *F : DeadComdatFunctions) { |
| 324 | MaybeDeadFunctions.insert(Ptr: F); |
| 325 | if (Comdat *C = F->getComdat()) |
| 326 | MaybeDeadComdats.insert(Ptr: C); |
| 327 | } |
| 328 | |
| 329 | // Find comdats for which all users are dead now. |
| 330 | SmallPtrSet<Comdat *, 32> DeadComdats; |
| 331 | for (Comdat *C : MaybeDeadComdats) { |
| 332 | auto IsUserDead = [&](GlobalObject *GO) { |
| 333 | auto *F = dyn_cast<Function>(Val: GO); |
| 334 | return F && MaybeDeadFunctions.contains(Ptr: F); |
| 335 | }; |
| 336 | if (all_of(Range: C->getUsers(), P: IsUserDead)) |
| 337 | DeadComdats.insert(Ptr: C); |
| 338 | } |
| 339 | |
| 340 | // Only keep functions which have no comdat or a dead comdat. |
| 341 | erase_if(C&: DeadComdatFunctions, P: [&](Function *F) { |
| 342 | Comdat *C = F->getComdat(); |
| 343 | return C && !DeadComdats.contains(Ptr: C); |
| 344 | }); |
| 345 | } |
| 346 | |
| 347 | std::string llvm::getUniqueModuleId(Module *M) { |
| 348 | MD5 Md5; |
| 349 | |
| 350 | auto *UniqueSourceFileIdentifier = dyn_cast_or_null<MDNode>( |
| 351 | Val: M->getModuleFlag(Key: "Unique Source File Identifier" )); |
| 352 | if (UniqueSourceFileIdentifier) { |
| 353 | Md5.update( |
| 354 | Str: cast<MDString>(Val: UniqueSourceFileIdentifier->getOperand(I: 0))->getString()); |
| 355 | } else { |
| 356 | bool ExportsSymbols = false; |
| 357 | for (auto &GV : M->global_values()) { |
| 358 | if (GV.isDeclaration() || GV.getName().starts_with(Prefix: "llvm." ) || |
| 359 | !GV.hasExternalLinkage() || GV.hasComdat()) |
| 360 | continue; |
| 361 | ExportsSymbols = true; |
| 362 | Md5.update(Str: GV.getName()); |
| 363 | Md5.update(Data: ArrayRef<uint8_t>{0}); |
| 364 | } |
| 365 | |
| 366 | if (!ExportsSymbols) |
| 367 | return "" ; |
| 368 | } |
| 369 | |
| 370 | MD5::MD5Result R; |
| 371 | Md5.final(Result&: R); |
| 372 | |
| 373 | SmallString<32> Str; |
| 374 | MD5::stringifyResult(Result&: R, Str); |
| 375 | return ("." + Str).str(); |
| 376 | } |
| 377 | |
| 378 | void llvm::embedBufferInModule(Module &M, MemoryBufferRef Buf, |
| 379 | StringRef SectionName, Align Alignment) { |
| 380 | // Embed the memory buffer into the module. |
| 381 | Constant *ModuleConstant = ConstantDataArray::get( |
| 382 | Context&: M.getContext(), Elts: ArrayRef(Buf.getBufferStart(), Buf.getBufferSize())); |
| 383 | GlobalVariable *GV = new GlobalVariable( |
| 384 | M, ModuleConstant->getType(), true, GlobalValue::PrivateLinkage, |
| 385 | ModuleConstant, "llvm.embedded.object" ); |
| 386 | GV->setSection(SectionName); |
| 387 | GV->setAlignment(Alignment); |
| 388 | |
| 389 | LLVMContext &Ctx = M.getContext(); |
| 390 | NamedMDNode *MD = M.getOrInsertNamedMetadata(Name: "llvm.embedded.objects" ); |
| 391 | Metadata *MDVals[] = {ConstantAsMetadata::get(C: GV), |
| 392 | MDString::get(Context&: Ctx, Str: SectionName)}; |
| 393 | |
| 394 | MD->addOperand(M: llvm::MDNode::get(Context&: Ctx, MDs: MDVals)); |
| 395 | GV->setMetadata(KindID: LLVMContext::MD_exclude, Node: llvm::MDNode::get(Context&: Ctx, MDs: {})); |
| 396 | |
| 397 | appendToCompilerUsed(M, Values: GV); |
| 398 | } |
| 399 | |
| 400 | bool llvm::lowerGlobalIFuncUsersAsGlobalCtor( |
| 401 | Module &M, ArrayRef<GlobalIFunc *> FilteredIFuncsToLower) { |
| 402 | SmallVector<GlobalIFunc *, 32> AllIFuncs; |
| 403 | ArrayRef<GlobalIFunc *> IFuncsToLower = FilteredIFuncsToLower; |
| 404 | if (FilteredIFuncsToLower.empty()) { // Default to lowering all ifuncs |
| 405 | for (GlobalIFunc &GI : M.ifuncs()) |
| 406 | AllIFuncs.push_back(Elt: &GI); |
| 407 | IFuncsToLower = AllIFuncs; |
| 408 | } |
| 409 | |
| 410 | bool UnhandledUsers = false; |
| 411 | LLVMContext &Ctx = M.getContext(); |
| 412 | const DataLayout &DL = M.getDataLayout(); |
| 413 | |
| 414 | PointerType *TableEntryTy = |
| 415 | PointerType::get(C&: Ctx, AddressSpace: DL.getProgramAddressSpace()); |
| 416 | |
| 417 | ArrayType *FuncPtrTableTy = |
| 418 | ArrayType::get(ElementType: TableEntryTy, NumElements: IFuncsToLower.size()); |
| 419 | |
| 420 | Align PtrAlign = DL.getABITypeAlign(Ty: TableEntryTy); |
| 421 | |
| 422 | // Create a global table of function pointers we'll initialize in a global |
| 423 | // constructor. |
| 424 | auto *FuncPtrTable = new GlobalVariable( |
| 425 | M, FuncPtrTableTy, false, GlobalValue::InternalLinkage, |
| 426 | PoisonValue::get(T: FuncPtrTableTy), "" , nullptr, |
| 427 | GlobalVariable::NotThreadLocal, DL.getDefaultGlobalsAddressSpace()); |
| 428 | FuncPtrTable->setAlignment(PtrAlign); |
| 429 | |
| 430 | // Create a function to initialize the function pointer table. |
| 431 | Function *NewCtor = Function::Create( |
| 432 | Ty: FunctionType::get(Result: Type::getVoidTy(C&: Ctx), isVarArg: false), Linkage: Function::InternalLinkage, |
| 433 | AddrSpace: DL.getProgramAddressSpace(), N: "" , M: &M); |
| 434 | |
| 435 | BasicBlock *BB = BasicBlock::Create(Context&: Ctx, Name: "" , Parent: NewCtor); |
| 436 | IRBuilder<> InitBuilder(BB); |
| 437 | |
| 438 | size_t TableIndex = 0; |
| 439 | for (GlobalIFunc *GI : IFuncsToLower) { |
| 440 | Function *ResolvedFunction = GI->getResolverFunction(); |
| 441 | |
| 442 | // We don't know what to pass to a resolver function taking arguments |
| 443 | // |
| 444 | // FIXME: Is this even valid? clang and gcc don't complain but this |
| 445 | // probably should be invalid IR. We could just pass through undef. |
| 446 | if (!std::empty(cont: ResolvedFunction->getFunctionType()->params())) { |
| 447 | LLVM_DEBUG(dbgs() << "Not lowering ifunc resolver function " |
| 448 | << ResolvedFunction->getName() << " with parameters\n" ); |
| 449 | UnhandledUsers = true; |
| 450 | continue; |
| 451 | } |
| 452 | |
| 453 | // Initialize the function pointer table. |
| 454 | CallInst *ResolvedFunc = InitBuilder.CreateCall(Callee: ResolvedFunction); |
| 455 | Value *Casted = InitBuilder.CreatePointerCast(V: ResolvedFunc, DestTy: TableEntryTy); |
| 456 | Constant *GEP = cast<Constant>(Val: InitBuilder.CreateConstInBoundsGEP2_32( |
| 457 | Ty: FuncPtrTableTy, Ptr: FuncPtrTable, Idx0: 0, Idx1: TableIndex++)); |
| 458 | InitBuilder.CreateAlignedStore(Val: Casted, Ptr: GEP, Align: PtrAlign); |
| 459 | |
| 460 | // Update all users to load a pointer from the global table. |
| 461 | for (User *User : make_early_inc_range(Range: GI->users())) { |
| 462 | Instruction *UserInst = dyn_cast<Instruction>(Val: User); |
| 463 | if (!UserInst) { |
| 464 | // TODO: Should handle constantexpr casts in user instructions. Probably |
| 465 | // can't do much about constant initializers. |
| 466 | UnhandledUsers = true; |
| 467 | continue; |
| 468 | } |
| 469 | |
| 470 | IRBuilder<> UseBuilder(UserInst); |
| 471 | LoadInst *ResolvedTarget = |
| 472 | UseBuilder.CreateAlignedLoad(Ty: TableEntryTy, Ptr: GEP, Align: PtrAlign); |
| 473 | Value *ResolvedCast = |
| 474 | UseBuilder.CreatePointerCast(V: ResolvedTarget, DestTy: GI->getType()); |
| 475 | UserInst->replaceUsesOfWith(From: GI, To: ResolvedCast); |
| 476 | } |
| 477 | |
| 478 | // If we handled all users, erase the ifunc. |
| 479 | if (GI->use_empty()) |
| 480 | GI->eraseFromParent(); |
| 481 | } |
| 482 | |
| 483 | InitBuilder.CreateRetVoid(); |
| 484 | |
| 485 | PointerType *ConstantDataTy = PointerType::get(C&: Ctx, AddressSpace: 0); |
| 486 | |
| 487 | // TODO: Is this the right priority? Probably should be before any other |
| 488 | // constructors? |
| 489 | const int Priority = 10; |
| 490 | appendToGlobalCtors(M, F: NewCtor, Priority, |
| 491 | Data: ConstantPointerNull::get(T: ConstantDataTy)); |
| 492 | return UnhandledUsers; |
| 493 | } |
| 494 | |