blob: 9ea26128991626989a81fb08f76202a10d1b5655 [file] [log] [blame]
Lang Hames11c8dfa52019-04-20 17:10:34 +00001//===- 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/Orc/ExecutionUtils.h"
18#include "llvm/MC/MCAsmInfo.h"
19#include "llvm/MC/MCContext.h"
20#include "llvm/MC/MCDisassembler/MCDisassembler.h"
21#include "llvm/MC/MCInstPrinter.h"
22#include "llvm/MC/MCInstrInfo.h"
23#include "llvm/MC/MCRegisterInfo.h"
24#include "llvm/MC/MCSubtargetInfo.h"
25#include "llvm/Object/COFF.h"
26#include "llvm/Object/MachO.h"
27#include "llvm/Object/ObjectFile.h"
28#include "llvm/Support/CommandLine.h"
29#include "llvm/Support/Debug.h"
30#include "llvm/Support/DynamicLibrary.h"
31#include "llvm/Support/InitLLVM.h"
32#include "llvm/Support/MemoryBuffer.h"
33#include "llvm/Support/TargetRegistry.h"
34#include "llvm/Support/TargetSelect.h"
35
36#include <list>
37#include <string>
38
39#include "dlfcn.h"
40
41#define DEBUG_TYPE "llvm-jitlink"
42
43using namespace llvm;
44using namespace llvm::jitlink;
45using namespace llvm::orc;
46
47static cl::list<std::string> InputFiles(cl::Positional, cl::OneOrMore,
48 cl::desc("input files"));
49
50static cl::opt<bool> NoExec("noexec", cl::desc("Do not execute loaded code"),
51 cl::init(false));
52
53static cl::list<std::string>
54 CheckFiles("check", cl::desc("File containing verifier checks"),
55 cl::ZeroOrMore);
56
57static cl::opt<std::string>
58 EntryPointName("entry", cl::desc("Symbol to call as main entry point"),
59 cl::init(""));
60
61static cl::list<std::string> JITLinkDylibs(
62 "jld", cl::desc("Specifies the JITDylib to be used for any subsequent "
63 "input file arguments"));
64
65static cl::list<std::string>
66 Dylibs("dlopen", cl::desc("Dynamic libraries to load before linking"),
67 cl::ZeroOrMore);
68
69static cl::opt<bool>
70 NoProcessSymbols("no-process-syms",
71 cl::desc("Do not resolve to llvm-jitlink process symbols"),
72 cl::init(false));
73
74static cl::list<std::string>
75AbsoluteDefs("define-abs",
76 cl::desc("Inject absolute symbol definitions (syntax: <name>=<addr>)"),
77 cl::ZeroOrMore);
78
79static cl::opt<bool> ShowAddrs(
80 "show-addrs",
81 cl::desc("Print registered symbol, section, got and stub addresses"),
82 cl::init(false));
83
84static cl::opt<bool> ShowAtomGraph(
85 "show-graph",
86 cl::desc("Print the atom graph after fixups have been applied"),
87 cl::init(false));
88
89static cl::opt<bool> ShowSizes(
90 "show-sizes",
91 cl::desc("Show sizes: pre- and post-dead stripping, and allocations"),
92 cl::init(false));
93
94ExitOnError ExitOnErr;
95
96namespace llvm {
97
98static raw_ostream &
99operator<<(raw_ostream &OS, const Session::MemoryRegionInfo &MRI) {
100 return OS << "target addr = " << format("0x%016" PRIx64, MRI.TargetAddress)
101 << ", content: " << (const void *)MRI.Content.data() << " -- "
102 << (const void *)(MRI.Content.data() + MRI.Content.size()) << " ("
103 << MRI.Content.size() << " bytes)";
104}
105
106static raw_ostream &
107operator<<(raw_ostream &OS, const Session::SymbolInfoMap &SIM) {
108 OS << "Symbols:\n";
109 for (auto &SKV : SIM)
110 OS << " \"" << SKV.first() << "\" " << SKV.second << "\n";
111 return OS;
112}
113
114static raw_ostream &
115operator<<(raw_ostream &OS, const Session::FileInfo &FI) {
116 for (auto &SIKV : FI.SectionInfos)
117 OS << " Section \"" << SIKV.first() << "\": " << SIKV.second << "\n";
118 for (auto &GOTKV : FI.GOTEntryInfos)
119 OS << " GOT \"" << GOTKV.first() << "\": " << GOTKV.second << "\n";
120 for (auto &StubKV : FI.StubInfos)
121 OS << " Stub \"" << StubKV.first() << "\": " << StubKV.second << "\n";
122 return OS;
123}
124
125static raw_ostream &
126operator<<(raw_ostream &OS, const Session::FileInfoMap &FIM) {
127 for (auto &FIKV : FIM)
128 OS << "File \"" << FIKV.first() << "\":\n" << FIKV.second;
129 return OS;
130}
131
132static uint64_t computeTotalAtomSizes(AtomGraph &G) {
133 uint64_t TotalSize = 0;
134 for (auto *DA : G.defined_atoms())
135 if (DA->isZeroFill())
136 TotalSize += DA->getZeroFillSize();
137 else
138 TotalSize += DA->getContent().size();
139 return TotalSize;
140}
141
142Session::Session(Triple TT)
143 : ObjLayer(ES, MemMgr, ObjectLinkingLayer::NotifyLoadedFunction(),
144 ObjectLinkingLayer::NotifyEmittedFunction(),
145 [this](const Triple &TT, PassConfiguration &PassConfig) {
146 modifyPassConfig(TT, PassConfig);
147 }),
148 TT(std::move(TT)) {}
149
150void Session::dumpSessionInfo(raw_ostream &OS) {
151 OS << "Registered addresses:\n" << SymbolInfos << FileInfos;
152}
153
154void Session::modifyPassConfig(const Triple &FTT,
155 PassConfiguration &PassConfig) {
156 if (!CheckFiles.empty())
157 PassConfig.PostFixupPasses.push_back([this](AtomGraph &G) {
158 if (TT.getObjectFormat() == Triple::MachO)
159 return registerMachOStubsAndGOT(*this, G);
160 return make_error<StringError>("Unsupported object format for GOT/stub "
161 "registration",
162 inconvertibleErrorCode());
163 });
164
165 if (ShowAtomGraph)
166 PassConfig.PostFixupPasses.push_back([](AtomGraph &G) -> Error {
167 outs() << "Atom graph post-fixup:\n";
168 G.dump(outs());
169 return Error::success();
170 });
171
172
173 if (ShowSizes) {
174 PassConfig.PrePrunePasses.push_back([this](AtomGraph &G) -> Error {
175 SizeBeforePruning += computeTotalAtomSizes(G);
176 return Error::success();
177 });
178 PassConfig.PostFixupPasses.push_back([this](AtomGraph &G) -> Error {
179 SizeAfterFixups += computeTotalAtomSizes(G);
180 return Error::success();
181 });
182 }
183
184}
185
186Expected<Session::FileInfo &> Session::findFileInfo(StringRef FileName) {
187 auto FileInfoItr = FileInfos.find(FileName);
188 if (FileInfoItr == FileInfos.end())
189 return make_error<StringError>("file \"" + FileName + "\" not recognized",
190 inconvertibleErrorCode());
191 return FileInfoItr->second;
192}
193
194Expected<Session::MemoryRegionInfo &>
195Session::findSectionInfo(StringRef FileName, StringRef SectionName) {
196 auto FI = findFileInfo(FileName);
197 if (!FI)
198 return FI.takeError();
199 auto SecInfoItr = FI->SectionInfos.find(SectionName);
200 if (SecInfoItr == FI->SectionInfos.end())
201 return make_error<StringError>("no section \"" + SectionName +
202 "\" registered for file \"" + FileName +
203 "\"",
204 inconvertibleErrorCode());
205 return SecInfoItr->second;
206}
207
208Expected<Session::MemoryRegionInfo &>
209Session::findStubInfo(StringRef FileName, StringRef TargetName) {
210 auto FI = findFileInfo(FileName);
211 if (!FI)
212 return FI.takeError();
213 auto StubInfoItr = FI->StubInfos.find(TargetName);
214 if (StubInfoItr == FI->StubInfos.end())
215 return make_error<StringError>("no stub for \"" + TargetName +
216 "\" registered for file \"" + FileName +
217 "\"",
218 inconvertibleErrorCode());
219 return StubInfoItr->second;
220}
221
222Expected<Session::MemoryRegionInfo &>
223Session::findGOTEntryInfo(StringRef FileName, StringRef TargetName) {
224 auto FI = findFileInfo(FileName);
225 if (!FI)
226 return FI.takeError();
227 auto GOTInfoItr = FI->GOTEntryInfos.find(TargetName);
228 if (GOTInfoItr == FI->GOTEntryInfos.end())
229 return make_error<StringError>("no GOT entry for \"" + TargetName +
230 "\" registered for file \"" + FileName +
231 "\"",
232 inconvertibleErrorCode());
233 return GOTInfoItr->second;
234}
235
236bool Session::isSymbolRegistered(StringRef SymbolName) {
237 return SymbolInfos.count(SymbolName);
238}
239
240Expected<Session::MemoryRegionInfo &>
241Session::findSymbolInfo(StringRef SymbolName, Twine ErrorMsgStem) {
242 auto SymInfoItr = SymbolInfos.find(SymbolName);
243 if (SymInfoItr == SymbolInfos.end())
244 return make_error<StringError>(ErrorMsgStem + ": symbol " + SymbolName +
245 " not found",
246 inconvertibleErrorCode());
247 return SymInfoItr->second;
248}
249
250} // end namespace llvm
251
252Error loadProcessSymbols(Session &S) {
253 std::string ErrMsg;
254 if (sys::DynamicLibrary::LoadLibraryPermanently(nullptr, &ErrMsg))
255 return make_error<StringError>(std::move(ErrMsg), inconvertibleErrorCode());
256
257 char GlobalPrefix = S.TT.getObjectFormat() == Triple::MachO ? '_' : '\0';
258 S.ES.getMainJITDylib().setGenerator(ExitOnErr(
259 orc::DynamicLibrarySearchGenerator::GetForCurrentProcess(GlobalPrefix)));
260
261 return Error::success();
262}
263
264Error loadDylibs() {
265 // FIXME: This should all be handled inside DynamicLibrary.
266 for (const auto &Dylib : Dylibs) {
267 if (!sys::fs::is_regular_file(Dylib))
268 return make_error<StringError>("\"" + Dylib + "\" is not a regular file",
269 inconvertibleErrorCode());
270 std::string ErrMsg;
271 if (sys::DynamicLibrary::LoadLibraryPermanently(Dylib.c_str(), &ErrMsg))
272 return make_error<StringError>(ErrMsg, inconvertibleErrorCode());
273 }
274
275 return Error::success();
276}
277
278Triple getFirstFileTriple() {
279 assert(!InputFiles.empty() && "InputFiles can not be empty");
280 auto ObjBuffer =
281 ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(InputFiles.front())));
282 auto Obj = ExitOnErr(
283 object::ObjectFile::createObjectFile(ObjBuffer->getMemBufferRef()));
284 return Obj->makeTriple();
285}
286
287Error loadObjects(Session &S) {
288
289 std::map<unsigned, JITDylib *> IdxToJLD;
290
291 // First, set up JITDylibs.
292 LLVM_DEBUG(dbgs() << "Creating JITDylibs...\n");
293 {
294 // Create a "main" JITLinkDylib.
295 auto &MainJD = S.ES.getMainJITDylib();
296 IdxToJLD[0] = &MainJD;
297 S.JDSearchOrder.push_back(&MainJD);
298 LLVM_DEBUG(dbgs() << " 0: " << MainJD.getName() << "\n");
299
300 // Add any extra JITLinkDylibs from the command line.
301 std::string JDNamePrefix("lib");
302 for (auto JLDItr = JITLinkDylibs.begin(), JLDEnd = JITLinkDylibs.end();
303 JLDItr != JLDEnd; ++JLDItr) {
304 auto &JD = S.ES.createJITDylib(JDNamePrefix + *JLDItr);
305 unsigned JDIdx =
306 JITLinkDylibs.getPosition(JLDItr - JITLinkDylibs.begin());
307 IdxToJLD[JDIdx] = &JD;
308 S.JDSearchOrder.push_back(&JD);
309 LLVM_DEBUG(dbgs() << " " << JDIdx << ": " << JD.getName() << "\n");
310 }
311
312 // Set every dylib to link against every other, in command line order.
313 for (auto *JD : S.JDSearchOrder) {
314 JITDylibSearchList O;
315 for (auto *JD2 : S.JDSearchOrder) {
316 if (JD2 == JD)
317 continue;
318 O.push_back(std::make_pair(JD2, false));
319 }
320 JD->setSearchOrder(std::move(O));
321 }
322 }
323
324 // Load each object into the corresponding JITDylib..
325 LLVM_DEBUG(dbgs() << "Adding objects...\n");
326 for (auto InputFileItr = InputFiles.begin(), InputFileEnd = InputFiles.end();
327 InputFileItr != InputFileEnd; ++InputFileItr) {
328 unsigned InputFileArgIdx =
329 InputFiles.getPosition(InputFileItr - InputFiles.begin());
330 StringRef InputFile = *InputFileItr;
331 auto &JD = *std::prev(IdxToJLD.lower_bound(InputFileArgIdx))->second;
332 LLVM_DEBUG(dbgs() << " " << InputFileArgIdx << ": \"" << InputFile
333 << "\" to " << JD.getName() << "\n";);
334 auto ObjBuffer =
335 ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(InputFile)));
336 ExitOnErr(S.ObjLayer.add(JD, std::move(ObjBuffer)));
337 }
338
339 // Define absolute symbols.
340 LLVM_DEBUG(dbgs() << "Defining absolute symbols...\n");
341 for (auto AbsDefItr = AbsoluteDefs.begin(), AbsDefEnd = AbsoluteDefs.end();
342 AbsDefItr != AbsDefEnd; ++AbsDefItr) {
343 unsigned AbsDefArgIdx =
344 AbsoluteDefs.getPosition(AbsDefItr - AbsoluteDefs.begin());
345 auto &JD = *std::prev(IdxToJLD.lower_bound(AbsDefArgIdx))->second;
346
347 StringRef AbsDefStmt = *AbsDefItr;
348 size_t EqIdx = AbsDefStmt.find_first_of('=');
349 if (EqIdx == StringRef::npos)
350 return make_error<StringError>("Invalid absolute define \"" + AbsDefStmt +
351 "\". Syntax: <name>=<addr>",
352 inconvertibleErrorCode());
353 StringRef Name = AbsDefStmt.substr(0, EqIdx).trim();
354 StringRef AddrStr = AbsDefStmt.substr(EqIdx + 1).trim();
355
356 uint64_t Addr;
357 if (AddrStr.getAsInteger(0, Addr))
358 return make_error<StringError>("Invalid address expression \"" + AddrStr +
359 "\" in absolute define \"" + AbsDefStmt +
360 "\"",
361 inconvertibleErrorCode());
362 JITEvaluatedSymbol AbsDef(Addr, JITSymbolFlags::Exported);
363 if (auto Err = JD.define(absoluteSymbols({{S.ES.intern(Name), AbsDef}})))
364 return Err;
365
366 // Register the absolute symbol with the session symbol infos.
367 S.SymbolInfos[Name] = { StringRef(), Addr };
368 }
369
370 LLVM_DEBUG({
371 dbgs() << "Dylib search order is [ ";
372 for (auto *JD : S.JDSearchOrder)
373 dbgs() << JD->getName() << " ";
374 dbgs() << "]\n";
375 });
376
377 return Error::success();
378}
379
380Error runChecks(Session &S) {
381
382 auto TripleName = S.TT.str();
383 std::string ErrorStr;
384 const Target *TheTarget = TargetRegistry::lookupTarget("", S.TT, ErrorStr);
385 if (!TheTarget)
386 ExitOnErr(make_error<StringError>("Error accessing target '" + TripleName +
387 "': " + ErrorStr,
388 inconvertibleErrorCode()));
389
390 std::unique_ptr<MCSubtargetInfo> STI(
391 TheTarget->createMCSubtargetInfo(TripleName, "", ""));
392 if (!STI)
393 ExitOnErr(
394 make_error<StringError>("Unable to create subtarget for " + TripleName,
395 inconvertibleErrorCode()));
396
397 std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName));
398 if (!MRI)
399 ExitOnErr(make_error<StringError>("Unable to create target register info "
400 "for " +
401 TripleName,
402 inconvertibleErrorCode()));
403
404 std::unique_ptr<MCAsmInfo> MAI(TheTarget->createMCAsmInfo(*MRI, TripleName));
405 if (!MAI)
406 ExitOnErr(make_error<StringError>("Unable to create target asm info " +
407 TripleName,
408 inconvertibleErrorCode()));
409
410 MCContext Ctx(MAI.get(), MRI.get(), nullptr);
411
412 std::unique_ptr<MCDisassembler> Disassembler(
413 TheTarget->createMCDisassembler(*STI, Ctx));
414 if (!Disassembler)
415 ExitOnErr(make_error<StringError>("Unable to create disassembler for " +
416 TripleName,
417 inconvertibleErrorCode()));
418
419 std::unique_ptr<MCInstrInfo> MII(TheTarget->createMCInstrInfo());
420
421 std::unique_ptr<MCInstPrinter> InstPrinter(
422 TheTarget->createMCInstPrinter(Triple(TripleName), 0, *MAI, *MII, *MRI));
423
424 auto IsSymbolValid = [&S](StringRef Symbol) {
425 return S.isSymbolRegistered(Symbol);
426 };
427
428 auto GetSymbolInfo = [&S](StringRef Symbol) {
429 return S.findSymbolInfo(Symbol, "Can not get symbol info");
430 };
431
432 auto GetSectionInfo = [&S](StringRef FileName, StringRef SectionName) {
433 return S.findSectionInfo(FileName, SectionName);
434 };
435
436 auto GetStubInfo = [&S](StringRef FileName, StringRef SectionName) {
437 return S.findStubInfo(FileName, SectionName);
438 };
439
440 auto GetGOTInfo = [&S](StringRef FileName, StringRef SectionName) {
441 return S.findGOTEntryInfo(FileName, SectionName);
442 };
443
444 RuntimeDyldChecker Checker(
445 IsSymbolValid, GetSymbolInfo, GetSectionInfo, GetStubInfo, GetGOTInfo,
446 S.TT.isLittleEndian() ? support::little : support::big,
447 Disassembler.get(), InstPrinter.get(), dbgs());
448
449 for (auto &CheckFile : CheckFiles) {
450 auto CheckerFileBuf =
451 ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(CheckFile)));
452 if (!Checker.checkAllRulesInBuffer("# jitlink-check:", &*CheckerFileBuf))
453 ExitOnErr(make_error<StringError>(
454 "Some checks in " + CheckFile + " failed", inconvertibleErrorCode()));
455 }
456
457 return Error::success();
458}
459
460static void dumpSessionStats(Session &S) {
461 if (ShowSizes)
462 outs() << "Total size of all atoms before pruning: " << S.SizeBeforePruning
463 << "\nTotal size of all atoms after fixups: " << S.SizeAfterFixups
464 << "\n";
465}
466
467static Expected<JITEvaluatedSymbol> getMainEntryPoint(Session &S) {
468
469 // First, if the entry point has not been set, set it to a sensible default
470 // for this process.
471 if (EntryPointName.empty()) {
472 if (S.TT.getObjectFormat() == Triple::MachO)
473 EntryPointName = "_main";
474 else
475 EntryPointName = "main";
476 }
477
478 return S.ES.lookup(S.JDSearchOrder, EntryPointName);
479}
480
481Expected<int> runEntryPoint(Session &S, JITEvaluatedSymbol EntryPoint) {
482 assert(EntryPoint.getAddress() && "Entry point address should not be null");
483
484 constexpr const char *JITProgramName = "<llvm-jitlink jit'd code>";
485 auto PNStorage = llvm::make_unique<char[]>(strlen(JITProgramName) + 1);
486 strcpy(PNStorage.get(), JITProgramName);
487
488 std::vector<const char *> EntryPointArgs;
489 EntryPointArgs.push_back(PNStorage.get());
490 EntryPointArgs.push_back(nullptr);
491
492 using MainTy = int (*)(int, const char *[]);
493 MainTy EntryPointPtr = reinterpret_cast<MainTy>(EntryPoint.getAddress());
494
495 return EntryPointPtr(EntryPointArgs.size() - 1, EntryPointArgs.data());
496}
497
498int main(int argc, char *argv[]) {
499 InitLLVM X(argc, argv);
500
501 InitializeAllTargetInfos();
502 InitializeAllTargetMCs();
503 InitializeAllDisassemblers();
504
505 cl::ParseCommandLineOptions(argc, argv, "llvm jitlink tool");
506 ExitOnErr.setBanner(std::string(argv[0]) + ": ");
507
508 Session S(getFirstFileTriple());
509
510 if (!NoProcessSymbols)
511 ExitOnErr(loadProcessSymbols(S));
512 ExitOnErr(loadDylibs());
513
514 ExitOnErr(loadObjects(S));
515
516 auto EntryPoint = ExitOnErr(getMainEntryPoint(S));
517
518 if (ShowAddrs)
519 S.dumpSessionInfo(outs());
520
521 ExitOnErr(runChecks(S));
522
523 dumpSessionStats(S);
524
525 if (NoExec)
526 return 0;
527
528 return ExitOnErr(runEntryPoint(S, EntryPoint));
529}