Rename member 'Size' to 'AllocatedSize' in order to provide a hint that the allocated size may be different than the requested size. Comments are added to clarify this point. Updated the InMemoryBuffer in FileOutputBuffer.cpp to track the requested buffer size. Patch by Machiel van Hooren. Thanks Machiel! https://reviews.llvm.org/D61599 llvm-svn: 361195
263 lines
8.1 KiB
C++
263 lines
8.1 KiB
C++
//===------------- JITLink.cpp - Core Run-time JIT linker APIs ------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/ExecutionEngine/JITLink/JITLink.h"
|
|
|
|
#include "llvm/BinaryFormat/Magic.h"
|
|
#include "llvm/ExecutionEngine/JITLink/MachO.h"
|
|
#include "llvm/Support/Format.h"
|
|
#include "llvm/Support/ManagedStatic.h"
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
#include "llvm/Support/Process.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::object;
|
|
|
|
#define DEBUG_TYPE "jitlink"
|
|
|
|
namespace {
|
|
|
|
enum JITLinkErrorCode { GenericJITLinkError = 1 };
|
|
|
|
// FIXME: This class is only here to support the transition to llvm::Error. It
|
|
// will be removed once this transition is complete. Clients should prefer to
|
|
// deal with the Error value directly, rather than converting to error_code.
|
|
class JITLinkerErrorCategory : public std::error_category {
|
|
public:
|
|
const char *name() const noexcept override { return "runtimedyld"; }
|
|
|
|
std::string message(int Condition) const override {
|
|
switch (static_cast<JITLinkErrorCode>(Condition)) {
|
|
case GenericJITLinkError:
|
|
return "Generic JITLink error";
|
|
}
|
|
llvm_unreachable("Unrecognized JITLinkErrorCode");
|
|
}
|
|
};
|
|
|
|
static ManagedStatic<JITLinkerErrorCategory> JITLinkerErrorCategory;
|
|
|
|
} // namespace
|
|
|
|
namespace llvm {
|
|
namespace jitlink {
|
|
|
|
char JITLinkError::ID = 0;
|
|
|
|
void JITLinkError::log(raw_ostream &OS) const { OS << ErrMsg << "\n"; }
|
|
|
|
std::error_code JITLinkError::convertToErrorCode() const {
|
|
return std::error_code(GenericJITLinkError, *JITLinkerErrorCategory);
|
|
}
|
|
|
|
JITLinkMemoryManager::~JITLinkMemoryManager() = default;
|
|
|
|
JITLinkMemoryManager::Allocation::~Allocation() = default;
|
|
|
|
const StringRef getGenericEdgeKindName(Edge::Kind K) {
|
|
switch (K) {
|
|
case Edge::Invalid:
|
|
return "INVALID RELOCATION";
|
|
case Edge::KeepAlive:
|
|
return "Keep-Alive";
|
|
case Edge::LayoutNext:
|
|
return "Layout-Next";
|
|
default:
|
|
llvm_unreachable("Unrecognized relocation kind");
|
|
}
|
|
}
|
|
|
|
raw_ostream &operator<<(raw_ostream &OS, const Atom &A) {
|
|
OS << "<";
|
|
if (A.getName().empty())
|
|
OS << "anon@" << format("0x%016" PRIx64, A.getAddress());
|
|
else
|
|
OS << A.getName();
|
|
OS << " [";
|
|
if (A.isDefined()) {
|
|
auto &DA = static_cast<const DefinedAtom &>(A);
|
|
OS << " section=" << DA.getSection().getName();
|
|
if (DA.isLive())
|
|
OS << " live";
|
|
if (DA.shouldDiscard())
|
|
OS << " should-discard";
|
|
} else
|
|
OS << " external";
|
|
OS << " ]>";
|
|
return OS;
|
|
}
|
|
|
|
void printEdge(raw_ostream &OS, const Atom &FixupAtom, const Edge &E,
|
|
StringRef EdgeKindName) {
|
|
OS << "edge@" << formatv("{0:x16}", FixupAtom.getAddress() + E.getOffset())
|
|
<< ": " << FixupAtom << " + " << E.getOffset() << " -- " << EdgeKindName
|
|
<< " -> " << E.getTarget() << " + " << E.getAddend();
|
|
}
|
|
|
|
Section::~Section() {
|
|
for (auto *DA : DefinedAtoms)
|
|
DA->~DefinedAtom();
|
|
}
|
|
|
|
void AtomGraph::dump(raw_ostream &OS,
|
|
std::function<StringRef(Edge::Kind)> EdgeKindToName) {
|
|
if (!EdgeKindToName)
|
|
EdgeKindToName = [](Edge::Kind K) { return StringRef(); };
|
|
|
|
OS << "Defined atoms:\n";
|
|
for (auto *DA : defined_atoms()) {
|
|
OS << " " << format("0x%016" PRIx64, DA->getAddress()) << ": " << *DA
|
|
<< "\n";
|
|
for (auto &E : DA->edges()) {
|
|
OS << " ";
|
|
StringRef EdgeName = (E.getKind() < Edge::FirstRelocation
|
|
? getGenericEdgeKindName(E.getKind())
|
|
: EdgeKindToName(E.getKind()));
|
|
|
|
if (!EdgeName.empty())
|
|
printEdge(OS, *DA, E, EdgeName);
|
|
else {
|
|
auto EdgeNumberString = std::to_string(E.getKind());
|
|
printEdge(OS, *DA, E, EdgeNumberString);
|
|
}
|
|
OS << "\n";
|
|
}
|
|
}
|
|
|
|
OS << "Absolute atoms:\n";
|
|
for (auto *A : absolute_atoms())
|
|
OS << " " << format("0x%016" PRIx64, A->getAddress()) << ": " << *A
|
|
<< "\n";
|
|
|
|
OS << "External atoms:\n";
|
|
for (auto *A : external_atoms())
|
|
OS << " " << format("0x%016" PRIx64, A->getAddress()) << ": " << *A
|
|
<< "\n";
|
|
}
|
|
|
|
Expected<std::unique_ptr<JITLinkMemoryManager::Allocation>>
|
|
InProcessMemoryManager::allocate(const SegmentsRequestMap &Request) {
|
|
|
|
using AllocationMap = DenseMap<unsigned, sys::MemoryBlock>;
|
|
|
|
// Local class for allocation.
|
|
class IPMMAlloc : public Allocation {
|
|
public:
|
|
IPMMAlloc(AllocationMap SegBlocks) : SegBlocks(std::move(SegBlocks)) {}
|
|
MutableArrayRef<char> getWorkingMemory(ProtectionFlags Seg) override {
|
|
assert(SegBlocks.count(Seg) && "No allocation for segment");
|
|
return {static_cast<char *>(SegBlocks[Seg].base()),
|
|
SegBlocks[Seg].allocatedSize()};
|
|
}
|
|
JITTargetAddress getTargetMemory(ProtectionFlags Seg) override {
|
|
assert(SegBlocks.count(Seg) && "No allocation for segment");
|
|
return reinterpret_cast<JITTargetAddress>(SegBlocks[Seg].base());
|
|
}
|
|
void finalizeAsync(FinalizeContinuation OnFinalize) override {
|
|
OnFinalize(applyProtections());
|
|
}
|
|
Error deallocate() override {
|
|
for (auto &KV : SegBlocks)
|
|
if (auto EC = sys::Memory::releaseMappedMemory(KV.second))
|
|
return errorCodeToError(EC);
|
|
return Error::success();
|
|
}
|
|
|
|
private:
|
|
Error applyProtections() {
|
|
for (auto &KV : SegBlocks) {
|
|
auto &Prot = KV.first;
|
|
auto &Block = KV.second;
|
|
if (auto EC = sys::Memory::protectMappedMemory(Block, Prot))
|
|
return errorCodeToError(EC);
|
|
if (Prot & sys::Memory::MF_EXEC)
|
|
sys::Memory::InvalidateInstructionCache(Block.base(),
|
|
Block.allocatedSize());
|
|
}
|
|
return Error::success();
|
|
}
|
|
|
|
AllocationMap SegBlocks;
|
|
};
|
|
|
|
AllocationMap Blocks;
|
|
const sys::Memory::ProtectionFlags ReadWrite =
|
|
static_cast<sys::Memory::ProtectionFlags>(sys::Memory::MF_READ |
|
|
sys::Memory::MF_WRITE);
|
|
|
|
for (auto &KV : Request) {
|
|
auto &Seg = KV.second;
|
|
|
|
if (Seg.getContentAlignment() > sys::Process::getPageSizeEstimate())
|
|
return make_error<StringError>("Cannot request higher than page "
|
|
"alignment",
|
|
inconvertibleErrorCode());
|
|
|
|
if (sys::Process::getPageSizeEstimate() % Seg.getContentAlignment() != 0)
|
|
return make_error<StringError>("Page size is not a multiple of "
|
|
"alignment",
|
|
inconvertibleErrorCode());
|
|
|
|
uint64_t ZeroFillStart =
|
|
alignTo(Seg.getContentSize(), Seg.getZeroFillAlignment());
|
|
uint64_t SegmentSize = ZeroFillStart + Seg.getZeroFillSize();
|
|
|
|
std::error_code EC;
|
|
auto SegMem =
|
|
sys::Memory::allocateMappedMemory(SegmentSize, nullptr, ReadWrite, EC);
|
|
|
|
if (EC)
|
|
return errorCodeToError(EC);
|
|
|
|
// Zero out the zero-fill memory.
|
|
memset(static_cast<char *>(SegMem.base()) + ZeroFillStart, 0,
|
|
Seg.getZeroFillSize());
|
|
|
|
// Record the block for this segment.
|
|
Blocks[KV.first] = std::move(SegMem);
|
|
}
|
|
return std::unique_ptr<InProcessMemoryManager::Allocation>(
|
|
new IPMMAlloc(std::move(Blocks)));
|
|
}
|
|
|
|
JITLinkContext::~JITLinkContext() {}
|
|
|
|
bool JITLinkContext::shouldAddDefaultTargetPasses(const Triple &TT) const {
|
|
return true;
|
|
}
|
|
|
|
AtomGraphPassFunction JITLinkContext::getMarkLivePass(const Triple &TT) const {
|
|
return AtomGraphPassFunction();
|
|
}
|
|
|
|
Error JITLinkContext::modifyPassConfig(const Triple &TT,
|
|
PassConfiguration &Config) {
|
|
return Error::success();
|
|
}
|
|
|
|
Error markAllAtomsLive(AtomGraph &G) {
|
|
for (auto *DA : G.defined_atoms())
|
|
DA->setLive(true);
|
|
return Error::success();
|
|
}
|
|
|
|
void jitLink(std::unique_ptr<JITLinkContext> Ctx) {
|
|
auto Magic = identify_magic(Ctx->getObjectBuffer().getBuffer());
|
|
switch (Magic) {
|
|
case file_magic::macho_object:
|
|
return jitLink_MachO(std::move(Ctx));
|
|
default:
|
|
Ctx->notifyFailed(make_error<JITLinkError>("Unsupported file format"));
|
|
};
|
|
}
|
|
|
|
} // end namespace jitlink
|
|
} // end namespace llvm
|