Author: Minsoo Choo Date: 2026-06-26T21:27:56-04:00 New Revision: 106644f6c835f9d2137aeb9cbfe1b9a60d03d17f
URL: https://github.com/llvm/llvm-project/commit/106644f6c835f9d2137aeb9cbfe1b9a60d03d17f DIFF: https://github.com/llvm/llvm-project/commit/106644f6c835f9d2137aeb9cbfe1b9a60d03d17f.diff LOG: [lldb][driver] Fix ELF interposition of HostInfoBase symbols causing segfault (#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. Tested-on: FreeBSD 15.1 arm64, Fedora 44 amd64 Closes: #204643 Assisted-by: Claude Signed-off-by: Minsoo Choo <[email protected]> Added: Modified: lldb/tools/driver/CMakeLists.txt Removed: ################################################################################ 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 diff erent locations. _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
