1 //===---- IndirectionUtils.cpp - Utilities for call indirection in Orc ----===//
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
7 //===----------------------------------------------------------------------===//
9 #include "llvm/ExecutionEngine/Orc/IndirectionUtils.h"
10 #include "llvm/ExecutionEngine/JITLink/x86_64.h"
11 #include "llvm/ExecutionEngine/Orc/OrcABISupport.h"
12 #include "llvm/IR/IRBuilder.h"
13 #include "llvm/IR/Module.h"
14 #include "llvm/MC/MCDisassembler/MCDisassembler.h"
15 #include "llvm/MC/MCInstrAnalysis.h"
16 #include "llvm/TargetParser/Triple.h"
17 #include "llvm/Transforms/Utils/Cloning.h"
19 #define DEBUG_TYPE "orc"
22 using namespace llvm::orc
;
26 class CompileCallbackMaterializationUnit
: public orc::MaterializationUnit
{
28 using CompileFunction
= JITCompileCallbackManager::CompileFunction
;
30 CompileCallbackMaterializationUnit(SymbolStringPtr Name
,
31 CompileFunction Compile
)
32 : MaterializationUnit(Interface(
33 SymbolFlagsMap({{Name
, JITSymbolFlags::Exported
}}), nullptr)),
34 Name(std::move(Name
)), Compile(std::move(Compile
)) {}
36 StringRef
getName() const override
{ return "<Compile Callbacks>"; }
39 void materialize(std::unique_ptr
<MaterializationResponsibility
> R
) override
{
41 Result
[Name
] = {Compile(), JITSymbolFlags::Exported
};
42 // No dependencies, so these calls cannot fail.
43 cantFail(R
->notifyResolved(Result
));
44 cantFail(R
->notifyEmitted({}));
47 void discard(const JITDylib
&JD
, const SymbolStringPtr
&Name
) override
{
48 llvm_unreachable("Discard should never occur on a LMU?");
52 CompileFunction Compile
;
60 TrampolinePool::~TrampolinePool() = default;
61 void IndirectStubsManager::anchor() {}
63 Expected
<ExecutorAddr
>
64 JITCompileCallbackManager::getCompileCallback(CompileFunction Compile
) {
65 if (auto TrampolineAddr
= TP
->getTrampoline()) {
67 ES
.intern(std::string("cc") + std::to_string(++NextCallbackId
));
69 std::lock_guard
<std::mutex
> Lock(CCMgrMutex
);
70 AddrToSymbol
[*TrampolineAddr
] = CallbackName
;
72 CallbacksJD
.define(std::make_unique
<CompileCallbackMaterializationUnit
>(
73 std::move(CallbackName
), std::move(Compile
))));
74 return *TrampolineAddr
;
76 return TrampolineAddr
.takeError();
80 JITCompileCallbackManager::executeCompileCallback(ExecutorAddr TrampolineAddr
) {
84 std::unique_lock
<std::mutex
> Lock(CCMgrMutex
);
85 auto I
= AddrToSymbol
.find(TrampolineAddr
);
87 // If this address is not associated with a compile callback then report an
88 // error to the execution session and return ErrorHandlerAddress to the
90 if (I
== AddrToSymbol
.end()) {
93 make_error
<StringError
>("No compile callback for trampoline at " +
94 formatv("{0:x}", TrampolineAddr
),
95 inconvertibleErrorCode()));
96 return ErrorHandlerAddress
;
102 ES
.lookup(makeJITDylibSearchOrder(
103 &CallbacksJD
, JITDylibLookupFlags::MatchAllSymbols
),
105 return Sym
->getAddress();
107 llvm::dbgs() << "Didn't find callback.\n";
108 // If anything goes wrong materializing Sym then report it to the session
109 // and return the ErrorHandlerAddress;
110 ES
.reportError(Sym
.takeError());
111 return ErrorHandlerAddress
;
115 Error
IndirectStubsManager::redirect(JITDylib
&JD
, const SymbolMap
&NewDests
) {
116 for (auto &[Name
, Dest
] : NewDests
)
117 if (auto Err
= updatePointer(*Name
, Dest
.getAddress()))
119 return Error::success();
122 void IndirectStubsManager::emitRedirectableSymbols(
123 std::unique_ptr
<MaterializationResponsibility
> MR
, SymbolMap InitialDests
) {
124 StubInitsMap StubInits
;
125 for (auto &[Name
, Dest
] : InitialDests
)
126 StubInits
[*Name
] = {Dest
.getAddress(), Dest
.getFlags()};
127 if (auto Err
= createStubs(StubInits
)) {
128 MR
->getExecutionSession().reportError(std::move(Err
));
129 return MR
->failMaterialization();
132 for (auto &[Name
, Dest
] : InitialDests
) {
133 auto StubSym
= findStub(*Name
, false);
134 assert(StubSym
.getAddress() && "Stub symbol should be present");
135 Stubs
[Name
] = StubSym
;
137 if (auto Err
= MR
->notifyResolved(Stubs
)) {
138 MR
->getExecutionSession().reportError(std::move(Err
));
139 return MR
->failMaterialization();
141 if (auto Err
= MR
->notifyEmitted({})) {
142 MR
->getExecutionSession().reportError(std::move(Err
));
143 return MR
->failMaterialization();
147 Expected
<std::unique_ptr
<JITCompileCallbackManager
>>
148 createLocalCompileCallbackManager(const Triple
&T
, ExecutionSession
&ES
,
149 ExecutorAddr ErrorHandlerAddress
) {
150 switch (T
.getArch()) {
152 return make_error
<StringError
>(
153 std::string("No callback manager available for ") + T
.str(),
154 inconvertibleErrorCode());
155 case Triple::aarch64
:
156 case Triple::aarch64_32
: {
157 typedef orc::LocalJITCompileCallbackManager
<orc::OrcAArch64
> CCMgrT
;
158 return CCMgrT::Create(ES
, ErrorHandlerAddress
);
162 typedef orc::LocalJITCompileCallbackManager
<orc::OrcI386
> CCMgrT
;
163 return CCMgrT::Create(ES
, ErrorHandlerAddress
);
166 case Triple::loongarch64
: {
167 typedef orc::LocalJITCompileCallbackManager
<orc::OrcLoongArch64
> CCMgrT
;
168 return CCMgrT::Create(ES
, ErrorHandlerAddress
);
172 typedef orc::LocalJITCompileCallbackManager
<orc::OrcMips32Be
> CCMgrT
;
173 return CCMgrT::Create(ES
, ErrorHandlerAddress
);
175 case Triple::mipsel
: {
176 typedef orc::LocalJITCompileCallbackManager
<orc::OrcMips32Le
> CCMgrT
;
177 return CCMgrT::Create(ES
, ErrorHandlerAddress
);
181 case Triple::mips64el
: {
182 typedef orc::LocalJITCompileCallbackManager
<orc::OrcMips64
> CCMgrT
;
183 return CCMgrT::Create(ES
, ErrorHandlerAddress
);
186 case Triple::riscv64
: {
187 typedef orc::LocalJITCompileCallbackManager
<orc::OrcRiscv64
> CCMgrT
;
188 return CCMgrT::Create(ES
, ErrorHandlerAddress
);
191 case Triple::x86_64
: {
192 if (T
.getOS() == Triple::OSType::Win32
) {
193 typedef orc::LocalJITCompileCallbackManager
<orc::OrcX86_64_Win32
> CCMgrT
;
194 return CCMgrT::Create(ES
, ErrorHandlerAddress
);
196 typedef orc::LocalJITCompileCallbackManager
<orc::OrcX86_64_SysV
> CCMgrT
;
197 return CCMgrT::Create(ES
, ErrorHandlerAddress
);
204 std::function
<std::unique_ptr
<IndirectStubsManager
>()>
205 createLocalIndirectStubsManagerBuilder(const Triple
&T
) {
206 switch (T
.getArch()) {
209 return std::make_unique
<
210 orc::LocalIndirectStubsManager
<orc::OrcGenericABI
>>();
213 case Triple::aarch64
:
214 case Triple::aarch64_32
:
216 return std::make_unique
<
217 orc::LocalIndirectStubsManager
<orc::OrcAArch64
>>();
222 return std::make_unique
<
223 orc::LocalIndirectStubsManager
<orc::OrcI386
>>();
226 case Triple::loongarch64
:
228 return std::make_unique
<
229 orc::LocalIndirectStubsManager
<orc::OrcLoongArch64
>>();
234 return std::make_unique
<
235 orc::LocalIndirectStubsManager
<orc::OrcMips32Be
>>();
240 return std::make_unique
<
241 orc::LocalIndirectStubsManager
<orc::OrcMips32Le
>>();
245 case Triple::mips64el
:
247 return std::make_unique
<
248 orc::LocalIndirectStubsManager
<orc::OrcMips64
>>();
251 case Triple::riscv64
:
253 return std::make_unique
<
254 orc::LocalIndirectStubsManager
<orc::OrcRiscv64
>>();
258 if (T
.getOS() == Triple::OSType::Win32
) {
260 return std::make_unique
<
261 orc::LocalIndirectStubsManager
<orc::OrcX86_64_Win32
>>();
265 return std::make_unique
<
266 orc::LocalIndirectStubsManager
<orc::OrcX86_64_SysV
>>();
273 Constant
* createIRTypedAddress(FunctionType
&FT
, ExecutorAddr Addr
) {
274 Constant
*AddrIntVal
=
275 ConstantInt::get(Type::getInt64Ty(FT
.getContext()), Addr
.getValue());
276 Constant
*AddrPtrVal
=
277 ConstantExpr::getIntToPtr(AddrIntVal
, PointerType::get(&FT
, 0));
281 GlobalVariable
* createImplPointer(PointerType
&PT
, Module
&M
,
282 const Twine
&Name
, Constant
*Initializer
) {
283 auto IP
= new GlobalVariable(M
, &PT
, false, GlobalValue::ExternalLinkage
,
284 Initializer
, Name
, nullptr,
285 GlobalValue::NotThreadLocal
, 0, true);
286 IP
->setVisibility(GlobalValue::HiddenVisibility
);
290 void makeStub(Function
&F
, Value
&ImplPointer
) {
291 assert(F
.isDeclaration() && "Can't turn a definition into a stub.");
292 assert(F
.getParent() && "Function isn't in a module.");
293 Module
&M
= *F
.getParent();
294 BasicBlock
*EntryBlock
= BasicBlock::Create(M
.getContext(), "entry", &F
);
295 IRBuilder
<> Builder(EntryBlock
);
296 LoadInst
*ImplAddr
= Builder
.CreateLoad(F
.getType(), &ImplPointer
);
297 std::vector
<Value
*> CallArgs
;
298 for (auto &A
: F
.args())
299 CallArgs
.push_back(&A
);
300 CallInst
*Call
= Builder
.CreateCall(F
.getFunctionType(), ImplAddr
, CallArgs
);
302 Call
->setAttributes(F
.getAttributes());
303 if (F
.getReturnType()->isVoidTy())
304 Builder
.CreateRetVoid();
306 Builder
.CreateRet(Call
);
309 std::vector
<GlobalValue
*> SymbolLinkagePromoter::operator()(Module
&M
) {
310 std::vector
<GlobalValue
*> PromotedGlobals
;
312 for (auto &GV
: M
.global_values()) {
313 bool Promoted
= true;
315 // Rename if necessary.
317 GV
.setName("__orc_anon." + Twine(NextId
++));
318 else if (GV
.getName().starts_with("\01L"))
319 GV
.setName("__" + GV
.getName().substr(1) + "." + Twine(NextId
++));
320 else if (GV
.hasLocalLinkage())
321 GV
.setName("__orc_lcl." + GV
.getName() + "." + Twine(NextId
++));
325 if (GV
.hasLocalLinkage()) {
326 GV
.setLinkage(GlobalValue::ExternalLinkage
);
327 GV
.setVisibility(GlobalValue::HiddenVisibility
);
330 GV
.setUnnamedAddr(GlobalValue::UnnamedAddr::None
);
333 PromotedGlobals
.push_back(&GV
);
336 return PromotedGlobals
;
339 Function
* cloneFunctionDecl(Module
&Dst
, const Function
&F
,
340 ValueToValueMapTy
*VMap
) {
342 Function::Create(cast
<FunctionType
>(F
.getValueType()),
343 F
.getLinkage(), F
.getName(), &Dst
);
344 NewF
->copyAttributesFrom(&F
);
348 auto NewArgI
= NewF
->arg_begin();
349 for (auto ArgI
= F
.arg_begin(), ArgE
= F
.arg_end(); ArgI
!= ArgE
;
351 (*VMap
)[&*ArgI
] = &*NewArgI
;
357 GlobalVariable
* cloneGlobalVariableDecl(Module
&Dst
, const GlobalVariable
&GV
,
358 ValueToValueMapTy
*VMap
) {
359 GlobalVariable
*NewGV
= new GlobalVariable(
360 Dst
, GV
.getValueType(), GV
.isConstant(),
361 GV
.getLinkage(), nullptr, GV
.getName(), nullptr,
362 GV
.getThreadLocalMode(), GV
.getType()->getAddressSpace());
363 NewGV
->copyAttributesFrom(&GV
);
365 (*VMap
)[&GV
] = NewGV
;
369 GlobalAlias
* cloneGlobalAliasDecl(Module
&Dst
, const GlobalAlias
&OrigA
,
370 ValueToValueMapTy
&VMap
) {
371 assert(OrigA
.getAliasee() && "Original alias doesn't have an aliasee?");
372 auto *NewA
= GlobalAlias::create(OrigA
.getValueType(),
373 OrigA
.getType()->getPointerAddressSpace(),
374 OrigA
.getLinkage(), OrigA
.getName(), &Dst
);
375 NewA
->copyAttributesFrom(&OrigA
);
380 Error
addFunctionPointerRelocationsToCurrentSymbol(jitlink::Symbol
&Sym
,
381 jitlink::LinkGraph
&G
,
382 MCDisassembler
&Disassembler
,
383 MCInstrAnalysis
&MIA
) {
384 // AArch64 appears to already come with the necessary relocations. Among other
385 // architectures, only x86_64 is currently implemented here.
386 if (G
.getTargetTriple().getArch() != Triple::x86_64
)
387 return Error::success();
389 raw_null_ostream CommentStream
;
390 auto &STI
= Disassembler
.getSubtargetInfo();
392 // Determine the function bounds
393 auto &B
= Sym
.getBlock();
394 assert(!B
.isZeroFill() && "expected content block");
395 auto SymAddress
= Sym
.getAddress();
396 auto SymStartInBlock
=
397 (const uint8_t *)B
.getContent().data() + Sym
.getOffset();
398 auto SymSize
= Sym
.getSize() ? Sym
.getSize() : B
.getSize() - Sym
.getOffset();
399 auto Content
= ArrayRef(SymStartInBlock
, SymSize
);
401 LLVM_DEBUG(dbgs() << "Adding self-relocations to " << Sym
.getName() << "\n");
403 SmallDenseSet
<uintptr_t, 8> ExistingRelocations
;
404 for (auto &E
: B
.edges()) {
405 if (E
.isRelocation())
406 ExistingRelocations
.insert(E
.getOffset());
410 while (I
< Content
.size()) {
412 uint64_t InstrSize
= 0;
413 uint64_t InstrStart
= SymAddress
.getValue() + I
;
414 auto DecodeStatus
= Disassembler
.getInstruction(
415 Instr
, InstrSize
, Content
.drop_front(I
), InstrStart
, CommentStream
);
416 if (DecodeStatus
!= MCDisassembler::Success
) {
417 LLVM_DEBUG(dbgs() << "Aborting due to disassembly failure at address "
419 return make_error
<StringError
>(
420 formatv("failed to disassemble at address {0:x16}", InstrStart
),
421 inconvertibleErrorCode());
423 // Advance to the next instruction.
426 // Check for a PC-relative address equal to the symbol itself.
428 MIA
.evaluateMemoryOperandAddress(Instr
, &STI
, InstrStart
, InstrSize
);
429 if (!PCRelAddr
|| *PCRelAddr
!= SymAddress
.getValue())
432 auto RelocOffInInstr
=
433 MIA
.getMemoryOperandRelocationOffset(Instr
, InstrSize
);
434 if (!RelocOffInInstr
|| InstrSize
- *RelocOffInInstr
!= 4) {
435 LLVM_DEBUG(dbgs() << "Skipping unknown self-relocation at "
440 auto RelocOffInBlock
= orc::ExecutorAddr(InstrStart
) + *RelocOffInInstr
-
441 SymAddress
+ Sym
.getOffset();
442 if (ExistingRelocations
.contains(RelocOffInBlock
))
445 LLVM_DEBUG(dbgs() << "Adding delta32 self-relocation at " << InstrStart
);
446 B
.addEdge(jitlink::x86_64::Delta32
, RelocOffInBlock
, Sym
, /*Addend=*/-4);
448 return Error::success();
451 } // End namespace orc.
452 } // End namespace llvm.