Implement xfer:libraries-svr4:read packet

Summary:
This is the fourth patch to improve module loading in a series that started here (where I explain the motivation and solution): D62499

Implement the `xfer:libraries-svr4` packet by adding a new function that generates the list and then in Handle_xfer I generate the XML for it. The XML is really simple so I'm just using string concatenation because I believe it's more readable than having to deal with a DOM api.

Reviewers: clayborg, xiaobai, labath

Reviewed By: labath

Subscribers: emaste, mgorny, srhines, krytarowski, lldb-commits

Tags: #lldb

Differential Revision: https://reviews.llvm.org/D62502

llvm-svn: 363707
This commit is contained in:
Antonio Afonso 2019-06-18 17:51:56 +00:00
parent 5cf216c9a7
commit fda83c9b0b
16 changed files with 362 additions and 4 deletions

View File

@ -32,6 +32,14 @@ namespace lldb_private {
class MemoryRegionInfo; class MemoryRegionInfo;
class ResumeActionList; class ResumeActionList;
struct SVR4LibraryInfo {
std::string name;
lldb::addr_t link_map;
lldb::addr_t base_addr;
lldb::addr_t ld_addr;
lldb::addr_t next;
};
// NativeProcessProtocol // NativeProcessProtocol
class NativeProcessProtocol { class NativeProcessProtocol {
public: public:
@ -86,6 +94,12 @@ public:
virtual lldb::addr_t GetSharedLibraryInfoAddress() = 0; virtual lldb::addr_t GetSharedLibraryInfoAddress() = 0;
virtual llvm::Expected<std::vector<SVR4LibraryInfo>>
GetLoadedSVR4Libraries() {
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Not implemented");
}
virtual bool IsAlive() const; virtual bool IsAlive() const;
virtual size_t UpdateThreads() = 0; virtual size_t UpdateThreads() = 0;

View File

@ -513,7 +513,8 @@ class GdbRemoteTestCaseBase(TestBase):
self, self,
inferior_args=None, inferior_args=None,
inferior_sleep_seconds=3, inferior_sleep_seconds=3,
inferior_exe_path=None): inferior_exe_path=None,
inferior_env=None):
"""Prep the debug monitor, the inferior, and the expected packet stream. """Prep the debug monitor, the inferior, and the expected packet stream.
Handle the separate cases of using the debug monitor in attach-to-inferior mode Handle the separate cases of using the debug monitor in attach-to-inferior mode
@ -576,6 +577,9 @@ class GdbRemoteTestCaseBase(TestBase):
# Build the expected protocol stream # Build the expected protocol stream
self.add_no_ack_remote_stream() self.add_no_ack_remote_stream()
if inferior_env:
for name, value in inferior_env.items():
self.add_set_environment_packets(name, value)
if self._inferior_startup == self._STARTUP_LAUNCH: if self._inferior_startup == self._STARTUP_LAUNCH:
self.add_verified_launch_packets(launch_args) self.add_verified_launch_packets(launch_args)
@ -656,6 +660,12 @@ class GdbRemoteTestCaseBase(TestBase):
{"direction": "send", "regex": r"^\$(.+)#[0-9a-fA-F]{2}$", "capture": {1: "process_info_raw"}}], {"direction": "send", "regex": r"^\$(.+)#[0-9a-fA-F]{2}$", "capture": {1: "process_info_raw"}}],
True) True)
def add_set_environment_packets(self, name, value):
self.test_sequence.add_log_lines(
["read packet: $QEnvironment:" + name + "=" + value + "#00",
"send packet: $OK#00",
], True)
_KNOWN_PROCESS_INFO_KEYS = [ _KNOWN_PROCESS_INFO_KEYS = [
"pid", "pid",
"parent-pid", "parent-pid",
@ -816,6 +826,7 @@ class GdbRemoteTestCaseBase(TestBase):
"error"]) "error"])
self.assertIsNotNone(val) 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 the dictionary of key-value pairs for the memory region.
return mem_region_dict return mem_region_dict
@ -1000,6 +1011,22 @@ class GdbRemoteTestCaseBase(TestBase):
return context return context
def continue_process_and_wait_for_stop(self):
self.test_sequence.add_log_lines(
[
"read packet: $vCont;c#a8",
{
"direction": "send",
"regex": r"^\$T([0-9a-fA-F]{2})(.*)#[0-9a-fA-F]{2}$",
"capture": {1: "stop_signo", 2: "stop_key_val_text"},
},
],
True,
)
context = self.expect_gdbremote_sequence()
self.assertIsNotNone(context)
return self.parse_interrupt_packets(context)
def select_modifiable_register(self, reg_infos): def select_modifiable_register(self, reg_infos):
"""Find a register that can be read/written freely.""" """Find a register that can be read/written freely."""
PREFERRED_REGISTER_NAMES = set(["rax", ]) PREFERRED_REGISTER_NAMES = set(["rax", ])

