diff --git a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp --- a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp +++ b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp @@ -56,6 +56,19 @@ "%s resolved via direct object file approach to 0x%" PRIx64, __FUNCTION__, info_location); } else { + const Symbol *_r_debug = + target->GetExecutableModule()->FindFirstSymbolWithNameAndType( + ConstString("_r_debug")); + if (_r_debug) { + info_addr = _r_debug->GetAddress().GetLoadAddress(target); + if (info_addr != LLDB_INVALID_ADDRESS) { + LLDB_LOGF(log, + "%s resolved by finding symbol '_r_debug' whose value is " + "0x%" PRIx64, + __FUNCTION__, info_addr); + return info_addr; + } + } LLDB_LOGF(log, "%s FAILED - direct object file approach did not yield a " "valid address", @@ -276,6 +289,14 @@ entry.base_addr = base_addr; entry.dyn_addr = dyn_addr; + // ld.so saves empty file name for the executable file in the link map. + // When argv[0] is ld.so, we need to be update executable path. + if (name.empty()) { + MemoryRegionInfo region; + Status region_status = + m_process->GetMemoryRegionInfo(entry.dyn_addr, region); + name = region.GetName().AsCString(); + } entry.file_spec.SetFile(name, FileSpec::Style::native); UpdateBaseAddrIfNecessary(entry, name); @@ -547,6 +568,15 @@ return false; std::string file_path = ReadStringFromMemory(entry.path_addr); + + // ld.so saves empty file name for the executable file in the link map. + // When argv[0] is ld.so, we need to be update executable path. + if (file_path.empty()) { + MemoryRegionInfo region; + Status region_status = + m_process->GetMemoryRegionInfo(entry.dyn_addr, region); + file_path = region.GetName().AsCString(); + } entry.file_spec.SetFile(file_path, FileSpec::Style::native); UpdateBaseAddrIfNecessary(entry, file_path); diff --git a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp --- a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp +++ b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp @@ -333,28 +333,48 @@ LLDB_LOG(log, "Rendezvous structure is not set up yet. " "Trying to locate rendezvous breakpoint in the interpreter " "by symbol name."); - ModuleSP interpreter = LoadInterpreterModule(); - if (!interpreter) { - LLDB_LOG(log, "Can't find interpreter, rendezvous breakpoint isn't set."); - return false; - } - - // Function names from different dynamic loaders that are known to be used - // as rendezvous between the loader and debuggers. + // Function names from different dynamic loaders that are known to be + // used as rendezvous between the loader and debuggers. static std::vector DebugStateCandidates{ "_dl_debug_state", "rtld_db_dlactivity", "__dl_rtld_db_dlactivity", "r_debug_state", "_r_debug_state", "_rtld_debug_state", }; - FileSpecList containingModules; - containingModules.Append(interpreter->GetFileSpec()); - dyld_break = target.CreateBreakpoint( - &containingModules, nullptr /* containingSourceFiles */, - DebugStateCandidates, eFunctionNameTypeFull, eLanguageTypeC, - 0, /* offset */ - eLazyBoolNo, /* skip_prologue */ - true, /* internal */ - false /* request_hardware */); + ModuleSP interpreter = LoadInterpreterModule(); + if (!interpreter) { + if (NameMatches(m_process->GetTarget() + .GetExecutableModulePointer() + ->GetFileSpec() + .GetFilename() + .GetCString(), + NameMatch::StartsWith, "ld-")) { + FileSpecList containingModules; + containingModules.Append( + m_process->GetTarget().GetExecutableModulePointer()->GetFileSpec()); + + dyld_break = target.CreateBreakpoint( + &containingModules, /*containingSourceFiles=*/nullptr, + DebugStateCandidates, eFunctionNameTypeFull, eLanguageTypeC, + /*offset=*/0, + /*skip_prologue=*/eLazyBoolNo, + /*internal=*/true, + /*request_hardware=*/false); + } else { + LLDB_LOG(log, + "Can't find interpreter, rendezvous breakpoint isn't set."); + return false; + } + } else { + FileSpecList containingModules; + containingModules.Append(interpreter->GetFileSpec()); + dyld_break = target.CreateBreakpoint( + &containingModules, /*containingSourceFiles=*/nullptr, + DebugStateCandidates, eFunctionNameTypeFull, eLanguageTypeC, + /*offset=*/0, + /*skip_prologue=*/eLazyBoolNo, + /*internal=*/true, + /*request_hardware=*/false); + } } if (dyld_break->GetNumResolvedLocations() != 1) { diff --git a/lldb/test/API/functionalities/dyld-launch-linux/Makefile b/lldb/test/API/functionalities/dyld-launch-linux/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/dyld-launch-linux/Makefile @@ -0,0 +1,4 @@ +CXX_SOURCES := main.cpp +DYLIB_NAME := signal_file +DYLIB_CXX_SOURCES := signal_file.cpp +include Makefile.rules diff --git a/lldb/test/API/functionalities/dyld-launch-linux/TestDyldLaunchLinux.py b/lldb/test/API/functionalities/dyld-launch-linux/TestDyldLaunchLinux.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/dyld-launch-linux/TestDyldLaunchLinux.py @@ -0,0 +1,52 @@ +""" +Test that LLDB can launch a linux executable through the dynamic loader and still hit a breakpoint. +""" + +import lldb +import os + +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * + +class TestLinux64LaunchingViaDynamicLoader(TestBase): + mydir = TestBase.compute_mydir(__file__) + + @skipIf(oslist=no_match(['linux'])) + @no_debug_info_test + def test(self): + self.build() + candidates = [ + "/lib64/ld-linux-x86-64.so.2", + "/usr/lib/ld-linux-x86-64.so.2" + ] + exe = next((c for c in candidates if os.path.exists(c)), None) + if not os.path.exists(exe): + return + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Set breakpoints both on shared library function as well as on + # main. Both of them will be pending breakpoints. + breakpoint_main = target.BreakpointCreateBySourceRegex("// Break here", lldb.SBFileSpec("main.cpp")) + breakpoint_shared_library = target.BreakpointCreateBySourceRegex("get_signal_crash", lldb.SBFileSpec("signal_file.cpp")) + launch_info = lldb.SBLaunchInfo([ "--library-path", self.get_process_working_directory(), self.getBuildArtifact("a.out")]) + launch_info.SetWorkingDirectory(self.get_process_working_directory()) + error = lldb.SBError() + process = target.Launch(launch_info, error) + self.assertTrue(error.Success()) + + # Stopped on main here. + self.assertEqual(process.GetState(), lldb.eStateStopped) + thread = process.GetSelectedThread() + self.assertIn("main", thread.GetFrameAtIndex(0).GetDisplayFunctionName()) + process.Continue() + + # Stopped on get_signal_crash function here. + self.assertEqual(process.GetState(), lldb.eStateStopped) + self.assertIn("get_signal_crash", thread.GetFrameAtIndex(0).GetDisplayFunctionName()) + process.Continue() + + # Stopped because of generated signal. + self.assertEqual(process.GetState(), lldb.eStateStopped) + self.assertIn("raise", thread.GetFrameAtIndex(0).GetDisplayFunctionName()) + self.assertIn("get_signal_crash", thread.GetFrameAtIndex(1).GetDisplayFunctionName()) diff --git a/lldb/test/API/functionalities/dyld-launch-linux/main.cpp b/lldb/test/API/functionalities/dyld-launch-linux/main.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/dyld-launch-linux/main.cpp @@ -0,0 +1,6 @@ +#include "signal_file.h" + +int main() { + // Break here + return get_signal_crash(); +} diff --git a/lldb/test/API/functionalities/dyld-launch-linux/signal_file.h b/lldb/test/API/functionalities/dyld-launch-linux/signal_file.h new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/dyld-launch-linux/signal_file.h @@ -0,0 +1 @@ +int get_signal_crash(); diff --git a/lldb/test/API/functionalities/dyld-launch-linux/signal_file.cpp b/lldb/test/API/functionalities/dyld-launch-linux/signal_file.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/dyld-launch-linux/signal_file.cpp @@ -0,0 +1,7 @@ +#include "signal_file.h" +#include + +int get_signal_crash(void) { + raise(SIGSEGV); + return 0; +}