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

Reply via email to