[llvm] [ORC] Update ELF debugger support plugin to use AllocActions (PR #167866)
Stefan Gränitz via llvm-commits
llvm-commits at lists.llvm.org
Sun Nov 16 00:42:15 PST 2025
https://github.com/weliveindetail updated https://github.com/llvm/llvm-project/pull/167866
>From 698184986b07af9addd5d1c85b6f4c1c83c70bae Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Thu, 13 Nov 2025 10:41:01 +0100
Subject: [PATCH 1/2] [ORC] Update ELF debugger support plugin to use
AllocActions
---
.../Orc/DebugObjectManagerPlugin.h | 14 +-
.../ExecutionEngine/Orc/Shared/OrcRTBridge.h | 2 +
.../Orc/DebugObjectManagerPlugin.cpp | 137 ++++++++++--------
.../Orc/Debugging/DebuggerSupport.cpp | 8 +-
.../Orc/Shared/OrcRTBridge.cpp | 3 +
.../DefaultHostBootstrapValues.cpp | 4 +
.../TargetProcess/SimpleRemoteEPCServer.cpp | 6 +-
llvm/tools/llvm-jitlink/llvm-jitlink.cpp | 8 +-
8 files changed, 104 insertions(+), 78 deletions(-)
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h b/llvm/include/llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h
index e84eb4bec297a..1988403715f57 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h
@@ -15,8 +15,8 @@
#include "llvm/ExecutionEngine/JITLink/JITLink.h"
#include "llvm/ExecutionEngine/Orc/Core.h"
-#include "llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h"
#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
+#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/Memory.h"
@@ -48,10 +48,6 @@ class DebugObject;
///
class LLVM_ABI DebugObjectManagerPlugin : public ObjectLinkingLayer::Plugin {
public:
- // DEPRECATED - Please specify options explicitly
- DebugObjectManagerPlugin(ExecutionSession &ES,
- std::unique_ptr<DebugObjectRegistrar> Target);
-
/// Create the plugin to submit DebugObjects for JITLink artifacts. For all
/// options the recommended setting is true.
///
@@ -67,16 +63,14 @@ class LLVM_ABI DebugObjectManagerPlugin : public ObjectLinkingLayer::Plugin {
/// sequence. When turning this off, the user has to issue the call to
/// __jit_debug_register_code() on the executor side manually.
///
- DebugObjectManagerPlugin(ExecutionSession &ES,
- std::unique_ptr<DebugObjectRegistrar> Target,
- bool RequireDebugSections, bool AutoRegisterCode);
+ DebugObjectManagerPlugin(ExecutionSession &ES, bool RequireDebugSections,
+ bool AutoRegisterCode, Error &Err);
~DebugObjectManagerPlugin() override;
void notifyMaterializing(MaterializationResponsibility &MR,
jitlink::LinkGraph &G, jitlink::JITLinkContext &Ctx,
MemoryBufferRef InputObject) override;
- Error notifyEmitted(MaterializationResponsibility &MR) override;
Error notifyFailed(MaterializationResponsibility &MR) override;
Error notifyRemovingResources(JITDylib &JD, ResourceKey K) override;
@@ -97,7 +91,7 @@ class LLVM_ABI DebugObjectManagerPlugin : public ObjectLinkingLayer::Plugin {
std::mutex PendingObjsLock;
std::mutex RegisteredObjsLock;
- std::unique_ptr<DebugObjectRegistrar> Target;
+ ExecutorAddr RegistrationAction;
bool RequireDebugSections;
bool AutoRegisterCode;
};
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h b/llvm/include/llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h
index d68a68992a638..b413c65f05f90 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h
@@ -59,6 +59,8 @@ LLVM_ABI extern const char *MemoryReadStringsWrapperName;
LLVM_ABI extern const char *RegisterEHFrameSectionAllocActionName;
LLVM_ABI extern const char *DeregisterEHFrameSectionAllocActionName;
+LLVM_ABI extern const char *RegisterJITLoaderGDBAllocActionName;
+
LLVM_ABI extern const char *RunAsMainWrapperName;
LLVM_ABI extern const char *RunAsVoidFunctionWrapperName;
LLVM_ABI extern const char *RunAsIntFunctionWrapperName;
diff --git a/llvm/lib/ExecutionEngine/Orc/DebugObjectManagerPlugin.cpp b/llvm/lib/ExecutionEngine/Orc/DebugObjectManagerPlugin.cpp
index 15e583ca7685d..d183134f3b769 100644
--- a/llvm/lib/ExecutionEngine/Orc/DebugObjectManagerPlugin.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/DebugObjectManagerPlugin.cpp
@@ -19,6 +19,7 @@
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/ExecutionEngine/JITLink/JITLinkDylib.h"
#include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h"
+#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/MSVCErrorWorkarounds.h"
@@ -115,7 +116,9 @@ class DebugObject {
public:
DebugObject(JITLinkMemoryManager &MemMgr, const JITLinkDylib *JD,
ExecutionSession &ES)
- : MemMgr(MemMgr), JD(JD), ES(ES), Flags(DebugObjectFlags{}) {}
+ : MemMgr(MemMgr), JD(JD), ES(ES), Flags(DebugObjectFlags{}) {
+ FinalizeFuture = FinalizePromise.get_future();
+ }
bool hasFlags(DebugObjectFlags F) const { return Flags & F; }
void setFlags(DebugObjectFlags F) {
@@ -126,8 +129,17 @@ class DebugObject {
}
using FinalizeContinuation = std::function<void(Expected<ExecutorAddrRange>)>;
+ void finalizeAsync(FinalizeContinuation OnAsync);
+
+ void failMaterialization(Error Err) {
+ FinalizePromise.set_value(std::move(Err));
+ }
- void finalizeAsync(FinalizeContinuation OnFinalize);
+ void reportTargetMem(ExecutorAddrRange TargetMem) {
+ FinalizePromise.set_value(TargetMem);
+ }
+
+ Expected<ExecutorAddrRange> awaitTargetMem() { return FinalizeFuture.get(); }
virtual ~DebugObject() {
if (Alloc) {
@@ -151,6 +163,9 @@ class DebugObject {
const JITLinkDylib *JD = nullptr;
ExecutionSession &ES;
+ std::promise<MSVCPExpected<ExecutorAddrRange>> FinalizePromise;
+ std::future<MSVCPExpected<ExecutorAddrRange>> FinalizeFuture;
+
private:
DebugObjectFlags Flags;
FinalizedAlloc Alloc;
@@ -160,8 +175,7 @@ class DebugObject {
// copying memory over to the target and pass on the result once we're done.
// Ownership of the allocation remains with us for the rest of our lifetime.
void DebugObject::finalizeAsync(FinalizeContinuation OnFinalize) {
- assert(!Alloc && "Cannot finalize more than once");
-
+ 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());
@@ -169,13 +183,19 @@ void DebugObject::finalizeAsync(FinalizeContinuation OnFinalize) {
[this, DebugObjRange,
OnFinalize = std::move(OnFinalize)](Expected<FinalizedAlloc> FA) {
if (FA) {
- Alloc = std::move(*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
+ } else {
+ // We could report this error synchronously, but it's easier this way,
+ // because the FinalizePromise will be triggered unconditionally.
OnFinalize(SimpleSegAlloc.takeError());
+ }
}
/// The current implementation of ELFDebugObject replicates the approach used in
@@ -386,16 +406,17 @@ createDebugObjectFromBuffer(ExecutionSession &ES, LinkGraph &G,
}
}
-DebugObjectManagerPlugin::DebugObjectManagerPlugin(
- ExecutionSession &ES, std::unique_ptr<DebugObjectRegistrar> Target,
- bool RequireDebugSections, bool AutoRegisterCode)
- : ES(ES), Target(std::move(Target)),
- RequireDebugSections(RequireDebugSections),
- AutoRegisterCode(AutoRegisterCode) {}
-
-DebugObjectManagerPlugin::DebugObjectManagerPlugin(
- ExecutionSession &ES, std::unique_ptr<DebugObjectRegistrar> Target)
- : DebugObjectManagerPlugin(ES, std::move(Target), true, true) {}
+DebugObjectManagerPlugin::DebugObjectManagerPlugin(ExecutionSession &ES,
+ bool RequireDebugSections,
+ bool AutoRegisterCode,
+ Error &Err)
+ : ES(ES), RequireDebugSections(RequireDebugSections),
+ AutoRegisterCode(AutoRegisterCode) {
+ // Pass bootstrap symbol for registration function to enable debugging
+ ErrorAsOutParameter _(&Err);
+ Err = ES.getExecutorProcessControl().getBootstrapSymbols(
+ {{RegistrationAction, rt::RegisterJITLoaderGDBAllocActionName}});
+}
DebugObjectManagerPlugin::~DebugObjectManagerPlugin() = default;
@@ -440,48 +461,50 @@ void DebugObjectManagerPlugin::modifyPassConfig(
SectionRange(GraphSection));
return Error::success();
});
- }
-}
-Error DebugObjectManagerPlugin::notifyEmitted(
- MaterializationResponsibility &MR) {
- std::lock_guard<std::mutex> Lock(PendingObjsLock);
- auto It = PendingObjs.find(&MR);
- if (It == PendingObjs.end())
- return Error::success();
-
- // During finalization the debug object is registered with the target.
- // Materialization must wait for this process to finish. Otherwise we might
- // start running code before the debugger processed the corresponding debug
- // info.
- std::promise<MSVCPError> FinalizePromise;
- std::future<MSVCPError> FinalizeErr = FinalizePromise.get_future();
-
- It->second->finalizeAsync(
- [this, &FinalizePromise, &MR](Expected<ExecutorAddrRange> TargetMem) {
- // Any failure here will fail materialization.
- if (!TargetMem) {
- FinalizePromise.set_value(TargetMem.takeError());
- return;
- }
- if (Error Err =
- Target->registerDebugObject(*TargetMem, AutoRegisterCode)) {
- FinalizePromise.set_value(std::move(Err));
- return;
- }
-
- // Once our tracking info is updated, notifyEmitted() can return and
- // finish materialization.
- FinalizePromise.set_value(MR.withResourceKeyDo([&](ResourceKey K) {
- assert(PendingObjs.count(&MR) && "We still hold PendingObjsLock");
- std::lock_guard<std::mutex> Lock(RegisteredObjsLock);
- auto It = PendingObjs.find(&MR);
- RegisteredObjs[K].push_back(std::move(It->second));
- PendingObjs.erase(It);
- }));
- });
-
- return FinalizeErr.get();
+ 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, &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 DebugObjectManagerPlugin::notifyFailed(
diff --git a/llvm/lib/ExecutionEngine/Orc/Debugging/DebuggerSupport.cpp b/llvm/lib/ExecutionEngine/Orc/Debugging/DebuggerSupport.cpp
index 1668473c0eb47..06667869b4803 100644
--- a/llvm/lib/ExecutionEngine/Orc/Debugging/DebuggerSupport.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/Debugging/DebuggerSupport.cpp
@@ -35,12 +35,10 @@ Error enableDebuggerSupport(LLJIT &J) {
switch (TT.getObjectFormat()) {
case Triple::ELF: {
- auto Registrar = createJITLoaderGDBRegistrar(ES);
- if (!Registrar)
- return Registrar.takeError();
+ Error TargetSymErr = Error::success();
ObjLinkingLayer->addPlugin(std::make_unique<DebugObjectManagerPlugin>(
- ES, std::move(*Registrar), false, true));
- return Error::success();
+ ES, false, true, TargetSymErr));
+ return TargetSymErr;
}
case Triple::MachO: {
auto DS = GDBJITDebugInfoRegistrationPlugin::Create(ES, *ProcessSymsJD, TT);
diff --git a/llvm/lib/ExecutionEngine/Orc/Shared/OrcRTBridge.cpp b/llvm/lib/ExecutionEngine/Orc/Shared/OrcRTBridge.cpp
index cc99d3c768772..004a110b0438b 100644
--- a/llvm/lib/ExecutionEngine/Orc/Shared/OrcRTBridge.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/Shared/OrcRTBridge.cpp
@@ -74,6 +74,9 @@ const char *RegisterEHFrameSectionAllocActionName =
const char *DeregisterEHFrameSectionAllocActionName =
"llvm_orc_deregisterEHFrameAllocAction";
+const char *RegisterJITLoaderGDBAllocActionName =
+ "llvm_orc_registerJITLoaderGDBAllocAction";
+
const char *RunAsMainWrapperName = "__llvm_orc_bootstrap_run_as_main_wrapper";
const char *RunAsVoidFunctionWrapperName =
"__llvm_orc_bootstrap_run_as_void_function_wrapper";
diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/DefaultHostBootstrapValues.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/DefaultHostBootstrapValues.cpp
index a30e87243ada8..81d95c6732be5 100644
--- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/DefaultHostBootstrapValues.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/DefaultHostBootstrapValues.cpp
@@ -9,6 +9,7 @@
#include "llvm/ExecutionEngine/Orc/TargetProcess/DefaultHostBootstrapValues.h"
#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"
+#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
#include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h"
#ifdef __APPLE__
@@ -27,6 +28,9 @@ void addDefaultBootstrapValuesForHostProcess(
BootstrapSymbols[rt::DeregisterEHFrameSectionAllocActionName] =
ExecutorAddr::fromPtr(&llvm_orc_deregisterEHFrameSectionAllocAction);
+ BootstrapSymbols[rt::RegisterJITLoaderGDBAllocActionName] =
+ ExecutorAddr::fromPtr(&llvm_orc_registerJITLoaderGDBAllocAction);
+
#ifdef __APPLE__
if (!dlsym(RTLD_DEFAULT, "__unw_add_find_dynamic_unwind_sections"))
BootstrapMap["darwin-use-ehframes-only"].push_back(1);
diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleRemoteEPCServer.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleRemoteEPCServer.cpp
index 74e3dfc567aa0..987ed710b039d 100644
--- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleRemoteEPCServer.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleRemoteEPCServer.cpp
@@ -9,6 +9,7 @@
#include "llvm/ExecutionEngine/Orc/TargetProcess/SimpleRemoteEPCServer.h"
#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"
+#include "llvm/ExecutionEngine/Orc/TargetProcess/DefaultHostBootstrapValues.h"
#include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/Process.h"
@@ -206,10 +207,7 @@ Error SimpleRemoteEPCServer::sendSetupMessage(
"Dispatch function name should not be set");
EI.BootstrapSymbols[ExecutorSessionObjectName] = ExecutorAddr::fromPtr(this);
EI.BootstrapSymbols[DispatchFnName] = ExecutorAddr::fromPtr(jitDispatchEntry);
- EI.BootstrapSymbols[rt::RegisterEHFrameSectionAllocActionName] =
- ExecutorAddr::fromPtr(&llvm_orc_registerEHFrameSectionAllocAction);
- EI.BootstrapSymbols[rt::DeregisterEHFrameSectionAllocActionName] =
- ExecutorAddr::fromPtr(&llvm_orc_deregisterEHFrameSectionAllocAction);
+ addDefaultBootstrapValuesForHostProcess(EI.BootstrapMap, EI.BootstrapSymbols);
using SPSSerialize =
shared::SPSArgList<shared::SPSSimpleRemoteEPCExecutorInfo>;
diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink.cpp b/llvm/tools/llvm-jitlink/llvm-jitlink.cpp
index 79216e89c7cba..bcc2a5bd9ccb8 100644
--- a/llvm/tools/llvm-jitlink/llvm-jitlink.cpp
+++ b/llvm/tools/llvm-jitlink/llvm-jitlink.cpp
@@ -1295,9 +1295,13 @@ Session::Session(std::unique_ptr<ExecutorProcessControl> EPC, Error &Err)
} else if (TT.isOSBinFormatELF()) {
if (!NoExec)
ObjLayer.addPlugin(ExitOnErr(EHFrameRegistrationPlugin::Create(ES)));
- if (DebuggerSupport)
+ if (DebuggerSupport) {
+ Error TargetSymErr = Error::success();
ObjLayer.addPlugin(std::make_unique<DebugObjectManagerPlugin>(
- ES, ExitOnErr(createJITLoaderGDBRegistrar(this->ES)), true, true));
+ ES, true, true, TargetSymErr));
+ logAllUnhandledErrors(std::move(TargetSymErr), errs(),
+ "Debugger support not available: ");
+ }
}
if (auto MainJDOrErr = ES.createJITDylib("main"))
>From bc8cdb71e96a10c1aea0262130c615955c075e8f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Thu, 13 Nov 2025 18:47:15 +0100
Subject: [PATCH 2/2] [llvm-jitlink] Don't add the plugin is case of symbol
errors
---
llvm/tools/llvm-jitlink/llvm-jitlink.cpp | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink.cpp b/llvm/tools/llvm-jitlink/llvm-jitlink.cpp
index bcc2a5bd9ccb8..1576ec05b86af 100644
--- a/llvm/tools/llvm-jitlink/llvm-jitlink.cpp
+++ b/llvm/tools/llvm-jitlink/llvm-jitlink.cpp
@@ -1297,10 +1297,13 @@ Session::Session(std::unique_ptr<ExecutorProcessControl> EPC, Error &Err)
ObjLayer.addPlugin(ExitOnErr(EHFrameRegistrationPlugin::Create(ES)));
if (DebuggerSupport) {
Error TargetSymErr = Error::success();
- ObjLayer.addPlugin(std::make_unique<DebugObjectManagerPlugin>(
- ES, true, true, TargetSymErr));
- logAllUnhandledErrors(std::move(TargetSymErr), errs(),
- "Debugger support not available: ");
+ auto Plugin = std::make_unique<DebugObjectManagerPlugin>(ES, true, true,
+ TargetSymErr);
+ if (!TargetSymErr)
+ ObjLayer.addPlugin(std::move(Plugin));
+ else
+ logAllUnhandledErrors(std::move(TargetSymErr), errs(),
+ "Debugger support not available: ");
}
}
More information about the llvm-commits
mailing list