[ORC] Initial MachO debugging support (via GDB JIT debug registration interface)
This commit adds a new plugin, GDBJITDebugInfoRegistrationPlugin, that checks for objects containing debug info and registers any debug info found via the GDB JIT registration API. To enable this registration without redundantly representing non-debug sections this plugin synthesizes a new embedded object within a section of the LinkGraph. An allocation action is used to make the registration call. Currently MachO only. ELF users can still use the DebugObjectManagerPlugin. The two are likely to be merged in the near future.
This commit is contained in:
@@ -343,6 +343,12 @@ private:
|
||||
std::vector<Edge> Edges;
|
||||
};
|
||||
|
||||
// Align a JITTargetAddress to conform with block alignment requirements.
|
||||
inline JITTargetAddress alignToBlock(JITTargetAddress Addr, Block &B) {
|
||||
uint64_t Delta = (B.getAlignmentOffset() - Addr) % B.getAlignment();
|
||||
return Addr + Delta;
|
||||
}
|
||||
|
||||
/// Describes symbol linkage. This can be used to make resolve definition
|
||||
/// clashes.
|
||||
enum class Linkage : uint8_t {
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
//===--- DebugerSupportPlugin.h -- Utils for debugger support ---*- 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Generates debug objects and registers them using the jit-loader-gdb protocol.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_EXECUTIONENGINE_ORC_DEBUGGERSUPPORT_H
|
||||
#define LLVM_EXECUTIONENGINE_ORC_DEBUGGERSUPPORT_H
|
||||
|
||||
#include "llvm/ExecutionEngine/Orc/Core.h"
|
||||
#include "llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h"
|
||||
#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace orc {
|
||||
|
||||
/// For each object containing debug info, installs JITLink passes to synthesize
|
||||
/// a debug object and then register it via the GDB JIT-registration interface.
|
||||
///
|
||||
/// Currently MachO only. For ELF use DebugObjectManagerPlugin. These two
|
||||
/// plugins will be merged in the near future.
|
||||
class GDBJITDebugInfoRegistrationPlugin : public ObjectLinkingLayer::Plugin {
|
||||
public:
|
||||
class DebugSectionSynthesizer {
|
||||
public:
|
||||
virtual ~DebugSectionSynthesizer() {}
|
||||
virtual Error startSynthesis() = 0;
|
||||
virtual Error completeSynthesisAndRegister() = 0;
|
||||
};
|
||||
|
||||
static Expected<std::unique_ptr<GDBJITDebugInfoRegistrationPlugin>>
|
||||
Create(ExecutionSession &ES, JITDylib &ProcessJD, const Triple &TT);
|
||||
|
||||
GDBJITDebugInfoRegistrationPlugin(ExecutorAddr RegisterActionAddr)
|
||||
: RegisterActionAddr(RegisterActionAddr) {}
|
||||
|
||||
Error notifyFailed(MaterializationResponsibility &MR) override;
|
||||
Error notifyRemovingResources(ResourceKey K) override;
|
||||
|
||||
void notifyTransferringResources(ResourceKey DstKey,
|
||||
ResourceKey SrcKey) override;
|
||||
|
||||
void modifyPassConfig(MaterializationResponsibility &MR,
|
||||
jitlink::LinkGraph &LG,
|
||||
jitlink::PassConfiguration &PassConfig) override;
|
||||
|
||||
private:
|
||||
void modifyPassConfigForMachO(MaterializationResponsibility &MR,
|
||||
jitlink::LinkGraph &LG,
|
||||
jitlink::PassConfiguration &PassConfig);
|
||||
|
||||
ExecutorAddr RegisterActionAddr;
|
||||
};
|
||||
|
||||
} // namespace orc
|
||||
} // namespace llvm
|
||||
|
||||
#endif // LLVM_EXECUTIONENGINE_ORC_DEBUGGERSUPPORT_H
|
||||
@@ -72,12 +72,6 @@ static Error runAllocAction(JITLinkMemoryManager::AllocActionCall &C) {
|
||||
static_cast<size_t>(C.CtxSize)));
|
||||
}
|
||||
|
||||
// Align a JITTargetAddress to conform with block alignment requirements.
|
||||
static JITTargetAddress alignToBlock(JITTargetAddress Addr, Block &B) {
|
||||
uint64_t Delta = (B.getAlignmentOffset() - Addr) % B.getAlignment();
|
||||
return Addr + Delta;
|
||||
}
|
||||
|
||||
BasicLayout::BasicLayout(LinkGraph &G) : G(G) {
|
||||
|
||||
for (auto &Sec : G.sections()) {
|
||||
|
||||
@@ -180,17 +180,10 @@ Error MachOLinkGraphBuilder::createNormalizedSections() {
|
||||
else
|
||||
Prot = MemProt::Read | MemProt::Write;
|
||||
|
||||
if (!isDebugSection(NSec)) {
|
||||
auto FullyQualifiedName =
|
||||
G->allocateString(StringRef(NSec.SegName) + "," + NSec.SectName);
|
||||
NSec.GraphSection = &G->createSection(
|
||||
StringRef(FullyQualifiedName.data(), FullyQualifiedName.size()),
|
||||
Prot);
|
||||
} else
|
||||
LLVM_DEBUG({
|
||||
dbgs() << " " << NSec.SegName << "," << NSec.SectName
|
||||
<< " is a debug section: No graph section will be created.\n";
|
||||
});
|
||||
auto FullyQualifiedName =
|
||||
G->allocateString(StringRef(NSec.SegName) + "," + NSec.SectName);
|
||||
NSec.GraphSection = &G->createSection(
|
||||
StringRef(FullyQualifiedName.data(), FullyQualifiedName.size()), Prot);
|
||||
|
||||
IndexToSection.insert(std::make_pair(SecIndex, std::move(NSec)));
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ add_llvm_component_library(LLVMOrcJIT
|
||||
CompileUtils.cpp
|
||||
Core.cpp
|
||||
DebugObjectManagerPlugin.cpp
|
||||
DebuggerSupportPlugin.cpp
|
||||
DebugUtils.cpp
|
||||
EPCDynamicLibrarySearchGenerator.cpp
|
||||
EPCDebugObjectRegistrar.cpp
|
||||
|
||||
450
llvm/lib/ExecutionEngine/Orc/DebuggerSupportPlugin.cpp
Normal file
450
llvm/lib/ExecutionEngine/Orc/DebuggerSupportPlugin.cpp
Normal file
@@ -0,0 +1,450 @@
|
||||
//===------- DebuggerSupportPlugin.cpp - Utils for debugger support -------===//
|
||||
//
|
||||
// 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/DebuggerSupportPlugin.h"
|
||||
|
||||
#include "llvm/ADT/SmallSet.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringSet.h"
|
||||
#include "llvm/BinaryFormat/MachO.h"
|
||||
|
||||
#define DEBUG_TYPE "orc"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::jitlink;
|
||||
using namespace llvm::orc;
|
||||
|
||||
static const char *SynthDebugSectionName = "__jitlink_synth_debug_object";
|
||||
|
||||
namespace {
|
||||
|
||||
struct MachO64LE {
|
||||
using UIntPtr = uint64_t;
|
||||
|
||||
using Header = MachO::mach_header_64;
|
||||
using SegmentLC = MachO::segment_command_64;
|
||||
using Section = MachO::section_64;
|
||||
using NList = MachO::nlist_64;
|
||||
|
||||
static constexpr support::endianness Endianness = support::little;
|
||||
static constexpr const uint32_t Magic = MachO::MH_MAGIC_64;
|
||||
static constexpr const uint32_t SegmentCmd = MachO::LC_SEGMENT_64;
|
||||
};
|
||||
|
||||
class MachODebugObjectSynthesizerBase
|
||||
: public GDBJITDebugInfoRegistrationPlugin::DebugSectionSynthesizer {
|
||||
public:
|
||||
static bool isDebugSection(Section &Sec) {
|
||||
return Sec.getName().startswith("__DWARF,");
|
||||
}
|
||||
|
||||
MachODebugObjectSynthesizerBase(LinkGraph &G, ExecutorAddr RegisterActionAddr)
|
||||
: G(G), RegisterActionAddr(RegisterActionAddr) {}
|
||||
virtual ~MachODebugObjectSynthesizerBase() {}
|
||||
|
||||
Error preserveDebugSections() {
|
||||
if (G.findSectionByName(SynthDebugSectionName)) {
|
||||
LLVM_DEBUG({
|
||||
dbgs() << "MachODebugObjectSynthesizer skipping graph " << G.getName()
|
||||
<< " which contains an unexpected existing "
|
||||
<< SynthDebugSectionName << " section.\n";
|
||||
});
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
LLVM_DEBUG({
|
||||
dbgs() << "MachODebugObjectSynthesizer visiting graph " << G.getName()
|
||||
<< "\n";
|
||||
});
|
||||
for (auto &Sec : G.sections()) {
|
||||
if (!isDebugSection(Sec))
|
||||
continue;
|
||||
// Preserve blocks in this debug section by marking one existing symbol
|
||||
// live for each block, and introducing a new live, anonymous symbol for
|
||||
// each currently unreferenced block.
|
||||
LLVM_DEBUG({
|
||||
dbgs() << " Preserving debug section " << Sec.getName() << "\n";
|
||||
});
|
||||
SmallSet<Block *, 8> PreservedBlocks;
|
||||
for (auto *Sym : Sec.symbols()) {
|
||||
bool NewPreservedBlock =
|
||||
PreservedBlocks.insert(&Sym->getBlock()).second;
|
||||
if (NewPreservedBlock)
|
||||
Sym->setLive(true);
|
||||
}
|
||||
for (auto *B : Sec.blocks())
|
||||
if (!PreservedBlocks.count(B))
|
||||
G.addAnonymousSymbol(*B, 0, 0, false, true);
|
||||
}
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
protected:
|
||||
LinkGraph &G;
|
||||
ExecutorAddr RegisterActionAddr;
|
||||
};
|
||||
|
||||
template <typename MachOTraits>
|
||||
class MachODebugObjectSynthesizer : public MachODebugObjectSynthesizerBase {
|
||||
private:
|
||||
class MachOStructWriter {
|
||||
public:
|
||||
MachOStructWriter(MutableArrayRef<char> Buffer) : Buffer(Buffer) {}
|
||||
|
||||
size_t getOffset() const { return Offset; }
|
||||
|
||||
template <typename MachOStruct> void write(MachOStruct S) {
|
||||
assert(Offset + sizeof(S) <= Buffer.size() &&
|
||||
"Container block overflow while constructing debug MachO");
|
||||
if (MachOTraits::Endianness != support::endian::system_endianness())
|
||||
MachO::swapStruct(S);
|
||||
memcpy(Buffer.data() + Offset, &S, sizeof(S));
|
||||
Offset += sizeof(S);
|
||||
}
|
||||
|
||||
private:
|
||||
MutableArrayRef<char> Buffer;
|
||||
size_t Offset = 0;
|
||||
};
|
||||
|
||||
public:
|
||||
using MachODebugObjectSynthesizerBase::MachODebugObjectSynthesizerBase;
|
||||
|
||||
Error startSynthesis() override {
|
||||
LLVM_DEBUG({
|
||||
dbgs() << "Creating " << SynthDebugSectionName << " for " << G.getName()
|
||||
<< "\n";
|
||||
});
|
||||
auto &SDOSec = G.createSection(SynthDebugSectionName, MemProt::Read);
|
||||
|
||||
struct DebugSectionInfo {
|
||||
Section *Sec = nullptr;
|
||||
StringRef SegName;
|
||||
StringRef SecName;
|
||||
JITTargetAddress Alignment = 0;
|
||||
JITTargetAddress StartAddr = 0;
|
||||
uint64_t Size = 0;
|
||||
};
|
||||
|
||||
SmallVector<DebugSectionInfo, 12> DebugSecInfos;
|
||||
size_t NumSections = 0;
|
||||
for (auto &Sec : G.sections()) {
|
||||
if (llvm::empty(Sec.blocks()))
|
||||
continue;
|
||||
|
||||
++NumSections;
|
||||
if (isDebugSection(Sec)) {
|
||||
size_t SepPos = Sec.getName().find(',');
|
||||
if (SepPos > 16 || (Sec.getName().size() - (SepPos + 1) > 16)) {
|
||||
LLVM_DEBUG({
|
||||
dbgs() << "Skipping debug object synthesis for graph "
|
||||
<< G.getName()
|
||||
<< ": encountered non-standard DWARF section name \""
|
||||
<< Sec.getName() << "\"\n";
|
||||
});
|
||||
return Error::success();
|
||||
}
|
||||
DebugSecInfos.push_back({&Sec, Sec.getName().substr(0, SepPos),
|
||||
Sec.getName().substr(SepPos + 1), 0, 0});
|
||||
} else
|
||||
NonDebugSections.push_back(&Sec);
|
||||
}
|
||||
|
||||
// Create container block.
|
||||
size_t SectionsCmdSize =
|
||||
sizeof(typename MachOTraits::Section) * NumSections;
|
||||
size_t SegmentLCSize =
|
||||
sizeof(typename MachOTraits::SegmentLC) + SectionsCmdSize;
|
||||
size_t ContainerBlockSize =
|
||||
sizeof(typename MachOTraits::Header) + SegmentLCSize;
|
||||
auto ContainerBlockContent = G.allocateBuffer(ContainerBlockSize);
|
||||
MachOContainerBlock =
|
||||
&G.createMutableContentBlock(SDOSec, ContainerBlockContent, 0, 8, 0);
|
||||
|
||||
// Copy debug section blocks and symbols.
|
||||
JITTargetAddress NextBlockAddr = MachOContainerBlock->getSize();
|
||||
for (auto &SI : DebugSecInfos) {
|
||||
assert(!llvm::empty(SI.Sec->blocks()) && "Empty debug info section?");
|
||||
|
||||
// Update addresses in debug section.
|
||||
LLVM_DEBUG({
|
||||
dbgs() << " Appending " << SI.Sec->getName() << " ("
|
||||
<< SI.Sec->blocks_size() << " block(s)) at "
|
||||
<< formatv("{0:x8}", NextBlockAddr) << "\n";
|
||||
});
|
||||
for (auto *B : SI.Sec->blocks()) {
|
||||
NextBlockAddr = alignToBlock(NextBlockAddr, *B);
|
||||
B->setAddress(NextBlockAddr);
|
||||
NextBlockAddr += B->getSize();
|
||||
}
|
||||
|
||||
auto &FirstBlock = **SI.Sec->blocks().begin();
|
||||
if (FirstBlock.getAlignmentOffset() != 0)
|
||||
return make_error<StringError>(
|
||||
"First block in " + SI.Sec->getName() +
|
||||
" section has non-zero alignment offset",
|
||||
inconvertibleErrorCode());
|
||||
if (FirstBlock.getAlignment() > std::numeric_limits<uint32_t>::max())
|
||||
return make_error<StringError>("First block in " + SI.Sec->getName() +
|
||||
" has alignment >4Gb",
|
||||
inconvertibleErrorCode());
|
||||
|
||||
SI.Alignment = FirstBlock.getAlignment();
|
||||
SI.StartAddr = FirstBlock.getAddress();
|
||||
SI.Size = NextBlockAddr - SI.StartAddr;
|
||||
G.mergeSections(SDOSec, *SI.Sec);
|
||||
SI.Sec = nullptr;
|
||||
}
|
||||
size_t DebugSectionsSize = NextBlockAddr - MachOContainerBlock->getSize();
|
||||
|
||||
// Write MachO header and debug section load commands.
|
||||
MachOStructWriter Writer(MachOContainerBlock->getAlreadyMutableContent());
|
||||
typename MachOTraits::Header Hdr;
|
||||
memset(&Hdr, 0, sizeof(Hdr));
|
||||
Hdr.magic = MachOTraits::Magic;
|
||||
switch (G.getTargetTriple().getArch()) {
|
||||
case Triple::x86_64:
|
||||
Hdr.cputype = MachO::CPU_TYPE_X86_64;
|
||||
Hdr.cpusubtype = MachO::CPU_SUBTYPE_X86_64_ALL;
|
||||
break;
|
||||
case Triple::aarch64:
|
||||
Hdr.cputype = MachO::CPU_TYPE_ARM64;
|
||||
Hdr.cpusubtype = MachO::CPU_SUBTYPE_ARM64_ALL;
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("Unsupported architecture");
|
||||
}
|
||||
Hdr.filetype = MachO::MH_OBJECT;
|
||||
Hdr.ncmds = 1;
|
||||
Hdr.sizeofcmds = SegmentLCSize;
|
||||
Hdr.flags = 0;
|
||||
Writer.write(Hdr);
|
||||
|
||||
typename MachOTraits::SegmentLC SegLC;
|
||||
memset(&SegLC, 0, sizeof(SegLC));
|
||||
SegLC.cmd = MachOTraits::SegmentCmd;
|
||||
SegLC.cmdsize = SegmentLCSize;
|
||||
SegLC.vmaddr = ContainerBlockSize;
|
||||
SegLC.vmsize = DebugSectionsSize;
|
||||
SegLC.fileoff = ContainerBlockSize;
|
||||
SegLC.filesize = DebugSectionsSize;
|
||||
SegLC.maxprot =
|
||||
MachO::VM_PROT_READ | MachO::VM_PROT_WRITE | MachO::VM_PROT_EXECUTE;
|
||||
SegLC.initprot =
|
||||
MachO::VM_PROT_READ | MachO::VM_PROT_WRITE | MachO::VM_PROT_EXECUTE;
|
||||
SegLC.nsects = NumSections;
|
||||
SegLC.flags = 0;
|
||||
Writer.write(SegLC);
|
||||
|
||||
StringSet<> ExistingLongNames;
|
||||
for (auto &SI : DebugSecInfos) {
|
||||
typename MachOTraits::Section Sec;
|
||||
memset(&Sec, 0, sizeof(Sec));
|
||||
memcpy(Sec.sectname, SI.SecName.data(), SI.SecName.size());
|
||||
memcpy(Sec.segname, SI.SegName.data(), SI.SegName.size());
|
||||
Sec.addr = SI.StartAddr;
|
||||
Sec.size = SI.Size;
|
||||
Sec.offset = SI.StartAddr;
|
||||
Sec.align = SI.Alignment;
|
||||
Sec.reloff = 0;
|
||||
Sec.nreloc = 0;
|
||||
Sec.flags = MachO::S_ATTR_DEBUG;
|
||||
Writer.write(Sec);
|
||||
}
|
||||
|
||||
// Set MachOContainerBlock to indicate success to
|
||||
// completeSynthesisAndRegister.
|
||||
NonDebugSectionsStart = Writer.getOffset();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error completeSynthesisAndRegister() override {
|
||||
if (!MachOContainerBlock) {
|
||||
LLVM_DEBUG({
|
||||
dbgs() << "Not writing MachO debug object header for " << G.getName()
|
||||
<< " since createDebugSection failed\n";
|
||||
});
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
LLVM_DEBUG({
|
||||
dbgs() << "Writing MachO debug object header for " << G.getName() << "\n";
|
||||
});
|
||||
|
||||
MachOStructWriter Writer(
|
||||
MachOContainerBlock->getAlreadyMutableContent().drop_front(
|
||||
NonDebugSectionsStart));
|
||||
|
||||
unsigned LongSectionNameIdx = 0;
|
||||
for (auto *Sec : NonDebugSections) {
|
||||
size_t SepPos = Sec->getName().find(',');
|
||||
StringRef SegName, SecName;
|
||||
std::string CustomSecName;
|
||||
|
||||
if ((SepPos == StringRef::npos && Sec->getName().size() <= 16)) {
|
||||
// No embedded segment name, short section name.
|
||||
SegName = "__JITLINK_CUSTOM";
|
||||
SecName = Sec->getName();
|
||||
} else if (SepPos < 16 && (Sec->getName().size() - (SepPos + 1) <= 16)) {
|
||||
// Canonical embedded segment and section name.
|
||||
SegName = Sec->getName().substr(0, SepPos);
|
||||
SecName = Sec->getName().substr(SepPos + 1);
|
||||
} else {
|
||||
// Long section name that needs to be truncated.
|
||||
assert(Sec->getName().size() > 16 &&
|
||||
"Short section name should have been handled above");
|
||||
SegName = "__JITLINK_CUSTOM";
|
||||
auto IdxStr = std::to_string(++LongSectionNameIdx);
|
||||
CustomSecName = Sec->getName().substr(0, 15 - IdxStr.size()).str();
|
||||
CustomSecName += ".";
|
||||
CustomSecName += IdxStr;
|
||||
SecName = StringRef(CustomSecName.data(), 16);
|
||||
}
|
||||
|
||||
SectionRange R(*Sec);
|
||||
if (R.getFirstBlock()->getAlignmentOffset() != 0)
|
||||
return make_error<StringError>(
|
||||
"While building MachO debug object for " + G.getName() +
|
||||
" first block has non-zero alignment offset",
|
||||
inconvertibleErrorCode());
|
||||
|
||||
typename MachOTraits::Section SecCmd;
|
||||
memset(&SecCmd, 0, sizeof(SecCmd));
|
||||
memcpy(SecCmd.sectname, SecName.data(), SecName.size());
|
||||
memcpy(SecCmd.segname, SegName.data(), SegName.size());
|
||||
SecCmd.addr = R.getStart();
|
||||
SecCmd.size = R.getSize();
|
||||
SecCmd.offset = 0;
|
||||
SecCmd.align = R.getFirstBlock()->getAlignment();
|
||||
SecCmd.reloff = 0;
|
||||
SecCmd.nreloc = 0;
|
||||
SecCmd.flags = 0;
|
||||
Writer.write(SecCmd);
|
||||
}
|
||||
|
||||
SectionRange R(MachOContainerBlock->getSection());
|
||||
G.allocActions().push_back(
|
||||
{{RegisterActionAddr.getValue(), R.getStart(), R.getSize()}, {}});
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
private:
|
||||
Block *MachOContainerBlock = nullptr;
|
||||
SmallVector<Section *, 16> NonDebugSections;
|
||||
size_t NonDebugSectionsStart = 0;
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
namespace llvm {
|
||||
namespace orc {
|
||||
|
||||
Expected<std::unique_ptr<GDBJITDebugInfoRegistrationPlugin>>
|
||||
GDBJITDebugInfoRegistrationPlugin::Create(ExecutionSession &ES,
|
||||
JITDylib &ProcessJD,
|
||||
const Triple &TT) {
|
||||
auto RegisterActionAddr =
|
||||
TT.isOSBinFormatMachO()
|
||||
? ES.intern("_llvm_orc_registerJITLoaderGDBAllocAction")
|
||||
: ES.intern("llvm_orc_registerJITLoaderGDBAllocAction");
|
||||
|
||||
if (auto Addr = ES.lookup({&ProcessJD}, RegisterActionAddr))
|
||||
return std::make_unique<GDBJITDebugInfoRegistrationPlugin>(
|
||||
ExecutorAddr(Addr->getAddress()));
|
||||
else
|
||||
return Addr.takeError();
|
||||
}
|
||||
|
||||
Error GDBJITDebugInfoRegistrationPlugin::notifyFailed(
|
||||
MaterializationResponsibility &MR) {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error GDBJITDebugInfoRegistrationPlugin::notifyRemovingResources(
|
||||
ResourceKey K) {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
void GDBJITDebugInfoRegistrationPlugin::notifyTransferringResources(
|
||||
ResourceKey DstKey, ResourceKey SrcKey) {}
|
||||
|
||||
void GDBJITDebugInfoRegistrationPlugin::modifyPassConfig(
|
||||
MaterializationResponsibility &MR, LinkGraph &LG,
|
||||
PassConfiguration &PassConfig) {
|
||||
|
||||
if (LG.getTargetTriple().getObjectFormat() == Triple::MachO)
|
||||
modifyPassConfigForMachO(MR, LG, PassConfig);
|
||||
else {
|
||||
LLVM_DEBUG({
|
||||
dbgs() << "GDBJITDebugInfoRegistrationPlugin skipping unspported graph "
|
||||
<< LG.getName() << "(triple = " << LG.getTargetTriple().str()
|
||||
<< "\n";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void GDBJITDebugInfoRegistrationPlugin::modifyPassConfigForMachO(
|
||||
MaterializationResponsibility &MR, jitlink::LinkGraph &LG,
|
||||
jitlink::PassConfiguration &PassConfig) {
|
||||
|
||||
switch (LG.getTargetTriple().getArch()) {
|
||||
case Triple::x86_64:
|
||||
case Triple::aarch64:
|
||||
// Supported, continue.
|
||||
assert(LG.getPointerSize() == 8 && "Graph has incorrect pointer size");
|
||||
assert(LG.getEndianness() == support::little &&
|
||||
"Graph has incorrect endianness");
|
||||
break;
|
||||
default:
|
||||
// Unsupported.
|
||||
LLVM_DEBUG({
|
||||
dbgs() << "GDBJITDebugInfoRegistrationPlugin skipping unsupported "
|
||||
<< "MachO graph " << LG.getName()
|
||||
<< "(triple = " << LG.getTargetTriple().str()
|
||||
<< ", pointer size = " << LG.getPointerSize() << ", endianness = "
|
||||
<< (LG.getEndianness() == support::big ? "big" : "little")
|
||||
<< ")\n";
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Scan for debug sections. If we find one then install passes.
|
||||
bool HasDebugSections = false;
|
||||
for (auto &Sec : LG.sections())
|
||||
if (MachODebugObjectSynthesizerBase::isDebugSection(Sec)) {
|
||||
HasDebugSections = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (HasDebugSections) {
|
||||
LLVM_DEBUG({
|
||||
dbgs() << "GDBJITDebugInfoRegistrationPlugin: Graph " << LG.getName()
|
||||
<< " contains debug info. Installing debugger support passes.\n";
|
||||
});
|
||||
|
||||
auto MDOS = std::make_shared<MachODebugObjectSynthesizer<MachO64LE>>(
|
||||
LG, RegisterActionAddr);
|
||||
PassConfig.PrePrunePasses.push_back(
|
||||
[=](LinkGraph &G) { return MDOS->preserveDebugSections(); });
|
||||
PassConfig.PostPrunePasses.push_back(
|
||||
[=](LinkGraph &G) { return MDOS->startSynthesis(); });
|
||||
PassConfig.PreFixupPasses.push_back(
|
||||
[=](LinkGraph &G) { return MDOS->completeSynthesisAndRegister(); });
|
||||
} else {
|
||||
LLVM_DEBUG({
|
||||
dbgs() << "GDBJITDebugInfoRegistrationPlugin: Graph " << LG.getName()
|
||||
<< " contains no debug info. Skipping.\n";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace orc
|
||||
} // namespace llvm
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "llvm/ExecutionEngine/JITSymbol.h"
|
||||
#include "llvm/Support/BinaryStreamReader.h"
|
||||
#include "llvm/Support/FormatVariadic.h"
|
||||
#include "llvm/Support/ManagedStatic.h"
|
||||
|
||||
#include <cstdint>
|
||||
@@ -70,10 +71,18 @@ using namespace llvm::orc;
|
||||
ManagedStatic<std::mutex> JITDebugLock;
|
||||
|
||||
// Register debug object, return error message or null for success.
|
||||
static void registerJITLoaderGDBImpl(ExecutorAddrRange DebugObjRange) {
|
||||
static void registerJITLoaderGDBImpl(const char *ObjAddr, size_t Size) {
|
||||
LLVM_DEBUG({
|
||||
dbgs() << "Registering debug object with GDB JIT interface "
|
||||
<< formatv("([{0:x16} -- {1:x16}])",
|
||||
reinterpret_cast<uintptr_t>(ObjAddr),
|
||||
reinterpret_cast<uintptr_t>(ObjAddr + Size))
|
||||
<< "\n";
|
||||
});
|
||||
|
||||
jit_code_entry *E = new jit_code_entry;
|
||||
E->symfile_addr = DebugObjRange.Start.toPtr<const char *>();
|
||||
E->symfile_size = DebugObjRange.size().getValue();
|
||||
E->symfile_addr = ObjAddr;
|
||||
E->symfile_size = Size;
|
||||
E->prev_entry = nullptr;
|
||||
|
||||
std::lock_guard<std::mutex> Lock(*JITDebugLock);
|
||||
@@ -93,10 +102,26 @@ static void registerJITLoaderGDBImpl(ExecutorAddrRange DebugObjRange) {
|
||||
__jit_debug_register_code();
|
||||
}
|
||||
|
||||
extern "C" orc::shared::CWrapperFunctionResult
|
||||
llvm_orc_registerJITLoaderGDBAllocAction(const char *Data, size_t Size) {
|
||||
using namespace orc::shared;
|
||||
return WrapperFunction<SPSError()>::handle(nullptr, 0,
|
||||
[=]() -> Error {
|
||||
registerJITLoaderGDBImpl(Data,
|
||||
Size);
|
||||
return Error::success();
|
||||
})
|
||||
.release();
|
||||
}
|
||||
|
||||
extern "C" orc::shared::CWrapperFunctionResult
|
||||
llvm_orc_registerJITLoaderGDBWrapper(const char *Data, uint64_t Size) {
|
||||
using namespace orc::shared;
|
||||
return WrapperFunction<void(SPSExecutorAddrRange)>::handle(
|
||||
Data, Size, registerJITLoaderGDBImpl)
|
||||
Data, Size,
|
||||
[](ExecutorAddrRange R) {
|
||||
registerJITLoaderGDBImpl(R.Start.toPtr<char *>(),
|
||||
R.size().getValue());
|
||||
})
|
||||
.release();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# REQUIRES: asserts
|
||||
# RUN: llvm-mc -triple=x86_64-apple-macosx10.9 -filetype=obj -o %t %s
|
||||
# RUN: llvm-jitlink -debug-only=orc -noexec %t 2>&1 | FileCheck %s
|
||||
#
|
||||
# Check that presence of a "__DWARF" section triggers the
|
||||
# GDBJITDebugInfoRegistrationPlugin.
|
||||
#
|
||||
# CHECK: Registering debug object with GDB JIT interface
|
||||
|
||||
.section __TEXT,__text,regular,pure_instructions
|
||||
.globl _main
|
||||
.p2align 4, 0x90
|
||||
_main:
|
||||
xorl %eax, %eax
|
||||
retq
|
||||
|
||||
.section __DWARF,__debug_str,regular,debug
|
||||
Linfo_string:
|
||||
.asciz "test dwarf string"
|
||||
|
||||
.subsections_via_symbols
|
||||
@@ -1,21 +0,0 @@
|
||||
# REQUIRES: asserts
|
||||
# RUN: llvm-mc -triple=x86_64-apple-macosx10.9 -filetype=obj -o %t %s
|
||||
# RUN: llvm-jitlink -debug-only=jitlink -noexec %t 2>&1 | FileCheck %s
|
||||
#
|
||||
# Check that debug sections are not emitted, and consequently that we don't
|
||||
# error out due to buggy past-the-end anonymous relocations in __debug_ranges.
|
||||
#
|
||||
# CHECK: __debug_ranges is a debug section: No graph section will be created.
|
||||
.section __TEXT,__text,regular,pure_instructions
|
||||
.macosx_version_min 10, 15
|
||||
.globl _main
|
||||
.p2align 4, 0x90
|
||||
_main:
|
||||
retq
|
||||
Lpast_the_end:
|
||||
|
||||
.section __DWARF,__debug_ranges
|
||||
.p2align 4
|
||||
.quad Lpast_the_end
|
||||
|
||||
.subsections_via_symbols
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "llvm/BinaryFormat/Magic.h"
|
||||
#include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h"
|
||||
#include "llvm/ExecutionEngine/Orc/DebuggerSupportPlugin.h"
|
||||
#include "llvm/ExecutionEngine/Orc/ELFNixPlatform.h"
|
||||
#include "llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h"
|
||||
#include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h"
|
||||
@@ -987,8 +988,13 @@ Session::Session(std::unique_ptr<ExecutorProcessControl> EPC, Error &Err)
|
||||
ExitOnErr(loadProcessSymbols(*this));
|
||||
ExitOnErr(loadDylibs(*this));
|
||||
|
||||
// Set up the platform.
|
||||
auto &TT = ES.getExecutorProcessControl().getTargetTriple();
|
||||
|
||||
if (TT.isOSBinFormatMachO())
|
||||
ObjLayer.addPlugin(ExitOnErr(
|
||||
GDBJITDebugInfoRegistrationPlugin::Create(this->ES, *MainJD, TT)));
|
||||
|
||||
// Set up the platform.
|
||||
if (TT.isOSBinFormatMachO() && !OrcRuntime.empty()) {
|
||||
if (auto P =
|
||||
MachOPlatform::Create(ES, ObjLayer, *MainJD, OrcRuntime.c_str()))
|
||||
|
||||
Reference in New Issue
Block a user