1 //===- llvm-jitlink.cpp -- Command line interface/tester for llvm-jitlink -===//
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 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.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #include "llvm-jitlink.h"
16 
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 
37 #include <list>
38 #include <string>
39 
40 #define DEBUG_TYPE "llvm-jitlink"
41 
42 using namespace llvm;
43 using namespace llvm::jitlink;
44 using namespace llvm::orc;
45 
46 static cl::list<std::string> InputFiles(cl::Positional, cl::OneOrMore,
47                                         cl::desc("input files"));
48 
49 static cl::opt<bool> NoExec("noexec", cl::desc("Do not execute loaded code"),
50                             cl::init(false));
51 
52 static cl::list<std::string>
53     CheckFiles("check", cl::desc("File containing verifier checks"),
54                cl::ZeroOrMore);
55 
56 static cl::opt<std::string>
57     EntryPointName("entry", cl::desc("Symbol to call as main entry point"),
58                    cl::init(""));
59 
60 static cl::list<std::string> JITLinkDylibs(
61     "jld", cl::desc("Specifies the JITDylib to be used for any subsequent "
62                     "input file arguments"));
63 
64 static cl::list<std::string>
65     Dylibs("dlopen", cl::desc("Dynamic libraries to load before linking"),
66            cl::ZeroOrMore);
67 
68 static cl::list<std::string> InputArgv("args", cl::Positional,
69                                        cl::desc("<program arguments>..."),
70                                        cl::ZeroOrMore, cl::PositionalEatsArgs);
71 
72 static cl::opt<bool>
73     NoProcessSymbols("no-process-syms",
74                      cl::desc("Do not resolve to llvm-jitlink process symbols"),
75                      cl::init(false));
76 
77 static cl::list<std::string> AbsoluteDefs(
78     "define-abs",
79     cl::desc("Inject absolute symbol definitions (syntax: <name>=<addr>)"),
80     cl::ZeroOrMore);
81 
82 static cl::opt<bool> ShowAddrs(
83     "show-addrs",
84     cl::desc("Print registered symbol, section, got and stub addresses"),
85     cl::init(false));
86 
87 static cl::opt<bool> ShowAtomGraph(
88     "show-graph",
89     cl::desc("Print the atom graph after fixups have been applied"),
90     cl::init(false));
91 
92 static cl::opt<bool> ShowSizes(
93     "show-sizes",
94     cl::desc("Show sizes pre- and post-dead stripping, and allocations"),
95     cl::init(false));
96 
97 static cl::opt<bool> ShowRelocatedSectionContents(
98     "show-relocated-section-contents",
99     cl::desc("show section contents after fixups have been applied"),
100     cl::init(false));
101 
102 ExitOnError ExitOnErr;
103 
104 namespace llvm {
105 
106 static raw_ostream &
107 operator<<(raw_ostream &OS, const Session::MemoryRegionInfo &MRI) {
108   return OS << "target addr = "
109             << format("0x%016" PRIx64, MRI.getTargetAddress())
110             << ", content: " << (const void *)MRI.getContent().data() << " -- "
111             << (const void *)(MRI.getContent().data() + MRI.getContent().size())
112             << " (" << MRI.getContent().size() << " bytes)";
113 }
114 
115 static raw_ostream &
116 operator<<(raw_ostream &OS, const Session::SymbolInfoMap &SIM) {
117   OS << "Symbols:\n";
118   for (auto &SKV : SIM)
119     OS << "  \"" << SKV.first() << "\" " << SKV.second << "\n";
120   return OS;
121 }
122 
123 static raw_ostream &
124 operator<<(raw_ostream &OS, const Session::FileInfo &FI) {
125   for (auto &SIKV : FI.SectionInfos)
126     OS << "  Section \"" << SIKV.first() << "\": " << SIKV.second << "\n";
127   for (auto &GOTKV : FI.GOTEntryInfos)
128     OS << "  GOT \"" << GOTKV.first() << "\": " << GOTKV.second << "\n";
129   for (auto &StubKV : FI.StubInfos)
130     OS << "  Stub \"" << StubKV.first() << "\": " << StubKV.second << "\n";
131   return OS;
132 }
133 
134 static raw_ostream &
135 operator<<(raw_ostream &OS, const Session::FileInfoMap &FIM) {
136   for (auto &FIKV : FIM)
137     OS << "File \"" << FIKV.first() << "\":\n" << FIKV.second;
138   return OS;
139 }
140 
141 static uint64_t computeTotalAtomSizes(AtomGraph &G) {
142   uint64_t TotalSize = 0;
143   for (auto *DA : G.defined_atoms())
144     if (DA->isZeroFill())
145       TotalSize += DA->getZeroFillSize();
146     else
147       TotalSize += DA->getContent().size();
148   return TotalSize;
149 }
150 
151 static void dumpSectionContents(raw_ostream &OS, AtomGraph &G) {
152   constexpr JITTargetAddress DumpWidth = 16;
153   static_assert(isPowerOf2_64(DumpWidth), "DumpWidth must be a power of two");
154 
155   // Put sections in address order.
156   std::vector<Section *> Sections;
157   for (auto &S : G.sections())
158     Sections.push_back(&S);
159 
160   std::sort(Sections.begin(), Sections.end(),
161             [](const Section *LHS, const Section *RHS) {
162               if (LHS->atoms_empty() && RHS->atoms_empty())
163                 return false;
164               if (LHS->atoms_empty())
165                 return false;
166               if (RHS->atoms_empty())
167                 return true;
168               return (*LHS->atoms().begin())->getAddress() <
169                      (*RHS->atoms().begin())->getAddress();
170             });
171 
172   for (auto *S : Sections) {
173     OS << S->getName() << " content:";
174     if (S->atoms_empty()) {
175       OS << "\n  section empty\n";
176       continue;
177     }
178 
179     // Sort atoms into order, then render.
180     std::vector<DefinedAtom *> Atoms(S->atoms().begin(), S->atoms().end());
181     std::sort(Atoms.begin(), Atoms.end(),
182               [](const DefinedAtom *LHS, const DefinedAtom *RHS) {
183                 return LHS->getAddress() < RHS->getAddress();
184               });
185 
186     JITTargetAddress NextAddr = Atoms.front()->getAddress() & ~(DumpWidth - 1);
187     for (auto *DA : Atoms) {
188       bool IsZeroFill = DA->isZeroFill();
189       JITTargetAddress AtomStart = DA->getAddress();
190       JITTargetAddress AtomSize =
191           IsZeroFill ? DA->getZeroFillSize() : DA->getContent().size();
192       JITTargetAddress AtomEnd = AtomStart + AtomSize;
193       const uint8_t *AtomData =
194           IsZeroFill ? nullptr : DA->getContent().bytes_begin();
195 
196       // Pad any space before the atom starts.
197       while (NextAddr != AtomStart) {
198         if (NextAddr % DumpWidth == 0)
199           OS << formatv("\n{0:x16}:", NextAddr);
200         OS << "   ";
201         ++NextAddr;
202       }
203 
204       // Render the atom content.
205       while (NextAddr != AtomEnd) {
206         if (NextAddr % DumpWidth == 0)
207           OS << formatv("\n{0:x16}:", NextAddr);
208         if (IsZeroFill)
209           OS << " 00";
210         else
211           OS << formatv(" {0:x-2}", AtomData[NextAddr - AtomStart]);
212         ++NextAddr;
213       }
214     }
215     OS << "\n";
216   }
217 }
218 
219 Session::Session(Triple TT) : ObjLayer(ES, MemMgr), TT(std::move(TT)) {
220 
221   /// Local ObjectLinkingLayer::Plugin class to forward modifyPassConfig to the
222   /// Session.
223   class JITLinkSessionPlugin : public ObjectLinkingLayer::Plugin {
224   public:
225     JITLinkSessionPlugin(Session &S) : S(S) {}
226     void modifyPassConfig(MaterializationResponsibility &MR, const Triple &TT,
227                           PassConfiguration &PassConfig) {
228       S.modifyPassConfig(TT, PassConfig);
229     }
230 
231   private:
232     Session &S;
233   };
234 
235   if (!NoExec && !TT.isOSWindows())
236     ObjLayer.addPlugin(std::make_unique<EHFrameRegistrationPlugin>(
237         InProcessEHFrameRegistrar::getInstance()));
238 
239   ObjLayer.addPlugin(std::make_unique<JITLinkSessionPlugin>(*this));
240 }
241 
242 void Session::dumpSessionInfo(raw_ostream &OS) {
243   OS << "Registered addresses:\n" << SymbolInfos << FileInfos;
244 }
245 
246 void Session::modifyPassConfig(const Triple &FTT,
247                                PassConfiguration &PassConfig) {
248   if (!CheckFiles.empty())
249     PassConfig.PostFixupPasses.push_back([this](AtomGraph &G) {
250       if (TT.getObjectFormat() == Triple::MachO)
251         return registerMachOStubsAndGOT(*this, G);
252       return make_error<StringError>("Unsupported object format for GOT/stub "
253                                      "registration",
254                                      inconvertibleErrorCode());
255     });
256 
257   if (ShowAtomGraph)
258     PassConfig.PostFixupPasses.push_back([](AtomGraph &G) -> Error {
259       outs() << "Atom graph post-fixup:\n";
260       G.dump(outs());
261       return Error::success();
262     });
263 
264 
265   if (ShowSizes) {
266     PassConfig.PrePrunePasses.push_back([this](AtomGraph &G) -> Error {
267         SizeBeforePruning += computeTotalAtomSizes(G);
268         return Error::success();
269       });
270     PassConfig.PostFixupPasses.push_back([this](AtomGraph &G) -> Error {
271         SizeAfterFixups += computeTotalAtomSizes(G);
272         return Error::success();
273       });
274   }
275 
276   if (ShowRelocatedSectionContents)
277     PassConfig.PostFixupPasses.push_back([](AtomGraph &G) -> Error {
278       outs() << "Relocated section contents for " << G.getName() << ":\n";
279       dumpSectionContents(outs(), G);
280       return Error::success();
281     });
282 }
283 
284 Expected<Session::FileInfo &> Session::findFileInfo(StringRef FileName) {
285   auto FileInfoItr = FileInfos.find(FileName);
286   if (FileInfoItr == FileInfos.end())
287     return make_error<StringError>("file \"" + FileName + "\" not recognized",
288                                    inconvertibleErrorCode());
289   return FileInfoItr->second;
290 }
291 
292 Expected<Session::MemoryRegionInfo &>
293 Session::findSectionInfo(StringRef FileName, StringRef SectionName) {
294   auto FI = findFileInfo(FileName);
295   if (!FI)
296     return FI.takeError();
297   auto SecInfoItr = FI->SectionInfos.find(SectionName);
298   if (SecInfoItr == FI->SectionInfos.end())
299     return make_error<StringError>("no section \"" + SectionName +
300                                        "\" registered for file \"" + FileName +
301                                        "\"",
302                                    inconvertibleErrorCode());
303   return SecInfoItr->second;
304 }
305 
306 Expected<Session::MemoryRegionInfo &>
307 Session::findStubInfo(StringRef FileName, StringRef TargetName) {
308   auto FI = findFileInfo(FileName);
309   if (!FI)
310     return FI.takeError();
311   auto StubInfoItr = FI->StubInfos.find(TargetName);
312   if (StubInfoItr == FI->StubInfos.end())
313     return make_error<StringError>("no stub for \"" + TargetName +
314                                        "\" registered for file \"" + FileName +
315                                        "\"",
316                                    inconvertibleErrorCode());
317   return StubInfoItr->second;
318 }
319 
320 Expected<Session::MemoryRegionInfo &>
321 Session::findGOTEntryInfo(StringRef FileName, StringRef TargetName) {
322   auto FI = findFileInfo(FileName);
323   if (!FI)
324     return FI.takeError();
325   auto GOTInfoItr = FI->GOTEntryInfos.find(TargetName);
326   if (GOTInfoItr == FI->GOTEntryInfos.end())
327     return make_error<StringError>("no GOT entry for \"" + TargetName +
328                                        "\" registered for file \"" + FileName +
329                                        "\"",
330                                    inconvertibleErrorCode());
331   return GOTInfoItr->second;
332 }
333 
334 bool Session::isSymbolRegistered(StringRef SymbolName) {
335   return SymbolInfos.count(SymbolName);
336 }
337 
338 Expected<Session::MemoryRegionInfo &>
339 Session::findSymbolInfo(StringRef SymbolName, Twine ErrorMsgStem) {
340   auto SymInfoItr = SymbolInfos.find(SymbolName);
341   if (SymInfoItr == SymbolInfos.end())
342     return make_error<StringError>(ErrorMsgStem + ": symbol " + SymbolName +
343                                        " not found",
344                                    inconvertibleErrorCode());
345   return SymInfoItr->second;
346 }
347 
348 } // end namespace llvm
349 
350 Triple getFirstFileTriple() {
351   assert(!InputFiles.empty() && "InputFiles can not be empty");
352   auto ObjBuffer =
353       ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(InputFiles.front())));
354   auto Obj = ExitOnErr(
355       object::ObjectFile::createObjectFile(ObjBuffer->getMemBufferRef()));
356   return Obj->makeTriple();
357 }
358 
359 Error sanitizeArguments(const Session &S) {
360   if (EntryPointName.empty()) {
361     if (S.TT.getObjectFormat() == Triple::MachO)
362       EntryPointName = "_main";
363     else
364       EntryPointName = "main";
365   }
366 
367   if (NoExec && !InputArgv.empty())
368     outs() << "Warning: --args passed to -noexec run will be ignored.\n";
369 
370   return Error::success();
371 }
372 
373 Error loadProcessSymbols(Session &S) {
374   std::string ErrMsg;
375   if (sys::DynamicLibrary::LoadLibraryPermanently(nullptr, &ErrMsg))
376     return make_error<StringError>(std::move(ErrMsg), inconvertibleErrorCode());
377 
378   char GlobalPrefix = S.TT.getObjectFormat() == Triple::MachO ? '_' : '\0';
379   auto InternedEntryPointName = S.ES.intern(EntryPointName);
380   auto FilterMainEntryPoint = [InternedEntryPointName](SymbolStringPtr Name) {
381     return Name != InternedEntryPointName;
382   };
383   S.ES.getMainJITDylib().addGenerator(
384       ExitOnErr(orc::DynamicLibrarySearchGenerator::GetForCurrentProcess(
385           GlobalPrefix, FilterMainEntryPoint)));
386 
387   return Error::success();
388 }
389 
390 Error loadDylibs() {
391   // FIXME: This should all be handled inside DynamicLibrary.
392   for (const auto &Dylib : Dylibs) {
393     if (!sys::fs::is_regular_file(Dylib))
394       return make_error<StringError>("\"" + Dylib + "\" is not a regular file",
395                                      inconvertibleErrorCode());
396     std::string ErrMsg;
397     if (sys::DynamicLibrary::LoadLibraryPermanently(Dylib.c_str(), &ErrMsg))
398       return make_error<StringError>(ErrMsg, inconvertibleErrorCode());
399   }
400 
401   return Error::success();
402 }
403 
404 Error loadObjects(Session &S) {
405 
406   std::map<unsigned, JITDylib *> IdxToJLD;
407 
408   // First, set up JITDylibs.
409   LLVM_DEBUG(dbgs() << "Creating JITDylibs...\n");
410   {
411     // Create a "main" JITLinkDylib.
412     auto &MainJD = S.ES.getMainJITDylib();
413     IdxToJLD[0] = &MainJD;
414     S.JDSearchOrder.push_back(&MainJD);
415     LLVM_DEBUG(dbgs() << "  0: " << MainJD.getName() << "\n");
416 
417     // Add any extra JITLinkDylibs from the command line.
418     std::string JDNamePrefix("lib");
419     for (auto JLDItr = JITLinkDylibs.begin(), JLDEnd = JITLinkDylibs.end();
420          JLDItr != JLDEnd; ++JLDItr) {
421       auto &JD = S.ES.createJITDylib(JDNamePrefix + *JLDItr);
422       unsigned JDIdx =
423           JITLinkDylibs.getPosition(JLDItr - JITLinkDylibs.begin());
424       IdxToJLD[JDIdx] = &JD;
425       S.JDSearchOrder.push_back(&JD);
426       LLVM_DEBUG(dbgs() << "  " << JDIdx << ": " << JD.getName() << "\n");
427     }
428 
429     // Set every dylib to link against every other, in command line order.
430     for (auto *JD : S.JDSearchOrder) {
431       JITDylibSearchList O;
432       for (auto *JD2 : S.JDSearchOrder) {
433         if (JD2 == JD)
434           continue;
435         O.push_back(std::make_pair(JD2, false));
436       }
437       JD->setSearchOrder(std::move(O));
438     }
439   }
440 
441   // Load each object into the corresponding JITDylib..
442   LLVM_DEBUG(dbgs() << "Adding objects...\n");
443   for (auto InputFileItr = InputFiles.begin(), InputFileEnd = InputFiles.end();
444        InputFileItr != InputFileEnd; ++InputFileItr) {
445     unsigned InputFileArgIdx =
446         InputFiles.getPosition(InputFileItr - InputFiles.begin());
447     StringRef InputFile = *InputFileItr;
448     auto &JD = *std::prev(IdxToJLD.lower_bound(InputFileArgIdx))->second;
449     LLVM_DEBUG(dbgs() << "  " << InputFileArgIdx << ": \"" << InputFile
450                       << "\" to " << JD.getName() << "\n";);
451     auto ObjBuffer =
452         ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(InputFile)));
453     ExitOnErr(S.ObjLayer.add(JD, std::move(ObjBuffer)));
454   }
455 
456   // Define absolute symbols.
457   LLVM_DEBUG(dbgs() << "Defining absolute symbols...\n");
458   for (auto AbsDefItr = AbsoluteDefs.begin(), AbsDefEnd = AbsoluteDefs.end();
459        AbsDefItr != AbsDefEnd; ++AbsDefItr) {
460     unsigned AbsDefArgIdx =
461       AbsoluteDefs.getPosition(AbsDefItr - AbsoluteDefs.begin());
462     auto &JD = *std::prev(IdxToJLD.lower_bound(AbsDefArgIdx))->second;
463 
464     StringRef AbsDefStmt = *AbsDefItr;
465     size_t EqIdx = AbsDefStmt.find_first_of('=');
466     if (EqIdx == StringRef::npos)
467       return make_error<StringError>("Invalid absolute define \"" + AbsDefStmt +
468                                      "\". Syntax: <name>=<addr>",
469                                      inconvertibleErrorCode());
470     StringRef Name = AbsDefStmt.substr(0, EqIdx).trim();
471     StringRef AddrStr = AbsDefStmt.substr(EqIdx + 1).trim();
472 
473     uint64_t Addr;
474     if (AddrStr.getAsInteger(0, Addr))
475       return make_error<StringError>("Invalid address expression \"" + AddrStr +
476                                      "\" in absolute define \"" + AbsDefStmt +
477                                      "\"",
478                                      inconvertibleErrorCode());
479     JITEvaluatedSymbol AbsDef(Addr, JITSymbolFlags::Exported);
480     if (auto Err = JD.define(absoluteSymbols({{S.ES.intern(Name), AbsDef}})))
481       return Err;
482 
483     // Register the absolute symbol with the session symbol infos.
484     S.SymbolInfos[Name] = { StringRef(), Addr };
485   }
486 
487   LLVM_DEBUG({
488     dbgs() << "Dylib search order is [ ";
489     for (auto *JD : S.JDSearchOrder)
490       dbgs() << JD->getName() << " ";
491     dbgs() << "]\n";
492   });
493 
494   return Error::success();
495 }
496 
497 Error runChecks(Session &S) {
498 
499   auto TripleName = S.TT.str();
500   std::string ErrorStr;
501   const Target *TheTarget = TargetRegistry::lookupTarget("", S.TT, ErrorStr);
502   if (!TheTarget)
503     ExitOnErr(make_error<StringError>("Error accessing target '" + TripleName +
504                                           "': " + ErrorStr,
505                                       inconvertibleErrorCode()));
506 
507   std::unique_ptr<MCSubtargetInfo> STI(
508       TheTarget->createMCSubtargetInfo(TripleName, "", ""));
509   if (!STI)
510     ExitOnErr(
511         make_error<StringError>("Unable to create subtarget for " + TripleName,
512                                 inconvertibleErrorCode()));
513 
514   std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName));
515   if (!MRI)
516     ExitOnErr(make_error<StringError>("Unable to create target register info "
517                                       "for " +
518                                           TripleName,
519                                       inconvertibleErrorCode()));
520 
521   std::unique_ptr<MCAsmInfo> MAI(TheTarget->createMCAsmInfo(*MRI, TripleName));
522   if (!MAI)
523     ExitOnErr(make_error<StringError>("Unable to create target asm info " +
524                                           TripleName,
525                                       inconvertibleErrorCode()));
526 
527   MCContext Ctx(MAI.get(), MRI.get(), nullptr);
528 
529   std::unique_ptr<MCDisassembler> Disassembler(
530       TheTarget->createMCDisassembler(*STI, Ctx));
531   if (!Disassembler)
532     ExitOnErr(make_error<StringError>("Unable to create disassembler for " +
533                                           TripleName,
534                                       inconvertibleErrorCode()));
535 
536   std::unique_ptr<MCInstrInfo> MII(TheTarget->createMCInstrInfo());
537 
538   std::unique_ptr<MCInstPrinter> InstPrinter(
539       TheTarget->createMCInstPrinter(Triple(TripleName), 0, *MAI, *MII, *MRI));
540 
541   auto IsSymbolValid = [&S](StringRef Symbol) {
542     return S.isSymbolRegistered(Symbol);
543   };
544 
545   auto GetSymbolInfo = [&S](StringRef Symbol) {
546     return S.findSymbolInfo(Symbol, "Can not get symbol info");
547   };
548 
549   auto GetSectionInfo = [&S](StringRef FileName, StringRef SectionName) {
550     return S.findSectionInfo(FileName, SectionName);
551   };
552 
553   auto GetStubInfo = [&S](StringRef FileName, StringRef SectionName) {
554     return S.findStubInfo(FileName, SectionName);
555   };
556 
557   auto GetGOTInfo = [&S](StringRef FileName, StringRef SectionName) {
558     return S.findGOTEntryInfo(FileName, SectionName);
559   };
560 
561   RuntimeDyldChecker Checker(
562       IsSymbolValid, GetSymbolInfo, GetSectionInfo, GetStubInfo, GetGOTInfo,
563       S.TT.isLittleEndian() ? support::little : support::big,
564       Disassembler.get(), InstPrinter.get(), dbgs());
565 
566   for (auto &CheckFile : CheckFiles) {
567     auto CheckerFileBuf =
568         ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(CheckFile)));
569     if (!Checker.checkAllRulesInBuffer("# jitlink-check:", &*CheckerFileBuf))
570       ExitOnErr(make_error<StringError>(
571           "Some checks in " + CheckFile + " failed", inconvertibleErrorCode()));
572   }
573 
574   return Error::success();
575 }
576 
577 static void dumpSessionStats(Session &S) {
578   if (ShowSizes)
579     outs() << "Total size of all atoms before pruning: " << S.SizeBeforePruning
580            << "\nTotal size of all atoms after fixups: " << S.SizeAfterFixups
581            << "\n";
582 }
583 
584 static Expected<JITEvaluatedSymbol> getMainEntryPoint(Session &S) {
585   return S.ES.lookup(S.JDSearchOrder, EntryPointName);
586 }
587 
588 Expected<int> runEntryPoint(Session &S, JITEvaluatedSymbol EntryPoint) {
589   assert(EntryPoint.getAddress() && "Entry point address should not be null");
590 
591   constexpr const char *JITProgramName = "<llvm-jitlink jit'd code>";
592   auto PNStorage = std::make_unique<char[]>(strlen(JITProgramName) + 1);
593   strcpy(PNStorage.get(), JITProgramName);
594 
595   std::vector<const char *> EntryPointArgs;
596   EntryPointArgs.push_back(PNStorage.get());
597   for (auto &InputArg : InputArgv)
598     EntryPointArgs.push_back(InputArg.data());
599   EntryPointArgs.push_back(nullptr);
600 
601   using MainTy = int (*)(int, const char *[]);
602   MainTy EntryPointPtr = reinterpret_cast<MainTy>(EntryPoint.getAddress());
603 
604   return EntryPointPtr(EntryPointArgs.size() - 1, EntryPointArgs.data());
605 }
606 
607 int main(int argc, char *argv[]) {
608   InitLLVM X(argc, argv);
609 
610   InitializeAllTargetInfos();
611   InitializeAllTargetMCs();
612   InitializeAllDisassemblers();
613 
614   cl::ParseCommandLineOptions(argc, argv, "llvm jitlink tool");
615   ExitOnErr.setBanner(std::string(argv[0]) + ": ");
616 
617   Session S(getFirstFileTriple());
618 
619   ExitOnErr(sanitizeArguments(S));
620 
621   if (!NoProcessSymbols)
622     ExitOnErr(loadProcessSymbols(S));
623   ExitOnErr(loadDylibs());
624 
625   ExitOnErr(loadObjects(S));
626 
627   auto EntryPoint = ExitOnErr(getMainEntryPoint(S));
628 
629   if (ShowAddrs)
630     S.dumpSessionInfo(outs());
631 
632   ExitOnErr(runChecks(S));
633 
634   dumpSessionStats(S);
635 
636   if (NoExec)
637     return 0;
638 
639   return ExitOnErr(runEntryPoint(S, EntryPoint));
640 }
641