mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-22 10:42:39 +01:00
[Orc] Add JITLink debug support plugin for ELF x86-64
Add a new ObjectLinkingLayer plugin `DebugObjectManagerPlugin` and infrastructure to handle creation of `DebugObject`s as well as their registration in OrcTargetProcess. The current implementation only covers ELF on x86-64, but the infrastructure is not limited to that. The journey starts with a new `LinkGraph` / `JITLinkContext` pair being created for a `MaterializationResponsibility` in ORC's `ObjectLinkingLayer`. It sends a `notifyMaterializing()` notification, which is forwarded to all registered plugins. The `DebugObjectManagerPlugin` aims to create a `DebugObject` form the provided target triple and object buffer. (Future implementations might create `DebugObject`s from a `LinkGraph` in other ways.) On success it will track it as the pending `DebugObject` for the `MaterializationResponsibility`. This patch only implements the `ELFDebugObject` for `x86-64` targets. It follows the RuntimeDyld approach for debug object setup: it captures a copy of the input object, parses all section headers and prepares to patch their load-address fields with their final addresses in target memory. It instructs the plugin to report the section load-addresses once they are available. The plugin overrides `modifyPassConfig()` and installs a JITLink post-allocation pass to capture them. Once JITLink emitted the finalized executable, the plugin emits and registers the `DebugObject`. For emission it requests a new `JITLinkMemoryManager::Allocation` with a single read-only segment, copies the object with patched section load-addresses over to working memory and triggers finalization to target memory. For registration, it notifies the `DebugObjectRegistrar` provided in the constructor and stores the previously pending`DebugObject` as registered for the corresponding MaterializationResponsibility. The `DebugObjectRegistrar` registers the `DebugObject` with the target process. `llvm-jitlink` uses the `TPCDebugObjectRegistrar`, which calls `llvm_orc_registerJITLoaderGDBWrapper()` in the target process via `TargetProcessControl` to emit a `jit_code_entry` compatible with the GDB JIT interface [1]. So far the implementation only supports registration and no removal. It appears to me that it wouldn't raise any new design questions, so I left this as an addition for the near future. [1] https://sourceware.org/gdb/current/onlinedocs/gdb/JIT-Interface.html Reviewed By: lhames Differential Revision: https://reviews.llvm.org/D97335
This commit is contained in:
parent
0e6bd8e63e
commit
44d6e1a3fb
84
include/llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h
Normal file
84
include/llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h
Normal file
@ -0,0 +1,84 @@
|
||||
//===---- DebugObjectManagerPlugin.h - JITLink debug objects ---*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// ObjectLinkingLayer plugin for emitting debug objects.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_EXECUTIONENGINE_ORC_DEBUGOBJECTMANAGERPLUGIN_H
|
||||
#define LLVM_EXECUTIONENGINE_ORC_DEBUGOBJECTMANAGERPLUGIN_H
|
||||
|
||||
#include "llvm/ADT/Triple.h"
|
||||
#include "llvm/ExecutionEngine/JITLink/JITLink.h"
|
||||
#include "llvm/ExecutionEngine/Orc/Core.h"
|
||||
#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
|
||||
#include "llvm/ExecutionEngine/Orc/TPCDebugObjectRegistrar.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include "llvm/Support/Memory.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
namespace llvm {
|
||||
namespace orc {
|
||||
|
||||
class DebugObject;
|
||||
|
||||
/// Creates and manages DebugObjects for JITLink artifacts.
|
||||
///
|
||||
/// DebugObjects are created when linking for a MaterializationResponsibility
|
||||
/// starts. They are pending as long as materialization is in progress.
|
||||
///
|
||||
/// There can only be one pending DebugObject per MaterializationResponsibility.
|
||||
/// If materialization fails, pending DebugObjects are discarded.
|
||||
///
|
||||
/// Once executable code for the MaterializationResponsibility is emitted, the
|
||||
/// corresponding DebugObject is finalized to target memory and the provided
|
||||
/// DebugObjectRegistrar is notified. Ownership of DebugObjects remains with the
|
||||
/// plugin.
|
||||
///
|
||||
class DebugObjectManagerPlugin : public ObjectLinkingLayer::Plugin {
|
||||
public:
|
||||
DebugObjectManagerPlugin(ExecutionSession &ES,
|
||||
std::unique_ptr<DebugObjectRegistrar> Target);
|
||||
~DebugObjectManagerPlugin();
|
||||
|
||||
void notifyMaterializing(MaterializationResponsibility &MR,
|
||||
jitlink::LinkGraph &G, jitlink::JITLinkContext &Ctx,
|
||||
MemoryBufferRef InputObject) override;
|
||||
|
||||
Error notifyEmitted(MaterializationResponsibility &MR) override;
|
||||
Error notifyFailed(MaterializationResponsibility &MR) override;
|
||||
Error notifyRemovingResources(ResourceKey K) override;
|
||||
|
||||
void notifyTransferringResources(ResourceKey DstKey,
|
||||
ResourceKey SrcKey) override;
|
||||
|
||||
void modifyPassConfig(MaterializationResponsibility &MR, const Triple &TT,
|
||||
jitlink::PassConfiguration &PassConfig) override;
|
||||
|
||||
private:
|
||||
ExecutionSession &ES;
|
||||
|
||||
using OwnedDebugObject = std::unique_ptr<DebugObject>;
|
||||
std::map<ResourceKey, OwnedDebugObject> PendingObjs;
|
||||
std::map<ResourceKey, std::vector<OwnedDebugObject>> RegisteredObjs;
|
||||
|
||||
std::mutex PendingObjsLock;
|
||||
std::mutex RegisteredObjsLock;
|
||||
|
||||
std::unique_ptr<DebugObjectRegistrar> Target;
|
||||
};
|
||||
|
||||
} // namespace orc
|
||||
} // namespace llvm
|
||||
|
||||
#endif // LLVM_EXECUTIONENGINE_ORC_DEBUGOBJECTMANAGERPLUGIN_H
|
@ -72,6 +72,13 @@ public:
|
||||
const Triple &TT,
|
||||
jitlink::PassConfiguration &Config) {}
|
||||
|
||||
// Deprecated. Don't use this in new code. There will be a proper mechanism
|
||||
// for capturing object buffers.
|
||||
virtual void notifyMaterializing(MaterializationResponsibility &MR,
|
||||
jitlink::LinkGraph &G,
|
||||
jitlink::JITLinkContext &Ctx,
|
||||
MemoryBufferRef InputObject) {}
|
||||
|
||||
virtual void notifyLoaded(MaterializationResponsibility &MR) {}
|
||||
virtual Error notifyEmitted(MaterializationResponsibility &MR) {
|
||||
return Error::success();
|
||||
|
67
include/llvm/ExecutionEngine/Orc/TPCDebugObjectRegistrar.h
Normal file
67
include/llvm/ExecutionEngine/Orc/TPCDebugObjectRegistrar.h
Normal file
@ -0,0 +1,67 @@
|
||||
//===- TPCDebugObjectRegistrar.h - TPC-based debug registration -*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// TargetProcessControl based registration of debug objects.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_EXECUTIONENGINE_ORC_TPCDEBUGOBJECTREGISTRAR_H
|
||||
#define LLVM_EXECUTIONENGINE_ORC_TPCDEBUGOBJECTREGISTRAR_H
|
||||
|
||||
#include "llvm/ExecutionEngine/JITSymbol.h"
|
||||
#include "llvm/ExecutionEngine/Orc/TargetProcessControl.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include "llvm/Support/Memory.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace llvm {
|
||||
namespace orc {
|
||||
|
||||
/// Abstract interface for registering debug objects in the target process.
|
||||
class DebugObjectRegistrar {
|
||||
public:
|
||||
virtual Error registerDebugObject(sys::MemoryBlock) = 0;
|
||||
virtual ~DebugObjectRegistrar() {}
|
||||
};
|
||||
|
||||
/// Use TargetProcessControl to register debug objects locally or in a remote
|
||||
/// target process.
|
||||
class TPCDebugObjectRegistrar : public DebugObjectRegistrar {
|
||||
public:
|
||||
using SerializeBlockInfoFn =
|
||||
std::vector<uint8_t> (*)(sys::MemoryBlock TargetMemBlock);
|
||||
|
||||
TPCDebugObjectRegistrar(TargetProcessControl &TPC,
|
||||
JITTargetAddress RegisterFn,
|
||||
SerializeBlockInfoFn SerializeBlockInfo)
|
||||
: TPC(TPC), RegisterFn(RegisterFn),
|
||||
SerializeBlockInfo(SerializeBlockInfo) {}
|
||||
|
||||
Error registerDebugObject(sys::MemoryBlock TargetMem) override {
|
||||
return TPC.runWrapper(RegisterFn, SerializeBlockInfo(TargetMem))
|
||||
.takeError();
|
||||
}
|
||||
|
||||
private:
|
||||
TargetProcessControl &TPC;
|
||||
JITTargetAddress RegisterFn;
|
||||
SerializeBlockInfoFn SerializeBlockInfo;
|
||||
};
|
||||
|
||||
/// Create a TargetProcessControl-based DebugObjectRegistrar that emits debug
|
||||
/// objects to the GDB JIT interface.
|
||||
Expected<std::unique_ptr<TPCDebugObjectRegistrar>>
|
||||
createJITLoaderGDBRegistrar(TargetProcessControl &TPC);
|
||||
|
||||
} // end namespace orc
|
||||
} // end namespace llvm
|
||||
|
||||
#endif // LLVM_EXECUTIONENGINE_ORC_TDEBUGOBJECTREGISTRAR_H
|
@ -0,0 +1,22 @@
|
||||
//===- JITLoaderGDB.h - Register objects via GDB JIT interface -*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Register objects for access by debuggers via the GDB JIT interface.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_JITLOADERGDB_H
|
||||
#define LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_JITLOADERGDB_H
|
||||
|
||||
#include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h"
|
||||
#include <cstdint>
|
||||
|
||||
extern "C" llvm::orc::tpctypes::CWrapperFunctionResult
|
||||
llvm_orc_registerJITLoaderGDBWrapper(uint8_t *Data, uint64_t Size);
|
||||
|
||||
#endif // LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_JITLOADERGDB_H
|
@ -2,6 +2,7 @@ add_llvm_component_library(LLVMOrcJIT
|
||||
CompileOnDemandLayer.cpp
|
||||
CompileUtils.cpp
|
||||
Core.cpp
|
||||
DebugObjectManagerPlugin.cpp
|
||||
DebugUtils.cpp
|
||||
ExecutionUtils.cpp
|
||||
IndirectionUtils.cpp
|
||||
@ -22,6 +23,7 @@ add_llvm_component_library(LLVMOrcJIT
|
||||
SpeculateAnalyses.cpp
|
||||
TargetProcessControl.cpp
|
||||
ThreadSafeModule.cpp
|
||||
TPCDebugObjectRegistrar.cpp
|
||||
TPCDynamicLibrarySearchGenerator.cpp
|
||||
TPCEHFrameRegistrar.cpp
|
||||
TPCIndirectionUtils.cpp
|
||||
|
482
lib/ExecutionEngine/Orc/DebugObjectManagerPlugin.cpp
Normal file
482
lib/ExecutionEngine/Orc/DebugObjectManagerPlugin.cpp
Normal file
@ -0,0 +1,482 @@
|
||||
//===---- DebugObjectManagerPlugin.h - JITLink debug objects ---*- C++ -*-===//
|
||||
//
|
||||
// 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 "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h"
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/BinaryFormat/ELF.h"
|
||||
#include "llvm/ExecutionEngine/JITLink/JITLinkDylib.h"
|
||||
#include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h"
|
||||
#include "llvm/ExecutionEngine/JITSymbol.h"
|
||||
#include "llvm/Object/ELFObjectFile.h"
|
||||
#include "llvm/Object/ObjectFile.h"
|
||||
#include "llvm/Support/Errc.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/Process.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
#define DEBUG_TYPE "orc"
|
||||
|
||||
using namespace llvm::jitlink;
|
||||
using namespace llvm::object;
|
||||
|
||||
namespace llvm {
|
||||
namespace orc {
|
||||
|
||||
class DebugObjectSection {
|
||||
public:
|
||||
virtual void setTargetMemoryRange(SectionRange Range) = 0;
|
||||
virtual void dump(raw_ostream &OS, StringRef Name) {}
|
||||
virtual ~DebugObjectSection() {}
|
||||
};
|
||||
|
||||
template <typename ELFT>
|
||||
class ELFDebugObjectSection : public DebugObjectSection {
|
||||
public:
|
||||
// BinaryFormat ELF is not meant as a mutable format. We can only make changes
|
||||
// that don't invalidate the file structure.
|
||||
ELFDebugObjectSection(const typename ELFT::Shdr *Header)
|
||||
: Header(const_cast<typename ELFT::Shdr *>(Header)) {}
|
||||
|
||||
void setTargetMemoryRange(SectionRange Range) override;
|
||||
void dump(raw_ostream &OS, StringRef Name) override;
|
||||
|
||||
private:
|
||||
typename ELFT::Shdr *Header;
|
||||
|
||||
bool isTextOrDataSection() const;
|
||||
};
|
||||
|
||||
template <typename ELFT>
|
||||
void ELFDebugObjectSection<ELFT>::setTargetMemoryRange(SectionRange Range) {
|
||||
// Only patch load-addresses for executable and data sections.
|
||||
if (isTextOrDataSection()) {
|
||||
Header->sh_addr = static_cast<typename ELFT::uint>(Range.getStart());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ELFT>
|
||||
void ELFDebugObjectSection<ELFT>::dump(raw_ostream &OS, StringRef Name) {
|
||||
if (auto Addr = static_cast<JITTargetAddress>(Header->sh_addr)) {
|
||||
OS << formatv(" {0:x16} {1}\n", Addr, Name);
|
||||
} else {
|
||||
OS << formatv(" {0}\n", Name);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ELFT>
|
||||
bool ELFDebugObjectSection<ELFT>::isTextOrDataSection() const {
|
||||
switch (Header->sh_type) {
|
||||
case ELF::SHT_PROGBITS:
|
||||
case ELF::SHT_X86_64_UNWIND:
|
||||
return Header->sh_flags & (ELF::SHF_EXECINSTR | ELF::SHF_ALLOC);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static constexpr sys::Memory::ProtectionFlags ReadOnly =
|
||||
static_cast<sys::Memory::ProtectionFlags>(sys::Memory::MF_READ);
|
||||
|
||||
enum class Requirement {
|
||||
// Request final target memory load-addresses for all sections.
|
||||
ReportFinalSectionLoadAddresses,
|
||||
};
|
||||
|
||||
/// The plugin creates a debug object from JITLinkContext when JITLink starts
|
||||
/// processing the corresponding LinkGraph. It provides access to the pass
|
||||
/// configuration of the LinkGraph and calls the finalization function, once
|
||||
/// the resulting link artifact was emitted.
|
||||
///
|
||||
class DebugObject {
|
||||
public:
|
||||
DebugObject(JITLinkContext &Ctx) : Ctx(Ctx) {}
|
||||
|
||||
void set(Requirement Req) { Reqs.insert(Req); }
|
||||
bool has(Requirement Req) const { return Reqs.count(Req) > 0; }
|
||||
|
||||
using FinalizeContinuation = std::function<void(Expected<sys::MemoryBlock>)>;
|
||||
void finalizeAsync(FinalizeContinuation OnFinalize);
|
||||
|
||||
virtual void reportSectionTargetMemoryRange(StringRef Name,
|
||||
SectionRange TargetMem) {}
|
||||
virtual ~DebugObject() {}
|
||||
|
||||
protected:
|
||||
using Allocation = JITLinkMemoryManager::Allocation;
|
||||
|
||||
virtual Expected<std::unique_ptr<Allocation>>
|
||||
finalizeWorkingMemory(JITLinkContext &Ctx) = 0;
|
||||
|
||||
private:
|
||||
JITLinkContext &Ctx;
|
||||
std::set<Requirement> Reqs;
|
||||
std::unique_ptr<Allocation> Alloc{nullptr};
|
||||
};
|
||||
|
||||
// Finalize working memory and take ownership of the resulting allocation. Start
|
||||
// copying memory over to the target and pass on the result once we're done.
|
||||
// Ownership of the allocation remains with us for the rest of our lifetime.
|
||||
void DebugObject::finalizeAsync(FinalizeContinuation OnFinalize) {
|
||||
assert(Alloc == nullptr && "Cannot finalize more than once");
|
||||
|
||||
auto AllocOrErr = finalizeWorkingMemory(Ctx);
|
||||
if (!AllocOrErr)
|
||||
OnFinalize(AllocOrErr.takeError());
|
||||
Alloc = std::move(*AllocOrErr);
|
||||
|
||||
Alloc->finalizeAsync([this, OnFinalize](Error Err) {
|
||||
if (Err)
|
||||
OnFinalize(std::move(Err));
|
||||
else
|
||||
OnFinalize(sys::MemoryBlock(
|
||||
jitTargetAddressToPointer<void *>(Alloc->getTargetMemory(ReadOnly)),
|
||||
Alloc->getWorkingMemory(ReadOnly).size()));
|
||||
});
|
||||
}
|
||||
|
||||
/// The current implementation of ELFDebugObject replicates the approach used in
|
||||
/// RuntimeDyld: It patches executable and data section headers in the given
|
||||
/// object buffer with load-addresses of their corresponding sections in target
|
||||
/// memory.
|
||||
///
|
||||
class ELFDebugObject : public DebugObject {
|
||||
public:
|
||||
static Expected<std::unique_ptr<DebugObject>> Create(MemoryBufferRef Buffer,
|
||||
JITLinkContext &Ctx);
|
||||
|
||||
void reportSectionTargetMemoryRange(StringRef Name,
|
||||
SectionRange TargetMem) override;
|
||||
|
||||
protected:
|
||||
Expected<std::unique_ptr<Allocation>>
|
||||
finalizeWorkingMemory(JITLinkContext &Ctx) override;
|
||||
|
||||
Error recordSection(StringRef Name,
|
||||
std::unique_ptr<DebugObjectSection> Section);
|
||||
DebugObjectSection *getSection(StringRef Name);
|
||||
|
||||
private:
|
||||
template <typename ELFT>
|
||||
static Expected<std::unique_ptr<ELFDebugObject>>
|
||||
CreateArchType(MemoryBufferRef Buffer, JITLinkContext &Ctx);
|
||||
|
||||
static Expected<std::unique_ptr<WritableMemoryBuffer>>
|
||||
CopyBuffer(MemoryBufferRef Buffer);
|
||||
|
||||
ELFDebugObject(std::unique_ptr<WritableMemoryBuffer> Buffer,
|
||||
JITLinkContext &Ctx)
|
||||
: DebugObject(Ctx), Buffer(std::move(Buffer)) {
|
||||
set(Requirement::ReportFinalSectionLoadAddresses);
|
||||
}
|
||||
|
||||
std::unique_ptr<WritableMemoryBuffer> Buffer;
|
||||
StringMap<std::unique_ptr<DebugObjectSection>> Sections;
|
||||
};
|
||||
|
||||
static const std::set<StringRef> DwarfSectionNames = {
|
||||
#define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION) \
|
||||
ELF_NAME,
|
||||
#include "llvm/BinaryFormat/Dwarf.def"
|
||||
#undef HANDLE_DWARF_SECTION
|
||||
};
|
||||
|
||||
static bool isDwarfSection(StringRef SectionName) {
|
||||
return DwarfSectionNames.count(SectionName) == 1;
|
||||
}
|
||||
|
||||
Expected<std::unique_ptr<WritableMemoryBuffer>>
|
||||
ELFDebugObject::CopyBuffer(MemoryBufferRef Buffer) {
|
||||
size_t Size = Buffer.getBufferSize();
|
||||
StringRef Name = Buffer.getBufferIdentifier();
|
||||
auto Copy = WritableMemoryBuffer::getNewUninitMemBuffer(Size, Name);
|
||||
if (!Copy)
|
||||
return errorCodeToError(make_error_code(errc::not_enough_memory));
|
||||
|
||||
memcpy(Copy->getBufferStart(), Buffer.getBufferStart(), Size);
|
||||
return std::move(Copy);
|
||||
}
|
||||
|
||||
template <typename ELFT>
|
||||
Expected<std::unique_ptr<ELFDebugObject>>
|
||||
ELFDebugObject::CreateArchType(MemoryBufferRef Buffer, JITLinkContext &Ctx) {
|
||||
using SectionHeader = typename ELFT::Shdr;
|
||||
|
||||
Expected<ELFFile<ELFT>> ObjRef = ELFFile<ELFT>::create(Buffer.getBuffer());
|
||||
if (!ObjRef)
|
||||
return ObjRef.takeError();
|
||||
|
||||
// TODO: Add support for other architectures.
|
||||
uint16_t TargetMachineArch = ObjRef->getHeader().e_machine;
|
||||
if (TargetMachineArch != ELF::EM_X86_64)
|
||||
return nullptr;
|
||||
|
||||
Expected<ArrayRef<SectionHeader>> Sections = ObjRef->sections();
|
||||
if (!Sections)
|
||||
return Sections.takeError();
|
||||
|
||||
Expected<std::unique_ptr<WritableMemoryBuffer>> Copy = CopyBuffer(Buffer);
|
||||
if (!Copy)
|
||||
return Copy.takeError();
|
||||
|
||||
std::unique_ptr<ELFDebugObject> DebugObj(
|
||||
new ELFDebugObject(std::move(*Copy), Ctx));
|
||||
|
||||
bool HasDwarfSection = false;
|
||||
for (const SectionHeader &Header : *Sections) {
|
||||
Expected<StringRef> Name = ObjRef->getSectionName(Header);
|
||||
if (!Name)
|
||||
return Name.takeError();
|
||||
if (Name->empty())
|
||||
continue;
|
||||
HasDwarfSection |= isDwarfSection(*Name);
|
||||
|
||||
auto Wrapped = std::make_unique<ELFDebugObjectSection<ELFT>>(&Header);
|
||||
if (Error Err = DebugObj->recordSection(*Name, std::move(Wrapped)))
|
||||
return std::move(Err);
|
||||
}
|
||||
|
||||
if (!HasDwarfSection) {
|
||||
LLVM_DEBUG(dbgs() << "Aborting debug registration for LinkGraph \""
|
||||
<< DebugObj->Buffer->getBufferIdentifier()
|
||||
<< "\": input object contains no debug info\n");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::move(DebugObj);
|
||||
}
|
||||
|
||||
Expected<std::unique_ptr<DebugObject>>
|
||||
ELFDebugObject::Create(MemoryBufferRef Buffer, JITLinkContext &Ctx) {
|
||||
unsigned char Class, Endian;
|
||||
std::tie(Class, Endian) = getElfArchType(Buffer.getBuffer());
|
||||
|
||||
if (Class == ELF::ELFCLASS32) {
|
||||
if (Endian == ELF::ELFDATA2LSB)
|
||||
return CreateArchType<ELF32LE>(Buffer, Ctx);
|
||||
if (Endian == ELF::ELFDATA2MSB)
|
||||
return CreateArchType<ELF32BE>(Buffer, Ctx);
|
||||
return nullptr;
|
||||
}
|
||||
if (Class == ELF::ELFCLASS64) {
|
||||
if (Endian == ELF::ELFDATA2LSB)
|
||||
return CreateArchType<ELF64LE>(Buffer, Ctx);
|
||||
if (Endian == ELF::ELFDATA2MSB)
|
||||
return CreateArchType<ELF64BE>(Buffer, Ctx);
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Expected<std::unique_ptr<DebugObject::Allocation>>
|
||||
ELFDebugObject::finalizeWorkingMemory(JITLinkContext &Ctx) {
|
||||
LLVM_DEBUG({
|
||||
dbgs() << "Section load-addresses in debug object for \""
|
||||
<< Buffer->getBufferIdentifier() << "\":\n";
|
||||
for (const auto &KV : Sections)
|
||||
KV.second->dump(dbgs(), KV.first());
|
||||
});
|
||||
|
||||
// TODO: This works, but what actual alignment requirements do we have?
|
||||
unsigned Alignment = sys::Process::getPageSizeEstimate();
|
||||
JITLinkMemoryManager &MemMgr = Ctx.getMemoryManager();
|
||||
const JITLinkDylib *JD = Ctx.getJITLinkDylib();
|
||||
size_t Size = Buffer->getBufferSize();
|
||||
|
||||
// Allocate working memory for debug object in read-only segment.
|
||||
auto AllocOrErr = MemMgr.allocate(JD, {{ReadOnly, {Alignment, Size, 0}}});
|
||||
if (!AllocOrErr)
|
||||
return AllocOrErr.takeError();
|
||||
|
||||
// Initialize working memory with a copy of our object buffer.
|
||||
// TODO: Use our buffer as working memory directly.
|
||||
std::unique_ptr<Allocation> Alloc = std::move(*AllocOrErr);
|
||||
MutableArrayRef<char> WorkingMem = Alloc->getWorkingMemory(ReadOnly);
|
||||
memcpy(WorkingMem.data(), Buffer->getBufferStart(), Size);
|
||||
Buffer.reset();
|
||||
|
||||
return std::move(Alloc);
|
||||
}
|
||||
|
||||
void ELFDebugObject::reportSectionTargetMemoryRange(StringRef Name,
|
||||
SectionRange TargetMem) {
|
||||
if (auto *DebugObjSection = getSection(Name))
|
||||
DebugObjSection->setTargetMemoryRange(TargetMem);
|
||||
}
|
||||
|
||||
Error ELFDebugObject::recordSection(
|
||||
StringRef Name, std::unique_ptr<DebugObjectSection> Section) {
|
||||
auto ItInserted = Sections.try_emplace(Name, std::move(Section));
|
||||
if (!ItInserted.second)
|
||||
return make_error<StringError>("Duplicate section",
|
||||
inconvertibleErrorCode());
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
DebugObjectSection *ELFDebugObject::getSection(StringRef Name) {
|
||||
auto It = Sections.find(Name);
|
||||
return It == Sections.end() ? nullptr : It->second.get();
|
||||
}
|
||||
|
||||
static ResourceKey getResourceKey(MaterializationResponsibility &MR) {
|
||||
ResourceKey Key;
|
||||
if (auto Err = MR.withResourceKeyDo([&](ResourceKey K) { Key = K; })) {
|
||||
MR.getExecutionSession().reportError(std::move(Err));
|
||||
return ResourceKey{};
|
||||
}
|
||||
assert(Key && "Invalid key");
|
||||
return Key;
|
||||
}
|
||||
|
||||
/// Creates a debug object based on the input object file from
|
||||
/// ObjectLinkingLayerJITLinkContext.
|
||||
///
|
||||
static Expected<std::unique_ptr<DebugObject>>
|
||||
createDebugObjectFromBuffer(LinkGraph &G, JITLinkContext &Ctx,
|
||||
MemoryBufferRef ObjBuffer) {
|
||||
switch (G.getTargetTriple().getObjectFormat()) {
|
||||
case Triple::ELF:
|
||||
return ELFDebugObject::Create(ObjBuffer, Ctx);
|
||||
|
||||
default:
|
||||
// TODO: Once we add support for other formats, we might want to split this
|
||||
// into multiple files.
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
DebugObjectManagerPlugin::DebugObjectManagerPlugin(
|
||||
ExecutionSession &ES, std::unique_ptr<DebugObjectRegistrar> Target)
|
||||
: ES(ES), Target(std::move(Target)) {}
|
||||
|
||||
DebugObjectManagerPlugin::~DebugObjectManagerPlugin() {}
|
||||
|
||||
void DebugObjectManagerPlugin::notifyMaterializing(
|
||||
MaterializationResponsibility &MR, LinkGraph &G, JITLinkContext &Ctx,
|
||||
MemoryBufferRef ObjBuffer) {
|
||||
assert(PendingObjs.count(getResourceKey(MR)) == 0 &&
|
||||
"Cannot have more than one pending debug object per "
|
||||
"MaterializationResponsibility");
|
||||
|
||||
std::lock_guard<std::mutex> Lock(PendingObjsLock);
|
||||
if (auto DebugObj = createDebugObjectFromBuffer(G, Ctx, ObjBuffer)) {
|
||||
// Not all link artifacts allow debugging.
|
||||
if (*DebugObj != nullptr) {
|
||||
ResourceKey Key = getResourceKey(MR);
|
||||
PendingObjs[Key] = std::move(*DebugObj);
|
||||
}
|
||||
} else {
|
||||
ES.reportError(DebugObj.takeError());
|
||||
}
|
||||
}
|
||||
|
||||
void DebugObjectManagerPlugin::modifyPassConfig(
|
||||
MaterializationResponsibility &MR, const Triple &TT,
|
||||
PassConfiguration &PassConfig) {
|
||||
// Not all link artifacts have associated debug objects.
|
||||
std::lock_guard<std::mutex> Lock(PendingObjsLock);
|
||||
auto It = PendingObjs.find(getResourceKey(MR));
|
||||
if (It == PendingObjs.end())
|
||||
return;
|
||||
|
||||
DebugObject &DebugObj = *It->second;
|
||||
if (DebugObj.has(Requirement::ReportFinalSectionLoadAddresses)) {
|
||||
PassConfig.PostAllocationPasses.push_back(
|
||||
[&DebugObj](LinkGraph &Graph) -> Error {
|
||||
for (const Section &GraphSection : Graph.sections())
|
||||
DebugObj.reportSectionTargetMemoryRange(GraphSection.getName(),
|
||||
SectionRange(GraphSection));
|
||||
return Error::success();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Error DebugObjectManagerPlugin::notifyEmitted(
|
||||
MaterializationResponsibility &MR) {
|
||||
ResourceKey Key = getResourceKey(MR);
|
||||
|
||||
std::lock_guard<std::mutex> Lock(PendingObjsLock);
|
||||
auto It = PendingObjs.find(Key);
|
||||
if (It == PendingObjs.end())
|
||||
return Error::success();
|
||||
|
||||
DebugObject *UnownedDebugObj = It->second.release();
|
||||
PendingObjs.erase(It);
|
||||
|
||||
// FIXME: We released ownership of the DebugObject, so we can easily capture
|
||||
// the raw pointer in the continuation function, which re-owns it immediately.
|
||||
if (UnownedDebugObj)
|
||||
UnownedDebugObj->finalizeAsync(
|
||||
[this, Key, UnownedDebugObj](Expected<sys::MemoryBlock> TargetMem) {
|
||||
std::unique_ptr<DebugObject> ReownedDebugObj(UnownedDebugObj);
|
||||
if (!TargetMem) {
|
||||
ES.reportError(TargetMem.takeError());
|
||||
return;
|
||||
}
|
||||
if (Error Err = Target->registerDebugObject(*TargetMem)) {
|
||||
ES.reportError(std::move(Err));
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> Lock(RegisteredObjsLock);
|
||||
RegisteredObjs[Key].push_back(std::move(ReownedDebugObj));
|
||||
});
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error DebugObjectManagerPlugin::notifyFailed(
|
||||
MaterializationResponsibility &MR) {
|
||||
std::lock_guard<std::mutex> Lock(PendingObjsLock);
|
||||
PendingObjs.erase(getResourceKey(MR));
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
void DebugObjectManagerPlugin::notifyTransferringResources(ResourceKey DstKey,
|
||||
ResourceKey SrcKey) {
|
||||
{
|
||||
std::lock_guard<std::mutex> Lock(RegisteredObjsLock);
|
||||
auto SrcIt = RegisteredObjs.find(SrcKey);
|
||||
if (SrcIt != RegisteredObjs.end()) {
|
||||
// Resources from distinct MaterializationResponsibilitys can get merged
|
||||
// after emission, so we can have multiple debug objects per resource key.
|
||||
for (std::unique_ptr<DebugObject> &DebugObj : SrcIt->second)
|
||||
RegisteredObjs[DstKey].push_back(std::move(DebugObj));
|
||||
RegisteredObjs.erase(SrcIt);
|
||||
}
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> Lock(PendingObjsLock);
|
||||
auto SrcIt = PendingObjs.find(SrcKey);
|
||||
if (SrcIt != PendingObjs.end()) {
|
||||
assert(PendingObjs.count(DstKey) == 0 &&
|
||||
"Cannot have more than one pending debug object per "
|
||||
"MaterializationResponsibility");
|
||||
PendingObjs[DstKey] = std::move(SrcIt->second);
|
||||
PendingObjs.erase(SrcIt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Error DebugObjectManagerPlugin::notifyRemovingResources(ResourceKey K) {
|
||||
{
|
||||
std::lock_guard<std::mutex> Lock(RegisteredObjsLock);
|
||||
RegisteredObjs.erase(K);
|
||||
// TODO: Implement unregister notifications.
|
||||
}
|
||||
std::lock_guard<std::mutex> Lock(PendingObjsLock);
|
||||
PendingObjs.erase(K);
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
} // namespace orc
|
||||
} // namespace llvm
|
@ -10,6 +10,8 @@
|
||||
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ExecutionEngine/JITLink/EHFrameSupport.h"
|
||||
#include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
@ -40,6 +42,11 @@ public:
|
||||
|
||||
JITLinkMemoryManager &getMemoryManager() override { return Layer.MemMgr; }
|
||||
|
||||
void notifyMaterializing(LinkGraph &G) {
|
||||
for (auto &P : Layer.Plugins)
|
||||
P->notifyMaterializing(*MR, G, *this, ObjBuffer->getMemBufferRef());
|
||||
}
|
||||
|
||||
void notifyFailed(Error Err) override {
|
||||
for (auto &P : Layer.Plugins)
|
||||
Err = joinErrors(std::move(Err), P->notifyFailed(*MR));
|
||||
@ -483,19 +490,24 @@ ObjectLinkingLayer::~ObjectLinkingLayer() {
|
||||
void ObjectLinkingLayer::emit(std::unique_ptr<MaterializationResponsibility> R,
|
||||
std::unique_ptr<MemoryBuffer> O) {
|
||||
assert(O && "Object must not be null");
|
||||
auto ObjBuffer = O->getMemBufferRef();
|
||||
MemoryBufferRef ObjBuffer = O->getMemBufferRef();
|
||||
|
||||
auto Ctx = std::make_unique<ObjectLinkingLayerJITLinkContext>(
|
||||
*this, std::move(R), std::move(O));
|
||||
if (auto G = createLinkGraphFromObject(ObjBuffer))
|
||||
if (auto G = createLinkGraphFromObject(ObjBuffer)) {
|
||||
Ctx->notifyMaterializing(**G);
|
||||
link(std::move(*G), std::move(Ctx));
|
||||
else
|
||||
} else {
|
||||
Ctx->notifyFailed(G.takeError());
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectLinkingLayer::emit(std::unique_ptr<MaterializationResponsibility> R,
|
||||
std::unique_ptr<LinkGraph> G) {
|
||||
link(std::move(G), std::make_unique<ObjectLinkingLayerJITLinkContext>(
|
||||
*this, std::move(R), nullptr));
|
||||
auto Ctx = std::make_unique<ObjectLinkingLayerJITLinkContext>(
|
||||
*this, std::move(R), nullptr);
|
||||
Ctx->notifyMaterializing(*G);
|
||||
link(std::move(G), std::move(Ctx));
|
||||
}
|
||||
|
||||
void ObjectLinkingLayer::modifyPassConfig(MaterializationResponsibility &MR,
|
||||
|
62
lib/ExecutionEngine/Orc/TPCDebugObjectRegistrar.cpp
Normal file
62
lib/ExecutionEngine/Orc/TPCDebugObjectRegistrar.cpp
Normal file
@ -0,0 +1,62 @@
|
||||
//===----- TPCDebugObjectRegistrar.cpp - TPC-based debug registration -----===//
|
||||
//
|
||||
// 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 "llvm/ExecutionEngine/Orc/TPCDebugObjectRegistrar.h"
|
||||
|
||||
#include "llvm/ExecutionEngine/Orc/Core.h"
|
||||
#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
|
||||
#include "llvm/Support/BinaryStreamWriter.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace orc {
|
||||
|
||||
// Counterpart for readDebugObjectInfo() in TargetProcess/JITLoaderGDB.cpp
|
||||
static std::vector<uint8_t>
|
||||
writeDebugObjectInfo(sys::MemoryBlock TargetMemBlock) {
|
||||
auto DebugObjAddr = pointerToJITTargetAddress(TargetMemBlock.base());
|
||||
uint64_t DebugObjSize = TargetMemBlock.allocatedSize();
|
||||
|
||||
std::vector<uint8_t> ArgBuffer;
|
||||
ArgBuffer.resize(sizeof(decltype(DebugObjAddr)) +
|
||||
sizeof(decltype(DebugObjSize)));
|
||||
|
||||
BinaryStreamWriter ArgWriter(ArgBuffer, support::endianness::big);
|
||||
cantFail(ArgWriter.writeInteger(DebugObjAddr));
|
||||
cantFail(ArgWriter.writeInteger(DebugObjSize));
|
||||
|
||||
return ArgBuffer;
|
||||
}
|
||||
|
||||
Expected<std::unique_ptr<TPCDebugObjectRegistrar>>
|
||||
createJITLoaderGDBRegistrar(TargetProcessControl &TPC) {
|
||||
auto ProcessHandle = TPC.loadDylib(nullptr);
|
||||
if (!ProcessHandle)
|
||||
return ProcessHandle.takeError();
|
||||
|
||||
SymbolStringPtr RegisterFn =
|
||||
TPC.getTargetTriple().isOSBinFormatMachO()
|
||||
? TPC.intern("_llvm_orc_registerJITLoaderGDBWrapper")
|
||||
: TPC.intern("llvm_orc_registerJITLoaderGDBWrapper");
|
||||
|
||||
SymbolLookupSet RegistrationSymbols;
|
||||
RegistrationSymbols.add(RegisterFn);
|
||||
|
||||
auto Result = TPC.lookupSymbols({{*ProcessHandle, RegistrationSymbols}});
|
||||
if (!Result)
|
||||
return Result.takeError();
|
||||
|
||||
assert(Result->size() == 1 && "Unexpected number of dylibs in result");
|
||||
assert((*Result)[0].size() == 1 &&
|
||||
"Unexpected number of addresses in result");
|
||||
|
||||
return std::make_unique<TPCDebugObjectRegistrar>(TPC, (*Result)[0][0],
|
||||
&writeDebugObjectInfo);
|
||||
}
|
||||
|
||||
} // namespace orc
|
||||
} // namespace llvm
|
@ -1,4 +1,5 @@
|
||||
add_llvm_component_library(LLVMOrcTargetProcess
|
||||
JITLoaderGDB.cpp
|
||||
RegisterEHFrames.cpp
|
||||
TargetExecutionUtils.cpp
|
||||
|
||||
|
110
lib/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.cpp
Normal file
110
lib/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.cpp
Normal file
@ -0,0 +1,110 @@
|
||||
//===- JITLoaderGDB.h - Register objects via GDB JIT interface -*- C++ -*-===//
|
||||
//
|
||||
// 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 "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
|
||||
|
||||
#include "llvm/ExecutionEngine/JITSymbol.h"
|
||||
#include "llvm/Support/BinaryStreamReader.h"
|
||||
#include "llvm/Support/ManagedStatic.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
|
||||
#define DEBUG_TYPE "orc"
|
||||
|
||||
// First version as landed in August 2009
|
||||
static constexpr uint32_t JitDescriptorVersion = 1;
|
||||
|
||||
// Keep in sync with gdb/gdb/jit.h
|
||||
extern "C" {
|
||||
|
||||
typedef enum {
|
||||
JIT_NOACTION = 0,
|
||||
JIT_REGISTER_FN,
|
||||
JIT_UNREGISTER_FN
|
||||
} jit_actions_t;
|
||||
|
||||
struct jit_code_entry {
|
||||
struct jit_code_entry *next_entry;
|
||||
struct jit_code_entry *prev_entry;
|
||||
const char *symfile_addr;
|
||||
uint64_t symfile_size;
|
||||
};
|
||||
|
||||
struct jit_descriptor {
|
||||
uint32_t version;
|
||||
// This should be jit_actions_t, but we want to be specific about the
|
||||
// bit-width.
|
||||
uint32_t action_flag;
|
||||
struct jit_code_entry *relevant_entry;
|
||||
struct jit_code_entry *first_entry;
|
||||
};
|
||||
|
||||
// We put information about the JITed function in this global, which the
|
||||
// debugger reads. Make sure to specify the version statically, because the
|
||||
// debugger checks the version before we can set it during runtime.
|
||||
struct jit_descriptor __jit_debug_descriptor = {JitDescriptorVersion, 0,
|
||||
nullptr, nullptr};
|
||||
|
||||
// Debuggers that implement the GDB JIT interface put a special breakpoint in
|
||||
// this function.
|
||||
LLVM_ATTRIBUTE_NOINLINE void __jit_debug_register_code() {
|
||||
// The noinline and the asm prevent calls to this function from being
|
||||
// optimized out.
|
||||
#if !defined(_MSC_VER)
|
||||
asm volatile("" ::: "memory");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
// Serialize rendezvous with the debugger as well as access to shared data.
|
||||
ManagedStatic<std::mutex> JITDebugLock;
|
||||
|
||||
static std::pair<const char *, uint64_t> readDebugObjectInfo(uint8_t *ArgData,
|
||||
uint64_t ArgSize) {
|
||||
BinaryStreamReader ArgReader(ArrayRef<uint8_t>(ArgData, ArgSize),
|
||||
support::endianness::big);
|
||||
uint64_t Addr, Size;
|
||||
cantFail(ArgReader.readInteger(Addr));
|
||||
cantFail(ArgReader.readInteger(Size));
|
||||
|
||||
return std::make_pair(jitTargetAddressToPointer<const char *>(Addr), Size);
|
||||
}
|
||||
|
||||
extern "C" orc::tpctypes::CWrapperFunctionResult
|
||||
llvm_orc_registerJITLoaderGDBWrapper(uint8_t *Data, uint64_t Size) {
|
||||
if (Size != sizeof(uint64_t) + sizeof(uint64_t))
|
||||
return orc::tpctypes::WrapperFunctionResult::from(
|
||||
"Invalid arguments to llvm_orc_registerJITLoaderGDBWrapper")
|
||||
.release();
|
||||
|
||||
jit_code_entry *E = new jit_code_entry;
|
||||
std::tie(E->symfile_addr, E->symfile_size) = readDebugObjectInfo(Data, Size);
|
||||
E->prev_entry = nullptr;
|
||||
|
||||
std::lock_guard<std::mutex> Lock(*JITDebugLock);
|
||||
|
||||
// Insert this entry at the head of the list.
|
||||
jit_code_entry *NextEntry = __jit_debug_descriptor.first_entry;
|
||||
E->next_entry = NextEntry;
|
||||
if (NextEntry) {
|
||||
NextEntry->prev_entry = E;
|
||||
}
|
||||
|
||||
__jit_debug_descriptor.first_entry = E;
|
||||
__jit_debug_descriptor.relevant_entry = E;
|
||||
|
||||
// Run into the rendezvous breakpoint.
|
||||
__jit_debug_descriptor.action_flag = JIT_REGISTER_FN;
|
||||
__jit_debug_register_code();
|
||||
|
||||
return orc::tpctypes::WrapperFunctionResult().release();
|
||||
}
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ExecutionEngine/Orc/Shared/FDRawByteChannel.h"
|
||||
#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
|
||||
#include "llvm/ExecutionEngine/Orc/TargetProcess/OrcRPCTPCServer.h"
|
||||
#include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h"
|
||||
#include "llvm/Support/DynamicLibrary.h"
|
||||
@ -33,7 +34,8 @@ ExitOnError ExitOnErr;
|
||||
|
||||
LLVM_ATTRIBUTE_USED void linkComponents() {
|
||||
errs() << (void *)&llvm_orc_registerEHFrameSectionWrapper
|
||||
<< (void *)&llvm_orc_deregisterEHFrameSectionWrapper;
|
||||
<< (void *)&llvm_orc_deregisterEHFrameSectionWrapper
|
||||
<< (void *)&llvm_orc_registerJITLoaderGDBWrapper;
|
||||
}
|
||||
|
||||
void printErrorAndExit(Twine ErrMsg) {
|
||||
|
@ -15,9 +15,13 @@
|
||||
#include "llvm-jitlink.h"
|
||||
|
||||
#include "llvm/BinaryFormat/Magic.h"
|
||||
#include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h"
|
||||
#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
|
||||
#include "llvm/ExecutionEngine/Orc/TPCDebugObjectRegistrar.h"
|
||||
#include "llvm/ExecutionEngine/Orc/TPCDynamicLibrarySearchGenerator.h"
|
||||
#include "llvm/ExecutionEngine/Orc/TPCEHFrameRegistrar.h"
|
||||
#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
|
||||
#include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h"
|
||||
#include "llvm/MC/MCAsmInfo.h"
|
||||
#include "llvm/MC/MCContext.h"
|
||||
#include "llvm/MC/MCDisassembler/MCDisassembler.h"
|
||||
@ -155,6 +159,12 @@ static cl::opt<std::string> OutOfProcessExecutorConnect(
|
||||
|
||||
ExitOnError ExitOnErr;
|
||||
|
||||
LLVM_ATTRIBUTE_USED void linkComponents() {
|
||||
errs() << (void *)&llvm_orc_registerEHFrameSectionWrapper
|
||||
<< (void *)&llvm_orc_deregisterEHFrameSectionWrapper
|
||||
<< (void *)&llvm_orc_registerJITLoaderGDBWrapper;
|
||||
}
|
||||
|
||||
namespace llvm {
|
||||
|
||||
static raw_ostream &
|
||||
@ -842,6 +852,9 @@ Session::Session(std::unique_ptr<TargetProcessControl> TPC, Error &Err)
|
||||
ObjLayer.addPlugin(std::make_unique<EHFrameRegistrationPlugin>(
|
||||
ES, ExitOnErr(TPCEHFrameRegistrar::Create(*this->TPC))));
|
||||
|
||||
ObjLayer.addPlugin(std::make_unique<DebugObjectManagerPlugin>(
|
||||
ES, ExitOnErr(createJITLoaderGDBRegistrar(*this->TPC))));
|
||||
|
||||
ObjLayer.addPlugin(std::make_unique<JITLinkSessionPlugin>(*this));
|
||||
|
||||
// Process any harness files.
|
||||
|
Loading…
Reference in New Issue
Block a user