Skip to content

Commit 1cbdc07

Browse files
committed
[lldb] Add support for debugging via the dynamic linker.
This patch adds support for shared library load when the executable is called through ld.so. Differential Revision:https://reviews.llvm.org/D108061
1 parent 5f0d265 commit 1cbdc07

File tree

7 files changed

+142
-17
lines changed

7 files changed

+142
-17
lines changed

lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,19 @@ static addr_t ResolveRendezvousAddress(Process *process) {
5656
"%s resolved via direct object file approach to 0x%" PRIx64,
5757
__FUNCTION__, info_location);
5858
} else {
59+
const Symbol *_r_debug =
60+
target->GetExecutableModule()->FindFirstSymbolWithNameAndType(
61+
ConstString("_r_debug"));
62+
if (_r_debug) {
63+
info_addr = _r_debug->GetAddress().GetLoadAddress(target);
64+
if (info_addr != LLDB_INVALID_ADDRESS) {
65+
LLDB_LOGF(log,
66+
"%s resolved by finding symbol '_r_debug' whose value is "
67+
"0x%" PRIx64,
68+
__FUNCTION__, info_addr);
69+
return info_addr;
70+
}
71+
}
5972
LLDB_LOGF(log,
6073
"%s FAILED - direct object file approach did not yield a "
6174
"valid address",
@@ -276,6 +289,14 @@ bool DYLDRendezvous::FillSOEntryFromModuleInfo(
276289
entry.base_addr = base_addr;
277290
entry.dyn_addr = dyn_addr;
278291

292+
// ld.so saves empty file name for the executable file in the link map.
293+
// When executable is run using ld.so, we need to be update executable path.
294+
if (name.empty()) {
295+
MemoryRegionInfo region;
296+
Status region_status =
297+
m_process->GetMemoryRegionInfo(entry.dyn_addr, region);
298+
name = region.GetName().AsCString();
299+
}
279300
entry.file_spec.SetFile(name, FileSpec::Style::native);
280301

281302
UpdateBaseAddrIfNecessary(entry, name);
@@ -547,6 +568,15 @@ bool DYLDRendezvous::ReadSOEntryFromMemory(lldb::addr_t addr, SOEntry &entry) {
547568
return false;
548569

549570
std::string file_path = ReadStringFromMemory(entry.path_addr);
571+
572+
// ld.so saves empty file name for the executable file in the link map.
573+
// When executable is run using ld.so, we need to be update executable path.
574+
if (file_path.empty()) {
575+
MemoryRegionInfo region;
576+
Status region_status =
577+
m_process->GetMemoryRegionInfo(entry.dyn_addr, region);
578+
file_path = region.GetName().AsCString();
579+
}
550580
entry.file_spec.SetFile(file_path, FileSpec::Style::native);
551581

552582
UpdateBaseAddrIfNecessary(entry, file_path);

lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -333,28 +333,48 @@ bool DynamicLoaderPOSIXDYLD::SetRendezvousBreakpoint() {
333333
LLDB_LOG(log, "Rendezvous structure is not set up yet. "
334334
"Trying to locate rendezvous breakpoint in the interpreter "
335335
"by symbol name.");
336-
ModuleSP interpreter = LoadInterpreterModule();
337-
if (!interpreter) {
338-
LLDB_LOG(log, "Can't find interpreter, rendezvous breakpoint isn't set.");
339-
return false;
340-
}
341-
342-
// Function names from different dynamic loaders that are known to be used
343-
// as rendezvous between the loader and debuggers.
336+
// Function names from different dynamic loaders that are known to be
337+
// used as rendezvous between the loader and debuggers.
344338
static std::vector<std::string> DebugStateCandidates{
345339
"_dl_debug_state", "rtld_db_dlactivity", "__dl_rtld_db_dlactivity",
346340
"r_debug_state", "_r_debug_state", "_rtld_debug_state",
347341
};
348342

349-
FileSpecList containingModules;
350-
containingModules.Append(interpreter->GetFileSpec());
351-
dyld_break = target.CreateBreakpoint(
352-
&containingModules, nullptr /* containingSourceFiles */,
353-
DebugStateCandidates, eFunctionNameTypeFull, eLanguageTypeC,
354-
0, /* offset */
355-
eLazyBoolNo, /* skip_prologue */
356-
true, /* internal */
357-
false /* request_hardware */);
343+
ModuleSP interpreter = LoadInterpreterModule();
344+
if (!interpreter) {
345+
if (NameMatches(m_process->GetTarget()
346+
.GetExecutableModulePointer()
347+
->GetFileSpec()
348+
.GetFilename()
349+
.GetCString(),
350+
NameMatch::StartsWith, "ld-")) {
351+
FileSpecList containingModules;
352+
containingModules.Append(
353+
m_process->GetTarget().GetExecutableModulePointer()->GetFileSpec());
354+
355+
dyld_break = target.CreateBreakpoint(
356+
&containingModules, /*containingSourceFiles=*/nullptr,
357+
DebugStateCandidates, eFunctionNameTypeFull, eLanguageTypeC,
358+
/*offset=*/0,
359+
/*skip_prologue=*/eLazyBoolNo,
360+
/*internal=*/true,
361+
/*request_hardware=*/false);
362+
} else {
363+
LLDB_LOG(log,
364+
"Can't find interpreter, rendezvous breakpoint isn't set.");
365+
return false;
366+
}
367+
} else {
368+
FileSpecList containingModules;
369+
containingModules.Append(interpreter->GetFileSpec());
370+
dyld_break = target.CreateBreakpoint(
371+
&containingModules, /*containingSourceFiles=*/nullptr,
372+
DebugStateCandidates, eFunctionNameTypeFull, eLanguageTypeC,
373+
/*offset=*/0,
374+
/*skip_prologue=*/eLazyBoolNo,
375+
/*internal=*/true,
376+
/*request_hardware=*/false);
377+
}
358378
}
359379

360380
if (dyld_break->GetNumResolvedLocations() != 1) {
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
CXX_SOURCES := main.cpp
2+
LD_EXTRAS := -Wl,-rpath "-Wl,$(shell pwd)"
3+
USE_LIBDL := 1
4+
5+
include Makefile.rules
6+
7+
# The following shared library will be used to test breakpoints under dynamic loading
8+
libsignal_file.so: signal_file.cpp
9+
$(MAKE) -f $(MAKEFILE_RULES) \
10+
DYLIB_ONLY=YES DYLIB_CXX_SOURCES=signal_file.cpp DYLIB_NAME=signal_file
11+
12+
a.out: libsignal_file.so main.cpp
13+
$(MAKE) -f $(MAKEFILE_RULES) \
14+
CXX_SOURCES=main.cpp LD_EXTRAS=libsignal_file.so
15+
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
"""
2+
Test that LLDB can launch a linux executable through the dynamic loader and still hit a breakpoint.
3+
"""
4+
5+
import lldb
6+
import os
7+
8+
from lldbsuite.test.lldbtest import *
9+
10+
class TestLinux64LaunchingViaDynamicLoader(TestBase):
11+
mydir = TestBase.compute_mydir(__file__)
12+
13+
def test(self):
14+
self.build()
15+
exe = "/lib64/ld-linux-x86-64.so.2"
16+
if(os.path.exists(exe)):
17+
target = self.dbg.CreateTarget(exe)
18+
self.assertTrue(target, VALID_TARGET)
19+
20+
# Set breakpoints both on shared library function as well as on
21+
# main. Both of them will be pending breakpoints.
22+
breakpoint_main = target.BreakpointCreateBySourceRegex("// Break here", lldb.SBFileSpec("main.cpp"))
23+
breakpoint_shared_library = target.BreakpointCreateBySourceRegex("get_signal_crash", lldb.SBFileSpec("signal_file.cpp"))
24+
launch_info = lldb.SBLaunchInfo([ "--library-path",self.get_process_working_directory(),self.getBuildArtifact("a.out")])
25+
launch_info.SetWorkingDirectory(self.get_process_working_directory())
26+
error = lldb.SBError()
27+
process = target.Launch(launch_info,error)
28+
self.assertTrue(error.Success())
29+
30+
# Stopped on main here.
31+
self.assertEqual(process.GetState(), lldb.eStateStopped)
32+
thread = process.GetSelectedThread()
33+
self.assertIn("main",thread.GetFrameAtIndex(0).GetDisplayFunctionName())
34+
process.Continue()
35+
36+
# Stopped on get_signal_crash function here.
37+
self.assertEqual(process.GetState(), lldb.eStateStopped)
38+
self.assertIn("get_signal_crash",thread.GetFrameAtIndex(0).GetDisplayFunctionName())
39+
process.Continue()
40+
41+
# Stopped because of generated signal.
42+
self.assertEqual(process.GetState(), lldb.eStateStopped)
43+
self.assertIn("raise",thread.GetFrameAtIndex(0).GetDisplayFunctionName())
44+
self.assertIn("get_signal_crash",thread.GetFrameAtIndex(1).GetDisplayFunctionName())
45+
46+
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#include "signal_file.h"
2+
3+
int main() {
4+
// Break here
5+
return get_signal_crash();
6+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#include "signal_file.h"
2+
#include <signal.h>
3+
4+
int get_signal_crash(void) {
5+
raise(SIGSEGV);
6+
return 0;
7+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
int get_signal_crash(void);

0 commit comments

Comments
 (0)