https://github.com/da-viper updated https://github.com/llvm/llvm-project/pull/178695
>From 8ef6eac983b5f31ddb75825180e1bd056f1a19d9 Mon Sep 17 00:00:00 2001 From: Ebuka Ezike <[email protected]> Date: Thu, 29 Jan 2026 16:17:05 +0000 Subject: [PATCH 1/4] [lldb] Improve trampoline function detection The mold linker adds a function symbol to the symbol table (.symtab) for every shared library exported function. So if we try to step into a library, lldb will fail because the symbol at the address is not a trampline function. Example: ```cpp lib.c int lib_add(int a, int b); ``` when we link the library to an executable, mold will create the normal trampoline functions in the .plt section but add an extra symbol in symbol table `lib_add$plt`. This patch adds a new method Module::FindSymbolsContainingFileAddress() to find symbols of a specific type that contain a given file address. --- lldb/include/lldb/Core/Module.h | 4 +++ lldb/source/Core/Module.cpp | 17 ++++++++++++ .../POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp | 27 +++++++++++++++++-- 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/lldb/include/lldb/Core/Module.h b/lldb/include/lldb/Core/Module.h index 643b9a5c3bf54..87d828fb41eed 100644 --- a/lldb/include/lldb/Core/Module.h +++ b/lldb/include/lldb/Core/Module.h @@ -265,6 +265,10 @@ class Module : public std::enable_shared_from_this<Module>, lldb::SymbolType symbol_type, SymbolContextList &sc_list); + void FindSymbolsContainingFileAddress(const Address &addr, + lldb::SymbolType symbol_type, + SymbolContextList &sc_list); + void FindSymbolsMatchingRegExAndType( const RegularExpression ®ex, lldb::SymbolType symbol_type, SymbolContextList &sc_list, diff --git a/lldb/source/Core/Module.cpp b/lldb/source/Core/Module.cpp index 486af1a053344..436c4ce027d84 100644 --- a/lldb/source/Core/Module.cpp +++ b/lldb/source/Core/Module.cpp @@ -1316,6 +1316,23 @@ void Module::FindSymbolsWithNameAndType(ConstString name, } } +void Module::FindSymbolsContainingFileAddress(const Address &addr, + lldb::SymbolType symbol_type, + SymbolContextList &sc_list) { + if (Symtab *symtab = GetSymtab()) { + std::vector<uint32_t> symbol_indexes; + symtab->ForEachSymbolContainingFileAddress( + addr.GetFileAddress(), [&, symbol_type](Symbol *match_sym) { + if (const lldb::SymbolType curr_type = match_sym->GetType(); + curr_type == lldb::eSymbolTypeAny || curr_type == symbol_type) { + symbol_indexes.push_back(symtab->GetIndexForSymbol(match_sym)); + } + return true; + }); + SymbolIndicesToSymbolContextList(symtab, symbol_indexes, sc_list); + } +} + void Module::FindSymbolsMatchingRegExAndType( const RegularExpression ®ex, SymbolType symbol_type, SymbolContextList &sc_list, Mangled::NamePreference mangling_preference) { diff --git a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp index 6705ac139f0fb..8802b4b7cd27b 100644 --- a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp +++ b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp @@ -540,11 +540,34 @@ DynamicLoaderPOSIXDYLD::GetStepThroughTrampolinePlan(Thread &thread, StackFrame *frame = thread.GetStackFrameAtIndex(0).get(); const SymbolContext &context = frame->GetSymbolContext(eSymbolContextSymbol); - const Symbol *sym = context.symbol; - if (sym == nullptr || !sym->IsTrampoline()) + Symbol *sym = context.symbol; + if (sym == nullptr) return thread_plan_sp; + if (!sym->IsTrampoline()) { + if (sym->GetType() != lldb::eSymbolTypeCode) + return thread_plan_sp; + + SymbolContextList addr_ctx_list; + context.module_sp->FindSymbolsContainingFileAddress( + context.GetFunctionOrSymbolAddress(), eSymbolTypeTrampoline, + addr_ctx_list); + if (addr_ctx_list.IsEmpty()) + return thread_plan_sp; + + for (const auto &sym_ctx : addr_ctx_list.SymbolContexts()) { + if (sym_ctx.symbol != nullptr) { + sym = sym_ctx.symbol; + break; + } + } + + // may not find a match + if (sym == nullptr) + return thread_plan_sp; + } + ConstString sym_name = sym->GetMangled().GetName(Mangled::ePreferMangled); if (!sym_name) return thread_plan_sp; >From 3c1a701b00f5b075b8f3d89c8ec3a6db98745a3d Mon Sep 17 00:00:00 2001 From: Ebuka Ezike <[email protected]> Date: Thu, 29 Jan 2026 16:35:09 +0000 Subject: [PATCH 2/4] missing comment --- .../Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp index 8802b4b7cd27b..97e3116029f25 100644 --- a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp +++ b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp @@ -549,6 +549,7 @@ DynamicLoaderPOSIXDYLD::GetStepThroughTrampolinePlan(Thread &thread, if (sym->GetType() != lldb::eSymbolTypeCode) return thread_plan_sp; + // Check if any trampoline symbols exists at the file address. SymbolContextList addr_ctx_list; context.module_sp->FindSymbolsContainingFileAddress( context.GetFunctionOrSymbolAddress(), eSymbolTypeTrampoline, >From f583543276287d58b7788653c07cfc096120ecd5 Mon Sep 17 00:00:00 2001 From: Ebuka Ezike <[email protected]> Date: Mon, 9 Feb 2026 13:12:37 +0000 Subject: [PATCH 3/4] add review changes Add testcase --- .../Python/lldbsuite/test/configuration.py | 11 ++ lldb/packages/Python/lldbsuite/test/dotest.py | 1 + lldb/source/Core/Module.cpp | 26 ++--- .../POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp | 2 +- .../Makefile | 7 ++ .../TestStepThroughTrampolineWithSymbol.py | 102 ++++++++++++++++++ .../step-through-trampoline-with-symbol/add.c | 5 + .../step-through-trampoline-with-symbol/add.h | 1 + .../main.c | 6 ++ 9 files changed, 148 insertions(+), 13 deletions(-) create mode 100644 lldb/test/API/lang/c/step-through-trampoline-with-symbol/Makefile create mode 100644 lldb/test/API/lang/c/step-through-trampoline-with-symbol/TestStepThroughTrampolineWithSymbol.py create mode 100644 lldb/test/API/lang/c/step-through-trampoline-with-symbol/add.c create mode 100644 lldb/test/API/lang/c/step-through-trampoline-with-symbol/add.h create mode 100644 lldb/test/API/lang/c/step-through-trampoline-with-symbol/main.c diff --git a/lldb/packages/Python/lldbsuite/test/configuration.py b/lldb/packages/Python/lldbsuite/test/configuration.py index f96fd31b17a37..082e9a90c6d05 100644 --- a/lldb/packages/Python/lldbsuite/test/configuration.py +++ b/lldb/packages/Python/lldbsuite/test/configuration.py @@ -65,6 +65,9 @@ # Path to the nm tool. nm: Optional[str] = None +# Path to the objcopy tool +objcopy: Optional[str] = None + # Path to the yaml2obj tool. Not optional. yaml2obj = None @@ -183,6 +186,14 @@ def get_nm_path(): return nm +def get_objcopy_path(): + """ + Get the path to the objcopy tool. + """ + if objcopy and os.path.lexists(objcopy): + return objcopy + + def get_yaml2obj_path(): """ Get the path to the yaml2obj tool. diff --git a/lldb/packages/Python/lldbsuite/test/dotest.py b/lldb/packages/Python/lldbsuite/test/dotest.py index 533be0a065e3a..f8595b50041e2 100644 --- a/lldb/packages/Python/lldbsuite/test/dotest.py +++ b/lldb/packages/Python/lldbsuite/test/dotest.py @@ -283,6 +283,7 @@ def parseOptionsAndInitTestdirs(): configuration.nm = shutil.which( "llvm-nm", path=args.llvm_tools_dir ) or shutil.which("nm", path=args.llvm_tools_dir) + configuration.objcopy = shutil.which("llvm-objcopy", path=args.llvm_tools_dir) if not configuration.get_filecheck_path(): logging.warning("No valid FileCheck executable; some tests may fail...") diff --git a/lldb/source/Core/Module.cpp b/lldb/source/Core/Module.cpp index 436c4ce027d84..3fe2a714be071 100644 --- a/lldb/source/Core/Module.cpp +++ b/lldb/source/Core/Module.cpp @@ -1319,18 +1319,20 @@ void Module::FindSymbolsWithNameAndType(ConstString name, void Module::FindSymbolsContainingFileAddress(const Address &addr, lldb::SymbolType symbol_type, SymbolContextList &sc_list) { - if (Symtab *symtab = GetSymtab()) { - std::vector<uint32_t> symbol_indexes; - symtab->ForEachSymbolContainingFileAddress( - addr.GetFileAddress(), [&, symbol_type](Symbol *match_sym) { - if (const lldb::SymbolType curr_type = match_sym->GetType(); - curr_type == lldb::eSymbolTypeAny || curr_type == symbol_type) { - symbol_indexes.push_back(symtab->GetIndexForSymbol(match_sym)); - } - return true; - }); - SymbolIndicesToSymbolContextList(symtab, symbol_indexes, sc_list); - } + Symtab *symtab = GetSymtab(); + if (!symtab) + return; + + std::vector<uint32_t> symbol_indexes; + symtab->ForEachSymbolContainingFileAddress( + addr.GetFileAddress(), [&, symbol_type](Symbol *match_sym) { + if (const lldb::SymbolType curr_type = match_sym->GetType(); + curr_type == lldb::eSymbolTypeAny || curr_type == symbol_type) { + symbol_indexes.push_back(symtab->GetIndexForSymbol(match_sym)); + } + return true; + }); + SymbolIndicesToSymbolContextList(symtab, symbol_indexes, sc_list); } void Module::FindSymbolsMatchingRegExAndType( diff --git a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp index 97e3116029f25..97890f77356ab 100644 --- a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp +++ b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp @@ -541,7 +541,7 @@ DynamicLoaderPOSIXDYLD::GetStepThroughTrampolinePlan(Thread &thread, StackFrame *frame = thread.GetStackFrameAtIndex(0).get(); const SymbolContext &context = frame->GetSymbolContext(eSymbolContextSymbol); - Symbol *sym = context.symbol; + const Symbol *sym = context.symbol; if (sym == nullptr) return thread_plan_sp; diff --git a/lldb/test/API/lang/c/step-through-trampoline-with-symbol/Makefile b/lldb/test/API/lang/c/step-through-trampoline-with-symbol/Makefile new file mode 100644 index 0000000000000..8ed10dfbb86ba --- /dev/null +++ b/lldb/test/API/lang/c/step-through-trampoline-with-symbol/Makefile @@ -0,0 +1,7 @@ +DYLIB_NAME := add +DYLIB_C_SOURCES := add.c +C_SOURCES := main.c +LD_EXTRAS := -fcf-protection=none + +include Makefile.rules + diff --git a/lldb/test/API/lang/c/step-through-trampoline-with-symbol/TestStepThroughTrampolineWithSymbol.py b/lldb/test/API/lang/c/step-through-trampoline-with-symbol/TestStepThroughTrampolineWithSymbol.py new file mode 100644 index 0000000000000..40d40df6daf75 --- /dev/null +++ b/lldb/test/API/lang/c/step-through-trampoline-with-symbol/TestStepThroughTrampolineWithSymbol.py @@ -0,0 +1,102 @@ +""" +Test case to verify stepping behavior into shared library functions. +Specifically, this tests the scenario where a function's trampoline +(PLT stub) has a different symbol at its file address, +""" + +import lldb +from lldbsuite.test.lldbtest import TestBase, line_number, configuration +import lldbsuite.test.lldbutil as lldbutil + + +class StepThroughTrampolineWithSymbol(TestBase): + SYMBOL_NAME = "lib_add" + FAKE_SYMBOL_NAME = "fake_lib_add" + + def test(self): + modified_exe = self.create_modified_exe() + (_target, _process, thread, _bkpt) = lldbutil.run_to_source_breakpoint( + self, + "// Set a breakpoint here", + lldb.SBFileSpec("main.c"), + exe_name=modified_exe, + extra_images=["add"], + ) + + error = lldb.SBError() + thread.StepInto( + None, lldb.LLDB_INVALID_LINE_NUMBER, error, lldb.eOnlyDuringStepping + ) + + self.assertTrue(error.Success(), f"step into failed: {error.GetCString()}") + + # Check frame in cli. + add_stop_line = line_number("add.c", "// End up here") + self.expect( + "frame info", substrs=[self.SYMBOL_NAME, "add.c:{}:".format(add_stop_line)] + ) + + # Check frame in SBAPI. + current_frame = thread.selected_frame + self.assertTrue(current_frame.IsValid()) + function_name = current_frame.function.name + self.assertEqual(function_name, self.SYMBOL_NAME) + + frame_module = current_frame.module + self.assertTrue(frame_module.IsValid()) + frame_module_name = frame_module.file.basename + self.assertEqual(frame_module_name, "libadd.so") + + def create_modified_exe(self) -> str: + """ + Build the executable, find the `lib_add` trampoline and add + an the symbol `fake_lib_add` at the trampoline's file address + + Returns the modified executable. + """ + self.build() + exe = self.getBuildArtifact("a.out") + modulespec = lldb.SBModuleSpec() + modulespec.SetFileSpec(lldb.SBFileSpec(exe)) + module = lldb.SBModule(modulespec) + self.assertTrue(module.IsValid()) + + add_trampoline = lldb.SBSymbol() + for sym in module.symbols: + if sym.name == self.SYMBOL_NAME and sym.type == lldb.eSymbolTypeTrampoline: + add_trampoline = sym + break + + self.assertTrue(add_trampoline.IsValid()) + + # Get the trampoline's section and offset. + add_address = add_trampoline.addr + self.assertTrue(add_address.IsValid()) + + add_section_name = add_address.section.name + self.assertIn(".plt", add_section_name) + add_section_offset = add_address.offset + + # Add a new symbol to the file address of lib_add trampoline. + modified_exe = self.getBuildArtifact("mod_a.out") + objcopy_bin = configuration.get_objcopy_path() + + build_command = [ + objcopy_bin, + "--add-symbol", + f"{self.FAKE_SYMBOL_NAME}={add_section_name}:{add_section_offset},function,local", + exe, + modified_exe, + ] + self.runBuildCommand(build_command) + + # Verify we added the fake symbol. + modified_modulespec = lldb.SBModuleSpec() + modified_modulespec.SetFileSpec(lldb.SBFileSpec(modified_exe)) + modified_module = lldb.SBModule(modified_modulespec) + self.assertTrue(modified_module.IsValid()) + + fake_symbol = modified_module.FindSymbol(self.FAKE_SYMBOL_NAME) + self.assertTrue(fake_symbol.IsValid()) + + return modified_exe diff --git a/lldb/test/API/lang/c/step-through-trampoline-with-symbol/add.c b/lldb/test/API/lang/c/step-through-trampoline-with-symbol/add.c new file mode 100644 index 0000000000000..0169aa4649d44 --- /dev/null +++ b/lldb/test/API/lang/c/step-through-trampoline-with-symbol/add.c @@ -0,0 +1,5 @@ +#include "add.h" + +int lib_add(int LHS, int RHS) { + return LHS + RHS; // End up here +} diff --git a/lldb/test/API/lang/c/step-through-trampoline-with-symbol/add.h b/lldb/test/API/lang/c/step-through-trampoline-with-symbol/add.h new file mode 100644 index 0000000000000..22f973b85ff23 --- /dev/null +++ b/lldb/test/API/lang/c/step-through-trampoline-with-symbol/add.h @@ -0,0 +1 @@ +LLDB_TEST_API extern int lib_add(int LHS, int RHS); diff --git a/lldb/test/API/lang/c/step-through-trampoline-with-symbol/main.c b/lldb/test/API/lang/c/step-through-trampoline-with-symbol/main.c new file mode 100644 index 0000000000000..62b0391bcb990 --- /dev/null +++ b/lldb/test/API/lang/c/step-through-trampoline-with-symbol/main.c @@ -0,0 +1,6 @@ +#include "add.h" + +int main(void) { + int result = lib_add(10, 20); // Set a breakpoint here + return 0; +} >From e0d2d2b56c26a333ea87ae06dea0c96f7f12198c Mon Sep 17 00:00:00 2001 From: Ebuka Ezike <[email protected]> Date: Tue, 10 Feb 2026 18:08:28 +0000 Subject: [PATCH 4/4] add review changes --- lldb/include/lldb/Core/Module.h | 21 ++++++++++++++++--- lldb/source/Core/Module.cpp | 16 ++++++++------ .../TestStepThroughTrampolineWithSymbol.py | 2 ++ 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/lldb/include/lldb/Core/Module.h b/lldb/include/lldb/Core/Module.h index 87d828fb41eed..4b4ce631f16e6 100644 --- a/lldb/include/lldb/Core/Module.h +++ b/lldb/include/lldb/Core/Module.h @@ -265,9 +265,24 @@ class Module : public std::enable_shared_from_this<Module>, lldb::SymbolType symbol_type, SymbolContextList &sc_list); - void FindSymbolsContainingFileAddress(const Address &addr, - lldb::SymbolType symbol_type, - SymbolContextList &sc_list); + /// Find all symbols at a given file address. + /// + /// This function searches for symbols of a specified type that contain + /// the provided file address within their address range. + /// + /// \param[in] addr + /// The file address to search for within symbol ranges. + /// + /// \param[in] symbol_type + /// The type of symbols to search for (e.g., code, data, trampoline). + /// Use lldb::eSymbolTypeAny to search all symbol types. + /// + /// \return + /// A SymbolContextList containing all matching symbols that contain + /// the specified address. Returns an empty list if no symbols are found. + SymbolContextList + FindSymbolsContainingFileAddress(const Address &addr, + lldb::SymbolType symbol_type); void FindSymbolsMatchingRegExAndType( const RegularExpression ®ex, lldb::SymbolType symbol_type, diff --git a/lldb/source/Core/Module.cpp b/lldb/source/Core/Module.cpp index 3fe2a714be071..c7b178506192d 100644 --- a/lldb/source/Core/Module.cpp +++ b/lldb/source/Core/Module.cpp @@ -1316,23 +1316,27 @@ void Module::FindSymbolsWithNameAndType(ConstString name, } } -void Module::FindSymbolsContainingFileAddress(const Address &addr, - lldb::SymbolType symbol_type, - SymbolContextList &sc_list) { +SymbolContextList +Module::FindSymbolsContainingFileAddress(const Address &addr, + lldb::SymbolType symbol_type) { Symtab *symtab = GetSymtab(); if (!symtab) - return; + return {}; std::vector<uint32_t> symbol_indexes; symtab->ForEachSymbolContainingFileAddress( addr.GetFileAddress(), [&, symbol_type](Symbol *match_sym) { - if (const lldb::SymbolType curr_type = match_sym->GetType(); - curr_type == lldb::eSymbolTypeAny || curr_type == symbol_type) { + if (const lldb::SymbolType match_sym_type = match_sym->GetType(); + match_sym_type == lldb::eSymbolTypeAny || + match_sym_type == symbol_type) { symbol_indexes.push_back(symtab->GetIndexForSymbol(match_sym)); } return true; }); + + SymbolContextList sc_list; SymbolIndicesToSymbolContextList(symtab, symbol_indexes, sc_list); + return sc_list; } void Module::FindSymbolsMatchingRegExAndType( diff --git a/lldb/test/API/lang/c/step-through-trampoline-with-symbol/TestStepThroughTrampolineWithSymbol.py b/lldb/test/API/lang/c/step-through-trampoline-with-symbol/TestStepThroughTrampolineWithSymbol.py index 40d40df6daf75..e738ab8553926 100644 --- a/lldb/test/API/lang/c/step-through-trampoline-with-symbol/TestStepThroughTrampolineWithSymbol.py +++ b/lldb/test/API/lang/c/step-through-trampoline-with-symbol/TestStepThroughTrampolineWithSymbol.py @@ -5,10 +5,12 @@ """ import lldb +from lldbsuite.test.decorators import skipUnlessPlatform from lldbsuite.test.lldbtest import TestBase, line_number, configuration import lldbsuite.test.lldbutil as lldbutil +@skipUnlessPlatform(["linux"]) class StepThroughTrampolineWithSymbol(TestBase): SYMBOL_NAME = "lib_add" FAKE_SYMBOL_NAME = "fake_lib_add" _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
