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) { _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
