GDBServer: Expose target memory map and registers

Ugh, XML.
pull/3782/head
Stenzek 2 months ago
parent e2b78d4cab
commit 8c1a2022e4
No known key found for this signature in database

@ -35,6 +35,121 @@ static constexpr u32 MAX_FRAMED_PACKET_SIZE = MAX_PACKET_SIZE + 4;
static constexpr u32 MAX_MEMORY_READ_SIZE = MAX_PACKET_SIZE / 2;
static constexpr u32 MAX_WATCHPOINT_LENGTH = 256;
static constexpr std::string_view TARGET_XML = R"(<?xml version="1.0"?>
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<target version="1.0">
<architecture>mips:3000</architecture>
<feature name="org.gnu.gdb.mips.cpu">
<reg name="r0" bitsize="32" regnum="0"/>
<reg name="r1" bitsize="32"/>
<reg name="r2" bitsize="32"/>
<reg name="r3" bitsize="32"/>
<reg name="r4" bitsize="32"/>
<reg name="r5" bitsize="32"/>
<reg name="r6" bitsize="32"/>
<reg name="r7" bitsize="32"/>
<reg name="r8" bitsize="32"/>
<reg name="r9" bitsize="32"/>
<reg name="r10" bitsize="32"/>
<reg name="r11" bitsize="32"/>
<reg name="r12" bitsize="32"/>
<reg name="r13" bitsize="32"/>
<reg name="r14" bitsize="32"/>
<reg name="r15" bitsize="32"/>
<reg name="r16" bitsize="32"/>
<reg name="r17" bitsize="32"/>
<reg name="r18" bitsize="32"/>
<reg name="r19" bitsize="32"/>
<reg name="r20" bitsize="32"/>
<reg name="r21" bitsize="32"/>
<reg name="r22" bitsize="32"/>
<reg name="r23" bitsize="32"/>
<reg name="r24" bitsize="32"/>
<reg name="r25" bitsize="32"/>
<reg name="r26" bitsize="32"/>
<reg name="r27" bitsize="32"/>
<reg name="r28" bitsize="32"/>
<reg name="r29" bitsize="32"/>
<reg name="r30" bitsize="32"/>
<reg name="r31" bitsize="32"/>
<reg name="lo" bitsize="32" regnum="33"/>
<reg name="hi" bitsize="32" regnum="34"/>
<reg name="pc" bitsize="32" regnum="37"/>
</feature>
<feature name="org.gnu.gdb.mips.cp0">
<reg name="status" bitsize="32" regnum="32"/>
<reg name="badvaddr" bitsize="32" regnum="35"/>
<reg name="cause" bitsize="32" regnum="36"/>
</feature>
<feature name="org.gnu.gdb.mips.fpu">
<reg name="f0" bitsize="32" type="ieee_single" regnum="38"/>
<reg name="f1" bitsize="32" type="ieee_single"/>
<reg name="f2" bitsize="32" type="ieee_single"/>
<reg name="f3" bitsize="32" type="ieee_single"/>
<reg name="f4" bitsize="32" type="ieee_single"/>
<reg name="f5" bitsize="32" type="ieee_single"/>
<reg name="f6" bitsize="32" type="ieee_single"/>
<reg name="f7" bitsize="32" type="ieee_single"/>
<reg name="f8" bitsize="32" type="ieee_single"/>
<reg name="f9" bitsize="32" type="ieee_single"/>
<reg name="f10" bitsize="32" type="ieee_single"/>
<reg name="f11" bitsize="32" type="ieee_single"/>
<reg name="f12" bitsize="32" type="ieee_single"/>
<reg name="f13" bitsize="32" type="ieee_single"/>
<reg name="f14" bitsize="32" type="ieee_single"/>
<reg name="f15" bitsize="32" type="ieee_single"/>
<reg name="f16" bitsize="32" type="ieee_single"/>
<reg name="f17" bitsize="32" type="ieee_single"/>
<reg name="f18" bitsize="32" type="ieee_single"/>
<reg name="f19" bitsize="32" type="ieee_single"/>
<reg name="f20" bitsize="32" type="ieee_single"/>
<reg name="f21" bitsize="32" type="ieee_single"/>
<reg name="f22" bitsize="32" type="ieee_single"/>
<reg name="f23" bitsize="32" type="ieee_single"/>
<reg name="f24" bitsize="32" type="ieee_single"/>
<reg name="f25" bitsize="32" type="ieee_single"/>
<reg name="f26" bitsize="32" type="ieee_single"/>
<reg name="f27" bitsize="32" type="ieee_single"/>
<reg name="f28" bitsize="32" type="ieee_single"/>
<reg name="f29" bitsize="32" type="ieee_single"/>
<reg name="f30" bitsize="32" type="ieee_single"/>
<reg name="f31" bitsize="32" type="ieee_single"/>
<reg name="fcsr" bitsize="32" group="float"/>
<reg name="fir" bitsize="32" group="float"/>
</feature>
<feature name="org.duckstation.gdb.mips">
<reg name="unused" bitsize="32" regnum="72" save-restore="no"/>
</feature>
</target>
)";
static constexpr std::string_view MEMORY_MAP_XML = R"(<?xml version="1.0"?>
<!DOCTYPE memory-map PUBLIC "+//IDN gnu.org//DTD GDB Memory Map V1.0//EN"
"http://sourceware.org/gdb/gdb-memory-map.dtd">
<memory-map>
<memory type="ram" start="0x00000000" length="0x00800000"/>
<memory type="rom" start="0x1f000000" length="0x00800000"/>
<memory type="ram" start="0x1f800000" length="0x00000400"/>
<memory type="rom" start="0x1fc00000" length="0x00080000"/>
<memory type="ram" start="0x80000000" length="0x00800000"/>
<memory type="rom" start="0x9f000000" length="0x00800000"/>
<memory type="ram" start="0x9f800000" length="0x00000400"/>
<memory type="rom" start="0x9fc00000" length="0x00080000"/>
<memory type="ram" start="0xa0000000" length="0x00800000"/>
<memory type="rom" start="0xbf000000" length="0x00800000"/>
<memory type="ram" start="0xbf800000" length="0x00000400"/>
<memory type="rom" start="0xbfc00000" length="0x00080000"/>
<memory type="ram" start="0xffffffff80000000" length="0x00800000"/>
<memory type="rom" start="0xffffffff9f000000" length="0x00800000"/>
<memory type="ram" start="0xffffffff9f800000" length="0x00000400"/>
<memory type="rom" start="0xffffffff9fc00000" length="0x00080000"/>
<memory type="ram" start="0xffffffffa0000000" length="0x00800000"/>
<memory type="rom" start="0xffffffffbf000000" length="0x00800000"/>
<memory type="ram" start="0xffffffffbf800000" length="0x00000400"/>
<memory type="rom" start="0xffffffffbfc00000" length="0x00080000"/>
</memory-map>
)";
namespace {
enum class GDBBreakpointType : u8
@ -122,10 +237,14 @@ template<bool add_breakpoint>
static bool Cmd$z(ClientSocket* client, std::string_view data);
static bool Cmd$vMustReplyEmpty(ClientSocket* client, std::string_view data);
static bool Cmd$qSupported(ClientSocket* client, std::string_view data);
static bool Cmd$qXferFeaturesRead(ClientSocket* client, std::string_view data);
static bool Cmd$qXferMemoryMapRead(ClientSocket* client, std::string_view data);
static bool ParseOptionalAddress(std::string_view data, std::optional<VirtualMemoryAddress>* address);
static bool ParseSignalAndOptionalAddress(std::string_view data, u8* signal,
std::optional<VirtualMemoryAddress>* address);
static bool SendXferRead(ClientSocket* client, std::string_view data, std::string_view expected_annex,
std::string_view contents);
static void InvalidateMemoryWrite(VirtualMemoryAddress address, u32 length);
static CPU::BreakpointCallbackAction OnBreakpointHit(CPU::BreakpointType type, VirtualMemoryAddress pc,
VirtualMemoryAddress address);
@ -200,6 +319,8 @@ static constexpr std::pair<std::string_view, bool (*)(ClientSocket*, std::string
{"z", Cmd$z<false>},
{"Z", Cmd$z<true>},
{"vMustReplyEmpty", Cmd$vMustReplyEmpty},
{"qXfer:features:read:", Cmd$qXferFeaturesRead},
{"qXfer:memory-map:read:", Cmd$qXferMemoryMapRead},
{"qSupported", Cmd$qSupported},
};
@ -552,7 +673,63 @@ bool GDBServer::Cmd$vMustReplyEmpty(ClientSocket* client, std::string_view data)
bool GDBServer::Cmd$qSupported(ClientSocket* client, std::string_view data)
{
client->SendReplyWithAck(TinyString::from_format("PacketSize={:x}", MAX_PACKET_SIZE));
client->SendReplyWithAck(
TinyString::from_format("PacketSize={:x};qXfer:features:read+;qXfer:memory-map:read+", MAX_PACKET_SIZE));
return true;
}
bool GDBServer::Cmd$qXferFeaturesRead(ClientSocket* client, std::string_view data)
{
return SendXferRead(client, data, "target.xml", TARGET_XML);
}
bool GDBServer::Cmd$qXferMemoryMapRead(ClientSocket* client, std::string_view data)
{
return SendXferRead(client, data, {}, MEMORY_MAP_XML);
}
bool GDBServer::SendXferRead(ClientSocket* client, std::string_view data, std::string_view expected_annex,
std::string_view contents)
{
const size_t annex_end = data.find(':');
if (annex_end == std::string_view::npos)
{
ERROR_LOG("Invalid qXfer read packet: {}", data);
client->SendReplyWithAck("E01");
return true;
}
if (data.substr(0, annex_end) != expected_annex)
{
client->SendReplyWithAck();
return true;
}
std::string_view caret = data.substr(annex_end + 1);
std::optional<u64> offset;
std::optional<u32> length;
if (!(offset = StringUtil::FromChars<u64>(caret, 16, &caret)).has_value() || caret.empty() || caret[0] != ',' ||
!(length = StringUtil::FromChars<u32>(caret.substr(1), 16, &caret)).has_value() || !caret.empty() ||
length.value() == 0)
{
ERROR_LOG("Invalid qXfer read packet: {}", data);
client->SendReplyWithAck("E01");
return true;
}
if (offset.value() >= contents.size())
{
client->SendReplyWithAck("l");
return true;
}
const size_t remaining = contents.size() - static_cast<size_t>(offset.value());
const size_t transfer_size = std::min<size_t>({length.value(), MAX_PACKET_SIZE - 1, remaining});
SmallString reply;
reply.push_back((transfer_size < remaining) ? 'm' : 'l');
reply.append(contents.substr(static_cast<size_t>(offset.value()), transfer_size));
client->SendReplyWithAck(reply);
return true;
}

Loading…
Cancel
Save