1 //===- llvm-jitlink.cpp -- Command line interface/tester for llvm-jitlink -===//
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 // This utility provides a simple command line interface to the llvm jitlink
10 // library, which makes relocatable object files executable in memory. Its
11 // primary function is as a testing utility for the jitlink library.
13 //===----------------------------------------------------------------------===//
15 #include "llvm-jitlink.h"
17 #include "llvm/ExecutionEngine/JITLink/EHFrameSupport.h"
18 #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
19 #include "llvm/MC/MCAsmInfo.h"
20 #include "llvm/MC/MCContext.h"
21 #include "llvm/MC/MCDisassembler/MCDisassembler.h"
22 #include "llvm/MC/MCInstPrinter.h"
23 #include "llvm/MC/MCInstrInfo.h"
24 #include "llvm/MC/MCRegisterInfo.h"
25 #include "llvm/MC/MCSubtargetInfo.h"
26 #include "llvm/Object/COFF.h"
27 #include "llvm/Object/MachO.h"
28 #include "llvm/Object/ObjectFile.h"
29 #include "llvm/Support/CommandLine.h"
30 #include "llvm/Support/Debug.h"
31 #include "llvm/Support/DynamicLibrary.h"
32 #include "llvm/Support/InitLLVM.h"
33 #include "llvm/Support/MemoryBuffer.h"
34 #include "llvm/Support/TargetRegistry.h"
35 #include "llvm/Support/TargetSelect.h"
36 #include "llvm/Support/Timer.h"
41 #define DEBUG_TYPE "llvm-jitlink"
44 using namespace llvm::jitlink
;
45 using namespace llvm::orc
;
47 static cl::list
<std::string
> InputFiles(cl::Positional
, cl::OneOrMore
,
48 cl::desc("input files"));
50 static cl::opt
<bool> NoExec("noexec", cl::desc("Do not execute loaded code"),
53 static cl::list
<std::string
>
54 CheckFiles("check", cl::desc("File containing verifier checks"),
57 static cl::opt
<std::string
>
58 EntryPointName("entry", cl::desc("Symbol to call as main entry point"),
61 static cl::list
<std::string
> JITLinkDylibs(
62 "jld", cl::desc("Specifies the JITDylib to be used for any subsequent "
63 "input file arguments"));
65 static cl::list
<std::string
>
66 Dylibs("dlopen", cl::desc("Dynamic libraries to load before linking"),
69 static cl::list
<std::string
> InputArgv("args", cl::Positional
,
70 cl::desc("<program arguments>..."),
71 cl::ZeroOrMore
, cl::PositionalEatsArgs
);
74 NoProcessSymbols("no-process-syms",
75 cl::desc("Do not resolve to llvm-jitlink process symbols"),
78 static cl::list
<std::string
> AbsoluteDefs(
80 cl::desc("Inject absolute symbol definitions (syntax: <name>=<addr>)"),
83 static cl::opt
<bool> ShowAddrs(
85 cl::desc("Print registered symbol, section, got and stub addresses"),
88 static cl::opt
<bool> ShowAtomGraph(
90 cl::desc("Print the atom graph after fixups have been applied"),
93 static cl::opt
<bool> ShowSizes(
95 cl::desc("Show sizes pre- and post-dead stripping, and allocations"),
98 static cl::opt
<bool> ShowTimes("show-times",
99 cl::desc("Show times for llvm-jitlink phases"),
102 static cl::opt
<bool> ShowRelocatedSectionContents(
103 "show-relocated-section-contents",
104 cl::desc("show section contents after fixups have been applied"),
107 ExitOnError ExitOnErr
;
112 operator<<(raw_ostream
&OS
, const Session::MemoryRegionInfo
&MRI
) {
113 return OS
<< "target addr = "
114 << format("0x%016" PRIx64
, MRI
.getTargetAddress())
115 << ", content: " << (const void *)MRI
.getContent().data() << " -- "
116 << (const void *)(MRI
.getContent().data() + MRI
.getContent().size())
117 << " (" << MRI
.getContent().size() << " bytes)";
121 operator<<(raw_ostream
&OS
, const Session::SymbolInfoMap
&SIM
) {
123 for (auto &SKV
: SIM
)
124 OS
<< " \"" << SKV
.first() << "\" " << SKV
.second
<< "\n";
129 operator<<(raw_ostream
&OS
, const Session::FileInfo
&FI
) {
130 for (auto &SIKV
: FI
.SectionInfos
)
131 OS
<< " Section \"" << SIKV
.first() << "\": " << SIKV
.second
<< "\n";
132 for (auto &GOTKV
: FI
.GOTEntryInfos
)
133 OS
<< " GOT \"" << GOTKV
.first() << "\": " << GOTKV
.second
<< "\n";
134 for (auto &StubKV
: FI
.StubInfos
)
135 OS
<< " Stub \"" << StubKV
.first() << "\": " << StubKV
.second
<< "\n";
140 operator<<(raw_ostream
&OS
, const Session::FileInfoMap
&FIM
) {
141 for (auto &FIKV
: FIM
)
142 OS
<< "File \"" << FIKV
.first() << "\":\n" << FIKV
.second
;
146 static uint64_t computeTotalAtomSizes(AtomGraph
&G
) {
147 uint64_t TotalSize
= 0;
148 for (auto *DA
: G
.defined_atoms())
149 if (DA
->isZeroFill())
150 TotalSize
+= DA
->getZeroFillSize();
152 TotalSize
+= DA
->getContent().size();
156 static void dumpSectionContents(raw_ostream
&OS
, AtomGraph
&G
) {
157 constexpr JITTargetAddress DumpWidth
= 16;
158 static_assert(isPowerOf2_64(DumpWidth
), "DumpWidth must be a power of two");
160 // Put sections in address order.
161 std::vector
<Section
*> Sections
;
162 for (auto &S
: G
.sections())
163 Sections
.push_back(&S
);
165 std::sort(Sections
.begin(), Sections
.end(),
166 [](const Section
*LHS
, const Section
*RHS
) {
167 if (LHS
->atoms_empty() && RHS
->atoms_empty())
169 if (LHS
->atoms_empty())
171 if (RHS
->atoms_empty())
173 return (*LHS
->atoms().begin())->getAddress() <
174 (*RHS
->atoms().begin())->getAddress();
177 for (auto *S
: Sections
) {
178 OS
<< S
->getName() << " content:";
179 if (S
->atoms_empty()) {
180 OS
<< "\n section empty\n";
184 // Sort atoms into order, then render.
185 std::vector
<DefinedAtom
*> Atoms(S
->atoms().begin(), S
->atoms().end());
186 std::sort(Atoms
.begin(), Atoms
.end(),
187 [](const DefinedAtom
*LHS
, const DefinedAtom
*RHS
) {
188 return LHS
->getAddress() < RHS
->getAddress();
191 JITTargetAddress NextAddr
= Atoms
.front()->getAddress() & ~(DumpWidth
- 1);
192 for (auto *DA
: Atoms
) {
193 bool IsZeroFill
= DA
->isZeroFill();
194 JITTargetAddress AtomStart
= DA
->getAddress();
195 JITTargetAddress AtomSize
=
196 IsZeroFill
? DA
->getZeroFillSize() : DA
->getContent().size();
197 JITTargetAddress AtomEnd
= AtomStart
+ AtomSize
;
198 const uint8_t *AtomData
=
199 IsZeroFill
? nullptr : DA
->getContent().bytes_begin();
201 // Pad any space before the atom starts.
202 while (NextAddr
!= AtomStart
) {
203 if (NextAddr
% DumpWidth
== 0)
204 OS
<< formatv("\n{0:x16}:", NextAddr
);
209 // Render the atom content.
210 while (NextAddr
!= AtomEnd
) {
211 if (NextAddr
% DumpWidth
== 0)
212 OS
<< formatv("\n{0:x16}:", NextAddr
);
216 OS
<< formatv(" {0:x-2}", AtomData
[NextAddr
- AtomStart
]);
224 Session::Session(Triple TT
) : ObjLayer(ES
, MemMgr
), TT(std::move(TT
)) {
226 /// Local ObjectLinkingLayer::Plugin class to forward modifyPassConfig to the
228 class JITLinkSessionPlugin
: public ObjectLinkingLayer::Plugin
{
230 JITLinkSessionPlugin(Session
&S
) : S(S
) {}
231 void modifyPassConfig(MaterializationResponsibility
&MR
, const Triple
&TT
,
232 PassConfiguration
&PassConfig
) {
233 S
.modifyPassConfig(TT
, PassConfig
);
240 if (!NoExec
&& !TT
.isOSWindows())
241 ObjLayer
.addPlugin(std::make_unique
<EHFrameRegistrationPlugin
>(
242 InProcessEHFrameRegistrar::getInstance()));
244 ObjLayer
.addPlugin(std::make_unique
<JITLinkSessionPlugin
>(*this));
247 void Session::dumpSessionInfo(raw_ostream
&OS
) {
248 OS
<< "Registered addresses:\n" << SymbolInfos
<< FileInfos
;
251 void Session::modifyPassConfig(const Triple
&FTT
,
252 PassConfiguration
&PassConfig
) {
253 if (!CheckFiles
.empty())
254 PassConfig
.PostFixupPasses
.push_back([this](AtomGraph
&G
) {
255 if (TT
.getObjectFormat() == Triple::MachO
)
256 return registerMachOStubsAndGOT(*this, G
);
257 return make_error
<StringError
>("Unsupported object format for GOT/stub "
259 inconvertibleErrorCode());
263 PassConfig
.PostFixupPasses
.push_back([](AtomGraph
&G
) -> Error
{
264 outs() << "Atom graph post-fixup:\n";
266 return Error::success();
271 PassConfig
.PrePrunePasses
.push_back([this](AtomGraph
&G
) -> Error
{
272 SizeBeforePruning
+= computeTotalAtomSizes(G
);
273 return Error::success();
275 PassConfig
.PostFixupPasses
.push_back([this](AtomGraph
&G
) -> Error
{
276 SizeAfterFixups
+= computeTotalAtomSizes(G
);
277 return Error::success();
281 if (ShowRelocatedSectionContents
)
282 PassConfig
.PostFixupPasses
.push_back([](AtomGraph
&G
) -> Error
{
283 outs() << "Relocated section contents for " << G
.getName() << ":\n";
284 dumpSectionContents(outs(), G
);
285 return Error::success();
289 Expected
<Session::FileInfo
&> Session::findFileInfo(StringRef FileName
) {
290 auto FileInfoItr
= FileInfos
.find(FileName
);
291 if (FileInfoItr
== FileInfos
.end())
292 return make_error
<StringError
>("file \"" + FileName
+ "\" not recognized",
293 inconvertibleErrorCode());
294 return FileInfoItr
->second
;
297 Expected
<Session::MemoryRegionInfo
&>
298 Session::findSectionInfo(StringRef FileName
, StringRef SectionName
) {
299 auto FI
= findFileInfo(FileName
);
301 return FI
.takeError();
302 auto SecInfoItr
= FI
->SectionInfos
.find(SectionName
);
303 if (SecInfoItr
== FI
->SectionInfos
.end())
304 return make_error
<StringError
>("no section \"" + SectionName
+
305 "\" registered for file \"" + FileName
+
307 inconvertibleErrorCode());
308 return SecInfoItr
->second
;
311 Expected
<Session::MemoryRegionInfo
&>
312 Session::findStubInfo(StringRef FileName
, StringRef TargetName
) {
313 auto FI
= findFileInfo(FileName
);
315 return FI
.takeError();
316 auto StubInfoItr
= FI
->StubInfos
.find(TargetName
);
317 if (StubInfoItr
== FI
->StubInfos
.end())
318 return make_error
<StringError
>("no stub for \"" + TargetName
+
319 "\" registered for file \"" + FileName
+
321 inconvertibleErrorCode());
322 return StubInfoItr
->second
;
325 Expected
<Session::MemoryRegionInfo
&>
326 Session::findGOTEntryInfo(StringRef FileName
, StringRef TargetName
) {
327 auto FI
= findFileInfo(FileName
);
329 return FI
.takeError();
330 auto GOTInfoItr
= FI
->GOTEntryInfos
.find(TargetName
);
331 if (GOTInfoItr
== FI
->GOTEntryInfos
.end())
332 return make_error
<StringError
>("no GOT entry for \"" + TargetName
+
333 "\" registered for file \"" + FileName
+
335 inconvertibleErrorCode());
336 return GOTInfoItr
->second
;
339 bool Session::isSymbolRegistered(StringRef SymbolName
) {
340 return SymbolInfos
.count(SymbolName
);
343 Expected
<Session::MemoryRegionInfo
&>
344 Session::findSymbolInfo(StringRef SymbolName
, Twine ErrorMsgStem
) {
345 auto SymInfoItr
= SymbolInfos
.find(SymbolName
);
346 if (SymInfoItr
== SymbolInfos
.end())
347 return make_error
<StringError
>(ErrorMsgStem
+ ": symbol " + SymbolName
+
349 inconvertibleErrorCode());
350 return SymInfoItr
->second
;
353 } // end namespace llvm
355 Triple
getFirstFileTriple() {
356 assert(!InputFiles
.empty() && "InputFiles can not be empty");
358 ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(InputFiles
.front())));
359 auto Obj
= ExitOnErr(
360 object::ObjectFile::createObjectFile(ObjBuffer
->getMemBufferRef()));
361 return Obj
->makeTriple();
364 Error
sanitizeArguments(const Session
&S
) {
365 if (EntryPointName
.empty()) {
366 if (S
.TT
.getObjectFormat() == Triple::MachO
)
367 EntryPointName
= "_main";
369 EntryPointName
= "main";
372 if (NoExec
&& !InputArgv
.empty())
373 outs() << "Warning: --args passed to -noexec run will be ignored.\n";
375 return Error::success();
378 Error
loadProcessSymbols(Session
&S
) {
380 if (sys::DynamicLibrary::LoadLibraryPermanently(nullptr, &ErrMsg
))
381 return make_error
<StringError
>(std::move(ErrMsg
), inconvertibleErrorCode());
383 char GlobalPrefix
= S
.TT
.getObjectFormat() == Triple::MachO
? '_' : '\0';
384 auto InternedEntryPointName
= S
.ES
.intern(EntryPointName
);
385 auto FilterMainEntryPoint
= [InternedEntryPointName
](SymbolStringPtr Name
) {
386 return Name
!= InternedEntryPointName
;
388 S
.ES
.getMainJITDylib().addGenerator(
389 ExitOnErr(orc::DynamicLibrarySearchGenerator::GetForCurrentProcess(
390 GlobalPrefix
, FilterMainEntryPoint
)));
392 return Error::success();
396 // FIXME: This should all be handled inside DynamicLibrary.
397 for (const auto &Dylib
: Dylibs
) {
398 if (!sys::fs::is_regular_file(Dylib
))
399 return make_error
<StringError
>("\"" + Dylib
+ "\" is not a regular file",
400 inconvertibleErrorCode());
402 if (sys::DynamicLibrary::LoadLibraryPermanently(Dylib
.c_str(), &ErrMsg
))
403 return make_error
<StringError
>(ErrMsg
, inconvertibleErrorCode());
406 return Error::success();
409 Error
loadObjects(Session
&S
) {
411 std::map
<unsigned, JITDylib
*> IdxToJLD
;
413 // First, set up JITDylibs.
414 LLVM_DEBUG(dbgs() << "Creating JITDylibs...\n");
416 // Create a "main" JITLinkDylib.
417 auto &MainJD
= S
.ES
.getMainJITDylib();
418 IdxToJLD
[0] = &MainJD
;
419 S
.JDSearchOrder
.push_back(&MainJD
);
420 LLVM_DEBUG(dbgs() << " 0: " << MainJD
.getName() << "\n");
422 // Add any extra JITLinkDylibs from the command line.
423 std::string
JDNamePrefix("lib");
424 for (auto JLDItr
= JITLinkDylibs
.begin(), JLDEnd
= JITLinkDylibs
.end();
425 JLDItr
!= JLDEnd
; ++JLDItr
) {
426 auto &JD
= S
.ES
.createJITDylib(JDNamePrefix
+ *JLDItr
);
428 JITLinkDylibs
.getPosition(JLDItr
- JITLinkDylibs
.begin());
429 IdxToJLD
[JDIdx
] = &JD
;
430 S
.JDSearchOrder
.push_back(&JD
);
431 LLVM_DEBUG(dbgs() << " " << JDIdx
<< ": " << JD
.getName() << "\n");
434 // Set every dylib to link against every other, in command line order.
435 for (auto *JD
: S
.JDSearchOrder
) {
436 JITDylibSearchList O
;
437 for (auto *JD2
: S
.JDSearchOrder
) {
440 O
.push_back(std::make_pair(JD2
, false));
442 JD
->setSearchOrder(std::move(O
));
446 // Load each object into the corresponding JITDylib..
447 LLVM_DEBUG(dbgs() << "Adding objects...\n");
448 for (auto InputFileItr
= InputFiles
.begin(), InputFileEnd
= InputFiles
.end();
449 InputFileItr
!= InputFileEnd
; ++InputFileItr
) {
450 unsigned InputFileArgIdx
=
451 InputFiles
.getPosition(InputFileItr
- InputFiles
.begin());
452 StringRef InputFile
= *InputFileItr
;
453 auto &JD
= *std::prev(IdxToJLD
.lower_bound(InputFileArgIdx
))->second
;
454 LLVM_DEBUG(dbgs() << " " << InputFileArgIdx
<< ": \"" << InputFile
455 << "\" to " << JD
.getName() << "\n";);
457 ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(InputFile
)));
458 ExitOnErr(S
.ObjLayer
.add(JD
, std::move(ObjBuffer
)));
461 // Define absolute symbols.
462 LLVM_DEBUG(dbgs() << "Defining absolute symbols...\n");
463 for (auto AbsDefItr
= AbsoluteDefs
.begin(), AbsDefEnd
= AbsoluteDefs
.end();
464 AbsDefItr
!= AbsDefEnd
; ++AbsDefItr
) {
465 unsigned AbsDefArgIdx
=
466 AbsoluteDefs
.getPosition(AbsDefItr
- AbsoluteDefs
.begin());
467 auto &JD
= *std::prev(IdxToJLD
.lower_bound(AbsDefArgIdx
))->second
;
469 StringRef AbsDefStmt
= *AbsDefItr
;
470 size_t EqIdx
= AbsDefStmt
.find_first_of('=');
471 if (EqIdx
== StringRef::npos
)
472 return make_error
<StringError
>("Invalid absolute define \"" + AbsDefStmt
+
473 "\". Syntax: <name>=<addr>",
474 inconvertibleErrorCode());
475 StringRef Name
= AbsDefStmt
.substr(0, EqIdx
).trim();
476 StringRef AddrStr
= AbsDefStmt
.substr(EqIdx
+ 1).trim();
479 if (AddrStr
.getAsInteger(0, Addr
))
480 return make_error
<StringError
>("Invalid address expression \"" + AddrStr
+
481 "\" in absolute define \"" + AbsDefStmt
+
483 inconvertibleErrorCode());
484 JITEvaluatedSymbol
AbsDef(Addr
, JITSymbolFlags::Exported
);
485 if (auto Err
= JD
.define(absoluteSymbols({{S
.ES
.intern(Name
), AbsDef
}})))
488 // Register the absolute symbol with the session symbol infos.
489 S
.SymbolInfos
[Name
] = { StringRef(), Addr
};
493 dbgs() << "Dylib search order is [ ";
494 for (auto *JD
: S
.JDSearchOrder
)
495 dbgs() << JD
->getName() << " ";
499 return Error::success();
502 Error
runChecks(Session
&S
) {
504 auto TripleName
= S
.TT
.str();
505 std::string ErrorStr
;
506 const Target
*TheTarget
= TargetRegistry::lookupTarget("", S
.TT
, ErrorStr
);
508 ExitOnErr(make_error
<StringError
>("Error accessing target '" + TripleName
+
510 inconvertibleErrorCode()));
512 std::unique_ptr
<MCSubtargetInfo
> STI(
513 TheTarget
->createMCSubtargetInfo(TripleName
, "", ""));
516 make_error
<StringError
>("Unable to create subtarget for " + TripleName
,
517 inconvertibleErrorCode()));
519 std::unique_ptr
<MCRegisterInfo
> MRI(TheTarget
->createMCRegInfo(TripleName
));
521 ExitOnErr(make_error
<StringError
>("Unable to create target register info "
524 inconvertibleErrorCode()));
526 std::unique_ptr
<MCAsmInfo
> MAI(TheTarget
->createMCAsmInfo(*MRI
, TripleName
));
528 ExitOnErr(make_error
<StringError
>("Unable to create target asm info " +
530 inconvertibleErrorCode()));
532 MCContext
Ctx(MAI
.get(), MRI
.get(), nullptr);
534 std::unique_ptr
<MCDisassembler
> Disassembler(
535 TheTarget
->createMCDisassembler(*STI
, Ctx
));
537 ExitOnErr(make_error
<StringError
>("Unable to create disassembler for " +
539 inconvertibleErrorCode()));
541 std::unique_ptr
<MCInstrInfo
> MII(TheTarget
->createMCInstrInfo());
543 std::unique_ptr
<MCInstPrinter
> InstPrinter(
544 TheTarget
->createMCInstPrinter(Triple(TripleName
), 0, *MAI
, *MII
, *MRI
));
546 auto IsSymbolValid
= [&S
](StringRef Symbol
) {
547 return S
.isSymbolRegistered(Symbol
);
550 auto GetSymbolInfo
= [&S
](StringRef Symbol
) {
551 return S
.findSymbolInfo(Symbol
, "Can not get symbol info");
554 auto GetSectionInfo
= [&S
](StringRef FileName
, StringRef SectionName
) {
555 return S
.findSectionInfo(FileName
, SectionName
);
558 auto GetStubInfo
= [&S
](StringRef FileName
, StringRef SectionName
) {
559 return S
.findStubInfo(FileName
, SectionName
);
562 auto GetGOTInfo
= [&S
](StringRef FileName
, StringRef SectionName
) {
563 return S
.findGOTEntryInfo(FileName
, SectionName
);
566 RuntimeDyldChecker
Checker(
567 IsSymbolValid
, GetSymbolInfo
, GetSectionInfo
, GetStubInfo
, GetGOTInfo
,
568 S
.TT
.isLittleEndian() ? support::little
: support::big
,
569 Disassembler
.get(), InstPrinter
.get(), dbgs());
571 for (auto &CheckFile
: CheckFiles
) {
572 auto CheckerFileBuf
=
573 ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(CheckFile
)));
574 if (!Checker
.checkAllRulesInBuffer("# jitlink-check:", &*CheckerFileBuf
))
575 ExitOnErr(make_error
<StringError
>(
576 "Some checks in " + CheckFile
+ " failed", inconvertibleErrorCode()));
579 return Error::success();
582 static void dumpSessionStats(Session
&S
) {
584 outs() << "Total size of all atoms before pruning: " << S
.SizeBeforePruning
585 << "\nTotal size of all atoms after fixups: " << S
.SizeAfterFixups
589 static Expected
<JITEvaluatedSymbol
> getMainEntryPoint(Session
&S
) {
590 return S
.ES
.lookup(S
.JDSearchOrder
, EntryPointName
);
593 Expected
<int> runEntryPoint(Session
&S
, JITEvaluatedSymbol EntryPoint
) {
594 assert(EntryPoint
.getAddress() && "Entry point address should not be null");
596 constexpr const char *JITProgramName
= "<llvm-jitlink jit'd code>";
597 auto PNStorage
= std::make_unique
<char[]>(strlen(JITProgramName
) + 1);
598 strcpy(PNStorage
.get(), JITProgramName
);
600 std::vector
<const char *> EntryPointArgs
;
601 EntryPointArgs
.push_back(PNStorage
.get());
602 for (auto &InputArg
: InputArgv
)
603 EntryPointArgs
.push_back(InputArg
.data());
604 EntryPointArgs
.push_back(nullptr);
606 using MainTy
= int (*)(int, const char *[]);
607 MainTy EntryPointPtr
= reinterpret_cast<MainTy
>(EntryPoint
.getAddress());
609 return EntryPointPtr(EntryPointArgs
.size() - 1, EntryPointArgs
.data());
612 struct JITLinkTimers
{
613 TimerGroup JITLinkTG
{"llvm-jitlink timers", "timers for llvm-jitlink phases"};
614 Timer LoadObjectsTimer
{"load", "time to load/add object files", JITLinkTG
};
615 Timer LinkTimer
{"link", "time to link object files", JITLinkTG
};
616 Timer RunTimer
{"run", "time to execute jitlink'd code", JITLinkTG
};
619 int main(int argc
, char *argv
[]) {
620 InitLLVM
X(argc
, argv
);
622 InitializeAllTargetInfos();
623 InitializeAllTargetMCs();
624 InitializeAllDisassemblers();
626 cl::ParseCommandLineOptions(argc
, argv
, "llvm jitlink tool");
627 ExitOnErr
.setBanner(std::string(argv
[0]) + ": ");
629 /// If timers are enabled, create a JITLinkTimers instance.
630 std::unique_ptr
<JITLinkTimers
> Timers
=
631 ShowTimes
? std::make_unique
<JITLinkTimers
>() : nullptr;
633 Session
S(getFirstFileTriple());
635 ExitOnErr(sanitizeArguments(S
));
637 if (!NoProcessSymbols
)
638 ExitOnErr(loadProcessSymbols(S
));
639 ExitOnErr(loadDylibs());
643 TimeRegion
TR(Timers
? &Timers
->LoadObjectsTimer
: nullptr);
644 ExitOnErr(loadObjects(S
));
647 JITEvaluatedSymbol EntryPoint
= 0;
649 TimeRegion
TR(Timers
? &Timers
->LinkTimer
: nullptr);
650 EntryPoint
= ExitOnErr(getMainEntryPoint(S
));
654 S
.dumpSessionInfo(outs());
656 ExitOnErr(runChecks(S
));
665 TimeRegion
TR(Timers
? &Timers
->RunTimer
: nullptr);
666 Result
= ExitOnErr(runEntryPoint(S
, EntryPoint
));