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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits