aadsm updated this revision to Diff 202652.
aadsm added a comment.

Address comments and add tests


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D62502/new/

https://reviews.llvm.org/D62502

Files:
  lldb/include/lldb/Host/common/NativeProcessProtocol.h
  
lldb/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteLibrariesSvr4Support.py
  lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
  lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
  lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp

Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -2765,6 +2765,27 @@
     return std::move(*buffer_or_error);
   }
 
+#if defined(__linux__)
+  if (object == "libraries-svr4") {
+    std::vector<SharedLibraryInfo> library_list;
+    auto status = m_debugged_process_up->GetLoadedSharedLibraries(library_list);
+    if (!status.Success())
+      return status.ToError();
+
+    StreamString response;
+    response.Printf("<library-list-svr4 version=\"1.0\">");
+    for (auto const &library : library_list) {
+      response.Printf("<library name=\"%s\" ", library.name.c_str());
+      response.Printf("lm=\"0x%" PRIx64 "\" ", library.link_map);
+      response.Printf("l_addr=\"0x%" PRIx64 "\" ", library.base_addr);
+      response.Printf("l_ld=\"0x%" PRIx64 "\" />", library.ld_addr);
+    }
+    response.Printf("</library-list-svr4>");
+    return std::move(
+        MemoryBuffer::getMemBufferCopy(response.GetString(), __FUNCTION__));
+  }
+#endif
+
   return llvm::make_error<PacketUnimplementedError>(
       "Xfer object not supported");
 }
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
@@ -826,6 +826,9 @@
   response.PutCString(";QPassSignals+");
   response.PutCString(";qXfer:auxv:read+");
 #endif
+#if defined(__linux__)
+  response.PutCString(";qXfer:libraries-svr4:read+");
+#endif
 
   return SendPacketNoLock(response.GetString());
 }
Index: lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
===================================================================
--- lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
+++ lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
@@ -79,6 +79,9 @@
 
   lldb::addr_t GetSharedLibraryInfoAddress() override;
 
+  Status GetLoadedSharedLibraries(
+      std::vector<SharedLibraryInfo> &library_list) override;
+
   size_t UpdateThreads() override;
 
   const ArchSpec &GetArchitecture() const override { return m_arch; }
@@ -134,6 +137,17 @@
   template <typename ELF_EHDR, typename ELF_PHDR, typename ELF_DYN>
   lldb::addr_t GetELFImageInfoAddress();
 
+  template <typename T> struct ELFLinkMap {
+    T l_addr;
+    T l_name;
+    T l_ld;
+    T l_next;
+    T l_prev;
+  };
+  template <typename T>
+  Status ReadSharedLibraryInfo(lldb::addr_t link_map_addr,
+                               SharedLibraryInfo &info);
+
 private:
   MainLoop::SignalHandleUP m_sigchld_handle;
   ArchSpec m_arch;
Index: lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
===================================================================
--- lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
+++ lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
@@ -2176,3 +2176,76 @@
 
   return LLDB_INVALID_ADDRESS;
 }
+
+template <typename T>
+Status NativeProcessLinux::ReadSharedLibraryInfo(lldb::addr_t link_map_addr,
+                                                 SharedLibraryInfo &info) {
+  ELFLinkMap<T> link_map;
+  size_t bytes_read;
+  auto error =
+      ReadMemory(link_map_addr, &link_map, sizeof(link_map), bytes_read);
+  if (!error.Success())
+    return error;
+
+  char name_buffer[PATH_MAX];
+  error = ReadMemory(link_map.l_name, &name_buffer, sizeof(name_buffer),
+                     bytes_read);
+  if (!error.Success())
+    return error;
+
+  info.name = std::string(name_buffer);
+  info.link_map = link_map_addr;
+  info.base_addr = link_map.l_addr;
+  info.ld_addr = link_map.l_ld;
+  info.next = link_map.l_next;
+#if defined(__linux__) && !defined(__ANDROID__)
+  // On non-android linux systems, main executable has an empty path.
+  info.main = info.name.empty();
+#elif defined(__linux__) && defined(__ANDROID__)
+  // On android, the main executable has a load address of 0.
+  info.main = info.ld_addr == 0;
+#else
+  info.main = false;
+#endif
+
+  return Status();
+}
+
+Status NativeProcessLinux::GetLoadedSharedLibraries(
+    std::vector<SharedLibraryInfo> &library_list) {
+  // Address of DT_DEBUG.d_ptr which points to r_debug
+  lldb::addr_t info_address = GetSharedLibraryInfoAddress();
+  if (info_address == LLDB_INVALID_ADDRESS)
+    return Status("Invalid shared library info address");
+  // Address of r_debug
+  lldb::addr_t address = 0;
+  size_t bytes_read;
+  auto error =
+      ReadMemory(info_address, &address, GetAddressByteSize(), bytes_read);
+  if (!error.Success())
+    return error;
+  if (address == 0)
+    return Status("Invalid r_debug address");
+  // Read r_debug.r_map
+  lldb::addr_t link_map = 0;
+  error = ReadMemory(address + GetAddressByteSize(), &link_map,
+                     GetAddressByteSize(), bytes_read);
+  if (!error.Success())
+    return error;
+  if (address == 0)
+    return Status("Invalid link_map address");
+
+  while (link_map) {
+    SharedLibraryInfo info;
+    if (GetAddressByteSize() == 8)
+      error = ReadSharedLibraryInfo<uint64_t>(link_map, info);
+    else
+      error = ReadSharedLibraryInfo<uint32_t>(link_map, info);
+    if (!error.Success())
+      return error;
+    library_list.push_back(info);
+    link_map = info.next;
+  }
+
+  return Status();
+}
Index: lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
===================================================================
--- lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
+++ lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
@@ -816,6 +816,7 @@
                     "error"])
             self.assertIsNotNone(val)
 
