https://github.com/mchoo7 created https://github.com/llvm/llvm-project/pull/204710
Commit 67e571d (#179306) added lldbHost and lldbUtility to `LLDB_DRIVER_LINK_LIBS` A side-effect is that HostInfoBase.cpp, which contains the file-static `g_fields` pointer, is now compiled into both the lldb binary and liblldb.so, giving each its own independent `g_fields`. On ELF platforms this creates an interposition hazard. When `LLDB_ENABLE_DYNAMIC_SCRIPTINTERPRETERS` is set, AddLLDB.cmake switches all LLDB libraries to `CXX_VISIBILITY_PRESET=default` so that the version script can re-export private symbols needed by dynamically loaded plugins. The Python plugin calls `HostInfo::GetShlibDir()` directly, so extract-dynamic-script-interpreter-exports.py adds `HostInfoBase::GetShlibDir` to liblldb.so's exports (global: in the version script). `HostInfoBase::Initialize()` is not called by the plugin and stays local:. At runtime the dynamic linker resolves liblldb.so's PLT entry for `GetShlibDir()` to lldb's copy (ELF interposition: executable symbols win), which holds a never-initialized `g_fields == nullptr`. `Initialize()` was already called, but it ran on liblldb.so's own copy via the version-script-local binding. Dereferencing `nullptr + 0x108` (offset of `m_lldb_so_dir_once`) crashes in `pthread_once`. To fix this, pass `--exclude-libs,ALL` to the lldb executable on ELF platforms. This keeps every archive-contributed symbol (lldbHost, lldbUtility, LLVM libs) out of lldb's `.dynsym`, eliminating the interposition. liblldb.so's PLT for `GetShlibDir()` then finds only liblldb.so's own exported definition, so `Initialize()` and `GetShlibDir()` consistently operate on the same `g_fields`. The issue does not affect Darwin because Mach-O uses a two-level namespace: symbol references in liblldb.dylib are bound to a specific library at link time, so lldb's copy is never a candidate for resolution regardless of visibility. Closes: #204643 Assisted-by: Claude >From 2740701f2a529f1294e68d35f7bc472c1dc543e9 Mon Sep 17 00:00:00 2001 From: Minsoo Choo <[email protected]> Date: Thu, 18 Jun 2026 20:34:01 -0400 Subject: [PATCH] [lldb][driver] Fix ELF interposition of HostInfoBase symbols causing segfault Commit 67e571d (#179306) added lldbHost and lldbUtility to LLDB_DRIVER_LINK_LIBS A side-effect is that HostInfoBase.cpp -- which contains the file-static g_fields pointer -- is now compiled into both the lldb binary and liblldb.so, giving each its own independent g_fields. On ELF platforms this creates an interposition hazard. When LLDB_ENABLE_DYNAMIC_SCRIPTINTERPRETERS is set, AddLLDB.cmake switches all LLDB libraries to CXX_VISIBILITY_PRESET=default so that the version script can re-export private symbols needed by dynamically loaded plugins. The Python plugin calls HostInfo::GetShlibDir() directly, so extract-dynamic-script-interpreter-exports.py adds HostInfoBase::GetShlibDir to liblldb.so's exports (global: in the version script). HostInfoBase::Initialize() is not called by the plugin and stays local:. At runtime the dynamic linker resolves liblldb.so's PLT entry for GetShlibDir() to lldb's copy (ELF interposition: executable symbols win), which holds a never-initialized g_fields == nullptr. Initialize() was already called, but it ran on liblldb.so's own copy via the version-script-local binding. Dereferencing nullptr + 0x108 (offsetof m_lldb_so_dir_once) crashes in pthread_once. To fix this, pass --exclude-libs,ALL to the lldb executable on ELF platforms. This keeps every archive-contributed symbol (lldbHost, lldbUtility, LLVM libs) out of lldb's .dynsym, eliminating the interposition. liblldb.so's PLT for GetShlibDir() then finds only liblldb.so's own exported definition, so Initialize() and GetShlibDir() consistently operate on the same g_fields. The issue does not affect Darwin because Mach-O uses a two-level namespace: symbol references in liblldb.dylib are bound to a specific library at link time, so lldb's copy is never a candidate for resolution regardless of visibility. Closes: #204643 Assisted-by: Claude Signed-off-by: Minsoo Choo <[email protected]> --- lldb/tools/driver/CMakeLists.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lldb/tools/driver/CMakeLists.txt b/lldb/tools/driver/CMakeLists.txt index 0534ddc9a58c3..38670a14a78b0 100644 --- a/lldb/tools/driver/CMakeLists.txt +++ b/lldb/tools/driver/CMakeLists.txt @@ -52,6 +52,17 @@ add_dependencies(lldb ${tablegen_deps} ) +# lldbHost and lldbUtility are also statically linked into liblldb.so, so any +# state they carry exists in two independent copies. On ELF platforms this +# creates an interposition hazard: when liblldb.so exports a symbol from those +# archives (e.g. for dynamic plugin loading), the dynamic linker can resolve +# it to lldb's copy instead of liblldb.so's, breaking shared-state assumptions. +# --exclude-libs,ALL keeps archive symbols out of lldb's .dynsym, eliminating +# the hazard without affecting the executable's own use of those symbols. +if(UNIX AND NOT APPLE) + target_link_options(lldb PRIVATE "LINKER:--exclude-libs,ALL") +endif() + if(LLDB_BUILD_FRAMEWORK) # In the build-tree, we know the exact path to the framework directory. # The installed framework can be in different locations. _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
