From 4e80a8bf1601805c8c901c871a0150b1398ac168 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 18 Jan 2025 19:38:01 +0100 Subject: [PATCH 1/8] Support threads in gdb --- src/gdb-stub/gdb_stub.cpp | 49 +++++++++++++++++++ src/gdb-stub/gdb_stub.hpp | 5 ++ .../debugging/win_x64_gdb_stub_handler.hpp | 28 +++++++++++ .../debugging/x64_gdb_stub_handler.hpp | 10 ++++ src/windows-emulator/windows_emulator.cpp | 43 ++++++++++++++-- src/windows-emulator/windows_emulator.hpp | 1 + 6 files changed, 132 insertions(+), 4 deletions(-) diff --git a/src/gdb-stub/gdb_stub.cpp b/src/gdb-stub/gdb_stub.cpp index 764fbf14..453426ae 100644 --- a/src/gdb-stub/gdb_stub.cpp +++ b/src/gdb-stub/gdb_stub.cpp @@ -115,6 +115,28 @@ namespace gdb_stub { connection.send_reply("OK"); } + else if (name == "C") + { + const auto thread_id = handler.get_current_thread_id(); + connection.send_reply("QC" + utils::string::to_hex_number(thread_id)); + } + else if (name == "sThreadInfo") + { + connection.send_reply("l"); + } + else if (name == "fThreadInfo") + { + std::string reply{}; + const auto ids = handler.get_thread_ids(); + + for (const auto id : ids) + { + reply.push_back(reply.empty() ? 'm' : ','); + reply.append(utils::string::to_hex_number(id)); + } + + connection.send_reply(reply); + } else { connection.send_reply({}); @@ -389,6 +411,29 @@ namespace gdb_stub connection.send_reply("OK"); } + void switch_to_thread(connection_handler& connection, debugging_handler& handler, + const std::string_view payload) + { + if (payload.size() < 2) + { + connection.send_reply({}); + return; + } + + const auto operation = payload[0]; + if (operation != 'g') + { + connection.send_reply("OK"); + return; + } + + uint32_t id{}; + rt_assert(sscanf_s(std::string(payload.substr(1)).c_str(), "%x", &id) == 1); + + const auto res = id == 0 || handler.switch_to_thread(id); + connection.send_reply(res ? "OK" : "E01"); + } + void handle_command(connection_handler& connection, async_handler& async, debugging_handler& handler, const uint8_t command, const std::string_view data) { @@ -453,6 +498,10 @@ namespace gdb_stub write_x_memory(connection, handler, data); break; + case 'H': + switch_to_thread(connection, handler, data); + break; + default: connection.send_reply({}); break; diff --git a/src/gdb-stub/gdb_stub.hpp b/src/gdb-stub/gdb_stub.hpp index c779aaaa..ffb22bdf 100644 --- a/src/gdb-stub/gdb_stub.hpp +++ b/src/gdb-stub/gdb_stub.hpp @@ -43,6 +43,11 @@ namespace gdb_stub virtual void on_interrupt() = 0; virtual std::string get_target_description(std::string_view file) = 0; + + virtual bool switch_to_thread(uint32_t thread_id) = 0; + + virtual uint32_t get_current_thread_id() = 0; + virtual std::vector get_thread_ids() = 0; }; bool run_gdb_stub(const network::address& bind_address, debugging_handler& handler); diff --git a/src/windows-emulator/debugging/win_x64_gdb_stub_handler.hpp b/src/windows-emulator/debugging/win_x64_gdb_stub_handler.hpp index 451078a8..7b9f520e 100644 --- a/src/windows-emulator/debugging/win_x64_gdb_stub_handler.hpp +++ b/src/windows-emulator/debugging/win_x64_gdb_stub_handler.hpp @@ -40,6 +40,34 @@ class win_x64_gdb_stub_handler : public x64_gdb_stub_handler return gdb_stub::action::resume; } + uint32_t get_current_thread_id() override + { + return this->win_emu_->current_thread().id; + } + + std::vector get_thread_ids() override + { + const auto& threads = this->win_emu_->process().threads; + + std::vector ids{}; + ids.reserve(threads.size()); + + for (const auto& t : threads | std::views::values) + { + if (!t.is_terminated()) + { + ids.push_back(t.id); + } + } + + return ids; + } + + bool switch_to_thread(const uint32_t thread_id) override + { + return this->win_emu_->activate_thread(thread_id); + } + private: windows_emulator* win_emu_{}; }; diff --git a/src/windows-emulator/debugging/x64_gdb_stub_handler.hpp b/src/windows-emulator/debugging/x64_gdb_stub_handler.hpp index 70a6e039..8adf56e3 100644 --- a/src/windows-emulator/debugging/x64_gdb_stub_handler.hpp +++ b/src/windows-emulator/debugging/x64_gdb_stub_handler.hpp @@ -250,6 +250,16 @@ class x64_gdb_stub_handler : public gdb_stub::debugging_handler return entry->second; } + uint32_t get_current_thread_id() override + { + return 1; + } + + std::vector get_thread_ids() override + { + return {this->get_current_thread_id()}; + } + private: x64_emulator* emu_{}; diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index 5836ae06..ff4821ed 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -525,14 +525,39 @@ namespace } } - bool switch_to_thread(windows_emulator& win_emu, emulator_thread& thread) + emulator_thread* get_thread_by_id(process_context& process, const uint32_t id) { + for (auto& t : process.threads | std::views::values) + { + if (t.id == id) + { + return &t; + } + } + + return nullptr; + } + + bool switch_to_thread(windows_emulator& win_emu, emulator_thread& thread, const bool force = false) + { + if (thread.is_terminated()) + { + return false; + } + auto& emu = win_emu.emu(); auto& context = win_emu.process(); - if (!thread.is_thread_ready(win_emu)) + const auto is_ready = thread.is_thread_ready(win_emu); + + if (!is_ready) { - return false; + if (!force) + { + return false; + } + + win_emu.yield_thread(); } auto* active_thread = context.active_thread; @@ -553,7 +578,6 @@ namespace thread.restore(emu); thread.setup_if_necessary(emu, context); - return true; } @@ -863,6 +887,17 @@ void windows_emulator::perform_thread_switch() } } +bool windows_emulator::activate_thread(const uint32_t id) +{ + const auto thread = get_thread_by_id(this->process(), id); + if (!thread) + { + return false; + } + + return switch_to_thread(*this, *thread, true); +} + void windows_emulator::on_instruction_execution(const uint64_t address) { auto& process = this->process(); diff --git a/src/windows-emulator/windows_emulator.hpp b/src/windows-emulator/windows_emulator.hpp index acf9b7d0..36f0f9b2 100644 --- a/src/windows-emulator/windows_emulator.hpp +++ b/src/windows-emulator/windows_emulator.hpp @@ -124,6 +124,7 @@ class windows_emulator void yield_thread(); void perform_thread_switch(); + bool activate_thread(uint32_t id); bool time_is_relative() const { From 62597d5f4e18644206d28381ea6f686a25dabec7 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 19 Jan 2025 13:23:21 +0100 Subject: [PATCH 2/8] Don't support vCont right now --- src/gdb-stub/gdb_stub.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gdb-stub/gdb_stub.cpp b/src/gdb-stub/gdb_stub.cpp index 453426ae..85b0cf66 100644 --- a/src/gdb-stub/gdb_stub.cpp +++ b/src/gdb-stub/gdb_stub.cpp @@ -205,7 +205,8 @@ namespace gdb_stub if (name == "Cont?") { - connection.send_reply("vCont;s;c"); + // connection.send_reply("vCont;s;c"); + connection.send_reply({}); } else if (name == "Cont;s") { From 87fb3defe4bee7b4f6c41ed33e0143ac7bee3336 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 19 Jan 2025 13:24:05 +0100 Subject: [PATCH 3/8] Cleanup xfer data transfer --- src/gdb-stub/gdb_stub.cpp | 42 ++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/src/gdb-stub/gdb_stub.cpp b/src/gdb-stub/gdb_stub.cpp index 85b0cf66..2652616e 100644 --- a/src/gdb-stub/gdb_stub.cpp +++ b/src/gdb-stub/gdb_stub.cpp @@ -49,6 +49,29 @@ namespace gdb_stub return {name, args}; } + void send_xfer_data(connection_handler& connection, const std::string& args, const std::string_view data) + { + size_t offset{}, length{}; + rt_assert(sscanf_s(args.c_str(), "%zx,%zx", &offset, &length) == 2); + + if (offset >= data.size()) + { + connection.send_reply("l"); + return; + } + + const auto remaining = data.size() - offset; + const auto real_length = std::min(remaining, length); + const auto is_end = real_length == remaining; + + const auto sub_region = data.substr(offset, real_length); + + std::string reply = is_end ? "l" : "m"; + reply.append(sub_region); + + connection.send_reply(reply); + } + void handle_features(connection_handler& connection, debugging_handler& handler, const std::string_view payload) { const auto [command, args] = split_string(payload, ':'); @@ -60,25 +83,8 @@ namespace gdb_stub } const auto [file, data] = split_string(args, ':'); - - size_t offset{}, length{}; - rt_assert(sscanf_s(std::string(data).c_str(), "%zx,%zx", &offset, &length) == 2); - const auto target_description = handler.get_target_description(file); - - if (offset >= target_description.size()) - { - connection.send_reply("l"); - return; - } - - const auto remaining = target_description.size() - offset; - const auto real_length = std::min(remaining, length); - const auto is_end = real_length == remaining; - - const auto sub_region = target_description.substr(offset, real_length); - - connection.send_reply((is_end ? "l" : "m") + sub_region); + send_xfer_data(connection, std::string(data), target_description); } void process_xfer(connection_handler& connection, debugging_handler& handler, const std::string_view payload) From ac4fd1b4fd964a3306381174a298f0c2db7e9964 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 19 Jan 2025 15:12:00 +0100 Subject: [PATCH 4/8] Signal stopped thread --- src/gdb-stub/gdb_stub.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/gdb-stub/gdb_stub.cpp b/src/gdb-stub/gdb_stub.cpp index 2652616e..c09e1a55 100644 --- a/src/gdb-stub/gdb_stub.cpp +++ b/src/gdb-stub/gdb_stub.cpp @@ -190,18 +190,25 @@ namespace gdb_stub connection.send_reply(res ? "OK" : "E01"); } + void signal_stop(connection_handler& connection, debugging_handler& handler) + { + const auto id = handler.get_current_thread_id(); + const auto hex_id = utils::string::to_hex_number(id); + connection.send_reply("T05thread:" + hex_id + ";"); + } + void continue_execution(connection_handler& connection, async_handler& async, debugging_handler& handler) { async.run(); process_action(connection, handler.run()); async.pause(); - connection.send_reply("S05"); + signal_stop(connection, handler); } void singlestep_execution(connection_handler& connection, debugging_handler& handler) { process_action(connection, handler.singlestep()); - connection.send_reply("S05"); + signal_stop(connection, handler); } void handle_v_packet(connection_handler& connection, async_handler& async, debugging_handler& handler, @@ -470,7 +477,7 @@ namespace gdb_stub break; case '?': - connection.send_reply("S05"); + signal_stop(connection, handler); break; case 'v': From 66805a55a0a09b7f72a1bc8685287bff4fe00895 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 19 Jan 2025 15:39:19 +0100 Subject: [PATCH 5/8] Introduce debugging state --- src/gdb-stub/gdb_stub.cpp | 71 ++++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 23 deletions(-) diff --git a/src/gdb-stub/gdb_stub.cpp b/src/gdb-stub/gdb_stub.cpp index c09e1a55..e07ac4de 100644 --- a/src/gdb-stub/gdb_stub.cpp +++ b/src/gdb-stub/gdb_stub.cpp @@ -22,6 +22,11 @@ namespace gdb_stub assert(condition); } + struct debugging_state + { + std::optional continuation_thread{}; + }; + network::tcp_client_socket accept_client(const network::address& bind_address) { network::tcp_server_socket server{bind_address.get_family()}; @@ -197,22 +202,35 @@ namespace gdb_stub connection.send_reply("T05thread:" + hex_id + ";"); } - void continue_execution(connection_handler& connection, async_handler& async, debugging_handler& handler) + void apply_continuation_thread(debugging_handler& handler, debugging_state& state) { + if (state.continuation_thread) + { + handler.switch_to_thread(*state.continuation_thread); + state.continuation_thread = std::nullopt; + } + } + + void continue_execution(connection_handler& connection, async_handler& async, debugging_state& state, + debugging_handler& handler) + { + apply_continuation_thread(handler, state); + async.run(); process_action(connection, handler.run()); async.pause(); signal_stop(connection, handler); } - void singlestep_execution(connection_handler& connection, debugging_handler& handler) + void singlestep_execution(connection_handler& connection, debugging_handler& handler, debugging_state& state) { + apply_continuation_thread(handler, state); process_action(connection, handler.singlestep()); signal_stop(connection, handler); } void handle_v_packet(connection_handler& connection, async_handler& async, debugging_handler& handler, - const std::string_view data) + debugging_state& state, const std::string_view data) { const auto [name, args] = split_string(data, ':'); @@ -223,11 +241,11 @@ namespace gdb_stub } else if (name == "Cont;s") { - singlestep_execution(connection, handler); + singlestep_execution(connection, handler, state); } else if (name == "Cont;c") { - continue_execution(connection, async, handler); + continue_execution(connection, async, state, handler); } else { @@ -425,7 +443,7 @@ namespace gdb_stub connection.send_reply("OK"); } - void switch_to_thread(connection_handler& connection, debugging_handler& handler, + void switch_to_thread(connection_handler& connection, debugging_handler& handler, debugging_state& state, const std::string_view payload) { if (payload.size() < 2) @@ -434,33 +452,39 @@ namespace gdb_stub return; } - const auto operation = payload[0]; - if (operation != 'g') - { - connection.send_reply("OK"); - return; - } - uint32_t id{}; rt_assert(sscanf_s(std::string(payload.substr(1)).c_str(), "%x", &id) == 1); - const auto res = id == 0 || handler.switch_to_thread(id); - connection.send_reply(res ? "OK" : "E01"); + const auto operation = payload[0]; + if (operation == 'c') + { + state.continuation_thread = id; + connection.send_reply("OK"); + } + else if (operation == 'g') + { + const auto res = id == 0 || handler.switch_to_thread(id); + connection.send_reply(res ? "OK" : "E01"); + } + else + { + connection.send_reply({}); + } } void handle_command(connection_handler& connection, async_handler& async, debugging_handler& handler, - const uint8_t command, const std::string_view data) + debugging_state& state, const uint8_t command, const std::string_view data) { // printf("GDB command: %c -> %.*s\n", command, static_cast(data.size()), data.data()); switch (command) { case 'c': - continue_execution(connection, async, handler); + continue_execution(connection, async, state, handler); break; case 's': - singlestep_execution(connection, handler); + singlestep_execution(connection, handler, state); break; case 'q': @@ -481,7 +505,7 @@ namespace gdb_stub break; case 'v': - handle_v_packet(connection, async, handler, data); + handle_v_packet(connection, async, handler, state, data); break; case 'g': @@ -513,7 +537,7 @@ namespace gdb_stub break; case 'H': - switch_to_thread(connection, handler, data); + switch_to_thread(connection, handler, state, data); break; default: @@ -523,7 +547,7 @@ namespace gdb_stub } void process_packet(connection_handler& connection, async_handler& async, debugging_handler& handler, - const std::string_view packet) + debugging_state& state, const std::string_view packet) { connection.send_raw_data("+"); @@ -533,7 +557,7 @@ namespace gdb_stub } const auto command = packet.front(); - handle_command(connection, async, handler, command, packet.substr(1)); + handle_command(connection, async, handler, state, command, packet.substr(1)); } bool is_interrupt_packet(const std::optional& data) @@ -565,6 +589,7 @@ namespace gdb_stub } }}; + debugging_state state{}; connection_handler connection{client}; while (true) @@ -575,7 +600,7 @@ namespace gdb_stub break; } - process_packet(connection, async, handler, *packet); + process_packet(connection, async, handler, state, *packet); } return true; From 15a1b3327ae0014a1e67e40cfeb14c9f83f55c37 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 19 Jan 2025 15:49:37 +0100 Subject: [PATCH 6/8] Add debugging context --- src/gdb-stub/gdb_stub.cpp | 239 ++++++++++++++++++++------------------ 1 file changed, 123 insertions(+), 116 deletions(-) diff --git a/src/gdb-stub/gdb_stub.cpp b/src/gdb-stub/gdb_stub.cpp index e07ac4de..c87f4306 100644 --- a/src/gdb-stub/gdb_stub.cpp +++ b/src/gdb-stub/gdb_stub.cpp @@ -27,6 +27,14 @@ namespace gdb_stub std::optional continuation_thread{}; }; + struct debugging_context + { + connection_handler& connection; + debugging_handler& handler; + debugging_state& state; + async_handler& async; + }; + network::tcp_client_socket accept_client(const network::address& bind_address) { network::tcp_server_socket server{bind_address.get_family()}; @@ -77,68 +85,68 @@ namespace gdb_stub connection.send_reply(reply); } - void handle_features(connection_handler& connection, debugging_handler& handler, const std::string_view payload) + void handle_features(const debugging_context& c, const std::string_view payload) { const auto [command, args] = split_string(payload, ':'); if (command != "read") { - connection.send_reply({}); + c.connection.send_reply({}); return; } const auto [file, data] = split_string(args, ':'); - const auto target_description = handler.get_target_description(file); - send_xfer_data(connection, std::string(data), target_description); + const auto target_description = c.handler.get_target_description(file); + send_xfer_data(c.connection, std::string(data), target_description); } - void process_xfer(connection_handler& connection, debugging_handler& handler, const std::string_view payload) + void process_xfer(const debugging_context& c, const std::string_view payload) { auto [name, args] = split_string(payload, ':'); if (name == "features") { - handle_features(connection, handler, args); + handle_features(c, args); } else { - connection.send_reply({}); + c.connection.send_reply({}); } } - void process_query(connection_handler& connection, debugging_handler& handler, const std::string_view payload) + void process_query(const debugging_context& c, const std::string_view payload) { const auto [name, args] = split_string(payload, ':'); if (name == "Supported") { - connection.send_reply("PacketSize=1024;qXfer:features:read+"); + c.connection.send_reply("PacketSize=1024;qXfer:features:read+"); } else if (name == "Attached") { - connection.send_reply("1"); + c.connection.send_reply("1"); } else if (name == "Xfer") { - process_xfer(connection, handler, args); + process_xfer(c, args); } else if (name == "Symbol") { - connection.send_reply("OK"); + c.connection.send_reply("OK"); } else if (name == "C") { - const auto thread_id = handler.get_current_thread_id(); - connection.send_reply("QC" + utils::string::to_hex_number(thread_id)); + const auto thread_id = c.handler.get_current_thread_id(); + c.connection.send_reply("QC" + utils::string::to_hex_number(thread_id)); } else if (name == "sThreadInfo") { - connection.send_reply("l"); + c.connection.send_reply("l"); } else if (name == "fThreadInfo") { std::string reply{}; - const auto ids = handler.get_thread_ids(); + const auto ids = c.handler.get_thread_ids(); for (const auto id : ids) { @@ -146,15 +154,15 @@ namespace gdb_stub reply.append(utils::string::to_hex_number(id)); } - connection.send_reply(reply); + c.connection.send_reply(reply); } else { - connection.send_reply({}); + c.connection.send_reply({}); } } - void process_action(connection_handler& connection, const action a) + void process_action(const connection_handler& connection, const action a) { if (a == action::shutdown) { @@ -183,91 +191,88 @@ namespace gdb_stub return handler.delete_breakpoint(type, address, size); } - void handle_breakpoint(connection_handler& connection, debugging_handler& handler, const std::string& data, - const bool set) + void handle_breakpoint(const debugging_context& c, const std::string& data, const bool set) { uint32_t type{}; uint64_t addr{}; size_t kind{}; rt_assert(sscanf_s(data.c_str(), "%x,%" PRIX64 ",%zx", &type, &addr, &kind) == 3); - const auto res = change_breakpoint(handler, set, translate_breakpoint_type(type), addr, kind); - connection.send_reply(res ? "OK" : "E01"); + const auto res = change_breakpoint(c.handler, set, translate_breakpoint_type(type), addr, kind); + c.connection.send_reply(res ? "OK" : "E01"); } - void signal_stop(connection_handler& connection, debugging_handler& handler) + void signal_stop(const debugging_context& c) { - const auto id = handler.get_current_thread_id(); + const auto id = c.handler.get_current_thread_id(); const auto hex_id = utils::string::to_hex_number(id); - connection.send_reply("T05thread:" + hex_id + ";"); + c.connection.send_reply("T05thread:" + hex_id + ";"); } - void apply_continuation_thread(debugging_handler& handler, debugging_state& state) + void apply_continuation_thread(const debugging_context& c) { - if (state.continuation_thread) + if (c.state.continuation_thread) { - handler.switch_to_thread(*state.continuation_thread); - state.continuation_thread = std::nullopt; + c.handler.switch_to_thread(*c.state.continuation_thread); + c.state.continuation_thread = std::nullopt; } } - void continue_execution(connection_handler& connection, async_handler& async, debugging_state& state, - debugging_handler& handler) + void continue_execution(const debugging_context& c) { - apply_continuation_thread(handler, state); + apply_continuation_thread(c); - async.run(); - process_action(connection, handler.run()); - async.pause(); - signal_stop(connection, handler); + c.async.run(); + process_action(c.connection, c.handler.run()); + c.async.pause(); + signal_stop(c); } - void singlestep_execution(connection_handler& connection, debugging_handler& handler, debugging_state& state) + void singlestep_execution(const debugging_context& c) { - apply_continuation_thread(handler, state); - process_action(connection, handler.singlestep()); - signal_stop(connection, handler); + apply_continuation_thread(c); + process_action(c.connection, c.handler.singlestep()); + signal_stop(c); } - void handle_v_packet(connection_handler& connection, async_handler& async, debugging_handler& handler, - debugging_state& state, const std::string_view data) + void handle_v_packet(const debugging_context& c, const std::string_view data) { const auto [name, args] = split_string(data, ':'); if (name == "Cont?") { // connection.send_reply("vCont;s;c"); - connection.send_reply({}); + c.connection.send_reply({}); } else if (name == "Cont;s") { - singlestep_execution(connection, handler, state); + singlestep_execution(c); } else if (name == "Cont;c") { - continue_execution(connection, async, state, handler); + continue_execution(c); } else { - connection.send_reply({}); + c.connection.send_reply({}); } } - void read_registers(connection_handler& connection, debugging_handler& handler) + void read_registers(const debugging_context& c) { std::string response{}; std::vector data{}; - data.resize(handler.get_max_register_size()); + data.resize(c.handler.get_max_register_size()); - const auto registers = handler.get_register_count(); + const auto registers = c.handler.get_register_count(); for (size_t i = 0; i < registers; ++i) { - const auto size = handler.read_register(i, data.data(), data.size()); + const auto size = c.handler.read_register(i, data.data(), data.size()); if (!size) { - connection.send_reply("E01"); + c.connection.send_reply("E01"); return; } @@ -275,64 +280,62 @@ namespace gdb_stub response.append(utils::string::to_hex_string(register_data)); } - connection.send_reply(response); + c.connection.send_reply(response); } - void write_registers(connection_handler& connection, debugging_handler& handler, const std::string_view payload) + void write_registers(const debugging_context& c, const std::string_view payload) { const auto data = utils::string::from_hex_string(payload); - const auto registers = handler.get_register_count(); - const auto register_size = handler.get_max_register_size(); + const auto registers = c.handler.get_register_count(); + const auto register_size = c.handler.get_max_register_size(); size_t offset = 0; for (size_t i = 0; i < registers; ++i) { if (offset >= data.size()) { - connection.send_reply("E01"); + c.connection.send_reply("E01"); return; } const auto max_size = std::min(register_size, data.size() - offset); - const auto size = handler.write_register(i, data.data() + offset, max_size); + const auto size = c.handler.write_register(i, data.data() + offset, max_size); offset += size; if (!size) { - connection.send_reply("E01"); + c.connection.send_reply("E01"); return; } } - connection.send_reply("OK"); + c.connection.send_reply("OK"); } - void read_single_register(connection_handler& connection, debugging_handler& handler, - const std::string& payload) + void read_single_register(const debugging_context& c, const std::string& payload) { size_t reg{}; rt_assert(sscanf_s(payload.c_str(), "%zx", ®) == 1); std::vector data{}; - data.resize(handler.get_max_register_size()); + data.resize(c.handler.get_max_register_size()); - const auto size = handler.read_register(reg, data.data(), data.size()); + const auto size = c.handler.read_register(reg, data.data(), data.size()); if (size) { const std::span register_data(data.data(), size); - connection.send_reply(utils::string::to_hex_string(register_data)); + c.connection.send_reply(utils::string::to_hex_string(register_data)); } else { - connection.send_reply("E01"); + c.connection.send_reply("E01"); } } - void write_single_register(connection_handler& connection, debugging_handler& handler, - const std::string_view payload) + void write_single_register(const debugging_context& c, const std::string_view payload) { const auto [reg, hex_data] = split_string(payload, '='); @@ -340,11 +343,11 @@ namespace gdb_stub rt_assert(sscanf_s(std::string(reg).c_str(), "%zx", ®ister_index) == 1); const auto data = utils::string::from_hex_string(hex_data); - const auto res = handler.write_register(register_index, data.data(), data.size()) > 0; - connection.send_reply(res ? "OK" : "E01"); + const auto res = c.handler.write_register(register_index, data.data(), data.size()) > 0; + c.connection.send_reply(res ? "OK" : "E01"); } - void read_memory(connection_handler& connection, debugging_handler& handler, const std::string& payload) + void read_memory(const debugging_context& c, const std::string& payload) { uint64_t address{}; size_t size{}; @@ -352,24 +355,24 @@ namespace gdb_stub if (size > 0x1000) { - connection.send_reply("E01"); + c.connection.send_reply("E01"); return; } std::vector data{}; data.resize(size); - const auto res = handler.read_memory(address, data.data(), data.size()); + const auto res = c.handler.read_memory(address, data.data(), data.size()); if (!res) { - connection.send_reply("E01"); + c.connection.send_reply("E01"); return; } - connection.send_reply(utils::string::to_hex_string(data)); + c.connection.send_reply(utils::string::to_hex_string(data)); } - void write_memory(connection_handler& connection, debugging_handler& handler, const std::string_view payload) + void write_memory(const debugging_context& c, const std::string_view payload) { const auto [info, hex_data] = split_string(payload, ':'); @@ -379,15 +382,15 @@ namespace gdb_stub if (size > 0x1000) { - connection.send_reply("E01"); + c.connection.send_reply("E01"); return; } auto data = utils::string::from_hex_string(hex_data); data.resize(size); - const auto res = handler.write_memory(address, data.data(), data.size()); - connection.send_reply(res ? "OK" : "E01"); + const auto res = c.handler.write_memory(address, data.data(), data.size()); + c.connection.send_reply(res ? "OK" : "E01"); } std::string decode_x_memory(const std::string_view payload) @@ -416,7 +419,7 @@ namespace gdb_stub return result; } - void write_x_memory(connection_handler& connection, debugging_handler& handler, const std::string_view payload) + void write_x_memory(const debugging_context& c, const std::string_view payload) { const auto [info, encoded_data] = split_string(payload, ':'); @@ -426,29 +429,28 @@ namespace gdb_stub if (size > 0x1000) { - connection.send_reply("E01"); + c.connection.send_reply("E01"); return; } auto data = decode_x_memory(encoded_data); data.resize(size); - const auto res = handler.write_memory(address, data.data(), data.size()); + const auto res = c.handler.write_memory(address, data.data(), data.size()); if (!res) { - connection.send_reply("E01"); + c.connection.send_reply("E01"); return; } - connection.send_reply("OK"); + c.connection.send_reply("OK"); } - void switch_to_thread(connection_handler& connection, debugging_handler& handler, debugging_state& state, - const std::string_view payload) + void switch_to_thread(const debugging_context& c, const std::string_view payload) { if (payload.size() < 2) { - connection.send_reply({}); + c.connection.send_reply({}); return; } @@ -458,98 +460,96 @@ namespace gdb_stub const auto operation = payload[0]; if (operation == 'c') { - state.continuation_thread = id; - connection.send_reply("OK"); + c.state.continuation_thread = id; + c.connection.send_reply("OK"); } else if (operation == 'g') { - const auto res = id == 0 || handler.switch_to_thread(id); - connection.send_reply(res ? "OK" : "E01"); + const auto res = id == 0 || c.handler.switch_to_thread(id); + c.connection.send_reply(res ? "OK" : "E01"); } else { - connection.send_reply({}); + c.connection.send_reply({}); } } - void handle_command(connection_handler& connection, async_handler& async, debugging_handler& handler, - debugging_state& state, const uint8_t command, const std::string_view data) + void handle_command(const debugging_context& c, const uint8_t command, const std::string_view data) { - // printf("GDB command: %c -> %.*s\n", command, static_cast(data.size()), data.data()); + printf("GDB command: %c -> %.*s\n", command, static_cast(data.size()), data.data()); switch (command) { case 'c': - continue_execution(connection, async, state, handler); + continue_execution(c); break; case 's': - singlestep_execution(connection, handler, state); + singlestep_execution(c); break; case 'q': - process_query(connection, handler, data); + process_query(c, data); break; case 'D': - connection.close(); + c.connection.close(); break; case 'z': case 'Z': - handle_breakpoint(connection, handler, std::string(data), command == 'Z'); + handle_breakpoint(c, std::string(data), command == 'Z'); break; case '?': - signal_stop(connection, handler); + signal_stop(c); break; case 'v': - handle_v_packet(connection, async, handler, state, data); + handle_v_packet(c, data); break; case 'g': - read_registers(connection, handler); + read_registers(c); break; case 'G': - write_registers(connection, handler, data); + write_registers(c, data); break; case 'p': - read_single_register(connection, handler, std::string(data)); + read_single_register(c, std::string(data)); break; case 'P': - write_single_register(connection, handler, data); + write_single_register(c, data); break; case 'm': - read_memory(connection, handler, std::string(data)); + read_memory(c, std::string(data)); break; case 'M': - write_memory(connection, handler, data); + write_memory(c, data); break; case 'X': - write_x_memory(connection, handler, data); + write_x_memory(c, data); break; case 'H': - switch_to_thread(connection, handler, state, data); + switch_to_thread(c, data); break; default: - connection.send_reply({}); + c.connection.send_reply({}); break; } } - void process_packet(connection_handler& connection, async_handler& async, debugging_handler& handler, - debugging_state& state, const std::string_view packet) + void process_packet(const debugging_context& c, const std::string_view packet) { - connection.send_raw_data("+"); + c.connection.send_raw_data("+"); if (packet.empty()) { @@ -557,7 +557,7 @@ namespace gdb_stub } const auto command = packet.front(); - handle_command(connection, async, handler, state, command, packet.substr(1)); + handle_command(c, command, packet.substr(1)); } bool is_interrupt_packet(const std::optional& data) @@ -592,6 +592,13 @@ namespace gdb_stub debugging_state state{}; connection_handler connection{client}; + debugging_context c{ + .connection = connection, + .handler = handler, + .state = state, + .async = async, + }; + while (true) { const auto packet = connection.get_packet(); @@ -600,7 +607,7 @@ namespace gdb_stub break; } - process_packet(connection, async, handler, state, *packet); + process_packet(c, *packet); } return true; From b52bb0315ade8e1cba99c0683f6355abba0f197e Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 19 Jan 2025 15:55:41 +0100 Subject: [PATCH 7/8] Handle vCont packets --- src/gdb-stub/gdb_stub.cpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/gdb-stub/gdb_stub.cpp b/src/gdb-stub/gdb_stub.cpp index c87f4306..5243bd55 100644 --- a/src/gdb-stub/gdb_stub.cpp +++ b/src/gdb-stub/gdb_stub.cpp @@ -235,21 +235,36 @@ namespace gdb_stub signal_stop(c); } + void apply_continuation_thread(const debugging_context& c, const std::string_view thread_string) + { + if (thread_string.empty()) + { + return; + } + + uint32_t thread_id{}; + rt_assert(sscanf_s(std::string(thread_string).c_str(), "%x", &thread_id) == 1); + c.state.continuation_thread = thread_id; + } + void handle_v_packet(const debugging_context& c, const std::string_view data) { const auto [name, args] = split_string(data, ':'); if (name == "Cont?") { - // connection.send_reply("vCont;s;c"); - c.connection.send_reply({}); + c.connection.send_reply("vCont;s;c"); } else if (name == "Cont;s") { + const auto [thread, _] = split_string(args, ':'); + apply_continuation_thread(c, thread); singlestep_execution(c); } else if (name == "Cont;c") { + const auto [thread, _] = split_string(args, ':'); + apply_continuation_thread(c, thread); continue_execution(c); } else From 55b0c71dc92e6eeb6ca24161aefb1d6c840cb55f Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 19 Jan 2025 16:02:31 +0100 Subject: [PATCH 8/8] Unify continuation --- src/gdb-stub/gdb_stub.cpp | 46 +++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/gdb-stub/gdb_stub.cpp b/src/gdb-stub/gdb_stub.cpp index 5243bd55..8d2d48d9 100644 --- a/src/gdb-stub/gdb_stub.cpp +++ b/src/gdb-stub/gdb_stub.cpp @@ -218,24 +218,28 @@ namespace gdb_stub } } - void continue_execution(const debugging_context& c) + void resume_execution(const debugging_context& c, const bool single_step) { apply_continuation_thread(c); - c.async.run(); - process_action(c.connection, c.handler.run()); - c.async.pause(); + action a{}; + + if (single_step) + { + a = c.handler.singlestep(); + } + else + { + c.async.run(); + a = c.handler.run(); + c.async.pause(); + } + + process_action(c.connection, a); signal_stop(c); } - void singlestep_execution(const debugging_context& c) - { - apply_continuation_thread(c); - process_action(c.connection, c.handler.singlestep()); - signal_stop(c); - } - - void apply_continuation_thread(const debugging_context& c, const std::string_view thread_string) + void store_continuation_thread(const debugging_context& c, const std::string_view thread_string) { if (thread_string.empty()) { @@ -255,17 +259,13 @@ namespace gdb_stub { c.connection.send_reply("vCont;s;c"); } - else if (name == "Cont;s") + else if (name == "Cont;s" || name == "Cont;c") { + const auto singlestep = name[5] == 's'; const auto [thread, _] = split_string(args, ':'); - apply_continuation_thread(c, thread); - singlestep_execution(c); - } - else if (name == "Cont;c") - { - const auto [thread, _] = split_string(args, ':'); - apply_continuation_thread(c, thread); - continue_execution(c); + + store_continuation_thread(c, thread); + resume_execution(c, singlestep); } else { @@ -496,11 +496,11 @@ namespace gdb_stub switch (command) { case 'c': - continue_execution(c); + resume_execution(c, false); break; case 's': - singlestep_execution(c); + resume_execution(c, true); break; case 'q':