From 22bce9f680615bc13845d670a1fb4ffc6fc2ddc1 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 17 Jul 2021 06:27:40 -0700 Subject: [PATCH] dmnt: refactor/add support for getting process list in gdb --- stratosphere/dmnt.gen2/dmnt.gen2.json | 9 +- .../source/dmnt2_gdb_packet_parser.hpp | 42 ---- .../dmnt.gen2/source/dmnt2_gdb_server.cpp | 72 ++----- ...t_parser.cpp => dmnt2_gdb_server_impl.cpp} | 181 +++++++++++++++++- .../source/dmnt2_gdb_server_impl.hpp | 58 ++++++ 5 files changed, 255 insertions(+), 107 deletions(-) delete mode 100644 stratosphere/dmnt.gen2/source/dmnt2_gdb_packet_parser.hpp rename stratosphere/dmnt.gen2/source/{dmnt2_gdb_packet_parser.cpp => dmnt2_gdb_server_impl.cpp} (75%) create mode 100644 stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.hpp diff --git a/stratosphere/dmnt.gen2/dmnt.gen2.json b/stratosphere/dmnt.gen2/dmnt.gen2.json index b6dc58e4e..cd3fa9bd0 100644 --- a/stratosphere/dmnt.gen2/dmnt.gen2.json +++ b/stratosphere/dmnt.gen2/dmnt.gen2.json @@ -99,5 +99,12 @@ }, { "type": "handle_table_size", "value": 0 - }] + }, + { + "type": "debug_flags", + "value": { + "allow_debug": false, + "force_debug": true + } + }] } \ No newline at end of file diff --git a/stratosphere/dmnt.gen2/source/dmnt2_gdb_packet_parser.hpp b/stratosphere/dmnt.gen2/source/dmnt2_gdb_packet_parser.hpp deleted file mode 100644 index 6bd1a6d2a..000000000 --- a/stratosphere/dmnt.gen2/source/dmnt2_gdb_packet_parser.hpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once -#include -#include "dmnt2_gdb_packet_io.hpp" - -namespace ams::dmnt { - - class GdbPacketParser { - private: - char *m_receive_packet; - char *m_reply_packet; - char m_buffer[GdbPacketBufferSize / 2]; - bool m_64_bit; - bool m_no_ack; - public: - GdbPacketParser(char *recv, char *reply, bool is_64_bit) : m_receive_packet(recv), m_reply_packet(reply), m_64_bit(is_64_bit), m_no_ack(false) { /* ... */ } - - void Process(); - - bool Is64Bit() const { return m_64_bit; } - private: - void q(); - - void qSupported(); - void qXferFeaturesRead(); - }; - -} \ No newline at end of file diff --git a/stratosphere/dmnt.gen2/source/dmnt2_gdb_server.cpp b/stratosphere/dmnt.gen2/source/dmnt2_gdb_server.cpp index 8758d9f54..0cca175b1 100644 --- a/stratosphere/dmnt.gen2/source/dmnt2_gdb_server.cpp +++ b/stratosphere/dmnt.gen2/source/dmnt2_gdb_server.cpp @@ -14,10 +14,9 @@ * along with this program. If not, see . */ #include -#include "dmnt2_gdb_packet_io.hpp" -#include "dmnt2_gdb_packet_parser.hpp" -#include "dmnt2_gdb_server.hpp" #include "dmnt2_debug_log.hpp" +#include "dmnt2_gdb_server.hpp" +#include "dmnt2_gdb_server_impl.hpp" namespace ams::dmnt { @@ -25,43 +24,13 @@ namespace ams::dmnt { constexpr size_t ServerThreadStackSize = util::AlignUp(4 * GdbPacketBufferSize + os::MemoryPageSize, os::ThreadStackAlignment); - alignas(os::ThreadStackAlignment) constinit u8 g_server_thread32_stack[ServerThreadStackSize]; - alignas(os::ThreadStackAlignment) constinit u8 g_server_thread64_stack[ServerThreadStackSize]; + alignas(os::ThreadStackAlignment) constinit u8 g_server_thread_stack[ServerThreadStackSize]; - constinit os::ThreadType g_server_thread32; - constinit os::ThreadType g_server_thread64; + constinit os::ThreadType g_server_thread; - constinit util::TypedStorage g_session32; - constinit util::TypedStorage g_session64; + constinit util::TypedStorage g_gdb_server; - - void ProcessForHtcsSession(HtcsSession *session, bool is_64_bit) { - /* Create packet io handler. */ - GdbPacketIo packet_io; - - /* Process packets. */ - while (session->IsValid()) { - /* Receive a packet. */ - bool do_break = false; - char recv_buf[GdbPacketBufferSize]; - char *packet = packet_io.ReceivePacket(std::addressof(do_break), recv_buf, sizeof(recv_buf), session); - - if (!do_break && packet != nullptr) { - /* Create a packet parser. */ - char reply_buffer[GdbPacketBufferSize]; - GdbPacketParser parser(packet, reply_buffer, is_64_bit); - - /* Process the packet. */ - parser.Process(); - - /* Send packet. */ - packet_io.SendPacket(std::addressof(do_break), reply_buffer, session); - } - } - } - - template - void GdbServerThreadFunction() { + void GdbServerThreadFunction(void *) { /* Loop forever, servicing our gdb server. */ while (true) { /* Get a socket. */ @@ -80,7 +49,7 @@ namespace ams::dmnt { htcs::SockAddrHtcs addr; addr.family = htcs::HTCS_AF_HTCS; addr.peer_name = htcs::GetPeerNameAny(); - std::strcpy(addr.port_name.name, Is64Bit ? "iywys@$gdb-aarch64" : "iywys@$gdb-aarch32"); + std::strcpy(addr.port_name.name, "iywys@$gdb"); /* Bind. */ if (htcs::Bind(fd, std::addressof(addr)) == -1) { @@ -97,13 +66,14 @@ namespace ams::dmnt { break; } - /* Create htcs session for the socket. */ - auto &session_storage = Is64Bit ? g_session64 : g_session32; - util::ConstructAt(session_storage, client_fd); - ON_SCOPE_EXIT { util::DestroyAt(session_storage); }; + { + /* Create gdb server for the socket. */ + util::ConstructAt(g_gdb_server, client_fd); + ON_SCOPE_EXIT { util::DestroyAt(g_gdb_server); }; - /* Process for the session. */ - ProcessForHtcsSession(util::GetPointer(session_storage), Is64Bit); + /* Process for the server. */ + util::GetReference(g_gdb_server).LoopProcess(); + } /* Close the client socket. */ htcs::Close(client_fd); @@ -112,22 +82,12 @@ namespace ams::dmnt { } } - void GdbServerThreadFunction64(void *) { - GdbServerThreadFunction(); - } - - void GdbServerThreadFunction32(void *) { - GdbServerThreadFunction(); - } - } void InitializeGdbServer() { /* Create and start gdb server threads. */ - R_ABORT_UNLESS(os::CreateThread(std::addressof(g_server_thread64), GdbServerThreadFunction64, nullptr, g_server_thread64_stack, sizeof(g_server_thread64_stack), os::HighestThreadPriority - 1)); - R_ABORT_UNLESS(os::CreateThread(std::addressof(g_server_thread32), GdbServerThreadFunction32, nullptr, g_server_thread32_stack, sizeof(g_server_thread32_stack), os::HighestThreadPriority - 1)); - os::StartThread(std::addressof(g_server_thread64)); - os::StartThread(std::addressof(g_server_thread32)); + R_ABORT_UNLESS(os::CreateThread(std::addressof(g_server_thread), GdbServerThreadFunction, nullptr, g_server_thread_stack, sizeof(g_server_thread_stack), os::HighestThreadPriority - 1)); + os::StartThread(std::addressof(g_server_thread)); } } diff --git a/stratosphere/dmnt.gen2/source/dmnt2_gdb_packet_parser.cpp b/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.cpp similarity index 75% rename from stratosphere/dmnt.gen2/source/dmnt2_gdb_packet_parser.cpp rename to stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.cpp index a02519bc6..18e83b40b 100644 --- a/stratosphere/dmnt.gen2/source/dmnt2_gdb_packet_parser.cpp +++ b/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.cpp @@ -15,7 +15,7 @@ */ #include #include "dmnt2_debug_log.hpp" -#include "dmnt2_gdb_packet_parser.hpp" +#include "dmnt2_gdb_server_impl.hpp" namespace ams::dmnt { @@ -323,9 +323,38 @@ namespace ams::dmnt { AMS_DMNT2_GDB_LOG_DEBUG("Offset/Length %x/%x\n", offset, length); } + constinit char g_process_list_buffer[0x2000]; + } - void GdbPacketParser::Process() { + GdbServerImpl::GdbServerImpl(int socket) : m_socket(socket), m_session(socket), m_packet_io() { /* ... */ } + + GdbServerImpl::~GdbServerImpl() { /* ... */ } + + void GdbServerImpl::LoopProcess() { + /* Process packets. */ + while (m_session.IsValid()) { + /* Receive a packet. */ + bool do_break = false; + char recv_buf[GdbPacketBufferSize]; + char *packet = this->ReceivePacket(std::addressof(do_break), recv_buf, sizeof(recv_buf)); + + if (!do_break && packet != nullptr) { + /* Process the packet. */ + char reply_buffer[GdbPacketBufferSize]; + this->ProcessPacket(packet, reply_buffer); + + /* Send packet. */ + this->SendPacket(std::addressof(do_break), reply_buffer); + } + } + } + + void GdbServerImpl::ProcessPacket(char *receive, char *reply) { + /* Set our fields. */ + m_receive_packet = receive; + m_reply_packet = reply; + /* Log the packet we're processing. */ AMS_DMNT2_GDB_LOG_DEBUG("Receive: %s\n", m_receive_packet); @@ -334,29 +363,66 @@ namespace ams::dmnt { /* Handle the received packet. */ switch (m_receive_packet[0]) { + case 'H': + this->H(); + break; case 'q': this->q(); break; case '!': SetReplyOk(m_reply_packet); break; + case '?': + this->QuestionMark(); + break; default: AMS_DMNT2_GDB_LOG_DEBUG("Not Implemented: %s\n", m_receive_packet); break; } } - void GdbPacketParser::q() { - if (ParsePrefix(m_receive_packet, "qSupported:")) { + void GdbServerImpl::H() { + if (this->HasDebugProcess()) { + /* TODO */ + SetReplyError(m_reply_packet, "E01"); + } else { + SetReplyError(m_reply_packet, "E01"); + } + } + + void GdbServerImpl::q() { + if (ParsePrefix(m_receive_packet, "qAttached:")) { + this->qAttached(); + } else if (ParsePrefix(m_receive_packet, "qC")) { + this->qC(); + } else if (ParsePrefix(m_receive_packet, "qSupported:")) { this->qSupported(); - } else if (ParsePrefix(m_receive_packet, "qXfer:features:read:")) { - this->qXferFeaturesRead(); + } else if (ParsePrefix(m_receive_packet, "qXfer:")) { + this->qXfer(); } else { AMS_DMNT2_GDB_LOG_DEBUG("Not Implemented q: %s\n", m_receive_packet); } } - void GdbPacketParser::qSupported() { + void GdbServerImpl::qAttached() { + if (this->HasDebugProcess()) { + /* TODO: Parse/Save the requested process id */ + SetReply(m_reply_packet, "1"); + } else { + SetReplyError(m_reply_packet, "E01"); + } + } + + void GdbServerImpl::qC() { + if (this->HasDebugProcess()) { + /* TODO */ + SetReplyError(m_reply_packet, "E01"); + } else { + SetReplyError(m_reply_packet, "E01"); + } + } + + void GdbServerImpl::qSupported() { /* Current string from devkita64-none-elf-gdb: */ /* qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+ */ @@ -370,7 +436,29 @@ namespace ams::dmnt { AppendReply(m_reply_packet, ";hwbreak+"); } - void GdbPacketParser::qXferFeaturesRead() { + void GdbServerImpl::qXfer() { + /* Check for osdata. */ + if (ParsePrefix(m_receive_packet, "osdata:read:")) { + this->qXferOsdataRead(); + } else { + /* All other qXfer require debug process. */ + if (!this->HasDebugProcess()) { + SetReplyError(m_reply_packet, "E01"); + return; + } + + /* Process. */ + if (ParsePrefix(m_receive_packet, "features:read:")) { + this->qXferFeaturesRead(); + } else { + AMS_DMNT2_GDB_LOG_DEBUG("Not Implemented qxfer: %s\n", m_receive_packet); + SetReplyError(m_reply_packet, "E01"); + } + } + } + + void GdbServerImpl::qXferFeaturesRead() { + /* Handle the qXfer. */ u32 offset, length; if (ParsePrefix(m_receive_packet, "target.xml:")) { @@ -378,6 +466,7 @@ namespace ams::dmnt { ParseOffsetLength(m_receive_packet, offset, length); /* Send the desired xml. */ + /* TODO: Detection of debug-process as 64 bit or not. */ std::strncpy(m_reply_packet, (this->Is64Bit() ? TargetXmlAarch64 : TargetXmlAarch32) + offset, length); m_reply_packet[length] = 0; m_reply_packet += std::strlen(m_reply_packet); @@ -415,7 +504,83 @@ namespace ams::dmnt { m_reply_packet += std::strlen(m_reply_packet); } else { AMS_DMNT2_GDB_LOG_DEBUG("Not Implemented qxfer:features:read: %s\n", m_receive_packet); + SetReplyError(m_reply_packet, "E01"); } } + void GdbServerImpl::qXferOsdataRead() { + /* Handle the qXfer. */ + u32 offset, length; + + if (ParsePrefix(m_receive_packet, "processes:")) { + /* Parse offset/length. */ + ParseOffsetLength(m_receive_packet, offset, length); + + /* If doing a fresh read, generate the process list. */ + if (offset == 0) { + /* Clear the process list buffer. */ + g_process_list_buffer[0] = 0; + + /* Set header. */ + SetReply(g_process_list_buffer, "\n\n\n"); + + /* Get all processes. */ + { + /* Get all process ids. */ + u64 process_ids[0x50]; + s32 num_process_ids; + R_ABORT_UNLESS(svc::GetProcessList(std::addressof(num_process_ids), process_ids, util::size(process_ids))); + + /* Send all processes. */ + for (s32 i = 0; i < num_process_ids; ++i) { + svc::Handle handle; + if (R_SUCCEEDED(svc::DebugActiveProcess(std::addressof(handle), process_ids[i]))) { + ON_SCOPE_EXIT { R_ABORT_UNLESS(svc::CloseHandle(handle)); }; + + /* Get the create process event. */ + svc::DebugEventInfo d; + while (true) { + R_ABORT_UNLESS(svc::GetDebugEvent(std::addressof(d), handle)); + if (d.type == svc::DebugEvent_CreateProcess) { + AppendReply(g_process_list_buffer, "\n%lu\n%s\n\n", d.info.create_process.process_id, d.info.create_process.name); + break; + } + } + } + } + } + + /* Set footer. */ + AppendReply(g_process_list_buffer, ""); + } + + /* Copy out the process list. */ + const u32 annex_len = std::strlen(g_process_list_buffer); + if (offset <= annex_len) { + if (offset + length < annex_len) { + m_reply_packet[0] = 'm'; + std::memcpy(m_reply_packet + 1, g_process_list_buffer + offset, length); + m_reply_packet[1 + length] = 0; + } else { + const auto size = annex_len - offset; + + m_reply_packet[0] = 'l'; + std::memcpy(m_reply_packet + 1, g_process_list_buffer + offset, size); + m_reply_packet[1 + size] = 0; + } + } else { + SetReply(m_reply_packet, "l"); + } + } else { + AMS_DMNT2_GDB_LOG_DEBUG("Not Implemented qxfer:osdata:read: %s\n", m_receive_packet); + SetReplyError(m_reply_packet, "E01"); + } + } + + void GdbServerImpl::QuestionMark() { + /* TODO */ + AMS_DMNT2_GDB_LOG_DEBUG("Not Implemented QuestionMark\n"); + SetReply(m_reply_packet, "W00"); + } + } \ No newline at end of file diff --git a/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.hpp b/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.hpp new file mode 100644 index 000000000..dc868c848 --- /dev/null +++ b/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.hpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "dmnt2_gdb_packet_io.hpp" + +namespace ams::dmnt { + + class GdbServerImpl { + private: + int m_socket; + HtcsSession m_session; + GdbPacketIo m_packet_io; + char *m_receive_packet{nullptr}; + char *m_reply_packet{nullptr}; + char m_buffer[GdbPacketBufferSize / 2]; + svc::Handle m_debug_handle{svc::InvalidHandle}; + public: + GdbServerImpl(int socket); + ~GdbServerImpl(); + + void LoopProcess(); + private: + void ProcessPacket(char *receive, char *reply); + + void SendPacket(bool *out_break, const char *src) { return m_packet_io.SendPacket(out_break, src, std::addressof(m_session)); } + char *ReceivePacket(bool *out_break, char *dst, size_t size) { return m_packet_io.ReceivePacket(out_break, dst, size, std::addressof(m_session)); } + private: + bool HasDebugProcess() const { return m_debug_handle != svc::InvalidHandle; } + bool Is64Bit() const { return true; /* TODO: Retrieve from debug process info. */ } + private: + void H(); + void q(); + + void qAttached(); + void qC(); + void qSupported(); + void qXfer(); + void qXferFeaturesRead(); + void qXferOsdataRead(); + + void QuestionMark(); + }; + +} \ No newline at end of file