Skip to content

release/20.x: [lldb] Add support for gdb-style 'x' packet (#124733) #125653

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions lldb/packages/Python/lldbsuite/test/gdbclientutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ def respond(self, packet):
if packet[0] == "m":
addr, length = [int(x, 16) for x in packet[1:].split(",")]
return self.readMemory(addr, length)
if packet[0] == "x":
addr, length = [int(x, 16) for x in packet[1:].split(",")]
return self.x(addr, length)
if packet[0] == "M":
location, encoded_data = packet[1:].split(":")
addr, length = [int(x, 16) for x in location.split(",")]
Expand Down Expand Up @@ -267,6 +270,9 @@ def writeRegister(self, register, value_hex):
def readMemory(self, addr, length):
return "00" * length

def x(self, addr, length):
return ""

def writeMemory(self, addr, data_hex):
return "OK"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,6 @@ void GDBRemoteCommunicationClient::ResetDiscoverableSettings(bool did_exec) {
m_supports_vCont_s = eLazyBoolCalculate;
m_supports_vCont_S = eLazyBoolCalculate;
m_supports_p = eLazyBoolCalculate;
m_supports_x = eLazyBoolCalculate;
m_supports_QSaveRegisterState = eLazyBoolCalculate;
m_qHostInfo_is_valid = eLazyBoolCalculate;
m_curr_pid_is_valid = eLazyBoolCalculate;
Expand All @@ -295,6 +294,7 @@ void GDBRemoteCommunicationClient::ResetDiscoverableSettings(bool did_exec) {
m_supports_qXfer_siginfo_read = eLazyBoolCalculate;
m_supports_augmented_libraries_svr4_read = eLazyBoolCalculate;
m_uses_native_signals = eLazyBoolCalculate;
m_x_packet_state.reset();
m_supports_qProcessInfoPID = true;
m_supports_qfProcessInfo = true;
m_supports_qUserName = true;
Expand Down Expand Up @@ -348,6 +348,7 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() {
m_supports_memory_tagging = eLazyBoolNo;
m_supports_qSaveCore = eLazyBoolNo;
m_uses_native_signals = eLazyBoolNo;
m_x_packet_state.reset();

m_max_packet_size = UINT64_MAX; // It's supposed to always be there, but if
// not, we assume no limit
Expand Down Expand Up @@ -401,6 +402,8 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() {
m_supports_qSaveCore = eLazyBoolYes;
else if (x == "native-signals+")
m_uses_native_signals = eLazyBoolYes;
else if (x == "binary-upload+")
m_x_packet_state = xPacketState::Prefixed;
// Look for a list of compressions in the features list e.g.
// qXfer:features:read+;PacketSize=20000;qEcho+;SupportedCompressions=zlib-
// deflate,lzma
Expand Down Expand Up @@ -715,19 +718,20 @@ Status GDBRemoteCommunicationClient::WriteMemoryTags(
return status;
}

bool GDBRemoteCommunicationClient::GetxPacketSupported() {
if (m_supports_x == eLazyBoolCalculate) {
GDBRemoteCommunicationClient::xPacketState
GDBRemoteCommunicationClient::GetxPacketState() {
if (!m_x_packet_state)
GetRemoteQSupported();
if (!m_x_packet_state) {
StringExtractorGDBRemote response;
m_supports_x = eLazyBoolNo;
char packet[256];
snprintf(packet, sizeof(packet), "x0,0");
if (SendPacketAndWaitForResponse(packet, response) ==
m_x_packet_state = xPacketState::Unimplemented;
if (SendPacketAndWaitForResponse("x0,0", response) ==
PacketResult::Success) {
if (response.IsOKResponse())
m_supports_x = eLazyBoolYes;
m_x_packet_state = xPacketState::Bare;
}
}
return m_supports_x;
return *m_x_packet_state;
}

lldb::pid_t GDBRemoteCommunicationClient::GetCurrentProcessID(bool allow_lazy) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,14 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {

bool GetpPacketSupported(lldb::tid_t tid);

bool GetxPacketSupported();
enum class xPacketState {
Unimplemented,
Prefixed, // Successful responses start with a 'b' character. This is the
// style used by GDB.
Bare, // No prefix, packets starts with the memory being read. This is
// LLDB's original style.
};
xPacketState GetxPacketState();

bool GetVAttachOrWaitSupported();

Expand Down Expand Up @@ -541,7 +548,6 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
LazyBool m_attach_or_wait_reply = eLazyBoolCalculate;
LazyBool m_prepare_for_reg_writing_reply = eLazyBoolCalculate;
LazyBool m_supports_p = eLazyBoolCalculate;
LazyBool m_supports_x = eLazyBoolCalculate;
LazyBool m_avoid_g_packets = eLazyBoolCalculate;
LazyBool m_supports_QSaveRegisterState = eLazyBoolCalculate;
LazyBool m_supports_qXfer_auxv_read = eLazyBoolCalculate;
Expand All @@ -561,6 +567,7 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
LazyBool m_supports_memory_tagging = eLazyBoolCalculate;
LazyBool m_supports_qSaveCore = eLazyBoolCalculate;
LazyBool m_uses_native_signals = eLazyBoolCalculate;
std::optional<xPacketState> m_x_packet_state;

bool m_supports_qProcessInfoPID : 1, m_supports_qfProcessInfo : 1,
m_supports_qUserName : 1, m_supports_qGroupName : 1,
Expand Down
36 changes: 23 additions & 13 deletions lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2609,11 +2609,15 @@ void ProcessGDBRemote::WillPublicStop() {
// Process Memory
size_t ProcessGDBRemote::DoReadMemory(addr_t addr, void *buf, size_t size,
Status &error) {
using xPacketState = GDBRemoteCommunicationClient::xPacketState;

GetMaxMemorySize();
bool binary_memory_read = m_gdb_comm.GetxPacketSupported();
xPacketState x_state = m_gdb_comm.GetxPacketState();

// M and m packets take 2 bytes for 1 byte of memory
size_t max_memory_size =
binary_memory_read ? m_max_memory_size : m_max_memory_size / 2;
size_t max_memory_size = x_state != xPacketState::Unimplemented
? m_max_memory_size
: m_max_memory_size / 2;
if (size > max_memory_size) {
// Keep memory read sizes down to a sane limit. This function will be
// called multiple times in order to complete the task by
Expand All @@ -2624,8 +2628,8 @@ size_t ProcessGDBRemote::DoReadMemory(addr_t addr, void *buf, size_t size,
char packet[64];
int packet_len;
packet_len = ::snprintf(packet, sizeof(packet), "%c%" PRIx64 ",%" PRIx64,
binary_memory_read ? 'x' : 'm', (uint64_t)addr,
(uint64_t)size);
x_state != xPacketState::Unimplemented ? 'x' : 'm',
(uint64_t)addr, (uint64_t)size);
assert(packet_len + 1 < (int)sizeof(packet));
UNUSED_IF_ASSERT_DISABLED(packet_len);
StringExtractorGDBRemote response;
Expand All @@ -2634,19 +2638,25 @@ size_t ProcessGDBRemote::DoReadMemory(addr_t addr, void *buf, size_t size,
GDBRemoteCommunication::PacketResult::Success) {
if (response.IsNormalResponse()) {
error.Clear();
if (binary_memory_read) {
if (x_state != xPacketState::Unimplemented) {
// The lower level GDBRemoteCommunication packet receive layer has
// already de-quoted any 0x7d character escaping that was present in
// the packet

size_t data_received_size = response.GetBytesLeft();
if (data_received_size > size) {
// Don't write past the end of BUF if the remote debug server gave us
// too much data for some reason.
data_received_size = size;
llvm::StringRef data_received = response.GetStringRef();
if (x_state == xPacketState::Prefixed &&
!data_received.consume_front("b")) {
error = Status::FromErrorStringWithFormatv(
"unexpected response to GDB server memory read packet '{0}': "
"'{1}'",
packet, data_received);
return 0;
}
memcpy(buf, response.GetStringRef().data(), data_received_size);
return data_received_size;
// Don't write past the end of BUF if the remote debug server gave us
// too much data for some reason.
size_t memcpy_size = std::min(size, data_received.size());
memcpy(buf, data_received.data(), memcpy_size);
return memcpy_size;
} else {
return response.GetHexBytes(
llvm::MutableArrayRef<uint8_t>((uint8_t *)buf, size), '\xdd');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import lldb
from lldbsuite.support import seven
from lldbsuite.test.lldbtest import *
from lldbsuite.test.decorators import *
from lldbsuite.test.gdbclientutils import *
from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase


class TestReadMemory(GDBRemoteTestBase):
def test_x_with_prefix(self):
class MyResponder(MockGDBServerResponder):
def qSupported(self, client_features):
# binary-upload+ indicates we use the gdb style of x packets
return super().qSupported(client_features) + ";binary-upload+"

def x(self, addr, length):
return "bfoobar" if addr == 0x1000 else "E01"

self.server.responder = MyResponder()
target = self.dbg.CreateTargetWithFileAndTargetTriple("", "x86_64-pc-linux")
process = self.connect(target)

error = lldb.SBError()
self.assertEqual(b"foobar", process.ReadMemory(0x1000, 10, error))

def test_x_bare(self):
class MyResponder(MockGDBServerResponder):
def x(self, addr, length):
# The OK response indicates we use the old lldb style.
if addr == 0 and length == 0:
return "OK"
return "foobar" if addr == 0x1000 else "E01"

self.server.responder = MyResponder()
target = self.dbg.CreateTargetWithFileAndTargetTriple("", "x86_64-pc-linux")
process = self.connect(target)

error = lldb.SBError()
self.assertEqual(b"foobar", process.ReadMemory(0x1000, 10, error))

def test_m_fallback(self):
class MyResponder(MockGDBServerResponder):
def x(self, addr, length):
# If `x` is unsupported, we should fall back to `m`.
return ""

def readMemory(self, addr, length):
return seven.hexlify("foobar") if addr == 0x1000 else "E01"

self.server.responder = MyResponder()
target = self.dbg.CreateTargetWithFileAndTargetTriple("", "x86_64-pc-linux")
process = self.connect(target)

error = lldb.SBError()
self.assertEqual(b"foobar", process.ReadMemory(0x1000, 10, error))
Loading