mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-26 18:36:05 +00:00
240 lines
7.9 KiB
C++
240 lines
7.9 KiB
C++
//===- 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
|