-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[lldb][DynamicLoader] Fix lldb unable to stop at _dl_debug_state if user set it before the process launched. #88792
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…ser set it before the process launched.
@llvm/pr-subscribers-lldb Author: Zequan Wu (ZequanWu) ChangesIf user sets a breakpoint at Full diff: https://github.com/llvm/llvm-project/pull/88792.diff 4 Files Affected:
diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h
index 2c2e6b2831ccee..3554ef0ea5a140 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -878,6 +878,9 @@ class Target : public std::enable_shared_from_this<Target>,
// the address of its previous instruction and return that address.
lldb::addr_t GetBreakableLoadAddress(lldb::addr_t addr);
+ void UpdateBreakpoints(ModuleList &module_list, bool added,
+ bool delete_locations);
+
void ModulesDidLoad(ModuleList &module_list);
void ModulesDidUnload(ModuleList &module_list, bool delete_locations);
diff --git a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp
index 9baf86da4dc799..901fa53682da8e 100644
--- a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp
+++ b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp
@@ -576,6 +576,11 @@ ModuleSP DynamicLoaderPOSIXDYLD::LoadInterpreterModule() {
UpdateLoadedSections(module_sp, LLDB_INVALID_ADDRESS, m_interpreter_base,
false);
m_interpreter_module = module_sp;
+ // Manually update breakpoints after updating loaded sections, because they
+ // cannot be resolve at the time when creating this module.
+ ModuleList module_list;
+ module_list.Append(module_sp);
+ m_process->GetTarget().UpdateBreakpoints(module_list, true, false);
return module_sp;
}
return nullptr;
diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp
index 09b0ac42631d1a..ec2dea5cc238d3 100644
--- a/lldb/source/Target/Target.cpp
+++ b/lldb/source/Target/Target.cpp
@@ -1687,6 +1687,13 @@ void Target::NotifyModulesRemoved(lldb_private::ModuleList &module_list) {
ModulesDidUnload(module_list, false);
}
+void Target::UpdateBreakpoints(ModuleList &module_list, bool added,
+ bool delete_locations) {
+ m_breakpoint_list.UpdateBreakpoints(module_list, added, delete_locations);
+ m_internal_breakpoint_list.UpdateBreakpoints(module_list, added,
+ delete_locations);
+}
+
void Target::ModulesDidLoad(ModuleList &module_list) {
const size_t num_images = module_list.GetSize();
if (m_valid && num_images) {
@@ -1694,8 +1701,7 @@ void Target::ModulesDidLoad(ModuleList &module_list) {
ModuleSP module_sp(module_list.GetModuleAtIndex(idx));
LoadScriptingResourceForModule(module_sp, this);
}
- m_breakpoint_list.UpdateBreakpoints(module_list, true, false);
- m_internal_breakpoint_list.UpdateBreakpoints(module_list, true, false);
+ UpdateBreakpoints(module_list, true, false);
if (m_process_sp) {
m_process_sp->ModulesDidLoad(module_list);
}
@@ -1713,8 +1719,7 @@ void Target::SymbolsDidLoad(ModuleList &module_list) {
}
}
- m_breakpoint_list.UpdateBreakpoints(module_list, true, false);
- m_internal_breakpoint_list.UpdateBreakpoints(module_list, true, false);
+ UpdateBreakpoints(module_list, true, false);
auto data_sp =
std::make_shared<TargetEventData>(shared_from_this(), module_list);
BroadcastEvent(eBroadcastBitSymbolsLoaded, data_sp);
@@ -1727,9 +1732,7 @@ void Target::ModulesDidUnload(ModuleList &module_list, bool delete_locations) {
auto data_sp =
std::make_shared<TargetEventData>(shared_from_this(), module_list);
BroadcastEvent(eBroadcastBitModulesUnloaded, data_sp);
- m_breakpoint_list.UpdateBreakpoints(module_list, false, delete_locations);
- m_internal_breakpoint_list.UpdateBreakpoints(module_list, false,
- delete_locations);
+ UpdateBreakpoints(module_list, false, delete_locations);
// If a module was torn down it will have torn down the 'TypeSystemClang's
// that we used as source 'ASTContext's for the persistent variables in
diff --git a/lldb/test/API/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py b/lldb/test/API/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py
index ea242952e409ec..a7e23fae14a21f 100644
--- a/lldb/test/API/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py
+++ b/lldb/test/API/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py
@@ -671,3 +671,20 @@ def test_breakpoint_statistics_hitcount(self):
self.assertNotEqual(breakpoints_stats, None)
for breakpoint_stats in breakpoints_stats:
self.assertIn("hitCount", breakpoint_stats)
+
+ @skipIf(oslist=no_match(["linux"]))
+ def test_break_at__dl_debug_state(self):
+ """
+ Test lldb is able to stop at _dl_debug_state if it is set before the
+ process is launched.
+ """
+ self.build()
+ exe = self.getBuildArtifact("a.out")
+ self.runCmd("target create %s" % exe)
+ bpid = lldbutil.run_break_set_by_symbol(self, "_dl_debug_state",
+ num_expected_locations=0)
+ self.runCmd("run")
+ self.assertIsNotNone(
+ lldbutil.get_one_thread_stopped_at_breakpoint_id(self.process(),
+ bpid)
+ )
|
✅ With the latest revision this PR passed the Python code formatter. |
When a shared library gets loaded, its sections have to have been loaded before asking the breakpoint system to try to set new breakpoints. Presumably this is how it happens for all the other shared library loads, or breakpoints in shared libraries wouldn't work. I'm missing why the _dl_debug_state breakpoint is special here, such that it requires a force load of the section info? How does that happen? |
Skimming DynamicLoaderPOSIXDYLD (as it is written today), it looks like attach/launch call The code path for other binaries goes through It seems like a simpler fix would be to have |
That's what I did first, but it breaks the test |
Jason's comment explains it well. It's because ld.so's loading is special here. Normal binaries are loaded via |
The point of the test is to ensure you don't get a hundred notifications, one for each module. Having one notification for ld.so, and 99 for the rest of the modules is ok. It should be fine to just update the test to match new reality. |
Updated to use |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good, thanks for the revisions.
Oh, so the problem is that the loaded event gets sent twice (and not that it gets sent as a separate event, as I originally thought). While it's not the end of the world (it appears the mac loader does something similar as well), this does seem like something that we could prevent. Can you check where does the second event get sent from? Is it by any chance when we go through this place ? If so, it should be fairly easy to refactor that code to avoid putting the interpreter module into the event list when it is already loaded. |
The first event is sent when creating the module for ld.so: llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp Line 574 in 458328a
llvm-project/lldb/source/Target/Target.cpp Line 1660 in 458328a
ModulesDidLoad in this change (the current version) after the creating the ld.so module.
It doesn't go through that place when loading ld.so module. That's for other binaries (non-dynamic-linker binaries). |
So, could the fix be as simple as passing notify=false in the first call ? |
Yeah, absolutely. Updated. |
✅ With the latest revision this PR passed the C/C++ code formatter. |
If user sets a breakpoint at
_dl_debug_state
before the process launched, the breakpoint is not resolved yet. When lldb loads dynamic loader module, it's created withTarget::GetOrCreateModule
which notifies any pending breakpoint to resolve. However, the module's sections are not loaded at this time. They are loaded after returned from Target::GetOrCreateModule. This change fixes it by manually resolving breakpoints after creating dynamic loader module.