[llvm-branch-commits] [llvm] 6195f9e - Revert "[ORC] Tailor ELF debugger support plugin to load-address patching onl…"
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri Nov 21 10:08:08 PST 2025
Author: Stefan Gränitz
Date: 2025-11-21T19:08:05+01:00
New Revision: 6195f9e528fbc1775c0f8fbfce20d3df18db732a
URL: https://github.com/llvm/llvm-project/commit/6195f9e528fbc1775c0f8fbfce20d3df18db732a
DIFF: https://github.com/llvm/llvm-project/commit/6195f9e528fbc1775c0f8fbfce20d3df18db732a.diff
LOG: Revert "[ORC] Tailor ELF debugger support plugin to load-address patching onl…"
This reverts commit db5eeddbd3f1d5cdb86e365a2a80b036bd66de7f.
Added:
Modified:
llvm/include/llvm/ExecutionEngine/Orc/Debugging/ELFDebugObjectPlugin.h
llvm/lib/ExecutionEngine/Orc/Debugging/ELFDebugObjectPlugin.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Debugging/ELFDebugObjectPlugin.h b/llvm/include/llvm/ExecutionEngine/Orc/Debugging/ELFDebugObjectPlugin.h
index 92dbfe1c79e6e..d946a029fd2ec 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/Debugging/ELFDebugObjectPlugin.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/Debugging/ELFDebugObjectPlugin.h
@@ -23,6 +23,7 @@
#include "llvm/Support/MemoryBufferRef.h"
#include "llvm/TargetParser/Triple.h"
+#include <functional>
#include <map>
#include <memory>
#include <mutex>
@@ -32,24 +33,35 @@ namespace orc {
class DebugObject;
-/// Debugger support for ELF platforms with the GDB JIT Interface. The plugin
-/// emits and manages a separate debug object allocation in addition to the
-/// LinkGraph's own allocation and it notifies the debugger when necessary.
+/// 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 LLVM_ABI ELFDebugObjectPlugin : public ObjectLinkingLayer::Plugin {
public:
- /// Create the plugin for the given session and set additional options
+ /// Create the plugin to submit DebugObjects for JITLink artifacts. For all
+ /// options the recommended setting is true.
///
/// RequireDebugSections:
- /// Emit debug objects only if the LinkGraph contains debug info. Turning
- /// this off allows minimal debugging based on raw symbol names, but it
- /// comes with significant overhead for release configurations.
+ /// Submit debug objects to the executor only if they contain actual debug
+ /// info. Turning this off may allow minimal debugging based on raw symbol
+ /// names. Note that this may cause significant memory and transport
+ /// overhead for objects built with a release configuration.
///
/// AutoRegisterCode:
/// Notify the debugger for each new debug object. This is a good default
/// mode, but it may cause significant overhead when adding many modules in
- /// sequence. Otherwise the user must call __jit_debug_register_code() in
- /// the debug session manually.
+ /// sequence. When turning this off, the user has to issue the call to
+ /// __jit_debug_register_code() on the executor side manually.
///
ELFDebugObjectPlugin(ExecutionSession &ES, bool RequireDebugSections,
bool AutoRegisterCode, Error &Err);
@@ -57,7 +69,7 @@ class LLVM_ABI ELFDebugObjectPlugin : public ObjectLinkingLayer::Plugin {
void notifyMaterializing(MaterializationResponsibility &MR,
jitlink::LinkGraph &G, jitlink::JITLinkContext &Ctx,
- MemoryBufferRef InputObj) override;
+ MemoryBufferRef InputObject) override;
Error notifyFailed(MaterializationResponsibility &MR) override;
Error notifyRemovingResources(JITDylib &JD, ResourceKey K) override;
@@ -82,8 +94,6 @@ class LLVM_ABI ELFDebugObjectPlugin : public ObjectLinkingLayer::Plugin {
ExecutorAddr RegistrationAction;
bool RequireDebugSections;
bool AutoRegisterCode;
-
- DebugObject *getPendingDebugObj(MaterializationResponsibility &MR);
};
} // namespace orc
diff --git a/llvm/lib/ExecutionEngine/Orc/Debugging/ELFDebugObjectPlugin.cpp b/llvm/lib/ExecutionEngine/Orc/Debugging/ELFDebugObjectPlugin.cpp
index 0e9b9a7ff76d3..653645ff03f15 100644
--- a/llvm/lib/ExecutionEngine/Orc/Debugging/ELFDebugObjectPlugin.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/Debugging/ELFDebugObjectPlugin.cpp
@@ -17,17 +17,11 @@
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/BinaryFormat/ELF.h"
-#include "llvm/ExecutionEngine/JITLink/JITLink.h"
#include "llvm/ExecutionEngine/JITLink/JITLinkDylib.h"
#include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h"
-#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
-#include "llvm/ExecutionEngine/Orc/Shared/MemoryFlags.h"
#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"
-#include "llvm/IR/Instructions.h"
#include "llvm/Object/ELFObjectFile.h"
-#include "llvm/Object/Error.h"
#include "llvm/Support/Errc.h"
-#include "llvm/Support/Error.h"
#include "llvm/Support/MSVCErrorWorkarounds.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Process.h"
@@ -43,19 +37,111 @@ using namespace llvm::object;
namespace llvm {
namespace orc {
-// Helper class to emit and fixup an individual debug object
-class DebugObject {
+class DebugObjectSection {
public:
- using FinalizedAlloc = JITLinkMemoryManager::FinalizedAlloc;
+ virtual void setTargetMemoryRange(SectionRange Range) = 0;
+ virtual void dump(raw_ostream &OS, StringRef Name) {}
+ virtual ~DebugObjectSection() = default;
+};
+
+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;
+
+ Error validateInBounds(StringRef Buffer, const char *Name) const;
+
+private:
+ typename ELFT::Shdr *Header;
+};
+
+template <typename ELFT>
+void ELFDebugObjectSection<ELFT>::setTargetMemoryRange(SectionRange Range) {
+ // All recorded sections are candidates for load-address patching.
+ Header->sh_addr =
+ static_cast<typename ELFT::uint>(Range.getStart().getValue());
+}
+
+template <typename ELFT>
+Error ELFDebugObjectSection<ELFT>::validateInBounds(StringRef Buffer,
+ const char *Name) const {
+ const uint8_t *Start = Buffer.bytes_begin();
+ const uint8_t *End = Buffer.bytes_end();
+ const uint8_t *HeaderPtr = reinterpret_cast<uint8_t *>(Header);
+ if (HeaderPtr < Start || HeaderPtr + sizeof(typename ELFT::Shdr) > End)
+ return make_error<StringError>(
+ formatv("{0} section header at {1:x16} not within bounds of the "
+ "given debug object buffer [{2:x16} - {3:x16}]",
+ Name, &Header->sh_addr, Start, End),
+ inconvertibleErrorCode());
+ if (Header->sh_offset + Header->sh_size > Buffer.size())
+ return make_error<StringError>(
+ formatv("{0} section data [{1:x16} - {2:x16}] not within bounds of "
+ "the given debug object buffer [{3:x16} - {4:x16}]",
+ Name, Start + Header->sh_offset,
+ Start + Header->sh_offset + Header->sh_size, Start, End),
+ inconvertibleErrorCode());
+ return Error::success();
+}
+
+template <typename ELFT>
+void ELFDebugObjectSection<ELFT>::dump(raw_ostream &OS, StringRef Name) {
+ if (uint64_t Addr = Header->sh_addr) {
+ OS << formatv(" {0:x16} {1}\n", Addr, Name);
+ } else {
+ OS << formatv(" {0}\n", Name);
+ }
+}
+
+enum DebugObjectFlags : int {
+ // Request final target memory load-addresses for all sections.
+ ReportFinalSectionLoadAddresses = 1 << 0,
+
+ // We found sections with debug information when processing the input object.
+ HasDebugSections = 1 << 1,
+};
- DebugObject(StringRef Name, SimpleSegmentAlloc Alloc, JITLinkContext &Ctx,
+/// The plugin creates a debug object from 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(JITLinkMemoryManager &MemMgr, const JITLinkDylib *JD,
ExecutionSession &ES)
- : Name(Name), WorkingMem(std::move(Alloc)),
- MemMgr(Ctx.getMemoryManager()), ES(ES) {
+ : MemMgr(MemMgr), JD(JD), ES(ES), Flags(DebugObjectFlags{}) {
FinalizeFuture = FinalizePromise.get_future();
}
- ~DebugObject() {
+ bool hasFlags(DebugObjectFlags F) const { return Flags & F; }
+ void setFlags(DebugObjectFlags F) {
+ Flags = static_cast<DebugObjectFlags>(Flags | F);
+ }
+ void clearFlags(DebugObjectFlags F) {
+ Flags = static_cast<DebugObjectFlags>(Flags & ~F);
+ }
+
+ using FinalizeContinuation = std::function<void(Expected<ExecutorAddrRange>)>;
+ void finalizeAsync(FinalizeContinuation OnAsync);
+
+ void failMaterialization(Error Err) {
+ FinalizePromise.set_value(std::move(Err));
+ }
+
+ void reportTargetMem(ExecutorAddrRange TargetMem) {
+ FinalizePromise.set_value(TargetMem);
+ }
+
+ Expected<ExecutorAddrRange> awaitTargetMem() { return FinalizeFuture.get(); }
+
+ virtual ~DebugObject() {
if (Alloc) {
std::vector<FinalizedAlloc> Allocs;
Allocs.push_back(std::move(Alloc));
@@ -64,122 +150,259 @@ class DebugObject {
}
}
- StringRef getName() const { return Name; }
+ virtual void reportSectionTargetMemoryRange(StringRef Name,
+ SectionRange TargetMem) {}
- StringRef getBuffer() {
- MutableArrayRef<char> Buffer = getMutBuffer();
- return StringRef(Buffer.data(), Buffer.size());
- }
+protected:
+ using InFlightAlloc = JITLinkMemoryManager::InFlightAlloc;
+ using FinalizedAlloc = JITLinkMemoryManager::FinalizedAlloc;
- MutableArrayRef<char> getMutBuffer() {
- auto SegInfo = WorkingMem.getSegInfo(MemProt::Read);
- return SegInfo.WorkingMem;
- }
+ virtual Expected<SimpleSegmentAlloc> finalizeWorkingMemory() = 0;
- SimpleSegmentAlloc &getTargetAlloc() { return WorkingMem; }
+ JITLinkMemoryManager &MemMgr;
+ const JITLinkDylib *JD = nullptr;
+ ExecutionSession &ES;
- void trackFinalizedAlloc(FinalizedAlloc FA) { Alloc = std::move(FA); }
+ std::promise<MSVCPExpected<ExecutorAddrRange>> FinalizePromise;
+ std::future<MSVCPExpected<ExecutorAddrRange>> FinalizeFuture;
- Expected<ExecutorAddrRange> awaitTargetMem() { return FinalizeFuture.get(); }
+private:
+ DebugObjectFlags Flags;
+ FinalizedAlloc Alloc;
+};
- void reportTargetMem(ExecutorAddrRange TargetMem) {
- FinalizePromise.set_value(TargetMem);
+// 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(!this->Alloc && "Cannot finalize more than once");
+ if (auto SimpleSegAlloc = finalizeWorkingMemory()) {
+ auto ROSeg = SimpleSegAlloc->getSegInfo(MemProt::Read);
+ ExecutorAddrRange DebugObjRange(ROSeg.Addr, ROSeg.WorkingMem.size());
+ SimpleSegAlloc->finalize(
+ [this, DebugObjRange,
+ OnFinalize = std::move(OnFinalize)](Expected<FinalizedAlloc> FA) {
+ if (FA) {
+ // Note: FA->getAddress() is supposed to be the address of the
+ // memory range on the target, but InProcessMemoryManager returns
+ // the address of a FinalizedAllocInfo helper instead.
+ this->Alloc = std::move(*FA);
+ OnFinalize(DebugObjRange);
+ } else
+ OnFinalize(FA.takeError());
+ });
+ } else {
+ // We could report this error synchronously, but it's easier this way,
+ // because the FinalizePromise will be triggered unconditionally.
+ OnFinalize(SimpleSegAlloc.takeError());
}
+}
- void failMaterialization(Error Err) {
- FinalizePromise.set_value(std::move(Err));
- }
+/// 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, ExecutionSession &ES);
+
+ void reportSectionTargetMemoryRange(StringRef Name,
+ SectionRange TargetMem) override;
- void reportError(Error Err) { ES.reportError(std::move(Err)); }
+ StringRef getBuffer() const { return Buffer->getMemBufferRef().getBuffer(); }
- using GetLoadAddressFn = llvm::unique_function<ExecutorAddr(StringRef)>;
- void visitSections(GetLoadAddressFn Callback);
+protected:
+ Expected<SimpleSegmentAlloc> finalizeWorkingMemory() override;
template <typename ELFT>
- void visitSectionLoadAddresses(GetLoadAddressFn Callback);
+ Error recordSection(StringRef Name,
+ std::unique_ptr<ELFDebugObjectSection<ELFT>> Section);
+ DebugObjectSection *getSection(StringRef Name);
private:
- std::string Name;
- SimpleSegmentAlloc WorkingMem;
- JITLinkMemoryManager &MemMgr;
- ExecutionSession &ES;
+ template <typename ELFT>
+ static Expected<std::unique_ptr<ELFDebugObject>>
+ CreateArchType(MemoryBufferRef Buffer, JITLinkMemoryManager &MemMgr,
+ const JITLinkDylib *JD, ExecutionSession &ES);
+
+ static std::unique_ptr<WritableMemoryBuffer>
+ CopyBuffer(MemoryBufferRef Buffer, Error &Err);
+
+ ELFDebugObject(std::unique_ptr<WritableMemoryBuffer> Buffer,
+ JITLinkMemoryManager &MemMgr, const JITLinkDylib *JD,
+ ExecutionSession &ES)
+ : DebugObject(MemMgr, JD, ES), Buffer(std::move(Buffer)) {
+ setFlags(ReportFinalSectionLoadAddresses);
+ }
- std::promise<MSVCPExpected<ExecutorAddrRange>> FinalizePromise;
- std::future<MSVCPExpected<ExecutorAddrRange>> FinalizeFuture;
+ std::unique_ptr<WritableMemoryBuffer> Buffer;
+ StringMap<std::unique_ptr<DebugObjectSection>> Sections;
+};
- FinalizedAlloc Alloc;
+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;
+}
+
+std::unique_ptr<WritableMemoryBuffer>
+ELFDebugObject::CopyBuffer(MemoryBufferRef Buffer, Error &Err) {
+ ErrorAsOutParameter _(Err);
+ size_t Size = Buffer.getBufferSize();
+ StringRef Name = Buffer.getBufferIdentifier();
+ if (auto Copy = WritableMemoryBuffer::getNewUninitMemBuffer(Size, Name)) {
+ memcpy(Copy->getBufferStart(), Buffer.getBufferStart(), Size);
+ return Copy;
+ }
+
+ Err = errorCodeToError(make_error_code(errc::not_enough_memory));
+ return nullptr;
+}
+
template <typename ELFT>
-void DebugObject::visitSectionLoadAddresses(GetLoadAddressFn Callback) {
+Expected<std::unique_ptr<ELFDebugObject>>
+ELFDebugObject::CreateArchType(MemoryBufferRef Buffer,
+ JITLinkMemoryManager &MemMgr,
+ const JITLinkDylib *JD, ExecutionSession &ES) {
using SectionHeader = typename ELFT::Shdr;
- Expected<ELFFile<ELFT>> ObjRef = ELFFile<ELFT>::create(getBuffer());
- if (!ObjRef) {
- reportError(ObjRef.takeError());
- return;
- }
+ Error Err = Error::success();
+ std::unique_ptr<ELFDebugObject> DebugObj(
+ new ELFDebugObject(CopyBuffer(Buffer, Err), MemMgr, JD, ES));
+ if (Err)
+ return std::move(Err);
+
+ Expected<ELFFile<ELFT>> ObjRef = ELFFile<ELFT>::create(DebugObj->getBuffer());
+ if (!ObjRef)
+ return ObjRef.takeError();
Expected<ArrayRef<SectionHeader>> Sections = ObjRef->sections();
- if (!Sections) {
- reportError(Sections.takeError());
- return;
- }
+ if (!Sections)
+ return Sections.takeError();
for (const SectionHeader &Header : *Sections) {
Expected<StringRef> Name = ObjRef->getSectionName(Header);
- if (!Name) {
- reportError(Name.takeError());
- return;
- }
+ if (!Name)
+ return Name.takeError();
if (Name->empty())
continue;
- ExecutorAddr LoadAddress = Callback(*Name);
- const_cast<SectionHeader &>(Header).sh_addr =
- static_cast<typename ELFT::uint>(LoadAddress.getValue());
+ if (isDwarfSection(*Name))
+ DebugObj->setFlags(HasDebugSections);
+
+ // Only record text and data sections (i.e. no bss, comments, rel, etc.)
+ if (Header.sh_type != ELF::SHT_PROGBITS &&
+ Header.sh_type != ELF::SHT_X86_64_UNWIND)
+ continue;
+ if (!(Header.sh_flags & ELF::SHF_ALLOC))
+ continue;
+
+ auto Wrapped = std::make_unique<ELFDebugObjectSection<ELFT>>(&Header);
+ if (Error Err = DebugObj->recordSection(*Name, std::move(Wrapped)))
+ return std::move(Err);
}
- LLVM_DEBUG({
- dbgs() << "Section load-addresses in debug object for \"" << getName()
- << "\":\n";
- for (const SectionHeader &Header : *Sections) {
- StringRef Name = cantFail(ObjRef->getSectionName(Header));
- if (uint64_t Addr = Header.sh_addr) {
- dbgs() << formatv(" {0:x16} {1}\n", Addr, Name);
- } else {
- dbgs() << formatv(" {0}\n", Name);
- }
- }
- });
+ return std::move(DebugObj);
}
-void DebugObject::visitSections(GetLoadAddressFn Callback) {
+Expected<std::unique_ptr<DebugObject>>
+ELFDebugObject::Create(MemoryBufferRef Buffer, JITLinkContext &Ctx,
+ ExecutionSession &ES) {
unsigned char Class, Endian;
- std::tie(Class, Endian) = getElfArchType(getBuffer());
+ std::tie(Class, Endian) = getElfArchType(Buffer.getBuffer());
- switch (Class) {
- case ELF::ELFCLASS32:
+ if (Class == ELF::ELFCLASS32) {
if (Endian == ELF::ELFDATA2LSB)
- return visitSectionLoadAddresses<ELF32LE>(std::move(Callback));
+ return CreateArchType<ELF32LE>(Buffer, Ctx.getMemoryManager(),
+ Ctx.getJITLinkDylib(), ES);
if (Endian == ELF::ELFDATA2MSB)
- return visitSectionLoadAddresses<ELF32BE>(std::move(Callback));
- return reportError(createStringError(
- object_error::invalid_file_type,
- "Invalid endian in 32-bit ELF object file: %x", Endian));
-
- case ELF::ELFCLASS64:
+ return CreateArchType<ELF32BE>(Buffer, Ctx.getMemoryManager(),
+ Ctx.getJITLinkDylib(), ES);
+ return nullptr;
+ }
+ if (Class == ELF::ELFCLASS64) {
if (Endian == ELF::ELFDATA2LSB)
- return visitSectionLoadAddresses<ELF64LE>(std::move(Callback));
+ return CreateArchType<ELF64LE>(Buffer, Ctx.getMemoryManager(),
+ Ctx.getJITLinkDylib(), ES);
if (Endian == ELF::ELFDATA2MSB)
- return visitSectionLoadAddresses<ELF64BE>(std::move(Callback));
- return reportError(createStringError(
- object_error::invalid_file_type,
- "Invalid endian in 64-bit ELF object file: %x", Endian));
+ return CreateArchType<ELF64BE>(Buffer, Ctx.getMemoryManager(),
+ Ctx.getJITLinkDylib(), ES);
+ return nullptr;
+ }
+ return nullptr;
+}
+
+Expected<SimpleSegmentAlloc> ELFDebugObject::finalizeWorkingMemory() {
+ 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 PageSize = sys::Process::getPageSizeEstimate();
+ size_t Size = Buffer->getBufferSize();
+
+ // Allocate working memory for debug object in read-only segment.
+ auto Alloc = SimpleSegmentAlloc::Create(
+ MemMgr, ES.getSymbolStringPool(), ES.getTargetTriple(), JD,
+ {{MemProt::Read, {Size, Align(PageSize)}}});
+ if (!Alloc)
+ return Alloc;
+
+ // Initialize working memory with a copy of our object buffer.
+ auto SegInfo = Alloc->getSegInfo(MemProt::Read);
+ memcpy(SegInfo.WorkingMem.data(), Buffer->getBufferStart(), Size);
+ Buffer.reset();
+
+ return Alloc;
+}
+
+void ELFDebugObject::reportSectionTargetMemoryRange(StringRef Name,
+ SectionRange TargetMem) {
+ if (auto *DebugObjSection = getSection(Name))
+ DebugObjSection->setTargetMemoryRange(TargetMem);
+}
+
+template <typename ELFT>
+Error ELFDebugObject::recordSection(
+ StringRef Name, std::unique_ptr<ELFDebugObjectSection<ELFT>> Section) {
+ if (Error Err = Section->validateInBounds(this->getBuffer(), Name.data()))
+ return Err;
+ bool Inserted = Sections.try_emplace(Name, std::move(Section)).second;
+ if (!Inserted)
+ LLVM_DEBUG(dbgs() << "Skipping debug registration for section '" << Name
+ << "' in object " << Buffer->getBufferIdentifier()
+ << " (duplicate name)\n");
+ return Error::success();
+}
+
+DebugObjectSection *ELFDebugObject::getSection(StringRef Name) {
+ auto It = Sections.find(Name);
+ return It == Sections.end() ? nullptr : It->second.get();
+}
+
+/// Creates a debug object based on the input object file from
+/// ObjectLinkingLayerJITLinkContext.
+///
+static Expected<std::unique_ptr<DebugObject>>
+createDebugObjectFromBuffer(ExecutionSession &ES, LinkGraph &G,
+ JITLinkContext &Ctx, MemoryBufferRef ObjBuffer) {
+ switch (G.getTargetTriple().getObjectFormat()) {
+ case Triple::ELF:
+ return ELFDebugObject::Create(ObjBuffer, Ctx, ES);
default:
- return reportError(createStringError(object_error::invalid_file_type,
- "Invalid arch in ELF object file: %x",
- Class));
+ // TODO: Once we add support for other formats, we might want to split this
+ // into multiple files.
+ return nullptr;
}
}
@@ -196,146 +419,91 @@ ELFDebugObjectPlugin::ELFDebugObjectPlugin(ExecutionSession &ES,
ELFDebugObjectPlugin::~ELFDebugObjectPlugin() = default;
-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;
-}
-
void ELFDebugObjectPlugin::notifyMaterializing(
MaterializationResponsibility &MR, LinkGraph &G, JITLinkContext &Ctx,
- MemoryBufferRef InputObj) {
- if (G.getTargetTriple().getObjectFormat() != Triple::ELF)
- return;
-
- // Step 1: We copy the raw input object into the working memory of a
- // single-segment read-only allocation
- size_t Size = InputObj.getBufferSize();
- auto Alignment = sys::Process::getPageSizeEstimate();
- SimpleSegmentAlloc::Segment Segment{Size, Align(Alignment)};
-
- auto Alloc = SimpleSegmentAlloc::Create(
- Ctx.getMemoryManager(), ES.getSymbolStringPool(), ES.getTargetTriple(),
- Ctx.getJITLinkDylib(), {{MemProt::Read, Segment}});
- if (!Alloc) {
- ES.reportError(Alloc.takeError());
- return;
- }
-
+ MemoryBufferRef ObjBuffer) {
std::lock_guard<std::mutex> Lock(PendingObjsLock);
- assert(PendingObjs.count(&MR) == 0 && "One debug object per materialization");
- PendingObjs[&MR] = std::make_unique<DebugObject>(
- InputObj.getBufferIdentifier(), std::move(*Alloc), Ctx, ES);
-
- MutableArrayRef<char> Buffer = PendingObjs[&MR]->getMutBuffer();
- memcpy(Buffer.data(), InputObj.getBufferStart(), Size);
-}
+ assert(PendingObjs.count(&MR) == 0 &&
+ "Cannot have more than one pending debug object per "
+ "MaterializationResponsibility");
-DebugObject *
-ELFDebugObjectPlugin::getPendingDebugObj(MaterializationResponsibility &MR) {
- std::lock_guard<std::mutex> Lock(PendingObjsLock);
- auto It = PendingObjs.find(&MR);
- return It == PendingObjs.end() ? nullptr : It->second.get();
+ if (auto DebugObj = createDebugObjectFromBuffer(ES, G, Ctx, ObjBuffer)) {
+ // Not all link artifacts allow debugging.
+ if (*DebugObj == nullptr)
+ return;
+ if (RequireDebugSections && !(**DebugObj).hasFlags(HasDebugSections)) {
+ LLVM_DEBUG(dbgs() << "Skipping debug registration for LinkGraph '"
+ << G.getName() << "': no debug info\n");
+ return;
+ }
+ PendingObjs[&MR] = std::move(*DebugObj);
+ } else {
+ ES.reportError(DebugObj.takeError());
+ }
}
void ELFDebugObjectPlugin::modifyPassConfig(MaterializationResponsibility &MR,
LinkGraph &G,
PassConfiguration &PassConfig) {
- if (!getPendingDebugObj(MR))
+ // Not all link artifacts have associated debug objects.
+ std::lock_guard<std::mutex> Lock(PendingObjsLock);
+ auto It = PendingObjs.find(&MR);
+ if (It == PendingObjs.end())
return;
- PassConfig.PostAllocationPasses.push_back([this, &MR](LinkGraph &G) -> Error {
- size_t SectionsPatched = 0;
- bool HasDebugSections = false;
- DebugObject *DebugObj = getPendingDebugObj(MR);
- assert(DebugObj && "Don't inject passes if we have no debug object");
-
- // Step 2: Once the target memory layout is ready, we write the
- // addresses of the LinkGraph sections into the load-address fields of the
- // section headers in our debug object allocation
- DebugObj->visitSections(
- [&G, &SectionsPatched, &HasDebugSections](StringRef Name) {
- SectionsPatched += 1;
- if (isDwarfSection(Name))
- HasDebugSections = true;
- Section *S = G.findSectionByName(Name);
- assert(S && "No graph section for object section");
- return SectionRange(*S).getStart();
+ DebugObject &DebugObj = *It->second;
+ if (DebugObj.hasFlags(ReportFinalSectionLoadAddresses)) {
+ PassConfig.PostAllocationPasses.push_back(
+ [&DebugObj](LinkGraph &Graph) -> Error {
+ for (const Section &GraphSection : Graph.sections())
+ DebugObj.reportSectionTargetMemoryRange(GraphSection.getName(),
+ SectionRange(GraphSection));
+ return Error::success();
});
- if (!SectionsPatched) {
- LLVM_DEBUG(dbgs() << "Skipping debug registration for LinkGraph '"
- << G.getName() << "': no debug info\n");
- return Error::success();
- }
-
- if (RequireDebugSections && !HasDebugSections) {
- LLVM_DEBUG(dbgs() << "Skipping debug registration for LinkGraph '"
- << G.getName() << "': no debug info\n");
- return Error::success();
- }
-
- // Step 3: We start copying the debug object into target memory
- auto &Alloc = DebugObj->getTargetAlloc();
-
- // FIXME: FA->getAddress() below is supposed to be the address of the memory
- // range on the target, but InProcessMemoryManager returns the address of a
- // FinalizedAllocInfo helper instead
- auto ROSeg = Alloc.getSegInfo(MemProt::Read);
- ExecutorAddrRange R(ROSeg.Addr, ROSeg.WorkingMem.size());
- Alloc.finalize([this, R, &MR](Expected<DebugObject::FinalizedAlloc> FA) {
- DebugObject *DebugObj = getPendingDebugObj(MR);
- if (!FA)
- DebugObj->failMaterialization(FA.takeError());
-
- // Keep allocation alive until the corresponding code is removed
- DebugObj->trackFinalizedAlloc(std::move(*FA));
-
- // Unblock post-fixup pass
- DebugObj->reportTargetMem(R);
- });
-
- return Error::success();
- });
+ PassConfig.PreFixupPasses.push_back(
+ [this, &DebugObj, &MR](LinkGraph &G) -> Error {
+ DebugObj.finalizeAsync([this, &DebugObj,
+ &MR](Expected<ExecutorAddrRange> TargetMem) {
+ if (!TargetMem) {
+ DebugObj.failMaterialization(TargetMem.takeError());
+ return;
+ }
+ // Update tracking info
+ Error Err = MR.withResourceKeyDo([&](ResourceKey K) {
+ std::lock_guard<std::mutex> LockPending(PendingObjsLock);
+ std::lock_guard<std::mutex> LockRegistered(RegisteredObjsLock);
+ auto It = PendingObjs.find(&MR);
+ RegisteredObjs[K].push_back(std::move(It->second));
+ PendingObjs.erase(It);
+ });
+
+ if (Err)
+ DebugObj.failMaterialization(std::move(Err));
+
+ // Unblock post-fixup pass
+ DebugObj.reportTargetMem(*TargetMem);
+ });
+ return Error::success();
+ });
- PassConfig.PostFixupPasses.push_back([this, &MR](LinkGraph &G) -> Error {
- // Step 4: We wait for the debug object copy to finish, so we can
- // register the memory range with the GDB JIT Interface in an allocation
- // action of the LinkGraph's own allocation
- DebugObject *DebugObj = getPendingDebugObj(MR);
- Expected<ExecutorAddrRange> R = DebugObj->awaitTargetMem();
- if (!R)
- return R.takeError();
-
- // Step 5: We have to keep the allocation alive until the corresponding
- // code is removed
- Error Err = MR.withResourceKeyDo([&](ResourceKey K) {
- std::lock_guard<std::mutex> LockPending(PendingObjsLock);
- std::lock_guard<std::mutex> LockRegistered(RegisteredObjsLock);
- auto It = PendingObjs.find(&MR);
- RegisteredObjs[K].push_back(std::move(It->second));
- PendingObjs.erase(It);
- });
-
- if (Err)
- return Err;
-
- if (R->empty())
- return Error::success();
-
- using namespace shared;
- G.allocActions().push_back(
- {cantFail(WrapperFunctionCall::Create<
- SPSArgList<SPSExecutorAddrRange, bool>>(
- RegistrationAction, *R, AutoRegisterCode)),
- {/* no deregistration */}});
- return Error::success();
- });
+ PassConfig.PostFixupPasses.push_back(
+ [this, &DebugObj](LinkGraph &G) -> Error {
+ Expected<ExecutorAddrRange> R = DebugObj.awaitTargetMem();
+ if (!R)
+ return R.takeError();
+ if (R->empty())
+ return Error::success();
+
+ using namespace shared;
+ G.allocActions().push_back(
+ {cantFail(WrapperFunctionCall::Create<
+ SPSArgList<SPSExecutorAddrRange, bool>>(
+ RegistrationAction, *R, AutoRegisterCode)),
+ {/* no deregistration */}});
+ return Error::success();
+ });
+ }
}
Error ELFDebugObjectPlugin::notifyFailed(MaterializationResponsibility &MR) {
More information about the llvm-branch-commits
mailing list