[Lldb-commits] [lldb] 03df971 - [lldb] Add support for debugging via the dynamic linker.

Rumeet Dhindsa via lldb-commits lldb-commits at lists.llvm.org
Fri Sep 10 11:00:19 PDT 2021


Author: Rumeet Dhindsa
Date: 2021-09-10T10:59:31-07:00
New Revision: 03df97101287e8cb647c6c0982c4efdb82585c21

URL: https://github.com/llvm/llvm-project/commit/03df97101287e8cb647c6c0982c4efdb82585c21
DIFF: https://github.com/llvm/llvm-project/commit/03df97101287e8cb647c6c0982c4efdb82585c21.diff

LOG: [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

Added: 
    lldb/test/API/functionalities/dyld-launch-linux/Makefile
    lldb/test/API/functionalities/dyld-launch-linux/TestDyldLaunchLinux.py
    lldb/test/API/functionalities/dyld-launch-linux/main.cpp
    lldb/test/API/functionalities/dyld-launch-linux/signal_file.cpp
    lldb/test/API/functionalities/dyld-launch-linux/signal_file.h

Modified: 
    lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp
    lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h
    lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp
index 866acbddbdc8a..7e80dc28e56b2 100644
--- a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp
+++ b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp
@@ -24,28 +24,35 @@
 using namespace lldb;
 using namespace lldb_private;
 
-/// Locates the address of the rendezvous structure.  Returns the address on
-/// success and LLDB_INVALID_ADDRESS on failure.
-static addr_t ResolveRendezvousAddress(Process *process) {
+DYLDRendezvous::DYLDRendezvous(Process *process)
+    : m_process(process), m_rendezvous_addr(LLDB_INVALID_ADDRESS),
+      m_executable_interpreter(false), m_current(), m_previous(),
+      m_loaded_modules(), m_soentries(), m_added_soentries(),
+      m_removed_soentries() {
+  m_thread_info.valid = false;
+  UpdateExecutablePath();
+}
+
+addr_t DYLDRendezvous::ResolveRendezvousAddress() {
   Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
   addr_t info_location;
   addr_t info_addr;
   Status error;
 
-  if (!process) {
+  if (!m_process) {
     LLDB_LOGF(log, "%s null process provided", __FUNCTION__);
     return LLDB_INVALID_ADDRESS;
   }
 
   // Try to get it from our process.  This might be a remote process and might
   // grab it via some remote-specific mechanism.
-  info_location = process->GetImageInfoAddress();
+  info_location = m_process->GetImageInfoAddress();
   LLDB_LOGF(log, "%s info_location = 0x%" PRIx64, __FUNCTION__, info_location);
 
   // If the process fails to return an address, fall back to seeing if the
   // local object file can help us find it.
   if (info_location == LLDB_INVALID_ADDRESS) {
-    Target *target = &process->GetTarget();
+    Target *target = &m_process->GetTarget();
     if (target) {
       ObjectFile *obj_file = target->GetExecutableModule()->GetObjectFile();
       Address addr = obj_file->GetImageInfoAddress(target);
@@ -56,6 +63,20 @@ static addr_t ResolveRendezvousAddress(Process *process) {
                   "%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);
+            m_executable_interpreter = true;
+            return info_addr;
+          }
+        }
         LLDB_LOGF(log,
                   "%s FAILED - direct object file approach did not yield a "
                   "valid address",
@@ -70,9 +91,9 @@ static addr_t ResolveRendezvousAddress(Process *process) {
   }
 
   LLDB_LOGF(log, "%s reading pointer (%" PRIu32 " bytes) from 0x%" PRIx64,
-            __FUNCTION__, process->GetAddressByteSize(), info_location);
+            __FUNCTION__, m_process->GetAddressByteSize(), info_location);
 
-  info_addr = process->ReadPointerFromMemory(info_location, error);
+  info_addr = m_process->ReadPointerFromMemory(info_location, error);
   if (error.Fail()) {
     LLDB_LOGF(log, "%s FAILED - could not read from the info location: %s",
               __FUNCTION__, error.AsCString());
@@ -90,14 +111,6 @@ static addr_t ResolveRendezvousAddress(Process *process) {
   return info_addr;
 }
 
-DYLDRendezvous::DYLDRendezvous(Process *process)
-    : m_process(process), m_rendezvous_addr(LLDB_INVALID_ADDRESS), m_current(),
-      m_previous(), m_loaded_modules(), m_soentries(), m_added_soentries(),
-      m_removed_soentries() {
-  m_thread_info.valid = false;
-  UpdateExecutablePath();
-}
-
 void DYLDRendezvous::UpdateExecutablePath() {
   if (m_process) {
     Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
@@ -132,7 +145,8 @@ bool DYLDRendezvous::Resolve() {
             __FUNCTION__, uint64_t(address_size), uint64_t(padding));
 
   if (m_rendezvous_addr == LLDB_INVALID_ADDRESS)
-    cursor = info_addr = ResolveRendezvousAddress(m_process);
+    cursor = info_addr =
+        ResolveRendezvousAddress();
   else
     cursor = info_addr = m_rendezvous_addr;
   LLDB_LOGF(log, "DYLDRendezvous::%s cursor = 0x%" PRIx64, __FUNCTION__,
@@ -296,8 +310,10 @@ bool DYLDRendezvous::SaveSOEntriesFromRemote(
       return false;
 
     // Only add shared libraries and not the executable.
-    if (!SOEntryIsMainExecutable(entry))
+    if (!SOEntryIsMainExecutable(entry)) {
+      UpdateFileSpecIfNecessary(entry);
       m_soentries.push_back(entry);
+    }
   }
 
   m_loaded_modules = module_list;
@@ -324,6 +340,7 @@ bool DYLDRendezvous::AddSOEntriesFromRemote(
 
     // Only add shared libraries and not the executable.
     if (!SOEntryIsMainExecutable(entry)) {
+      UpdateFileSpecIfNecessary(entry);
       m_soentries.push_back(entry);
       m_added_soentries.push_back(entry);
     }
@@ -383,6 +400,8 @@ bool DYLDRendezvous::AddSOEntries() {
     if (SOEntryIsMainExecutable(entry))
       continue;
 
+    UpdateFileSpecIfNecessary(entry);
+
     pos = std::find(m_soentries.begin(), m_soentries.end(), entry);
     if (pos == m_soentries.end()) {
       m_soentries.push_back(entry);
@@ -424,6 +443,10 @@ bool DYLDRendezvous::SOEntryIsMainExecutable(const SOEntry &entry) {
   case llvm::Triple::Linux:
     if (triple.isAndroid())
       return entry.file_spec == m_exe_file_spec;
+    // If we are debugging ld.so, then all SOEntries should be treated as
+    // libraries, including the "main" one (denoted by an empty string).
+    if (!entry.file_spec && m_executable_interpreter)
+      return false;
     return !entry.file_spec;
   default:
     return false;
@@ -447,6 +470,8 @@ bool DYLDRendezvous::TakeSnapshot(SOEntryList &entry_list) {
     if (SOEntryIsMainExecutable(entry))
       continue;
 
+    UpdateFileSpecIfNecessary(entry);
+
     entry_list.push_back(entry);
   }
 
@@ -512,6 +537,19 @@ void DYLDRendezvous::UpdateBaseAddrIfNecessary(SOEntry &entry,
   }
 }
 
+void DYLDRendezvous::UpdateFileSpecIfNecessary(SOEntry &entry) {
+  // Updates filename if empty. It is useful while debugging ld.so,
+  // when the link map returns empty string for the main executable.
+  if (!entry.file_spec) {
+    MemoryRegionInfo region;
+    Status region_status =
+        m_process->GetMemoryRegionInfo(entry.dyn_addr, region);
+    if (!region.GetName().IsEmpty())
+      entry.file_spec.SetFile(region.GetName().AsCString(),
+                              FileSpec::Style::native);
+  }
+}
+
 bool DYLDRendezvous::ReadSOEntryFromMemory(lldb::addr_t addr, SOEntry &entry) {
   entry.clear();
 

diff  --git a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h
index 5775f5a730cd7..04d3e665f8598 100644
--- a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h
+++ b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h
@@ -47,6 +47,12 @@ class DYLDRendezvous {
     Rendezvous() = default;
   };
 
+  /// Locates the address of the rendezvous structure.  It updates
+  /// m_executable_interpreter if address is extracted from _r_debug.
+  ///
+  /// \returns address on success and LLDB_INVALID_ADDRESS on failure.
+  lldb::addr_t ResolveRendezvousAddress();
+
 public:
   // Various metadata supplied by the inferior's threading library to describe
   // the per-thread state.
@@ -183,6 +189,9 @@ class DYLDRendezvous {
   /// Location of the r_debug structure in the inferiors address space.
   lldb::addr_t m_rendezvous_addr;
 
+  // True if the main program is the dynamic linker/loader/program interpreter.
+  bool m_executable_interpreter;
+
   /// Current and previous snapshots of the rendezvous structure.
   Rendezvous m_current;
   Rendezvous m_previous;
@@ -246,6 +255,8 @@ class DYLDRendezvous {
 
   void UpdateBaseAddrIfNecessary(SOEntry &entry, std::string const &file_path);
 
+  void UpdateFileSpecIfNecessary(SOEntry &entry);
+
   bool SOEntryIsMainExecutable(const SOEntry &entry);
 
   /// Reads the current list of shared objects according to the link map

diff  --git a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp
index 160faa74af239..75b9d69bc7e03 100644
--- a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp
+++ b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp
@@ -333,28 +333,37 @@ bool DynamicLoaderPOSIXDYLD::SetRendezvousBreakpoint() {
     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 
diff erent dynamic loaders that are known to be used
-    // as rendezvous between the loader and debuggers.
+    // Function names from 
diff erent dynamic loaders that are known to be
+    // used as rendezvous between the loader and debuggers.
     static std::vector<std::string> 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) {
+      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 {
+      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
index 0000000000000..2621dc44502bf
--- /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
index 0000000000000..bf475890035ab
--- /dev/null
+++ b/lldb/test/API/functionalities/dyld-launch-linux/TestDyldLaunchLinux.py
@@ -0,0 +1,58 @@
+"""
+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()
+
+        # Extracts path of the interpreter.
+        spec = lldb.SBModuleSpec()
+        spec.SetFileSpec(lldb.SBFileSpec(self.getBuildArtifact("a.out")))
+        interp_section = lldb.SBModule(spec).FindSection(".interp")
+        if not interp_section:
+          return
+        section_data = interp_section.GetSectionData()
+        error = lldb.SBError()
+        exe = section_data.GetString(error,0)
+        if error.Fail():
+          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
index 0000000000000..0ff05466c58ca
--- /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.cpp b/lldb/test/API/functionalities/dyld-launch-linux/signal_file.cpp
new file mode 100644
index 0000000000000..3c904a34f4210
--- /dev/null
+++ b/lldb/test/API/functionalities/dyld-launch-linux/signal_file.cpp
@@ -0,0 +1,7 @@
+#include "signal_file.h"
+#include <signal.h>
+
+int get_signal_crash(void) {
+  raise(SIGSEGV);
+  return 0;
+}

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
index 0000000000000..a93d9125d5c62
--- /dev/null
+++ b/lldb/test/API/functionalities/dyld-launch-linux/signal_file.h
@@ -0,0 +1 @@
+int get_signal_crash();


        


More information about the lldb-commits mailing list