mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 12:21:18 +00:00
dmnt: refactor/add support for getting process list in gdb
This commit is contained in:
parent
a7f9729f63
commit
649a0052d0
5 changed files with 255 additions and 107 deletions
|
@ -99,5 +99,12 @@
|
|||
}, {
|
||||
"type": "handle_table_size",
|
||||
"value": 0
|
||||
}]
|
||||
},
|
||||
{
|
||||
"type": "debug_flags",
|
||||
"value": {
|
||||
"allow_debug": false,
|
||||
"force_debug": true
|
||||
}
|
||||
}]
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#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();
|
||||
};
|
||||
|
||||
}
|
|
@ -14,10 +14,9 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#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<HtcsSession> g_session32;
|
||||
constinit util::TypedStorage<HtcsSession> g_session64;
|
||||
constinit util::TypedStorage<GdbServerImpl> 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<bool Is64Bit>
|
||||
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<true>();
|
||||
}
|
||||
|
||||
void GdbServerThreadFunction32(void *) {
|
||||
GdbServerThreadFunction<false>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#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, "<?xml version=\"1.0\"?>\n<!DOCTYPE target SYSTEM \"osdata.dtd\">\n<osdata type=\"processes\">\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, "<item>\n<column name=\"pid\">%lu</column>\n<column name=\"command\">%s</column>\n</item>\n", d.info.create_process.process_id, d.info.create_process.name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Set footer. */
|
||||
AppendReply(g_process_list_buffer, "</osdata>");
|
||||
}
|
||||
|
||||
/* 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");
|
||||
}
|
||||
|
||||
}
|
58
stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.hpp
Normal file
58
stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.hpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#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();
|
||||
};
|
||||
|
||||
}
|
Loading…
Reference in a new issue