This patch changes the return type of sys::Process::getPageSize to Expected<unsigned> to account for the fact that the underlying syscalls used to obtain the page size may fail (see below). For clients who use the page size as an optimization only this patch adds a new method, getPageSizeEstimate, which calls through to getPageSize but discards any error returned and substitues a "reasonable" page size estimate estimate instead. All existing LLVM clients are updated to call getPageSizeEstimate rather than getPageSize. On Unix, sys::Process::getPageSize is implemented in terms of getpagesize or sysconf, depending on which macros are set. The sysconf call is documented to return -1 on failure. On Darwin getpagesize is implemented in terms of sysconf and may also fail (though the manpage documentation does not mention this). These failures have been observed in practice when highly restrictive sandbox permissions have been applied. Without this patch, the result is that getPageSize returns -1, which wreaks havoc on any subsequent code that was assuming a sane page size value. <rdar://problem/41654857> Reviewers: dblaikie, echristo Subscribers: kristina, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D59107 llvm-svn: 360221
262 lines
8.0 KiB
C++
262 lines
8.0 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].size()};
|
|
}
|
|
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.size());
|
|
}
|
|
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
|