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 {