| //===- bolt/Rewrite/JITLinkLinker.cpp - BOLTLinker using JITLink ----------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "bolt/Rewrite/JITLinkLinker.h" |
| #include "bolt/Core/BinaryContext.h" |
| #include "bolt/Core/BinaryData.h" |
| #include "bolt/Core/BinarySection.h" |
| #include "llvm/ExecutionEngine/JITLink/ELF_riscv.h" |
| #include "llvm/ExecutionEngine/JITLink/JITLink.h" |
| #include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h" |
| #include "llvm/ExecutionEngine/Orc/Shared/ExecutorSymbolDef.h" |
| #include "llvm/Support/Debug.h" |
| |
| #define DEBUG_TYPE "bolt" |
| |
| namespace llvm { |
| namespace bolt { |
| |
| namespace { |
| |
| bool hasSymbols(const jitlink::Block &B) { |
| return llvm::any_of(B.getSection().symbols(), |
| [&B](const auto &S) { return &S->getBlock() == &B; }); |
| } |
| |
| /// Liveness in JITLink is based on symbols so sections that do not contain |
| /// any symbols will always be pruned. This pass adds anonymous symbols to |
| /// needed sections to prevent pruning. |
| Error markSectionsLive(jitlink::LinkGraph &G) { |
| for (auto &Section : G.sections()) { |
| // We only need allocatable sections. |
| if (Section.getMemLifetime() == orc::MemLifetime::NoAlloc) |
| continue; |
| |
| // Skip empty sections. |
| if (JITLinkLinker::sectionSize(Section) == 0) |
| continue; |
| |
| for (auto *Block : Section.blocks()) { |
| // No need to add symbols if it already has some. |
| if (hasSymbols(*Block)) |
| continue; |
| |
| G.addAnonymousSymbol(*Block, /*Offset=*/0, /*Size=*/0, |
| /*IsCallable=*/false, /*IsLive=*/true); |
| } |
| } |
| |
| return jitlink::markAllSymbolsLive(G); |
| } |
| |
| void reassignSectionAddress(jitlink::LinkGraph &LG, |
| const BinarySection &BinSection, uint64_t Address) { |
| auto *JLSection = LG.findSectionByName(BinSection.getSectionID()); |
| assert(JLSection && "cannot find section in LinkGraph"); |
| |
| auto BlockAddress = Address; |
| for (auto *Block : JITLinkLinker::orderedBlocks(*JLSection)) { |
| // FIXME it would seem to make sense to align here. However, in |
| // non-relocation mode, we simply use the original address of functions |
| // which might not be aligned with the minimum alignment used by |
| // BinaryFunction (2). Example failing test when aligning: |
| // bolt/test/X86/addr32.s |
| Block->setAddress(orc::ExecutorAddr(BlockAddress)); |
| BlockAddress += Block->getSize(); |
| } |
| } |
| |
| } // anonymous namespace |
| |
| struct JITLinkLinker::Context : jitlink::JITLinkContext { |
| JITLinkLinker &Linker; |
| JITLinkLinker::SectionsMapper MapSections; |
| |
| Context(JITLinkLinker &Linker, JITLinkLinker::SectionsMapper MapSections) |
| : JITLinkContext(&Linker.Dylib), Linker(Linker), |
| MapSections(MapSections) {} |
| |
| jitlink::JITLinkMemoryManager &getMemoryManager() override { |
| return *Linker.MM; |
| } |
| |
| bool shouldAddDefaultTargetPasses(const Triple &TT) const override { |
| // The default passes manipulate DWARF sections in a way incompatible with |
| // BOLT. |
| // TODO check if we can actually use these passes to remove some of the |
| // DWARF manipulation done in BOLT. |
| return false; |
| } |
| |
| Error modifyPassConfig(jitlink::LinkGraph &G, |
| jitlink::PassConfiguration &Config) override { |
| Config.PrePrunePasses.push_back(markSectionsLive); |
| Config.PostAllocationPasses.push_back([this](auto &G) { |
| MapSections([&G](const BinarySection &Section, uint64_t Address) { |
| reassignSectionAddress(G, Section, Address); |
| }); |
| return Error::success(); |
| }); |
| |
| if (G.getTargetTriple().isRISCV()) { |
| Config.PostAllocationPasses.push_back( |
| jitlink::createRelaxationPass_ELF_riscv()); |
| } |
| |
| return Error::success(); |
| } |
| |
| void notifyFailed(Error Err) override { |
| errs() << "BOLT-ERROR: JITLink failed: " << Err << '\n'; |
| exit(1); |
| } |
| |
| void |
| lookup(const LookupMap &Symbols, |
| std::unique_ptr<jitlink::JITLinkAsyncLookupContinuation> LC) override { |
| jitlink::AsyncLookupResult AllResults; |
| |
| for (const auto &Symbol : Symbols) { |
| std::string SymName = Symbol.first.str(); |
| LLVM_DEBUG(dbgs() << "BOLT: looking for " << SymName << "\n"); |
| |
| if (auto Address = Linker.lookupSymbol(SymName)) { |
| LLVM_DEBUG(dbgs() << "Resolved to address 0x" |
| << Twine::utohexstr(*Address) << "\n"); |
| AllResults[Symbol.first] = orc::ExecutorSymbolDef( |
| orc::ExecutorAddr(*Address), JITSymbolFlags()); |
| continue; |
| } |
| |
| if (const BinaryData *I = Linker.BC.getBinaryDataByName(SymName)) { |
| uint64_t Address = I->isMoved() && !I->isJumpTable() |
| ? I->getOutputAddress() |
| : I->getAddress(); |
| LLVM_DEBUG(dbgs() << "Resolved to address 0x" |
| << Twine::utohexstr(Address) << "\n"); |
| AllResults[Symbol.first] = orc::ExecutorSymbolDef( |
| orc::ExecutorAddr(Address), JITSymbolFlags()); |
| continue; |
| } |
| |
| if (Linker.BC.isGOTSymbol(SymName)) { |
| if (const BinaryData *I = Linker.BC.getGOTSymbol()) { |
| uint64_t Address = |
| I->isMoved() ? I->getOutputAddress() : I->getAddress(); |
| LLVM_DEBUG(dbgs() << "Resolved to address 0x" |
| << Twine::utohexstr(Address) << "\n"); |
| AllResults[Symbol.first] = orc::ExecutorSymbolDef( |
| orc::ExecutorAddr(Address), JITSymbolFlags()); |
| continue; |
| } |
| } |
| |
| LLVM_DEBUG(dbgs() << "Resolved to address 0x0\n"); |
| AllResults[Symbol.first] = |
| orc::ExecutorSymbolDef(orc::ExecutorAddr(0), JITSymbolFlags()); |
| } |
| |
| LC->run(std::move(AllResults)); |
| } |
| |
| Error notifyResolved(jitlink::LinkGraph &G) override { |
| for (auto *Symbol : G.defined_symbols()) { |
| SymbolInfo Info{Symbol->getAddress().getValue(), Symbol->getSize()}; |
| Linker.Symtab.insert({Symbol->getName().str(), Info}); |
| } |
| |
| return Error::success(); |
| } |
| |
| void notifyFinalized( |
| jitlink::JITLinkMemoryManager::FinalizedAlloc Alloc) override { |
| if (Alloc) |
| Linker.Allocs.push_back(std::move(Alloc)); |
| ++Linker.MM->ObjectsLoaded; |
| } |
| }; |
| |
| JITLinkLinker::JITLinkLinker(BinaryContext &BC, |
| std::unique_ptr<ExecutableFileMemoryManager> MM) |
| : BC(BC), MM(std::move(MM)) {} |
| |
| JITLinkLinker::~JITLinkLinker() { cantFail(MM->deallocate(std::move(Allocs))); } |
| |
| void JITLinkLinker::loadObject(MemoryBufferRef Obj, |
| SectionsMapper MapSections) { |
| auto LG = jitlink::createLinkGraphFromObject(Obj); |
| if (auto E = LG.takeError()) { |
| errs() << "BOLT-ERROR: JITLink failed: " << E << '\n'; |
| exit(1); |
| } |
| |
| if ((*LG)->getTargetTriple().getArch() != BC.TheTriple->getArch()) { |
| errs() << "BOLT-ERROR: linking object with arch " |
| << (*LG)->getTargetTriple().getArchName() |
| << " into context with arch " << BC.TheTriple->getArchName() << "\n"; |
| exit(1); |
| } |
| |
| auto Ctx = std::make_unique<Context>(*this, MapSections); |
| jitlink::link(std::move(*LG), std::move(Ctx)); |
| } |
| |
| std::optional<JITLinkLinker::SymbolInfo> |
| JITLinkLinker::lookupSymbolInfo(StringRef Name) const { |
| auto It = Symtab.find(Name.data()); |
| if (It == Symtab.end()) |
| return std::nullopt; |
| |
| return It->second; |
| } |
| |
| SmallVector<jitlink::Block *, 2> |
| JITLinkLinker::orderedBlocks(const jitlink::Section &Section) { |
| SmallVector<jitlink::Block *, 2> Blocks(Section.blocks()); |
| llvm::sort(Blocks, [](const auto *LHS, const auto *RHS) { |
| return LHS->getAddress() < RHS->getAddress(); |
| }); |
| return Blocks; |
| } |
| |
| size_t JITLinkLinker::sectionSize(const jitlink::Section &Section) { |
| size_t Size = 0; |
| |
| for (const auto *Block : orderedBlocks(Section)) { |
| Size = jitlink::alignToBlock(Size, *Block); |
| Size += Block->getSize(); |
| } |
| |
| return Size; |
| } |
| |
| } // namespace bolt |
| } // namespace llvm |