Author: Jonas Devlieghere
Date: 2026-06-02T16:47:54-07:00
New Revision: 3eb13f8db39ed42827122489c830c414cb6660e3

URL: 
https://github.com/llvm/llvm-project/commit/3eb13f8db39ed42827122489c830c414cb6660e3
DIFF: 
https://github.com/llvm/llvm-project/commit/3eb13f8db39ed42827122489c830c414cb6660e3.diff

LOG: [lldb] Stop hard-linking libpython into the dynamic Python plugin (#200530)

Drops ${Python3_LIBRARIES} from the SHARED build of
lldbPluginScriptInterpreterPython and lets undefined Python symbols
through at link time (`-undefined dynamic_lookup` on Darwin,
`--allow-shlib-undefined` on Linux; Windows keeps its existing
delay-load + import lib).

SystemInitializerFull::Initialize resolves the Python runtime loader
via ScriptInterpreterRuntimeLoader::Get(eScriptLanguagePython) and
calls Load() before initializing any plugin, so libpython is mapped
into the process before either entry point that references it: the
static script interpreter's Initialize() (which invokes Python via
the LLDB_PLUGIN_INITIALIZE loop) and the dynamic plugin's dlopen
(whose undefined references resolve against the in-process
libpython). This covers both LLDB_ENABLE_DYNAMIC_SCRIPTINTERPRETERS
=ON and =OFF, and keeps Windows working in static builds where the
delay-load thunks live in liblldb itself. The loader is
once_flag-cached, and errors propagate out via the existing Expected
return.

`import lldb` from a running Python works because libpython is
already mapped into the process. The Python runtime loader probes
for stable-ABI symbols (Py_IsInitialized, Py_InitializeFromConfig)
via dlsym(RTLD_DEFAULT) / GetProcAddress and, finding them, returns
success without dlopen'ing a second libpython on top.

The dynamic plugins are added as test-suite dependencies so
check-lldb-shell and check-lldb-api wait for them; they can't be
build-order deps of liblldb itself because the plugins link against
liblldb (CMake SHARED cycle).

The unit-test static variant lldbStaticScriptInterpreterPython keeps
hard-linking libpython so link-time symbol checking is preserved.

Added: 
    

Modified: 
    lldb/source/API/SystemInitializerFull.cpp
    lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt
    lldb/test/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/lldb/source/API/SystemInitializerFull.cpp 
b/lldb/source/API/SystemInitializerFull.cpp
index d994915913cc1..6aadcae673a2b 100644
--- a/lldb/source/API/SystemInitializerFull.cpp
+++ b/lldb/source/API/SystemInitializerFull.cpp
@@ -22,6 +22,11 @@
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/TargetSelect.h"
 
+#if LLDB_ENABLE_PYTHON
+#include "lldb/Host/ScriptInterpreterRuntimeLoader.h"
+#include "lldb/lldb-enumerations.h"
+#endif
+
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wglobal-constructors"
 #include "llvm/ExecutionEngine/MCJIT.h"
@@ -42,6 +47,21 @@ llvm::Error SystemInitializerFull::Initialize() {
   if (error)
     return error;
 
+#if LLDB_ENABLE_PYTHON
+  // Map libpython into the process before any code that might reference it
+  // runs. This is required by both the static script interpreter (whose
+  // Initialize() invokes Python via the LLDB_PLUGIN_INITIALIZE loop below)
+  // and the dynamic plugin (whose dlopen needs Python's symbols visible in
+  // the process). The loader is once_flag-cached and a no-op when libpython
+  // is already in the process (e.g. `import lldb` from Python).
+  llvm::Expected<ScriptInterpreterRuntimeLoader &> python_loader =
+      ScriptInterpreterRuntimeLoader::Get(lldb::eScriptLanguagePython);
+  if (!python_loader)
+    return python_loader.takeError();
+  if (llvm::Error err = python_loader->Load())
+    return err;
+#endif
+
   // Initialize LLVM and Clang
   llvm::InitializeAllTargets();
   llvm::InitializeAllAsmPrinters();

diff  --git a/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt 
b/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt
index 574dcdf874ada..03ef6b10b06c4 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt
@@ -46,14 +46,28 @@ endif()
 if (LLDB_ENABLE_DYNAMIC_SCRIPTINTERPRETERS)
   # Shared library loaded at runtime by PluginManager. Private lldb symbols are
   # resolved via liblldb's re-exports, so lldb_private libs aren't linked here.
+  # Python is dlopen'd at runtime by ScriptInterpreterRuntimeLoader, so the
+  # plugin is built with allow-undefined-symbol semantics on POSIX.
   add_lldb_library(lldbPluginScriptInterpreterPython SHARED
     ${python_plugin_sources}
     LINK_LIBS
       liblldb
-      ${Python3_LIBRARIES}
       ${LLDB_LIBEDIT_LIBS}
   )
   add_python_wrapper(lldbPluginScriptInterpreterPython)
+  if (APPLE)
+    target_link_options(lldbPluginScriptInterpreterPython PRIVATE
+      "LINKER:-undefined,dynamic_lookup")
+  elseif (UNIX)
+    target_link_options(lldbPluginScriptInterpreterPython PRIVATE
+      "LINKER:--unresolved-symbols=ignore-all")
+  else()
+    # Windows continues to use delay-load + import lib.
+    target_link_libraries(lldbPluginScriptInterpreterPython PRIVATE
+      ${Python3_LIBRARIES})
+    target_link_directories(lldbPluginScriptInterpreterPython PRIVATE
+      ${PYTHON_SABI_LIBRARY_DIRS})
+  endif()
 
   # Static variant linked directly by unit tests. Separate compilation is
   # required so llvm::Error RTTI (ErrorInfoBase::ID) has a single address
@@ -76,7 +90,6 @@ if (LLDB_ENABLE_DYNAMIC_SCRIPTINTERPRETERS)
   target_include_directories(lldbStaticScriptInterpreterPython
     PUBLIC ${Python3_INCLUDE_DIRS})
 
-  target_link_directories(lldbPluginScriptInterpreterPython PRIVATE 
${PYTHON_SABI_LIBRARY_DIRS})
   target_link_directories(lldbStaticScriptInterpreterPython PUBLIC 
${PYTHON_SABI_LIBRARY_DIRS})
 else()
   add_lldb_library(lldbPluginScriptInterpreterPython PLUGIN

diff  --git a/lldb/test/CMakeLists.txt b/lldb/test/CMakeLists.txt
index 0a5c3b99f0cc5..0b01936c2590b 100644
--- a/lldb/test/CMakeLists.txt
+++ b/lldb/test/CMakeLists.txt
@@ -128,6 +128,17 @@ if(TARGET liblldb)
   add_lldb_test_dependency(liblldb)
 endif()
 
+# When script interpreter plugins are built as separate shared libraries, they
+# are loaded by liblldb at runtime and aren't pulled in by the liblldb target.
+if(LLDB_ENABLE_DYNAMIC_SCRIPTINTERPRETERS)
+  if(TARGET lldbPluginScriptInterpreterPython)
+    add_lldb_test_dependency(lldbPluginScriptInterpreterPython)
+  endif()
+  if(TARGET lldbPluginScriptInterpreterLua)
+    add_lldb_test_dependency(lldbPluginScriptInterpreterLua)
+  endif()
+endif()
+
 if(TARGET lldb-framework)
   add_lldb_test_dependency(lldb-framework)
 endif()


        
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to