[Lldb-commits] [lldb] [lldb] Improve ELF trampoline function detection (PR #178695)
Ebuka Ezike via lldb-commits
lldb-commits at lists.llvm.org
Tue Feb 10 10:09:28 PST 2026
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 <yerimyah1 at gmail.com>
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 <yerimyah1 at gmail.com>
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 <yerimyah1 at gmail.com>
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 <yerimyah1 at gmail.com>
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
+ at skipUnlessPlatform(["linux"])
class StepThroughTrampolineWithSymbol(TestBase):
SYMBOL_NAME = "lib_add"
FAKE_SYMBOL_NAME = "fake_lib_add"
More information about the lldb-commits
mailing list