View File

@ -0,0 +1,17 @@
LEVEL = ../../../make
LIB_PREFIX := svr4lib
LD_EXTRAS := -L. -l$(LIB_PREFIX)_a -l$(LIB_PREFIX)_b\"
CXX_SOURCES := main.cpp
USE_LIBDL := 1
MAKE_DSYM := NO
include $(LEVEL)/Makefile.rules
a.out: $(LIB_PREFIX)_a $(LIB_PREFIX)_b_quote
svr4lib_%:
$(MAKE) VPATH=$(SRCDIR) -I $(SRCDIR) -f "$(SRCDIR)/$(LIB_PREFIX)_$*.mk"
clean::
$(MAKE) -f $(SRCDIR)/$(LIB_PREFIX)_a.mk clean

View File

@ -0,0 +1,127 @@
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 setup_test(self):
self.init_llgs_test()
self.build()
self.set_inferior_startup_launch()
env = {}
env[self.dylibPath] = self.getBuildDir()
self.prep_debug_monitor_and_inferior(inferior_env=env)
self.continue_process_and_wait_for_stop()
def has_libraries_svr4_support(self):
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")
# Grab the libraries-svr4 data.
self.reset_test_sequence()
self.test_sequence.add_log_lines(
[
"read packet: $qXfer:libraries-svr4:read::0,ffff:#00",
{
"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 libraries_svr4_libs_present(self):
xml_root = self.get_libraries_svr4_xml()
libraries_svr4_names = []
for child in xml_root:
name = child.attrib.get("name")
libraries_svr4_names.append(os.path.realpath(name))
expected_libs = ["libsvr4lib_a.so", 'libsvr4lib_b".so']
for lib in expected_libs:
self.assertIn(self.getBuildDir() + "/" + lib, libraries_svr4_names)
@llgs_test
@skipUnlessPlatform(["linux", "android", "netbsd"])
def test_supports_libraries_svr4(self):
self.setup_test()
self.assertTrue(self.has_libraries_svr4_support())
@llgs_test
@skipUnlessPlatform(["linux", "android", "netbsd"])
def test_libraries_svr4_well_formed(self):
self.setup_test()
self.libraries_svr4_well_formed()
@llgs_test
@skipUnlessPlatform(["linux", "android", "netbsd"])
def test_libraries_svr4_load_addr(self):
self.setup_test()
self.libraries_svr4_has_correct_load_addr()
@llgs_test
@skipUnlessPlatform(["linux", "android", "netbsd"])
def test_libraries_svr4_libs_present(self):
self.setup_test()
self.libraries_svr4_libs_present()

View File

@ -0,0 +1,15 @@
//===-- main.cpp ------------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
int main(int argc, char **argv) {
// Perform a null pointer access.
int *const null_int_ptr = nullptr;
*null_int_ptr = 0xDEAD;
return 0;
}

View File

@ -0,0 +1,9 @@
//===-- main.cpp ------------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
int svr4lib_a() { return 42; }

View File

@ -0,0 +1,9 @@
LEVEL = ../../../make
LIB_PREFIX := svr4lib
DYLIB_NAME := $(LIB_PREFIX)_a
DYLIB_CXX_SOURCES := $(LIB_PREFIX)_a.cpp
DYLIB_ONLY := YES
include $(LEVEL)/Makefile.rules

View File

@ -0,0 +1,9 @@
//===-- main.cpp ------------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
int svr4lib_b_quote() { return 42; }

View File

@ -0,0 +1,9 @@
LEVEL = ../../../make
LIB_PREFIX := svr4lib
DYLIB_NAME := $(LIB_PREFIX)_b\"
DYLIB_CXX_SOURCES := $(LIB_PREFIX)_b_quote.cpp
DYLIB_ONLY := YES
include $(LEVEL)/Makefile.rules

View File

@ -2076,4 +2076,4 @@ Status NativeProcessLinux::StopProcessorTracingOnThread(lldb::user_id_t traceid,
m_processor_trace_monitor.erase(iter); m_processor_trace_monitor.erase(iter);
return error; return error;
} }

View File

@ -9,12 +9,12 @@
#ifndef liblldb_NativeProcessNetBSD_H_ #ifndef liblldb_NativeProcessNetBSD_H_
#define liblldb_NativeProcessNetBSD_H_ #define liblldb_NativeProcessNetBSD_H_
#include "Plugins/Process/POSIX/NativeProcessELF.h"
#include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Target/MemoryRegionInfo.h"
#include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/ArchSpec.h"
#include "lldb/Utility/FileSpec.h" #include "lldb/Utility/FileSpec.h"
#include "NativeThreadNetBSD.h" #include "NativeThreadNetBSD.h"
#include "lldb/Host/common/NativeProcessProtocol.h"
namespace lldb_private { namespace lldb_private {
namespace process_netbsd { namespace process_netbsd {
@ -25,7 +25,7 @@ namespace process_netbsd {
/// for debugging. /// for debugging.
/// ///
/// Changes in the inferior process state are broadcasted. /// Changes in the inferior process state are broadcasted.
class NativeProcessNetBSD : public NativeProcessProtocol { class NativeProcessNetBSD : public NativeProcessELF {
public: public:
class Factory : public NativeProcessProtocol::Factory { class Factory : public NativeProcessProtocol::Factory {
public: public:

View File

@ -107,4 +107,73 @@ lldb::addr_t NativeProcessELF::GetELFImageInfoAddress() {
return LLDB_INVALID_ADDRESS; return LLDB_INVALID_ADDRESS;
} }
template <typename T>
llvm::Expected<SVR4LibraryInfo>
NativeProcessELF::ReadSVR4LibraryInfo(lldb::addr_t link_map_addr) {
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.ToError();
char name_buffer[PATH_MAX];
error = ReadMemory(link_map.l_name, &name_buffer, sizeof(name_buffer),
bytes_read);
if (!error.Success())
return error.ToError();
name_buffer[PATH_MAX - 1] = '\0';
SVR4LibraryInfo info;
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;
return info;
}
llvm::Expected<std::vector<SVR4LibraryInfo>>
NativeProcessELF::GetLoadedSVR4Libraries() {
// Address of DT_DEBUG.d_ptr which points to r_debug
lldb::addr_t info_address = GetSharedLibraryInfoAddress();
if (info_address == LLDB_INVALID_ADDRESS)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Invalid shared library info address");
// Address of r_debug
lldb::addr_t address = 0;
size_t bytes_read;
auto status =
ReadMemory(info_address, &address, GetAddressByteSize(), bytes_read);
if (!status.Success())
return status.ToError();
if (address == 0)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Invalid r_debug address");
// Read r_debug.r_map
lldb::addr_t link_map = 0;
status = ReadMemory(address + GetAddressByteSize(), &link_map,
GetAddressByteSize(), bytes_read);
if (!status.Success())
return status.ToError();
if (address == 0)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Invalid link_map address");
std::vector<SVR4LibraryInfo> library_list;
while (link_map) {
llvm::Expected<SVR4LibraryInfo> info =
GetAddressByteSize() == 8 ? ReadSVR4LibraryInfo<uint64_t>(link_map)
: ReadSVR4LibraryInfo<uint32_t>(link_map);
if (!info)
return info.takeError();
if (!info->name.empty() && info->base_addr != 0)
library_list.push_back(*info);
link_map = info->next;
}
return library_list;
}
} // namespace lldb_private } // namespace lldb_private

View File

@ -37,6 +37,13 @@ protected:
template <typename ELF_EHDR, typename ELF_PHDR, typename ELF_DYN> template <typename ELF_EHDR, typename ELF_PHDR, typename ELF_DYN>
lldb::addr_t GetELFImageInfoAddress(); lldb::addr_t GetELFImageInfoAddress();
llvm::Expected<std::vector<SVR4LibraryInfo>>
GetLoadedSVR4Libraries() override;
template <typename T>
llvm::Expected<SVR4LibraryInfo>
ReadSVR4LibraryInfo(lldb::addr_t link_map_addr);
std::unique_ptr<AuxVector> m_aux_vector; std::unique_ptr<AuxVector> m_aux_vector;
llvm::Optional<lldb::addr_t> m_shared_library_info_addr; llvm::Optional<lldb::addr_t> m_shared_library_info_addr;
}; };

View File

@ -825,6 +825,7 @@ GDBRemoteCommunicationServerCommon::Handle_qSupported(
#if defined(__linux__) || defined(__NetBSD__) #if defined(__linux__) || defined(__NetBSD__)
response.PutCString(";QPassSignals+"); response.PutCString(";QPassSignals+");
response.PutCString(";qXfer:auxv:read+"); response.PutCString(";qXfer:auxv:read+");
response.PutCString(";qXfer:libraries-svr4:read+");
#endif #endif
return SendPacketNoLock(response.GetString()); return SendPacketNoLock(response.GetString());

View File

@ -2765,6 +2765,24 @@ GDBRemoteCommunicationServerLLGS::ReadXferObject(llvm::StringRef object,
return std::move(*buffer_or_error); return std::move(*buffer_or_error);
} }
if (object == "libraries-svr4") {
auto library_list = m_debugged_process_up->GetLoadedSVR4Libraries();
if (!library_list)
return library_list.takeError();
StreamString response;
response.Printf("<library-list-svr4 version=\"1.0\">");
for (auto const &library : *library_list) {
response.Printf("<library name=\"%s\" ",
XMLEncodeAttributeValue(library.name.c_str()).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 MemoryBuffer::getMemBufferCopy(response.GetString(), __FUNCTION__);
}
return llvm::make_error<PacketUnimplementedError>( return llvm::make_error<PacketUnimplementedError>(
"Xfer object not supported"); "Xfer object not supported");
} }
@ -3283,3 +3301,28 @@ GDBRemoteCommunicationServerLLGS::FindModuleFile(const std::string &module_path,
return GDBRemoteCommunicationServerCommon::FindModuleFile(module_path, arch); return GDBRemoteCommunicationServerCommon::FindModuleFile(module_path, arch);
} }
std::string GDBRemoteCommunicationServerLLGS::XMLEncodeAttributeValue(
llvm::StringRef value) {
std::string result;
for (const char &c : value) {
switch (c) {
case '\'':
result += "&apos;";
break;
case '"':
result += "&quot;";
break;
case '<':
result += "&lt;";
break;
case '>':
result += "&gt;";
break;
default:
result += c;
break;
}
}
return result;
}

View File

@ -196,6 +196,8 @@ protected:
llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>> llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
ReadXferObject(llvm::StringRef object, llvm::StringRef annex); ReadXferObject(llvm::StringRef object, llvm::StringRef annex);
static std::string XMLEncodeAttributeValue(llvm::StringRef value);
private: private:
void HandleInferiorState_Exited(NativeProcessProtocol *process); void HandleInferiorState_Exited(NativeProcessProtocol *process);