+        mem_region_dict["name"] = seven.unhexlify(mem_region_dict.get("name", ""))
         # Return the dictionary of key-value pairs for the memory region.
         return mem_region_dict
 
Index: lldb/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteLibrariesSvr4Support.py
===================================================================
--- /dev/null
+++ lldb/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteLibrariesSvr4Support.py
@@ -0,0 +1,179 @@
+import xml.etree.ElementTree as ET
+
+import gdbremote_testcase
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+
+
+class TestGdbRemoteLibrariesSvr4Support(gdbremote_testcase.GdbRemoteTestCaseBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    FEATURE_NAME = "qXfer:libraries-svr4:read"
+
+    def has_libraries_svr4_support(self):
+        inferior_args = ["message:main entered", "sleep:5"]
+        # inferior_args = [ "sleep:500"]
+        procs = self.prep_debug_monitor_and_inferior(inferior_args=inferior_args)
+
+        # Don't do anything until we match the launched inferior main entry output.
+        # Then immediately interrupt the process.
+        # This prevents libraries-svr4 data being asked for before it's ready and leaves
+        # us in a stopped state.
+        self.test_sequence.add_log_lines(
+            [
+                # Start the inferior...
+                "read packet: $c#63",
+                # ... match output....
+                {
+                    "type": "output_match",
+                    "regex": self.maybe_strict_output_regex(
+                        r"message:main entered\r\n"
+                    ),
+                },
+            ],
+            True,
+        )
+        # ... then interrupt.
+        self.add_interrupt_packets()
+        self.add_qSupported_packets()
+
+        context = self.expect_gdbremote_sequence()
+        self.assertIsNotNone(context)
+        features = self.parse_qSupported_response(context)
+        return self.FEATURE_NAME in features and features[self.FEATURE_NAME] == "+"
+
+    def get_libraries_svr4_data(self):
+        # Start up llgs and inferior, and check for libraries-svr4 support.
+        if not self.has_libraries_svr4_support():
+            self.skipTest("libraries-svr4 not supported")
+
+        OFFSET = 0
+        LENGTH = 0xFFFF
+
+        # Grab the libraries-svr4 data.
+        self.reset_test_sequence()
+        self.test_sequence.add_log_lines(
+            [
+                "read packet: $qXfer:libraries-svr4:read::{:x},{:x}:#00".format(
+                    OFFSET, LENGTH
+                ),
+                {
+                    "direction": "send",
+                    "regex": re.compile(
+                        r"^\$([^E])(.*)#[0-9a-fA-F]{2}$", re.MULTILINE | re.DOTALL
+                    ),
+                    "capture": {1: "response_type", 2: "content_raw"},
+                },
+            ],
+            True,
+        )
+
+        context = self.expect_gdbremote_sequence()
+        self.assertIsNotNone(context)
+
+        # Ensure we end up with all libraries-svr4 data in one packet.
+        self.assertEqual(context.get("response_type"), "l")
+
+        # Decode binary data.
+        content_raw = context.get("content_raw")
+        self.assertIsNotNone(content_raw)
+        return content_raw
+
+    def get_libraries_svr4_xml(self):
+        libraries_svr4 = self.get_libraries_svr4_data()
+        xml_root = None
+        try:
+            xml_root = ET.fromstring(libraries_svr4)
+        except xml.etree.ElementTree.ParseError:
+            pass
+        self.assertIsNotNone(xml_root, "Malformed libraries-svr4 XML")
+        return xml_root
+
+    def libraries_svr4_well_formed(self):
+        xml_root = self.get_libraries_svr4_xml()
+        self.assertEqual(xml_root.tag, "library-list-svr4")
+        for child in xml_root:
+            self.assertEqual(child.tag, "library")
+            self.assertItemsEqual(child.attrib.keys(), ["name", "lm", "l_addr", "l_ld"])
+
+    def libraries_svr4_has_correct_load_addr(self):
+        xml_root = self.get_libraries_svr4_xml()
+        for child in xml_root:
+            name = child.attrib.get("name")
+            if not name:
+                continue
+            load_addr = int(child.attrib.get("l_addr"), 16)
+            self.reset_test_sequence()
+            self.add_query_memory_region_packets(load_addr)
+            context = self.expect_gdbremote_sequence()
+            mem_region = self.parse_memory_region_packet(context)
+            self.assertEqual(load_addr, int(mem_region.get("start", 0), 16))
+            self.assertEqual(
+                os.path.realpath(name), os.path.realpath(mem_region.get("name", ""))
+            )
+
+    def get_pid(self):
+        self.reset_test_sequence()
+        self.add_process_info_collection_packets()
+        context = self.expect_gdbremote_sequence()
+        self.assertIsNotNone(context)
+        process_info = self.parse_process_info_response(context)
+        return int(process_info.get("pid"), 16)
+
+    def get_mapped_libs(self):
+        libs = []
+        exe_name = self.getBuildArtifact("a.out")
+        with open("/proc/{pid}/maps".format(pid=self.get_pid()), "r") as file:
+            for line in file:
+                lib = line.split()
+                if len(lib) < 6:
+                    continue
+                name = lib[5]
+                if exe_name == name or not os.path.exists(name) or name in libs:
+                    continue
+                libs.append(name)
+        return libs
+
+    def libraries_svr4_all_present(self):
+        xml_root = self.get_libraries_svr4_xml()
+        libraries_svr4_names = []
+        for child in xml_root:
+            name = child.attrib.get("name")
+            if not name:
+                continue
+            libraries_svr4_names.append(os.path.realpath(name))
+        libs = self.get_mapped_libs()
+        self.assertItemsEqual(libraries_svr4_names, libs)
+
+    @llgs_test
+    @skipUnlessPlatform(["linux", "android"])
+    def test_supports_libraries_svr4(self):
+        self.init_llgs_test()
+        self.build()
+        self.set_inferior_startup_launch()
+        self.assertTrue(self.has_libraries_svr4_support())
+
+    @llgs_test
+    @skipUnlessPlatform(["linux", "android"])
+    def test_libraries_svr4_well_formed(self):
+        self.init_llgs_test()
+        self.build()
+        self.set_inferior_startup_launch()
+        self.libraries_svr4_well_formed()
+
+    @llgs_test
+    @skipUnlessPlatform(["linux", "android"])
+    def test_libraries_svr4_load_addr(self):
+        self.init_llgs_test()
+        self.build()
+        self.set_inferior_startup_launch()
+        self.libraries_svr4_has_correct_load_addr()
+
+    @llgs_test
+    @skipUnlessPlatform(["linux", "android"])
+    def test_libraries_svr4_all_present(self):
+        self.init_llgs_test()
+        self.build()
+        self.set_inferior_startup_launch()
+        self.libraries_svr4_all_present()
Index: lldb/include/lldb/Host/common/NativeProcessProtocol.h
===================================================================
--- lldb/include/lldb/Host/common/NativeProcessProtocol.h
+++ lldb/include/lldb/Host/common/NativeProcessProtocol.h
@@ -32,6 +32,15 @@
 class MemoryRegionInfo;
 class ResumeActionList;
 
+struct SharedLibraryInfo {
+  std::string name;
+  lldb::addr_t link_map;
+  lldb::addr_t base_addr;
+  lldb::addr_t ld_addr;
+  bool main;
+  lldb::addr_t next;
+};
+
 // NativeProcessProtocol
 class NativeProcessProtocol {
 public:
@@ -86,6 +95,11 @@
 
   virtual lldb::addr_t GetSharedLibraryInfoAddress() = 0;
 
+  virtual Status
+  GetLoadedSharedLibraries(std::vector<SharedLibraryInfo> &library_list) {
+    return Status("Not implemented");
+  }
+
   virtual bool IsAlive() const;
 
   virtual size_t UpdateThreads() = 0;
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to