|  | //===- 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/BinaryData.h" | 
|  | #include "bolt/Rewrite/RewriteInstance.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 |