From 8adc73de71930e1f3eec1c81d1dbb395fab3c0e5 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 12 Jan 2025 11:39:20 +0100 Subject: [PATCH 01/20] Prepare custom gdb-stub implementation --- src/CMakeLists.txt | 1 + src/analyzer/CMakeLists.txt | 1 + src/analyzer/main.cpp | 3 + src/gdb-stub/CMakeLists.txt | 21 ++++++ src/gdb-stub/gdb_stub.cpp | 130 ++++++++++++++++++++++++++++++++++++ src/gdb-stub/gdb_stub.hpp | 48 +++++++++++++ 6 files changed, 204 insertions(+) create mode 100644 src/gdb-stub/CMakeLists.txt create mode 100644 src/gdb-stub/gdb_stub.cpp create mode 100644 src/gdb-stub/gdb_stub.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 24b3d98f..77aef22b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,6 @@ add_subdirectory(common) add_subdirectory(emulator) +add_subdirectory(gdb-stub) add_subdirectory(unicorn-emulator) add_subdirectory(windows-emulator) if (NOT MOMO_BUILD_AS_LIBRARY) diff --git a/src/analyzer/CMakeLists.txt b/src/analyzer/CMakeLists.txt index 54c497d1..2fbf8005 100644 --- a/src/analyzer/CMakeLists.txt +++ b/src/analyzer/CMakeLists.txt @@ -15,6 +15,7 @@ target_precompile_headers(analyzer PRIVATE std_include.hpp) target_link_libraries(analyzer PRIVATE reflect windows-emulator + gdb-stub ) set_property(GLOBAL PROPERTY VS_STARTUP_PROJECT analyzer) diff --git a/src/analyzer/main.cpp b/src/analyzer/main.cpp index d33d82ea..6dbdafdc 100644 --- a/src/analyzer/main.cpp +++ b/src/analyzer/main.cpp @@ -4,6 +4,7 @@ #include #include "object_watching.hpp" +#include "gdb-stub/gdb_stub.hpp" namespace { @@ -229,6 +230,8 @@ int main(const int argc, char** argv) { try { + gdb_stub::run_gdb_stub(network::address{"0.0.0.0:28960", AF_INET}); + auto args = bundle_arguments(argc, argv); const auto options = parse_options(args); diff --git a/src/gdb-stub/CMakeLists.txt b/src/gdb-stub/CMakeLists.txt new file mode 100644 index 00000000..ca23c4c1 --- /dev/null +++ b/src/gdb-stub/CMakeLists.txt @@ -0,0 +1,21 @@ +file(GLOB_RECURSE SRC_FILES CONFIGURE_DEPENDS + *.cpp + *.hpp + *.rc +) + +list(SORT SRC_FILES) + +add_library(gdb-stub ${SRC_FILES}) + +momo_assign_source_group(${SRC_FILES}) + +target_link_libraries(gdb-stub PRIVATE + common +) + +target_include_directories(gdb-stub INTERFACE + "${CMAKE_CURRENT_LIST_DIR}" +) + +momo_strip_target(gdb-stub) diff --git a/src/gdb-stub/gdb_stub.cpp b/src/gdb-stub/gdb_stub.cpp new file mode 100644 index 00000000..85dd688c --- /dev/null +++ b/src/gdb-stub/gdb_stub.cpp @@ -0,0 +1,130 @@ +#include "gdb_stub.hpp" + +#include +#include +#include + +namespace gdb_stub +{ + namespace + { + constexpr size_t CHECKSUM_SIZE = 2; + + uint8_t compute_checksum(const std::string_view data) + { + uint8_t csum = 0; + for (const auto c : data) + { + csum += static_cast(c); + } + + return csum; + } + + network::tcp_client_socket accept_client(const network::address& bind_address) + { + network::tcp_server_socket server{bind_address.get_family()}; + if (!server.bind(bind_address)) + { + return false; + } + + return server.accept(); + } + + struct packet_queue + { + std::string buffer{}; + std::queue packets{}; + + void enqueue(const std::string& data) + { + buffer.append(data); + this->process(); + } + + void process() + { + while (true) + { + this->trim_start(); + + const auto end = this->buffer.find_first_of('#'); + if (end == std::string::npos) + { + break; + } + + const auto packet_size = end + CHECKSUM_SIZE + 1; + + if (packet_size > this->buffer.size()) + { + break; + } + + auto packet = this->buffer.substr(0, packet_size); + this->buffer.erase(0, packet_size); + + this->enqueue_packet(std::move(packet)); + } + } + + void enqueue_packet(std::string packet) + { + constexpr auto END_BYTES = CHECKSUM_SIZE + 1; + + if (packet.size() < (END_BYTES + 1) // + || packet.front() != '$' // + || packet[packet.size() - END_BYTES] != '#') + { + return; + } + + const auto checksum = strtoul(packet.c_str() + packet.size() - CHECKSUM_SIZE, nullptr, 16); + assert((checksum & 0xFF) == checksum); + + packet.erase(packet.begin()); + packet.erase(packet.size() - END_BYTES, END_BYTES); + + const auto computed_checksum = compute_checksum(packet); + + if (computed_checksum == checksum) + { + this->packets.push(std::move(packet)); + } + } + + void trim_start() + { + while (!this->buffer.empty() && this->buffer.front() != '$') + { + buffer.erase(buffer.begin()); + } + } + }; + } + + bool run_gdb_stub(const network::address& bind_address) + { + const auto client = accept_client(bind_address); + if (!client) + { + return false; + } + + packet_queue queue{}; + + while (true) + { + std::string packet{}; + if (!client.receive(packet)) + { + break; + } + + queue.enqueue(packet); + } + + return true; + } +} diff --git a/src/gdb-stub/gdb_stub.hpp b/src/gdb-stub/gdb_stub.hpp new file mode 100644 index 00000000..c8a09ae8 --- /dev/null +++ b/src/gdb-stub/gdb_stub.hpp @@ -0,0 +1,48 @@ +#pragma once +#include +#include + +namespace gdb_stub +{ + /* + enum class gdb_action : uint8_t + { + none, + resume, + shutdown, + }; + + enum class breakpoint_type : uint8_t + { + software, + hardware_exec, + hardware_write, + hardware_read, + hardware_read_write, + }; + + struct gdb_stub_handler + { + virtual ~gdb_stub_handler() = default; + + virtual gdb_action cont() = 0; + virtual gdb_action stepi() = 0; + + virtual bool read_reg(int regno, size_t* value) = 0; + virtual bool write_reg(int regno, size_t value) = 0; + + virtual bool read_mem(size_t addr, size_t len, void* val) = 0; + virtual bool write_mem(size_t addr, size_t len, void* val) = 0; + + virtual bool set_bp(breakpoint_type type, size_t addr, size_t size) = 0; + virtual bool del_bp(breakpoint_type type, size_t addr, size_t size) = 0; + + virtual void on_interrupt() = 0; + + virtual std::string get_target_description() const = 0; + }; + + bool run_gdb_stub(gdb_stub_handler& handler, size_t register_count, const network::address& bind_address); + */ + bool run_gdb_stub(const network::address& bind_address); +} From 9e268ea4dc0f3c21f38e840db2473d5f8abceeeb Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 12 Jan 2025 16:03:41 +0100 Subject: [PATCH 02/20] GDB stub progress --- src/common/network/address.hpp | 8 +++ src/common/network/socket.cpp | 36 ++++++---- src/common/network/socket.hpp | 21 +++--- src/common/network/tcp_client_socket.cpp | 45 ++++++++++--- src/common/network/tcp_client_socket.hpp | 2 +- src/common/network/udp_socket.cpp | 26 +++++--- src/common/network/udp_socket.hpp | 2 +- src/gdb-stub/gdb_stub.cpp | 65 ++++++++++++++++--- src/windows-emulator/devices/afd_endpoint.cpp | 4 +- 9 files changed, 159 insertions(+), 50 deletions(-) diff --git a/src/common/network/address.hpp b/src/common/network/address.hpp index dbf11e18..d42604b0 100644 --- a/src/common/network/address.hpp +++ b/src/common/network/address.hpp @@ -48,6 +48,14 @@ namespace network address(const sockaddr_in6& addr); address(const sockaddr* addr, socklen_t length); + address(const address&) = default; + address(address&&) noexcept = default; + + address& operator=(const address&) = default; + address& operator=(address&&) noexcept = default; + + ~address() = default; + void set_ipv4(uint32_t ip); void set_ipv4(const in_addr& addr); void set_ipv6(const in6_addr& addr); diff --git a/src/common/network/socket.cpp b/src/common/network/socket.cpp index 3339d3f4..1e057da7 100644 --- a/src/common/network/socket.cpp +++ b/src/common/network/socket.cpp @@ -31,7 +31,7 @@ namespace network socket::~socket() { - this->release(); + this->close(); } socket::socket(socket&& obj) noexcept @@ -43,7 +43,7 @@ namespace network { if (this != &obj) { - this->release(); + this->close(); this->socket_ = obj.socket_; obj.socket_ = INVALID_SOCKET; @@ -53,15 +53,20 @@ namespace network } socket::operator bool() const + { + return this->is_valid(); + } + + bool socket::is_valid() const { return this->socket_ != INVALID_SOCKET; } - void socket::release() + void socket::close() { if (this->socket_ != INVALID_SOCKET) { - closesocket(this->socket_); + ::closesocket(this->socket_); this->socket_ = INVALID_SOCKET; } } @@ -90,7 +95,7 @@ namespace network #endif } - bool socket::sleep(const std::chrono::milliseconds timeout) const + bool socket::sleep(const std::chrono::milliseconds timeout, const bool in_poll) const { /*fd_set fdr; FD_ZERO(&fdr); @@ -119,13 +124,13 @@ namespace network std::vector sockets{}; sockets.push_back(this); - return sleep_sockets(sockets, timeout); + return sleep_sockets(sockets, timeout, in_poll); } - bool socket::sleep_until(const std::chrono::high_resolution_clock::time_point time_point) const + bool socket::sleep_until(const std::chrono::high_resolution_clock::time_point time_point, const bool in_poll) const { const auto duration = time_point - std::chrono::high_resolution_clock::now(); - return this->sleep(std::chrono::duration_cast(duration)); + return this->sleep(std::chrono::duration_cast(duration), in_poll); } SOCKET socket::get_socket() const @@ -167,7 +172,13 @@ namespace network return address->get_addr().sa_family; } - bool socket::sleep_sockets(const std::span& sockets, const std::chrono::milliseconds timeout) + bool socket::is_ready(const bool in_poll) const + { + return this->is_valid() && is_socket_ready(this->socket_, in_poll); + } + + bool socket::sleep_sockets(const std::span& sockets, const std::chrono::milliseconds timeout, + const bool in_poll) { std::vector pfds{}; pfds.resize(sockets.size()); @@ -178,7 +189,7 @@ namespace network const auto& socket = sockets[i]; pfd.fd = socket->get_socket(); - pfd.events = POLLIN; + pfd.events = in_poll ? POLLIN : POLLOUT; pfd.revents = 0; } @@ -223,9 +234,10 @@ namespace network } bool socket::sleep_sockets_until(const std::span& sockets, - const std::chrono::high_resolution_clock::time_point time_point) + const std::chrono::high_resolution_clock::time_point time_point, + const bool in_poll) { const auto duration = time_point - std::chrono::high_resolution_clock::now(); - return sleep_sockets(sockets, std::chrono::duration_cast(duration)); + return sleep_sockets(sockets, std::chrono::duration_cast(duration), in_poll); } } diff --git a/src/common/network/socket.hpp b/src/common/network/socket.hpp index 4696fb88..957c9c1b 100644 --- a/src/common/network/socket.hpp +++ b/src/common/network/socket.hpp @@ -10,7 +10,7 @@ using send_size = int; #define GET_SOCKET_ERROR() (WSAGetLastError()) #define poll WSAPoll -#define SOCK_WOULDBLOCK WSAEWOULDBLOCK +#define SERR(x) (WSA##x) #define SHUT_RDWR SD_BOTH #else using SOCKET = int; @@ -19,7 +19,7 @@ using send_size = size_t; #define SOCKET_ERROR (-1) #define GET_SOCKET_ERROR() (errno) #define closesocket close -#define SOCK_WOULDBLOCK EWOULDBLOCK +#define SERR(x) (x) #endif namespace network @@ -42,14 +42,16 @@ namespace network operator bool() const; + bool is_valid() const; + bool bind(const address& target); bool set_blocking(bool blocking); static bool set_blocking(SOCKET s, bool blocking); static constexpr bool socket_is_ready = true; - bool sleep(std::chrono::milliseconds timeout) const; - bool sleep_until(std::chrono::high_resolution_clock::time_point time_point) const; + bool sleep(std::chrono::milliseconds timeout, bool in_poll = true) const; + bool sleep_until(std::chrono::high_resolution_clock::time_point time_point, bool in_poll = true) const; SOCKET get_socket() const; uint16_t get_port() const; @@ -57,15 +59,18 @@ namespace network int get_address_family() const; - static bool sleep_sockets(const std::span& sockets, std::chrono::milliseconds timeout); + bool is_ready(bool in_poll) const; + + static bool sleep_sockets(const std::span& sockets, std::chrono::milliseconds timeout, + bool in_poll); static bool sleep_sockets_until(const std::span& sockets, - std::chrono::high_resolution_clock::time_point time_point); + std::chrono::high_resolution_clock::time_point time_point, bool in_poll); static bool is_socket_ready(SOCKET s, bool in_poll); + void close(); + private: SOCKET socket_ = INVALID_SOCKET; - - void release(); }; } diff --git a/src/common/network/tcp_client_socket.cpp b/src/common/network/tcp_client_socket.cpp index 9739b394..efb4ad34 100644 --- a/src/common/network/tcp_client_socket.cpp +++ b/src/common/network/tcp_client_socket.cpp @@ -26,27 +26,52 @@ namespace network bool tcp_client_socket::send(const void* data, const size_t size) const { - const auto res = ::send(this->get_socket(), static_cast(data), static_cast(size), 0); - return static_cast(res) == size; + return this->send(std::string_view(static_cast(data), size)); } - bool tcp_client_socket::send(const std::string_view data) const + bool tcp_client_socket::send(std::string_view data) const { - return this->send(data.data(), data.size()); + while (!data.empty()) + { + const auto res = ::send(this->get_socket(), data.data(), static_cast(data.size()), 0); + if (res < 0) + { + if (GET_SOCKET_ERROR() != SERR(EWOULDBLOCK)) + { + break; + } + + this->sleep(std::chrono::milliseconds(10), true); + continue; + } + + if (static_cast(res) > data.size()) + { + break; + } + + data = data.substr(res); + } + + return data.empty(); } - bool tcp_client_socket::receive(std::string& data) const + std::optional tcp_client_socket::receive() { char buffer[0x2000]; const auto result = recv(this->get_socket(), buffer, static_cast(sizeof(buffer)), 0); - if (result == SOCKET_ERROR) + if (result > 0) { - return false; + return std::string(buffer, result); } - data.assign(buffer, buffer + result); - return true; + if (result == 0 || (result < 0 && GET_SOCKET_ERROR() == SERR(ECONNRESET))) + { + this->close(); + } + + return std::nullopt; } std::optional
tcp_client_socket::get_target() const @@ -69,6 +94,6 @@ namespace network } const auto error = GET_SOCKET_ERROR(); - return error == SOCK_WOULDBLOCK; + return error == SERR(EWOULDBLOCK); } } diff --git a/src/common/network/tcp_client_socket.hpp b/src/common/network/tcp_client_socket.hpp index e5b586a1..ec032601 100644 --- a/src/common/network/tcp_client_socket.hpp +++ b/src/common/network/tcp_client_socket.hpp @@ -21,7 +21,7 @@ namespace network [[maybe_unused]] bool send(const void* data, size_t size) const; [[maybe_unused]] bool send(std::string_view data) const; - bool receive(std::string& data) const; + std::optional receive(); std::optional
get_target() const; diff --git a/src/common/network/udp_socket.cpp b/src/common/network/udp_socket.cpp index 99c6e9e6..379ccbab 100644 --- a/src/common/network/udp_socket.cpp +++ b/src/common/network/udp_socket.cpp @@ -9,29 +9,39 @@ namespace network bool udp_socket::send(const address& target, const void* data, const size_t size) const { - const auto res = sendto(this->get_socket(), static_cast(data), static_cast(size), 0, - &target.get_addr(), target.get_size()); - return static_cast(res) == size; + return this->send(target, std::string_view(static_cast(data), size)); } bool udp_socket::send(const address& target, const std::string_view data) const { - return this->send(target, data.data(), data.size()); + while (true) + { + const auto res = sendto(this->get_socket(), data.data(), static_cast(data.size()), 0, + &target.get_addr(), target.get_size()); + + if (res < 0 && GET_SOCKET_ERROR() == SERR(EWOULDBLOCK)) + { + this->sleep(std::chrono::milliseconds(10), true); + continue; + } + + return static_cast(res) == data.size(); + } } - bool udp_socket::receive(address& source, std::string& data) const + std::optional> udp_socket::receive() const { char buffer[0x2000]; + address source{}; auto len = source.get_max_size(); const auto result = recvfrom(this->get_socket(), buffer, static_cast(sizeof(buffer)), 0, &source.get_addr(), &len); if (result == SOCKET_ERROR) { - return false; + return std::nullopt; } - data.assign(buffer, buffer + result); - return true; + return {{source, std::string(buffer, result)}}; } } diff --git a/src/common/network/udp_socket.hpp b/src/common/network/udp_socket.hpp index 2e240728..affbe6c8 100644 --- a/src/common/network/udp_socket.hpp +++ b/src/common/network/udp_socket.hpp @@ -17,6 +17,6 @@ namespace network [[maybe_unused]] bool send(const address& target, const void* data, size_t size) const; [[maybe_unused]] bool send(const address& target, std::string_view data) const; - bool receive(address& source, std::string& data) const; + std::optional> receive() const; }; } diff --git a/src/gdb-stub/gdb_stub.cpp b/src/gdb-stub/gdb_stub.cpp index 85dd688c..4c983b83 100644 --- a/src/gdb-stub/gdb_stub.cpp +++ b/src/gdb-stub/gdb_stub.cpp @@ -21,6 +21,21 @@ namespace gdb_stub return csum; } + std::string compute_checksum_string(const std::string_view data) + { + const auto checksum = compute_checksum(data); + + std::stringstream stream{}; + stream << std::hex << checksum; + return stream.str(); + } + + bool send_packet_reply(const network::tcp_client_socket& socket, const std::string_view data) + { + const auto checksum = compute_checksum_string(data); + return socket.send("$" + std::string(data) + "#" + checksum); + } + network::tcp_client_socket accept_client(const network::address& bind_address) { network::tcp_server_socket server{bind_address.get_family()}; @@ -102,27 +117,61 @@ namespace gdb_stub } } }; + + void read_from_socket(packet_queue& queue, network::tcp_client_socket& client) + { + while (client.is_ready(true)) + { + auto packet = client.receive(); + if (packet) + { + queue.enqueue(std::move(*packet)); + } + } + } + + void handle_command(const uint8_t command, const std::string_view data) + { + switch (command) + { + case 'q': + break; + } + } + + void process_packet(const std::string_view packet) + { + if (packet.empty()) + { + return; + } + + const auto command = packet.front(); + handle_command(command, packet.substr(1)); + } } bool run_gdb_stub(const network::address& bind_address) { - const auto client = accept_client(bind_address); + packet_queue queue{}; + + auto client = accept_client(bind_address); if (!client) { return false; } - packet_queue queue{}; + client.set_blocking(false); while (true) { - std::string packet{}; - if (!client.receive(packet)) - { - break; - } + read_from_socket(queue, client); - queue.enqueue(packet); + while (!queue.packets.empty()) + { + process_packet(queue.packets.front()); + queue.packets.pop(); + } } return true; diff --git a/src/windows-emulator/devices/afd_endpoint.cpp b/src/windows-emulator/devices/afd_endpoint.cpp index f0ab1cb0..1a364eb4 100644 --- a/src/windows-emulator/devices/afd_endpoint.cpp +++ b/src/windows-emulator/devices/afd_endpoint.cpp @@ -457,7 +457,7 @@ namespace if (recevied_data < 0) { const auto error = GET_SOCKET_ERROR(); - if (error == SOCK_WOULDBLOCK) + if (error == SERR(EWOULDBLOCK)) { this->delay_ioctrl(c, {}, true); return STATUS_PENDING; @@ -512,7 +512,7 @@ namespace if (sent_data < 0) { const auto error = GET_SOCKET_ERROR(); - if (error == SOCK_WOULDBLOCK) + if (error == SERR(EWOULDBLOCK)) { this->delay_ioctrl(c, {}, false); return STATUS_PENDING; From 942411686c10e873abac64238b36aa71491a5a90 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 12 Jan 2025 19:19:37 +0100 Subject: [PATCH 03/20] Progress --- src/gdb-stub/gdb_stub.cpp | 71 ++++++++++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 8 deletions(-) diff --git a/src/gdb-stub/gdb_stub.cpp b/src/gdb-stub/gdb_stub.cpp index 4c983b83..1ed91092 100644 --- a/src/gdb-stub/gdb_stub.cpp +++ b/src/gdb-stub/gdb_stub.cpp @@ -10,6 +10,14 @@ namespace gdb_stub { constexpr size_t CHECKSUM_SIZE = 2; + enum class continuation_event + { + none, + cont, + detach, + step, + }; + uint8_t compute_checksum(const std::string_view data) { uint8_t csum = 0; @@ -130,24 +138,71 @@ namespace gdb_stub } } - void handle_command(const uint8_t command, const std::string_view data) + void process_query(const network::tcp_client_socket& client, const std::string_view payload) { - switch (command) + auto name = payload; + std::string_view args{}; + + const auto separator = payload.find_first_of(':'); + if (separator != std::string_view::npos) + { - case 'q': - break; + name = payload.substr(0, separator); + args = payload.substr(separator + 1); + } + + if (name == "Supported") + { + send_packet_reply(client, "PacketSize=1024;qXfer:features:read+"); + } + else if (name == "Attached") + { + send_packet_reply(client, "1"); + } + else if (name == "Xfer") + { + // process_xfer(gdbstub, args); + } + else if (name == "Symbol") + { + send_packet_reply(client, "OK"); + } + else + { + send_packet_reply(client, {}); } } - void process_packet(const std::string_view packet) + continuation_event handle_command(const network::tcp_client_socket& client, const uint8_t command, + const std::string_view data) { + auto event = continuation_event::none; + + switch (command) + { + case 'q': + process_query(client, data); + break; + + default: + send_packet_reply(client, {}); + break; + } + + return event; + } + + void process_packet(const network::tcp_client_socket& client, const std::string_view packet) + { + (void)client.send("+"); + if (packet.empty()) { return; } const auto command = packet.front(); - handle_command(command, packet.substr(1)); + const auto event = handle_command(client, command, packet.substr(1)); } } @@ -163,13 +218,13 @@ namespace gdb_stub client.set_blocking(false); - while (true) + while (client.is_valid()) { read_from_socket(queue, client); while (!queue.packets.empty()) { - process_packet(queue.packets.front()); + process_packet(client, queue.packets.front()); queue.packets.pop(); } } From 64179c658080caf31c90e98bcc05644f8f3e86dd Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 17 Jan 2025 15:11:48 +0100 Subject: [PATCH 04/20] Introduce helper classes --- src/gdb-stub/CMakeLists.txt | 2 +- src/gdb-stub/async_handler.cpp | 77 +++++++++++++++++++ src/gdb-stub/async_handler.hpp | 38 ++++++++++ src/gdb-stub/checksum.hpp | 28 +++++++ src/gdb-stub/gdb_stub.cpp | 118 ++++-------------------------- src/gdb-stub/stream_processor.cpp | 93 +++++++++++++++++++++++ src/gdb-stub/stream_processor.hpp | 22 ++++++ 7 files changed, 272 insertions(+), 106 deletions(-) create mode 100644 src/gdb-stub/async_handler.cpp create mode 100644 src/gdb-stub/async_handler.hpp create mode 100644 src/gdb-stub/checksum.hpp create mode 100644 src/gdb-stub/stream_processor.cpp create mode 100644 src/gdb-stub/stream_processor.hpp diff --git a/src/gdb-stub/CMakeLists.txt b/src/gdb-stub/CMakeLists.txt index ca23c4c1..2b3e2d80 100644 --- a/src/gdb-stub/CMakeLists.txt +++ b/src/gdb-stub/CMakeLists.txt @@ -11,7 +11,7 @@ add_library(gdb-stub ${SRC_FILES}) momo_assign_source_group(${SRC_FILES}) target_link_libraries(gdb-stub PRIVATE - common + emulator-common ) target_include_directories(gdb-stub INTERFACE diff --git a/src/gdb-stub/async_handler.cpp b/src/gdb-stub/async_handler.cpp new file mode 100644 index 00000000..8afb0ce3 --- /dev/null +++ b/src/gdb-stub/async_handler.cpp @@ -0,0 +1,77 @@ +#include "async_handler.hpp" +#include + +using namespace std::chrono_literals; + +namespace gdb_stub +{ + async_handler::async_handler(handler_function h) + : handler_(std::move(h)) + { + this->stop_ = false; + } + + async_handler::~async_handler() + { + this->stop_ = true; + this->run_ = false; + + if (this->runner_.joinable()) + { + this->runner_.join(); + } + } + + void async_handler::pause() + { + this->run_ = false; + + while (this->is_running_ && !this->stop_) + { + std::this_thread::sleep_for(1ms); + } + } + + void async_handler::run() + { + if (this->stop_) + { + return; + } + + this->run_ = true; + + while (!this->is_running_ && !this->stop_) + { + std::this_thread::sleep_for(1ms); + } + } + + bool async_handler::is_running() const + { + return this->is_running_; + } + + void async_handler::work() + { + while (true) + { + while (!this->run_ && !this->stop_) + { + std::this_thread::sleep_for(10ms); + } + + if (this->stop_) + { + break; + } + + const auto _ = utils::finally([this] { + this->is_running_ = false; // + }); + + this->is_running_ = true; + this->handler_(this->run_); + } + } +} diff --git a/src/gdb-stub/async_handler.hpp b/src/gdb-stub/async_handler.hpp new file mode 100644 index 00000000..30bfd769 --- /dev/null +++ b/src/gdb-stub/async_handler.hpp @@ -0,0 +1,38 @@ +#pragma once +#include +#include +#include +#include + +namespace gdb_stub +{ + class async_handler + { + public: + using handler = void(const std::atomic_bool& should_run); + using handler_function = std::function; + + async_handler(handler_function handler); + ~async_handler(); + + async_handler(async_handler&&) = delete; + async_handler(const async_handler&) = delete; + + async_handler& operator=(async_handler&&) = delete; + async_handler& operator=(const async_handler&) = delete; + + void run(); + void pause(); + bool is_running() const; + + private: + std::atomic_bool run_{false}; + std::atomic_bool stop_{false}; + std::atomic_bool is_running_{false}; + + handler_function handler_{}; + std::thread runner_{}; + + void work(); + }; +} diff --git a/src/gdb-stub/checksum.hpp b/src/gdb-stub/checksum.hpp new file mode 100644 index 00000000..1ad71bc1 --- /dev/null +++ b/src/gdb-stub/checksum.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include + +namespace gdb_stub +{ + constexpr size_t CHECKSUM_SIZE = 2; + + inline uint8_t compute_checksum(const std::string_view data) + { + uint8_t checksum = 0; + for (const auto c : data) + { + checksum += static_cast(c); + } + + return checksum; + } + + inline std::string compute_checksum_as_string(const std::string_view data) + { + const auto checksum = compute_checksum(data); + + std::stringstream stream{}; + stream << std::hex << checksum; + return stream.str(); + } +} diff --git a/src/gdb-stub/gdb_stub.cpp b/src/gdb-stub/gdb_stub.cpp index 1ed91092..a587fb79 100644 --- a/src/gdb-stub/gdb_stub.cpp +++ b/src/gdb-stub/gdb_stub.cpp @@ -1,15 +1,14 @@ #include "gdb_stub.hpp" -#include -#include #include +#include "stream_processor.hpp" +#include "checksum.hpp" + namespace gdb_stub { namespace { - constexpr size_t CHECKSUM_SIZE = 2; - enum class continuation_event { none, @@ -18,29 +17,9 @@ namespace gdb_stub step, }; - uint8_t compute_checksum(const std::string_view data) - { - uint8_t csum = 0; - for (const auto c : data) - { - csum += static_cast(c); - } - - return csum; - } - - std::string compute_checksum_string(const std::string_view data) - { - const auto checksum = compute_checksum(data); - - std::stringstream stream{}; - stream << std::hex << checksum; - return stream.str(); - } - bool send_packet_reply(const network::tcp_client_socket& socket, const std::string_view data) { - const auto checksum = compute_checksum_string(data); + const auto checksum = compute_checksum_as_string(data); return socket.send("$" + std::string(data) + "#" + checksum); } @@ -55,85 +34,14 @@ namespace gdb_stub return server.accept(); } - struct packet_queue - { - std::string buffer{}; - std::queue packets{}; - - void enqueue(const std::string& data) - { - buffer.append(data); - this->process(); - } - - void process() - { - while (true) - { - this->trim_start(); - - const auto end = this->buffer.find_first_of('#'); - if (end == std::string::npos) - { - break; - } - - const auto packet_size = end + CHECKSUM_SIZE + 1; - - if (packet_size > this->buffer.size()) - { - break; - } - - auto packet = this->buffer.substr(0, packet_size); - this->buffer.erase(0, packet_size); - - this->enqueue_packet(std::move(packet)); - } - } - - void enqueue_packet(std::string packet) - { - constexpr auto END_BYTES = CHECKSUM_SIZE + 1; - - if (packet.size() < (END_BYTES + 1) // - || packet.front() != '$' // - || packet[packet.size() - END_BYTES] != '#') - { - return; - } - - const auto checksum = strtoul(packet.c_str() + packet.size() - CHECKSUM_SIZE, nullptr, 16); - assert((checksum & 0xFF) == checksum); - - packet.erase(packet.begin()); - packet.erase(packet.size() - END_BYTES, END_BYTES); - - const auto computed_checksum = compute_checksum(packet); - - if (computed_checksum == checksum) - { - this->packets.push(std::move(packet)); - } - } - - void trim_start() - { - while (!this->buffer.empty() && this->buffer.front() != '$') - { - buffer.erase(buffer.begin()); - } - } - }; - - void read_from_socket(packet_queue& queue, network::tcp_client_socket& client) + void read_from_socket(stream_processor& processor, network::tcp_client_socket& client) { while (client.is_ready(true)) { - auto packet = client.receive(); - if (packet) + const auto data = client.receive(); + if (data) { - queue.enqueue(std::move(*packet)); + processor.push_stream_data(*data); } } } @@ -208,7 +116,7 @@ namespace gdb_stub bool run_gdb_stub(const network::address& bind_address) { - packet_queue queue{}; + stream_processor processor{}; auto client = accept_client(bind_address); if (!client) @@ -220,12 +128,12 @@ namespace gdb_stub while (client.is_valid()) { - read_from_socket(queue, client); + read_from_socket(processor, client); - while (!queue.packets.empty()) + while (processor.has_packet()) { - process_packet(client, queue.packets.front()); - queue.packets.pop(); + const auto packet = processor.get_next_packet(); + process_packet(client, packet); } } diff --git a/src/gdb-stub/stream_processor.cpp b/src/gdb-stub/stream_processor.cpp new file mode 100644 index 00000000..ba3aabac --- /dev/null +++ b/src/gdb-stub/stream_processor.cpp @@ -0,0 +1,93 @@ +#include "stream_processor.hpp" +#include "checksum.hpp" + +#include + +namespace gdb_stub +{ + namespace + { + void trim_data_stream_start(std::string& stream) + { + while (!stream.empty() && stream.front() != '$') + { + stream.erase(stream.begin()); + } + } + } + + bool stream_processor::has_packet() const + { + return !this->packets_.empty(); + } + + std::string stream_processor::get_next_packet() + { + if (this->packets_.empty()) + { + throw std::runtime_error("No packet available"); + } + + auto packet = std::move(this->packets_.front()); + this->packets_.pop(); + + return packet; + } + + void stream_processor::push_stream_data(const std::string& data) + { + this->stream_.append(data); + this->process_data_stream(); + } + + void stream_processor::process_data_stream() + { + while (true) + { + trim_data_stream_start(this->stream_); + + const auto end = this->stream_.find_first_of('#'); + if (end == std::string::npos) + { + break; + } + + const auto packet_size = end + CHECKSUM_SIZE + 1; + + if (packet_size > this->stream_.size()) + { + break; + } + + auto packet = this->stream_.substr(0, packet_size); + this->stream_.erase(0, packet_size); + + this->enqueue_packet(std::move(packet)); + } + } + + void stream_processor::enqueue_packet(std::string packet) + { + constexpr auto END_BYTES = CHECKSUM_SIZE + 1; + + if (packet.size() < (END_BYTES + 1) // + || packet.front() != '$' // + || packet[packet.size() - END_BYTES] != '#') + { + return; + } + + const auto checksum = strtoul(packet.c_str() + packet.size() - CHECKSUM_SIZE, nullptr, 16); + assert((checksum & 0xFF) == checksum); + + packet.erase(packet.begin()); + packet.erase(packet.size() - END_BYTES, END_BYTES); + + const auto computed_checksum = compute_checksum(packet); + + if (computed_checksum == checksum) + { + this->packets_.push(std::move(packet)); + } + } +} diff --git a/src/gdb-stub/stream_processor.hpp b/src/gdb-stub/stream_processor.hpp new file mode 100644 index 00000000..c6b48442 --- /dev/null +++ b/src/gdb-stub/stream_processor.hpp @@ -0,0 +1,22 @@ +#pragma once +#include +#include +#include + +namespace gdb_stub +{ + class stream_processor + { + public: + bool has_packet() const; + std::string get_next_packet(); + void push_stream_data(const std::string& data); + + private: + std::string stream_{}; + std::queue packets_{}; + + void process_data_stream(); + void enqueue_packet(std::string packet); + }; +} From 7d62d1e20e39b2840ef95aefc415c2c98db9baaf Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 17 Jan 2025 16:54:57 +0100 Subject: [PATCH 05/20] Support size limit --- src/common/network/tcp_client_socket.cpp | 5 +++-- src/common/network/tcp_client_socket.hpp | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/common/network/tcp_client_socket.cpp b/src/common/network/tcp_client_socket.cpp index efb4ad34..fc5921f1 100644 --- a/src/common/network/tcp_client_socket.cpp +++ b/src/common/network/tcp_client_socket.cpp @@ -56,11 +56,12 @@ namespace network return data.empty(); } - std::optional tcp_client_socket::receive() + std::optional tcp_client_socket::receive(const std::optional max_size) { char buffer[0x2000]; + const auto size = std::min(sizeof(buffer), max_size.value_or(sizeof(buffer))); - const auto result = recv(this->get_socket(), buffer, static_cast(sizeof(buffer)), 0); + const auto result = recv(this->get_socket(), buffer, static_cast(size), 0); if (result > 0) { return std::string(buffer, result); diff --git a/src/common/network/tcp_client_socket.hpp b/src/common/network/tcp_client_socket.hpp index ec032601..187829be 100644 --- a/src/common/network/tcp_client_socket.hpp +++ b/src/common/network/tcp_client_socket.hpp @@ -21,7 +21,7 @@ namespace network [[maybe_unused]] bool send(const void* data, size_t size) const; [[maybe_unused]] bool send(std::string_view data) const; - std::optional receive(); + std::optional receive(std::optional max_size = std::nullopt); std::optional
get_target() const; From 0253592ae96b63e461fdfc7c8dd89f8d9078489b Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 17 Jan 2025 17:23:05 +0100 Subject: [PATCH 06/20] Integrate new gdb stub --- src/analyzer/main.cpp | 7 +- src/gdb-stub/async_handler.hpp | 2 +- src/gdb-stub/checksum.hpp | 1 + src/gdb-stub/gdb_stub.cpp | 26 +++- src/gdb-stub/gdb_stub.hpp | 5 +- src/windows-emulator/debugging/gdb_stub.cpp | 136 ------------------ src/windows-emulator/debugging/gdb_stub.hpp | 39 ----- .../debugging/win_x64_gdb_stub_handler.hpp | 17 ++- .../debugging/x64_gdb_stub_handler.hpp | 32 +++-- 9 files changed, 58 insertions(+), 207 deletions(-) delete mode 100644 src/windows-emulator/debugging/gdb_stub.cpp delete mode 100644 src/windows-emulator/debugging/gdb_stub.hpp diff --git a/src/analyzer/main.cpp b/src/analyzer/main.cpp index 6dbdafdc..ef180990 100644 --- a/src/analyzer/main.cpp +++ b/src/analyzer/main.cpp @@ -4,7 +4,6 @@ #include #include "object_watching.hpp" -#include "gdb-stub/gdb_stub.hpp" namespace { @@ -53,7 +52,7 @@ namespace win_emu.log.print(color::pink, "Waiting for GDB connection on %s...\n", address); win_x64_gdb_stub_handler handler{win_emu}; - run_gdb_stub(handler, "i386:x86-64", gdb_registers.size(), address); + gdb_stub::run_gdb_stub(network::address{"0.0.0.0:28960", AF_INET}, handler); } else { @@ -182,7 +181,7 @@ namespace for (int i = 1; i < argc; ++i) { - args.push_back(argv[i]); + args.emplace_back(argv[i]); } return args; @@ -230,8 +229,6 @@ int main(const int argc, char** argv) { try { - gdb_stub::run_gdb_stub(network::address{"0.0.0.0:28960", AF_INET}); - auto args = bundle_arguments(argc, argv); const auto options = parse_options(args); diff --git a/src/gdb-stub/async_handler.hpp b/src/gdb-stub/async_handler.hpp index 30bfd769..608fdd66 100644 --- a/src/gdb-stub/async_handler.hpp +++ b/src/gdb-stub/async_handler.hpp @@ -9,7 +9,7 @@ namespace gdb_stub class async_handler { public: - using handler = void(const std::atomic_bool& should_run); + using handler = void(std::atomic_bool& should_run); using handler_function = std::function; async_handler(handler_function handler); diff --git a/src/gdb-stub/checksum.hpp b/src/gdb-stub/checksum.hpp index 1ad71bc1..533329d3 100644 --- a/src/gdb-stub/checksum.hpp +++ b/src/gdb-stub/checksum.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include namespace gdb_stub diff --git a/src/gdb-stub/gdb_stub.cpp b/src/gdb-stub/gdb_stub.cpp index a587fb79..4647ec64 100644 --- a/src/gdb-stub/gdb_stub.cpp +++ b/src/gdb-stub/gdb_stub.cpp @@ -3,8 +3,11 @@ #include #include "stream_processor.hpp" +#include "async_handler.hpp" #include "checksum.hpp" +using namespace std::literals; + namespace gdb_stub { namespace @@ -111,10 +114,16 @@ namespace gdb_stub const auto command = packet.front(); const auto event = handle_command(client, command, packet.substr(1)); + (void)event; + } + + bool is_interrupt_packet(const std::optional& data) + { + return data && data->size() == 1 && data->front() == '\x03'; } } - bool run_gdb_stub(const network::address& bind_address) + bool run_gdb_stub(const network::address& bind_address, gdb_stub_handler& handler) { stream_processor processor{}; @@ -124,6 +133,21 @@ namespace gdb_stub return false; } + async_handler async{[&](std::atomic_bool& can_run) { + while (can_run) + { + std::this_thread::sleep_for(10ms); + + const auto data = client.receive(1); + + if (is_interrupt_packet(data) || !client.is_valid()) + { + handler.on_interrupt(); + can_run = false; + } + } + }}; + client.set_blocking(false); while (client.is_valid()) diff --git a/src/gdb-stub/gdb_stub.hpp b/src/gdb-stub/gdb_stub.hpp index c8a09ae8..ec7629ac 100644 --- a/src/gdb-stub/gdb_stub.hpp +++ b/src/gdb-stub/gdb_stub.hpp @@ -4,7 +4,6 @@ namespace gdb_stub { - /* enum class gdb_action : uint8_t { none, @@ -42,7 +41,5 @@ namespace gdb_stub virtual std::string get_target_description() const = 0; }; - bool run_gdb_stub(gdb_stub_handler& handler, size_t register_count, const network::address& bind_address); - */ - bool run_gdb_stub(const network::address& bind_address); + bool run_gdb_stub(const network::address& bind_address, gdb_stub_handler& handler); } diff --git a/src/windows-emulator/debugging/gdb_stub.cpp b/src/windows-emulator/debugging/gdb_stub.cpp deleted file mode 100644 index 6d38a08f..00000000 --- a/src/windows-emulator/debugging/gdb_stub.cpp +++ /dev/null @@ -1,136 +0,0 @@ -#include "../std_include.hpp" -#include "gdb_stub.hpp" - -#include - -extern "C" -{ -#include -} - -namespace -{ - gdb_action_t map_gdb_action(const gdb_action action) - { - switch (action) - { - case gdb_action::none: - return ACT_NONE; - case gdb_action::resume: - return ACT_RESUME; - case gdb_action::shutdown: - return ACT_SHUTDOWN; - } - - throw std::runtime_error("Bad action"); - } - - breakpoint_type map_breakpoint_type(const bp_type_t type) - { - switch (type) - { - case BP_SOFTWARE: - return breakpoint_type::software; - case BP_HARDWARE_EXEC: - return breakpoint_type::hardware_exec; - case BP_HARDWARE_WRITE: - return breakpoint_type::hardware_write; - case BP_HARDWARE_READ: - return breakpoint_type::hardware_read; - case BP_HARDWARE_READ_WRITE: - return breakpoint_type::hardware_read_write; - } - - throw std::runtime_error("Bad breakpoint type"); - } - - gdb_stub_handler& get_handler(void* args) - { - return *static_cast(args); - } - - gdb_action_t cont(void* args) - { - return map_gdb_action(get_handler(args).cont()); - } - - gdb_action_t stepi(void* args) - { - return map_gdb_action(get_handler(args).stepi()); - } - - int read_reg(void* args, const int regno, size_t* value) - { - return get_handler(args).read_reg(regno, value) ? 0 : 1; - } - - int write_reg(void* args, const int regno, const size_t value) - { - return get_handler(args).write_reg(regno, value) ? 0 : 1; - } - - int read_mem(void* args, const size_t addr, const size_t len, void* val) - { - return get_handler(args).read_mem(addr, len, val) ? 0 : 1; - } - - int write_mem(void* args, const size_t addr, const size_t len, void* val) - { - return get_handler(args).write_mem(addr, len, val) ? 0 : 1; - } - - bool set_bp(void* args, const size_t addr, const bp_type_t type, const size_t size) - { - return get_handler(args).set_bp(map_breakpoint_type(type), addr, size); - } - - bool del_bp(void* args, const size_t addr, const bp_type_t type, const size_t size) - { - return get_handler(args).del_bp(map_breakpoint_type(type), addr, size); - } - - void on_interrupt(void* args) - { - get_handler(args).on_interrupt(); - } - - target_ops get_target_ops() - { - target_ops ops{}; - - ops.cont = cont; - ops.stepi = stepi; - ops.read_reg = read_reg; - ops.write_reg = write_reg; - ops.read_mem = read_mem; - ops.write_mem = write_mem; - ops.set_bp = set_bp; - ops.del_bp = del_bp; - ops.on_interrupt = on_interrupt; - - return ops; - } -} - -bool run_gdb_stub(gdb_stub_handler& handler, std::string target_description, const size_t register_count, - std::string bind_address) -{ - const arch_info_t info{ - target_description.data(), - static_cast(register_count), - sizeof(uint64_t), - }; - - auto ops = get_target_ops(); - - gdbstub_t stub{}; - - if (!gdbstub_init(&stub, &ops, info, bind_address.data())) - { - return false; - } - - const auto _ = utils::finally([&] { gdbstub_close(&stub); }); - - return gdbstub_run(&stub, &handler); -} diff --git a/src/windows-emulator/debugging/gdb_stub.hpp b/src/windows-emulator/debugging/gdb_stub.hpp deleted file mode 100644 index de6101f3..00000000 --- a/src/windows-emulator/debugging/gdb_stub.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -enum class gdb_action : uint8_t -{ - none, - resume, - shutdown, -}; - -enum class breakpoint_type : uint8_t -{ - software, - hardware_exec, - hardware_write, - hardware_read, - hardware_read_write, -}; - -struct gdb_stub_handler -{ - virtual ~gdb_stub_handler() = default; - - virtual gdb_action cont() = 0; - virtual gdb_action stepi() = 0; - - virtual bool read_reg(int regno, size_t* value) = 0; - virtual bool write_reg(int regno, size_t value) = 0; - - virtual bool read_mem(size_t addr, size_t len, void* val) = 0; - virtual bool write_mem(size_t addr, size_t len, void* val) = 0; - - virtual bool set_bp(breakpoint_type type, size_t addr, size_t size) = 0; - virtual bool del_bp(breakpoint_type type, size_t addr, size_t size) = 0; - - virtual void on_interrupt() = 0; -}; - -bool run_gdb_stub(gdb_stub_handler& handler, std::string target_description, size_t register_count, - std::string bind_address); 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 ce2bc314..7f9574b6 100644 --- a/src/windows-emulator/debugging/win_x64_gdb_stub_handler.hpp +++ b/src/windows-emulator/debugging/win_x64_gdb_stub_handler.hpp @@ -12,7 +12,7 @@ class win_x64_gdb_stub_handler : public x64_gdb_stub_handler { } - gdb_action cont() override + gdb_stub::gdb_action cont() override { try { @@ -20,13 +20,13 @@ class win_x64_gdb_stub_handler : public x64_gdb_stub_handler } catch (const std::exception& e) { - puts(e.what()); + this->win_emu_->log.error("%s\n", e.what()); } - return gdb_action::resume; + return gdb_stub::gdb_action::resume; } - gdb_action stepi() override + gdb_stub::gdb_action stepi() override { try { @@ -34,10 +34,15 @@ class win_x64_gdb_stub_handler : public x64_gdb_stub_handler } catch (const std::exception& e) { - puts(e.what()); + this->win_emu_->log.error("%s\n", e.what()); } - return gdb_action::resume; + return gdb_stub::gdb_action::resume; + } + + std::string get_target_description() const override + { + return "i386:x86-64"; } private: diff --git a/src/windows-emulator/debugging/x64_gdb_stub_handler.hpp b/src/windows-emulator/debugging/x64_gdb_stub_handler.hpp index 58e1a47b..fae1fe04 100644 --- a/src/windows-emulator/debugging/x64_gdb_stub_handler.hpp +++ b/src/windows-emulator/debugging/x64_gdb_stub_handler.hpp @@ -1,8 +1,8 @@ #pragma once #include -#include "gdb_stub.hpp" #include "scoped_hook.hpp" #include +#include inline std::vector gdb_registers{ x64_register::rax, x64_register::rbx, x64_register::rcx, x64_register::rdx, x64_register::rsi, x64_register::rdi, @@ -16,18 +16,20 @@ inline std::vector gdb_registers{ x64_register::gs,*/ }; -inline memory_operation map_breakpoint_type(const breakpoint_type type) +inline memory_operation map_breakpoint_type(const gdb_stub::breakpoint_type type) { + using enum gdb_stub::breakpoint_type; + switch (type) { - case breakpoint_type::software: - case breakpoint_type::hardware_exec: + case software: + case hardware_exec: return memory_operation::exec; - case breakpoint_type::hardware_read: + case hardware_read: return memory_permission::read; - case breakpoint_type::hardware_write: + case hardware_write: return memory_permission::write; - case breakpoint_type::hardware_read_write: + case hardware_read_write: return memory_permission::read_write; default: throw std::runtime_error("Bad bp type"); @@ -38,7 +40,7 @@ struct breakpoint_key { size_t addr{}; size_t size{}; - breakpoint_type type{}; + gdb_stub::breakpoint_type type{}; bool operator==(const breakpoint_key& other) const { @@ -56,7 +58,7 @@ struct std::hash } }; -class x64_gdb_stub_handler : public gdb_stub_handler +class x64_gdb_stub_handler : public gdb_stub::gdb_stub_handler { public: x64_gdb_stub_handler(x64_emulator& emu) @@ -66,7 +68,7 @@ class x64_gdb_stub_handler : public gdb_stub_handler ~x64_gdb_stub_handler() override = default; - gdb_action cont() override + gdb_stub::gdb_action cont() override { try { @@ -77,10 +79,10 @@ class x64_gdb_stub_handler : public gdb_stub_handler puts(e.what()); } - return gdb_action::resume; + return gdb_stub::gdb_action::resume; } - gdb_action stepi() override + gdb_stub::gdb_action stepi() override { try { @@ -91,7 +93,7 @@ class x64_gdb_stub_handler : public gdb_stub_handler puts(e.what()); } - return gdb_action::resume; + return gdb_stub::gdb_action::resume; } bool read_reg(const int regno, size_t* value) override @@ -150,7 +152,7 @@ class x64_gdb_stub_handler : public gdb_stub_handler } } - bool set_bp(const breakpoint_type type, const size_t addr, const size_t size) override + bool set_bp(const gdb_stub::breakpoint_type type, const size_t addr, const size_t size) override { try { @@ -170,7 +172,7 @@ class x64_gdb_stub_handler : public gdb_stub_handler } } - bool del_bp(const breakpoint_type type, const size_t addr, const size_t size) override + bool del_bp(const gdb_stub::breakpoint_type type, const size_t addr, const size_t size) override { try { From cf76d5b4dc766749ba0db0bc33413ae745c678c5 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 17 Jan 2025 18:02:35 +0100 Subject: [PATCH 07/20] Extract connection handling --- src/gdb-stub/connection_handler.cpp | 59 ++++++++++++++++++++++++++++ src/gdb-stub/connection_handler.hpp | 21 ++++++++++ src/gdb-stub/gdb_stub.cpp | 60 ++++++++++------------------- 3 files changed, 100 insertions(+), 40 deletions(-) create mode 100644 src/gdb-stub/connection_handler.cpp create mode 100644 src/gdb-stub/connection_handler.hpp diff --git a/src/gdb-stub/connection_handler.cpp b/src/gdb-stub/connection_handler.cpp new file mode 100644 index 00000000..664ef59f --- /dev/null +++ b/src/gdb-stub/connection_handler.cpp @@ -0,0 +1,59 @@ +#include "connection_handler.hpp" +#include "checksum.hpp" + +#include + +using namespace std::literals; + +namespace gdb_stub +{ + namespace + { + bool read_from_socket(stream_processor& processor, network::tcp_client_socket& client) + { + const auto data = client.receive(); + if (!data) + { + return false; + } + + processor.push_stream_data(*data); + return true; + } + } + + connection_handler::connection_handler(network::tcp_client_socket& client) + : client_(client) + { + this->client_.set_blocking(false); + } + + std::optional connection_handler::get_packet() + { + while (this->client_.is_valid() && !this->processor_.has_packet()) + { + if (!read_from_socket(this->processor_, this->client_)) + { + std::this_thread::sleep_for(100ms); + } + } + + if (this->processor_.has_packet()) + { + return this->processor_.get_next_packet(); + } + + return std::nullopt; + } + + void connection_handler::send_packet(const std::string_view data) const + { + const auto checksum = compute_checksum_as_string(data); + this->send_raw_data("$" + std::string(data) + "#" + checksum); + } + + void connection_handler::send_raw_data(const std::string_view data) const + { + (void)this->client_.send(data); + } +} diff --git a/src/gdb-stub/connection_handler.hpp b/src/gdb-stub/connection_handler.hpp new file mode 100644 index 00000000..1e9c2f9e --- /dev/null +++ b/src/gdb-stub/connection_handler.hpp @@ -0,0 +1,21 @@ +#pragma once +#include "stream_processor.hpp" +#include + +namespace gdb_stub +{ + class connection_handler + { + public: + connection_handler(network::tcp_client_socket& client); + + std::optional get_packet(); + + void send_packet(std::string_view data) const; + void send_raw_data(std::string_view data) const; + + private: + network::tcp_client_socket& client_; + stream_processor processor_{}; + }; +} diff --git a/src/gdb-stub/gdb_stub.cpp b/src/gdb-stub/gdb_stub.cpp index 4647ec64..38040ead 100644 --- a/src/gdb-stub/gdb_stub.cpp +++ b/src/gdb-stub/gdb_stub.cpp @@ -2,9 +2,9 @@ #include -#include "stream_processor.hpp" -#include "async_handler.hpp" #include "checksum.hpp" +#include "async_handler.hpp" +#include "connection_handler.hpp" using namespace std::literals; @@ -20,12 +20,6 @@ namespace gdb_stub step, }; - bool send_packet_reply(const network::tcp_client_socket& socket, const std::string_view data) - { - const auto checksum = compute_checksum_as_string(data); - return socket.send("$" + std::string(data) + "#" + checksum); - } - network::tcp_client_socket accept_client(const network::address& bind_address) { network::tcp_server_socket server{bind_address.get_family()}; @@ -37,19 +31,7 @@ namespace gdb_stub return server.accept(); } - void read_from_socket(stream_processor& processor, network::tcp_client_socket& client) - { - while (client.is_ready(true)) - { - const auto data = client.receive(); - if (data) - { - processor.push_stream_data(*data); - } - } - } - - void process_query(const network::tcp_client_socket& client, const std::string_view payload) + void process_query(const connection_handler& connection, const std::string_view payload) { auto name = payload; std::string_view args{}; @@ -64,11 +46,11 @@ namespace gdb_stub if (name == "Supported") { - send_packet_reply(client, "PacketSize=1024;qXfer:features:read+"); + connection.send_packet("PacketSize=1024;qXfer:features:read+"); } else if (name == "Attached") { - send_packet_reply(client, "1"); + connection.send_packet("1"); } else if (name == "Xfer") { @@ -76,15 +58,15 @@ namespace gdb_stub } else if (name == "Symbol") { - send_packet_reply(client, "OK"); + connection.send_packet("OK"); } else { - send_packet_reply(client, {}); + connection.send_packet({}); } } - continuation_event handle_command(const network::tcp_client_socket& client, const uint8_t command, + continuation_event handle_command(const connection_handler& connection, const uint8_t command, const std::string_view data) { auto event = continuation_event::none; @@ -92,20 +74,20 @@ namespace gdb_stub switch (command) { case 'q': - process_query(client, data); + process_query(connection, data); break; default: - send_packet_reply(client, {}); + connection.send_packet({}); break; } return event; } - void process_packet(const network::tcp_client_socket& client, const std::string_view packet) + void process_packet(const connection_handler& connection, const std::string_view packet) { - (void)client.send("+"); + (void)connection.send_raw_data("+"); if (packet.empty()) { @@ -113,7 +95,7 @@ namespace gdb_stub } const auto command = packet.front(); - const auto event = handle_command(client, command, packet.substr(1)); + const auto event = handle_command(connection, command, packet.substr(1)); (void)event; } @@ -125,8 +107,6 @@ namespace gdb_stub bool run_gdb_stub(const network::address& bind_address, gdb_stub_handler& handler) { - stream_processor processor{}; - auto client = accept_client(bind_address); if (!client) { @@ -148,17 +128,17 @@ namespace gdb_stub } }}; - client.set_blocking(false); + connection_handler connection{client}; - while (client.is_valid()) + while (true) { - read_from_socket(processor, client); - - while (processor.has_packet()) + const auto packet = connection.get_packet(); + if (!packet) { - const auto packet = processor.get_next_packet(); - process_packet(client, packet); + break; } + + process_packet(connection, *packet); } return true; From 2f6d17fde65a93d34a150c330de6439064f9e7f2 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 17 Jan 2025 18:21:23 +0100 Subject: [PATCH 08/20] Process more commands --- src/gdb-stub/connection_handler.cpp | 5 ++++ src/gdb-stub/connection_handler.hpp | 2 ++ src/gdb-stub/gdb_stub.cpp | 42 ++++++++++++++++------------- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/gdb-stub/connection_handler.cpp b/src/gdb-stub/connection_handler.cpp index 664ef59f..f3c50311 100644 --- a/src/gdb-stub/connection_handler.cpp +++ b/src/gdb-stub/connection_handler.cpp @@ -56,4 +56,9 @@ namespace gdb_stub { (void)this->client_.send(data); } + + void connection_handler::close() const + { + this->client_.close(); + } } diff --git a/src/gdb-stub/connection_handler.hpp b/src/gdb-stub/connection_handler.hpp index 1e9c2f9e..bd1f13af 100644 --- a/src/gdb-stub/connection_handler.hpp +++ b/src/gdb-stub/connection_handler.hpp @@ -14,6 +14,8 @@ namespace gdb_stub void send_packet(std::string_view data) const; void send_raw_data(std::string_view data) const; + void close() const; + private: network::tcp_client_socket& client_; stream_processor processor_{}; diff --git a/src/gdb-stub/gdb_stub.cpp b/src/gdb-stub/gdb_stub.cpp index 38040ead..859c8a24 100644 --- a/src/gdb-stub/gdb_stub.cpp +++ b/src/gdb-stub/gdb_stub.cpp @@ -12,14 +12,6 @@ namespace gdb_stub { namespace { - enum class continuation_event - { - none, - cont, - detach, - step, - }; - network::tcp_client_socket accept_client(const network::address& bind_address) { network::tcp_server_socket server{bind_address.get_family()}; @@ -66,13 +58,29 @@ namespace gdb_stub } } - continuation_event handle_command(const connection_handler& connection, const uint8_t command, - const std::string_view data) + void process_action(const connection_handler& connection, const gdb_action action) { - auto event = continuation_event::none; + if (action == gdb_action::shutdown) + { + connection.close(); + } + } + void handle_command(const connection_handler& connection, async_handler& async, gdb_stub_handler& handler, + const uint8_t command, const std::string_view data) + { switch (command) { + case 'c': + async.run(); + process_action(connection, handler.cont()); + async.pause(); + break; + + case 's': + process_action(connection, handler.stepi()); + break; + case 'q': process_query(connection, data); break; @@ -81,13 +89,12 @@ namespace gdb_stub connection.send_packet({}); break; } - - return event; } - void process_packet(const connection_handler& connection, const std::string_view packet) + void process_packet(const connection_handler& connection, async_handler& async, gdb_stub_handler& handler, + const std::string_view packet) { - (void)connection.send_raw_data("+"); + connection.send_raw_data("+"); if (packet.empty()) { @@ -95,8 +102,7 @@ namespace gdb_stub } const auto command = packet.front(); - const auto event = handle_command(connection, command, packet.substr(1)); - (void)event; + handle_command(connection, async, handler, command, packet.substr(1)); } bool is_interrupt_packet(const std::optional& data) @@ -138,7 +144,7 @@ namespace gdb_stub break; } - process_packet(connection, *packet); + process_packet(connection, async, handler, *packet); } return true; From b180d9629c1df0943918d8ed305b7bb5519b6c9b Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 17 Jan 2025 19:50:06 +0100 Subject: [PATCH 09/20] Refactor gdb interface --- src/common/utils/string.hpp | 26 +++++++++++++++++++++++++- src/gdb-stub/checksum.hpp | 10 ---------- src/gdb-stub/connection_handler.cpp | 3 ++- src/gdb-stub/gdb_stub.cpp | 10 ++++++++-- src/gdb-stub/gdb_stub.hpp | 21 ++++++++++++--------- src/gdb-stub/stream_processor.cpp | 1 + 6 files changed, 48 insertions(+), 23 deletions(-) diff --git a/src/common/utils/string.hpp b/src/common/utils/string.hpp index a1c6497e..d085cf8b 100644 --- a/src/common/utils/string.hpp +++ b/src/common/utils/string.hpp @@ -1,6 +1,7 @@ #pragma once #include -#include +#include +#include #include #include @@ -44,4 +45,27 @@ namespace utils::string { return to_lower(std::move(str)); } + + template + requires(std::is_integral_v) + std::string to_hex_string(const Integer& i) + { + std::stringstream stream{}; + stream << std::hex << i; + return stream.str(); + } + + inline std::string to_hex_string(const void* data, const size_t size) + { + std::stringstream stream{}; + stream << std::hex; + + for (size_t i = 0; i < size; ++i) + { + const auto value = static_cast(data)[i]; + stream << value; + } + + return stream.str(); + } } diff --git a/src/gdb-stub/checksum.hpp b/src/gdb-stub/checksum.hpp index 533329d3..cd2c0a71 100644 --- a/src/gdb-stub/checksum.hpp +++ b/src/gdb-stub/checksum.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include namespace gdb_stub { @@ -17,13 +16,4 @@ namespace gdb_stub return checksum; } - - inline std::string compute_checksum_as_string(const std::string_view data) - { - const auto checksum = compute_checksum(data); - - std::stringstream stream{}; - stream << std::hex << checksum; - return stream.str(); - } } diff --git a/src/gdb-stub/connection_handler.cpp b/src/gdb-stub/connection_handler.cpp index f3c50311..3738456b 100644 --- a/src/gdb-stub/connection_handler.cpp +++ b/src/gdb-stub/connection_handler.cpp @@ -1,5 +1,6 @@ #include "connection_handler.hpp" #include "checksum.hpp" +#include #include @@ -48,7 +49,7 @@ namespace gdb_stub void connection_handler::send_packet(const std::string_view data) const { - const auto checksum = compute_checksum_as_string(data); + const auto checksum = utils::string::to_hex_string(compute_checksum(data)); this->send_raw_data("$" + std::string(data) + "#" + checksum); } diff --git a/src/gdb-stub/gdb_stub.cpp b/src/gdb-stub/gdb_stub.cpp index 859c8a24..676ae1d6 100644 --- a/src/gdb-stub/gdb_stub.cpp +++ b/src/gdb-stub/gdb_stub.cpp @@ -66,6 +66,10 @@ namespace gdb_stub } } + void read_registers() + { + } + void handle_command(const connection_handler& connection, async_handler& async, gdb_stub_handler& handler, const uint8_t command, const std::string_view data) { @@ -73,18 +77,20 @@ namespace gdb_stub { case 'c': async.run(); - process_action(connection, handler.cont()); + process_action(connection, handler.run()); async.pause(); break; case 's': - process_action(connection, handler.stepi()); + process_action(connection, handler.singlestep()); break; case 'q': process_query(connection, data); break; + case 'g': + default: connection.send_packet({}); break; diff --git a/src/gdb-stub/gdb_stub.hpp b/src/gdb-stub/gdb_stub.hpp index ec7629ac..c1e946df 100644 --- a/src/gdb-stub/gdb_stub.hpp +++ b/src/gdb-stub/gdb_stub.hpp @@ -24,21 +24,24 @@ namespace gdb_stub { virtual ~gdb_stub_handler() = default; - virtual gdb_action cont() = 0; - virtual gdb_action stepi() = 0; + virtual gdb_action run() = 0; + virtual gdb_action singlestep() = 0; - virtual bool read_reg(int regno, size_t* value) = 0; - virtual bool write_reg(int regno, size_t value) = 0; + virtual size_t get_register_count() = 0; + virtual size_t get_max_register_size() = 0; - virtual bool read_mem(size_t addr, size_t len, void* val) = 0; - virtual bool write_mem(size_t addr, size_t len, void* val) = 0; + virtual bool read_register(size_t reg, void* data, size_t max_length) = 0; + virtual bool write_register(size_t reg, const void* data, size_t size) = 0; - virtual bool set_bp(breakpoint_type type, size_t addr, size_t size) = 0; - virtual bool del_bp(breakpoint_type type, size_t addr, size_t size) = 0; + virtual bool read_memory(size_t address, void* data, size_t length) = 0; + virtual bool write_memory(size_t address, const void* data, size_t length) = 0; + + virtual bool set_breakpoint(breakpoint_type type, size_t address, size_t size) = 0; + virtual bool delete_breakpoint(breakpoint_type type, size_t address, size_t size) = 0; virtual void on_interrupt() = 0; - virtual std::string get_target_description() const = 0; + virtual std::string get_target_description() = 0; }; bool run_gdb_stub(const network::address& bind_address, gdb_stub_handler& handler); diff --git a/src/gdb-stub/stream_processor.cpp b/src/gdb-stub/stream_processor.cpp index ba3aabac..0dd90d1b 100644 --- a/src/gdb-stub/stream_processor.cpp +++ b/src/gdb-stub/stream_processor.cpp @@ -2,6 +2,7 @@ #include "checksum.hpp" #include +#include namespace gdb_stub { From 2044a3c4abe6712273b147e96b13df94b18a9d88 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 18 Jan 2025 09:20:08 +0100 Subject: [PATCH 10/20] GDB command progress --- src/gdb-stub/connection_handler.cpp | 2 +- src/gdb-stub/connection_handler.hpp | 2 +- src/gdb-stub/gdb_stub.cpp | 142 ++++++++++++++++++++++++---- src/gdb-stub/gdb_stub.hpp | 19 ++-- 4 files changed, 134 insertions(+), 31 deletions(-) diff --git a/src/gdb-stub/connection_handler.cpp b/src/gdb-stub/connection_handler.cpp index 3738456b..9ef40d21 100644 --- a/src/gdb-stub/connection_handler.cpp +++ b/src/gdb-stub/connection_handler.cpp @@ -47,7 +47,7 @@ namespace gdb_stub return std::nullopt; } - void connection_handler::send_packet(const std::string_view data) const + void connection_handler::send_reply(const std::string_view data) const { const auto checksum = utils::string::to_hex_string(compute_checksum(data)); this->send_raw_data("$" + std::string(data) + "#" + checksum); diff --git a/src/gdb-stub/connection_handler.hpp b/src/gdb-stub/connection_handler.hpp index bd1f13af..afd326ca 100644 --- a/src/gdb-stub/connection_handler.hpp +++ b/src/gdb-stub/connection_handler.hpp @@ -11,7 +11,7 @@ namespace gdb_stub std::optional get_packet(); - void send_packet(std::string_view data) const; + void send_reply(std::string_view data) const; void send_raw_data(std::string_view data) const; void close() const; diff --git a/src/gdb-stub/gdb_stub.cpp b/src/gdb-stub/gdb_stub.cpp index 676ae1d6..2dfa44e2 100644 --- a/src/gdb-stub/gdb_stub.cpp +++ b/src/gdb-stub/gdb_stub.cpp @@ -6,12 +6,20 @@ #include "async_handler.hpp" #include "connection_handler.hpp" +#include +#include + using namespace std::literals; namespace gdb_stub { namespace { + void rt_assert(const bool condition) + { + assert(condition); + } + network::tcp_client_socket accept_client(const network::address& bind_address) { network::tcp_server_socket server{bind_address.get_family()}; @@ -23,7 +31,7 @@ namespace gdb_stub return server.accept(); } - void process_query(const connection_handler& connection, const std::string_view payload) + std::pair split_colon(const std::string_view payload) { auto name = payload; std::string_view args{}; @@ -36,25 +44,50 @@ namespace gdb_stub args = payload.substr(separator + 1); } - if (name == "Supported") + return {name, args}; + } + + void process_xfer(const connection_handler& connection, gdb_stub_handler& handler, + const std::string_view payload) + { + auto [name, args] = split_colon(payload); + + if (name == "features") { - connection.send_packet("PacketSize=1024;qXfer:features:read+"); - } - else if (name == "Attached") - { - connection.send_packet("1"); - } - else if (name == "Xfer") - { - // process_xfer(gdbstub, args); - } - else if (name == "Symbol") - { - connection.send_packet("OK"); + connection.send_reply("l" // + + handler.get_target_description() // + + "%s"); } else { - connection.send_packet({}); + connection.send_reply({}); + } + } + + void process_query(const connection_handler& connection, gdb_stub_handler& handler, + const std::string_view payload) + { + auto [name, args] = split_colon(payload); + + if (name == "Supported") + { + connection.send_reply("PacketSize=1024;qXfer:features:read+"); + } + else if (name == "Attached") + { + connection.send_reply("1"); + } + else if (name == "Xfer") + { + process_xfer(connection, handler, args); + } + else if (name == "Symbol") + { + connection.send_reply("OK"); + } + else + { + connection.send_reply({}); } } @@ -66,8 +99,54 @@ namespace gdb_stub } } - void read_registers() + breakpoint_type translate_breakpoint_type(const uint32_t type) { + if (type >= static_cast(breakpoint_type::END)) + { + return breakpoint_type::software; + } + + return static_cast(type); + } + + bool change_breakpoint(gdb_stub_handler& handler, const bool set, const breakpoint_type type, + const uint64_t address, const size_t size) + { + if (set) + { + return handler.set_breakpoint(type, address, size); + } + + return handler.delete_breakpoint(type, address, size); + } + + void handle_breakpoint(const connection_handler& connection, gdb_stub_handler& handler, const std::string& data, + const bool set) + { + uint32_t type{}; + uint64_t addr{}; + size_t kind{}; + rt_assert(sscanf(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"); + } + + void handle_v_packet(const connection_handler& connection, const std::string_view data) + { + auto [name, args] = split_colon(data); + + if (name == "Cont?") + { + // IDA pro gets confused if the reply arrives too early :( + std::this_thread::sleep_for(1s); + + connection.send_reply("vCont;s;c;"); + } + else + { + connection.send_reply({}); + } } void handle_command(const connection_handler& connection, async_handler& async, gdb_stub_handler& handler, @@ -86,13 +165,36 @@ namespace gdb_stub break; case 'q': - process_query(connection, data); + process_query(connection, handler, data); break; - case 'g': + case 'D': + connection.close(); + break; + case 'z': + case 'Z': + handle_breakpoint(connection, handler, std::string(data), command == 'Z'); + break; + + case '?': + connection.send_reply("S05"); + break; + + case 'v': + handle_v_packet(connection, data); + break; + + // TODO + case 'g': + case 'm': + case 'p': + case 'G': + case 'M': + case 'P': + case 'X': default: - connection.send_packet({}); + connection.send_reply({}); break; } } diff --git a/src/gdb-stub/gdb_stub.hpp b/src/gdb-stub/gdb_stub.hpp index c1e946df..40ce53d6 100644 --- a/src/gdb-stub/gdb_stub.hpp +++ b/src/gdb-stub/gdb_stub.hpp @@ -13,11 +13,12 @@ namespace gdb_stub enum class breakpoint_type : uint8_t { - software, - hardware_exec, - hardware_write, - hardware_read, - hardware_read_write, + software = 0, + hardware_exec = 1, + hardware_write = 2, + hardware_read = 3, + hardware_read_write = 4, + END, }; struct gdb_stub_handler @@ -33,11 +34,11 @@ namespace gdb_stub virtual bool read_register(size_t reg, void* data, size_t max_length) = 0; virtual bool write_register(size_t reg, const void* data, size_t size) = 0; - virtual bool read_memory(size_t address, void* data, size_t length) = 0; - virtual bool write_memory(size_t address, const void* data, size_t length) = 0; + virtual bool read_memory(uint64_t address, void* data, size_t length) = 0; + virtual bool write_memory(uint64_t address, const void* data, size_t length) = 0; - virtual bool set_breakpoint(breakpoint_type type, size_t address, size_t size) = 0; - virtual bool delete_breakpoint(breakpoint_type type, size_t address, size_t size) = 0; + virtual bool set_breakpoint(breakpoint_type type, uint64_t address, size_t size) = 0; + virtual bool delete_breakpoint(breakpoint_type type, uint64_t address, size_t size) = 0; virtual void on_interrupt() = 0; From e79d919bbb0304ccb60df6fc0c0bf1f83de5721f Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 18 Jan 2025 09:28:23 +0100 Subject: [PATCH 11/20] More fixes --- src/common/platform/compiler.hpp | 1 + src/gdb-stub/async_handler.cpp | 1 + src/gdb-stub/gdb_stub.cpp | 2 +- .../debugging/win_x64_gdb_stub_handler.hpp | 6 +- .../debugging/x64_gdb_stub_handler.hpp | 72 ++++++++++--------- 5 files changed, 46 insertions(+), 36 deletions(-) diff --git a/src/common/platform/compiler.hpp b/src/common/platform/compiler.hpp index eda34323..34c1cc4d 100644 --- a/src/common/platform/compiler.hpp +++ b/src/common/platform/compiler.hpp @@ -28,6 +28,7 @@ #define DECLSPEC_ALIGN(n) alignas(n) #define fopen_s fopen +#define sscanf_s sscanf #define RESTRICTED_POINTER __restrict diff --git a/src/gdb-stub/async_handler.cpp b/src/gdb-stub/async_handler.cpp index 8afb0ce3..4af41429 100644 --- a/src/gdb-stub/async_handler.cpp +++ b/src/gdb-stub/async_handler.cpp @@ -58,6 +58,7 @@ namespace gdb_stub { while (!this->run_ && !this->stop_) { + this->is_running_ = false; std::this_thread::sleep_for(10ms); } diff --git a/src/gdb-stub/gdb_stub.cpp b/src/gdb-stub/gdb_stub.cpp index 2dfa44e2..5ff29756 100644 --- a/src/gdb-stub/gdb_stub.cpp +++ b/src/gdb-stub/gdb_stub.cpp @@ -126,7 +126,7 @@ namespace gdb_stub uint32_t type{}; uint64_t addr{}; size_t kind{}; - rt_assert(sscanf(data.c_str(), "%x,%" PRIX64 ",%zx", &type, &addr, &kind) == 3); + 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"); 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 7f9574b6..df9d1261 100644 --- a/src/windows-emulator/debugging/win_x64_gdb_stub_handler.hpp +++ b/src/windows-emulator/debugging/win_x64_gdb_stub_handler.hpp @@ -12,7 +12,7 @@ class win_x64_gdb_stub_handler : public x64_gdb_stub_handler { } - gdb_stub::gdb_action cont() override + gdb_stub::gdb_action run() override { try { @@ -26,7 +26,7 @@ class win_x64_gdb_stub_handler : public x64_gdb_stub_handler return gdb_stub::gdb_action::resume; } - gdb_stub::gdb_action stepi() override + gdb_stub::gdb_action singlestep() override { try { @@ -40,7 +40,7 @@ class win_x64_gdb_stub_handler : public x64_gdb_stub_handler return gdb_stub::gdb_action::resume; } - std::string get_target_description() const override + std::string get_target_description() override { return "i386:x86-64"; } diff --git a/src/windows-emulator/debugging/x64_gdb_stub_handler.hpp b/src/windows-emulator/debugging/x64_gdb_stub_handler.hpp index fae1fe04..e5e147cc 100644 --- a/src/windows-emulator/debugging/x64_gdb_stub_handler.hpp +++ b/src/windows-emulator/debugging/x64_gdb_stub_handler.hpp @@ -68,7 +68,7 @@ class x64_gdb_stub_handler : public gdb_stub::gdb_stub_handler ~x64_gdb_stub_handler() override = default; - gdb_stub::gdb_action cont() override + gdb_stub::gdb_action run() override { try { @@ -82,7 +82,7 @@ class x64_gdb_stub_handler : public gdb_stub::gdb_stub_handler return gdb_stub::gdb_action::resume; } - gdb_stub::gdb_action stepi() override + gdb_stub::gdb_action singlestep() override { try { @@ -96,36 +96,26 @@ class x64_gdb_stub_handler : public gdb_stub::gdb_stub_handler return gdb_stub::gdb_action::resume; } - bool read_reg(const int regno, size_t* value) override + size_t get_register_count() override { - *value = 0; - - try - { - if (static_cast(regno) >= gdb_registers.size()) - { - return true; - } - - this->emu_->read_register(gdb_registers[regno], value, sizeof(*value)); - return true; - } - catch (...) - { - return true; - } + return gdb_registers.size(); } - bool write_reg(const int regno, const size_t value) override + size_t get_max_register_size() override + { + return 256 / 8; + } + + bool read_register(const size_t reg, void* data, const size_t max_length) override { try { - if (static_cast(regno) >= gdb_registers.size()) + if (reg >= gdb_registers.size()) { - return true; + return false; } - this->emu_->write_register(gdb_registers[regno], &value, sizeof(value)); + this->emu_->read_register(gdb_registers[reg], data, max_length); return true; } catch (...) @@ -134,16 +124,16 @@ class x64_gdb_stub_handler : public gdb_stub::gdb_stub_handler } } - bool read_mem(const size_t addr, const size_t len, void* val) override - { - return this->emu_->try_read_memory(addr, val, len); - } - - bool write_mem(const size_t addr, const size_t len, void* val) override + bool write_register(const size_t reg, const void* data, const size_t size) override { try { - this->emu_->write_memory(addr, val, len); + if (reg >= gdb_registers.size()) + { + return false; + } + + this->emu_->write_register(gdb_registers[reg], data, size); return true; } catch (...) @@ -152,7 +142,25 @@ class x64_gdb_stub_handler : public gdb_stub::gdb_stub_handler } } - bool set_bp(const gdb_stub::breakpoint_type type, const size_t addr, const size_t size) override + bool read_memory(const uint64_t address, void* data, const size_t length) override + { + return this->emu_->try_read_memory(address, data, length); + } + + bool write_memory(const uint64_t address, const void* data, const size_t length) override + { + try + { + this->emu_->write_memory(address, data, length); + return true; + } + catch (...) + { + return false; + } + } + + bool set_breakpoint(const gdb_stub::breakpoint_type type, const uint64_t addr, const size_t size) override { try { @@ -172,7 +180,7 @@ class x64_gdb_stub_handler : public gdb_stub::gdb_stub_handler } } - bool del_bp(const gdb_stub::breakpoint_type type, const size_t addr, const size_t size) override + bool delete_breakpoint(const gdb_stub::breakpoint_type type, const uint64_t addr, const size_t size) override { try { From 062c20d9020f1d2463be615e13f6a9dabacca97f Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 18 Jan 2025 09:30:03 +0100 Subject: [PATCH 12/20] Fix compilation --- src/gdb-stub/gdb_stub.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gdb-stub/gdb_stub.cpp b/src/gdb-stub/gdb_stub.cpp index 5ff29756..53523e8c 100644 --- a/src/gdb-stub/gdb_stub.cpp +++ b/src/gdb-stub/gdb_stub.cpp @@ -1,5 +1,6 @@ #include "gdb_stub.hpp" +#include #include #include "checksum.hpp" @@ -17,6 +18,7 @@ namespace gdb_stub { void rt_assert(const bool condition) { + (void)condition; assert(condition); } From 5079b750cdd89a4f089e6571cdfb256d164406f3 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 18 Jan 2025 11:43:54 +0100 Subject: [PATCH 13/20] Finish implementation --- src/common/utils/string.hpp | 42 ++++++ src/gdb-stub/gdb_stub.cpp | 252 ++++++++++++++++++++++++++++++++++-- 2 files changed, 282 insertions(+), 12 deletions(-) diff --git a/src/common/utils/string.hpp b/src/common/utils/string.hpp index d085cf8b..13bf4ab0 100644 --- a/src/common/utils/string.hpp +++ b/src/common/utils/string.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include #include #include @@ -68,4 +69,45 @@ namespace utils::string return stream.str(); } + + inline std::string to_hex_string(const std::span data) + { + return to_hex_string(data.data(), data.size()); + } + + inline uint8_t parse_nibble(const char nibble) + { + const auto lower = char_to_lower(nibble); + + if (lower >= '0' && lower <= '9') + { + return static_cast(lower - '0'); + } + + if (lower >= 'a' && lower <= 'f') + { + return static_cast(lower - 'a'); + } + + return 0; + } + + inline std::vector from_hex_string(const std::string_view str) + { + const auto size = str.size() / 2; + + std::vector data{}; + data.resize(size); + + for (size_t i = 0; i < size; ++i) + { + const auto high = parse_nibble(str[i * 2 + 0]); + const auto low = parse_nibble(str[i * 2 + 1]); + const auto value = static_cast((high << 4) | low); + + data.push_back(value); + } + + return data; + } } diff --git a/src/gdb-stub/gdb_stub.cpp b/src/gdb-stub/gdb_stub.cpp index 53523e8c..0ff6053b 100644 --- a/src/gdb-stub/gdb_stub.cpp +++ b/src/gdb-stub/gdb_stub.cpp @@ -6,6 +6,7 @@ #include "checksum.hpp" #include "async_handler.hpp" #include "connection_handler.hpp" +#include "utils/string.hpp" #include #include @@ -33,17 +34,17 @@ namespace gdb_stub return server.accept(); } - std::pair split_colon(const std::string_view payload) + std::pair split_string(const std::string_view payload, const char separator) { auto name = payload; std::string_view args{}; - const auto separator = payload.find_first_of(':'); - if (separator != std::string_view::npos) + const auto separator_pos = payload.find_first_of(separator); + if (separator_pos != std::string_view::npos) { - name = payload.substr(0, separator); - args = payload.substr(separator + 1); + name = payload.substr(0, separator_pos); + args = payload.substr(separator_pos + 1); } return {name, args}; @@ -52,7 +53,7 @@ namespace gdb_stub void process_xfer(const connection_handler& connection, gdb_stub_handler& handler, const std::string_view payload) { - auto [name, args] = split_colon(payload); + auto [name, args] = split_string(payload, ':'); if (name == "features") { @@ -69,7 +70,7 @@ namespace gdb_stub void process_query(const connection_handler& connection, gdb_stub_handler& handler, const std::string_view payload) { - auto [name, args] = split_colon(payload); + const auto [name, args] = split_string(payload, ':'); if (name == "Supported") { @@ -136,7 +137,7 @@ namespace gdb_stub void handle_v_packet(const connection_handler& connection, const std::string_view data) { - auto [name, args] = split_colon(data); + const auto [name, args] = split_string(data, ':'); if (name == "Cont?") { @@ -151,6 +152,213 @@ namespace gdb_stub } } + void read_registers(const connection_handler& connection, gdb_stub_handler& handler) + { + std::string response{}; + std::vector data{}; + data.resize(handler.get_max_register_size()); + + const auto registers = handler.get_register_count(); + + for (size_t i = 0; i < registers; ++i) + { + memset(data.data(), 0, data.size()); + const auto res = handler.read_register(i, data.data(), data.size()); + + if (!res) + { + connection.send_reply("E01"); + return; + } + + response.append(utils::string::to_hex_string(data)); + } + + connection.send_reply(response); + } + + void write_registers(const connection_handler& connection, gdb_stub_handler& handler, + 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(); + + for (size_t i = 0; i < registers; ++i) + { + const auto offset = i * register_size; + const auto end_offset = offset + register_size; + + if (data.size() < end_offset) + { + connection.send_reply("E01"); + return; + } + + const auto res = handler.write_register(i, data.data() + offset, register_size); + + if (!res) + { + connection.send_reply("E01"); + return; + } + } + + connection.send_reply("OK"); + } + + void read_single_register(const connection_handler& connection, gdb_stub_handler& handler, + const std::string& payload) + { + size_t reg{}; + rt_assert(sscanf_s(payload.c_str(), "%zx", ®) == 3); + + std::vector data{}; + data.resize(handler.get_max_register_size()); + + const auto res = handler.read_register(reg, data.data(), data.size()); + + if (res) + { + connection.send_reply(utils::string::to_hex_string(data)); + } + else + { + connection.send_reply("E01"); + } + } + + void write_single_register(const connection_handler& connection, gdb_stub_handler& handler, + const std::string_view payload) + { + const auto [reg, hex_data] = split_string(payload, '='); + + size_t register_index{}; + rt_assert(sscanf_s(std::string(reg).c_str(), "%zx", ®ister_index) == 1); + + const auto register_size = handler.get_max_register_size(); + const auto data = utils::string::from_hex_string(hex_data); + + const auto res = register_size <= data.size() && // + handler.write_register(register_index, data.data(), register_size); + + if (!res) + { + connection.send_reply("E01"); + return; + } + + connection.send_reply("OK"); + } + + void read_memory(const connection_handler& connection, gdb_stub_handler& handler, const std::string& payload) + { + uint64_t address{}; + size_t size{}; + rt_assert(sscanf_s(payload.c_str(), "%" PRIx64 ",%zx", &address, &size) == 2); + + if (size > 0x1000) + { + connection.send_reply("E01"); + return; + } + + std::vector data{}; + data.resize(size); + + const auto res = handler.read_memory(address, data.data(), data.size()); + if (!res) + { + connection.send_reply("E01"); + return; + } + + connection.send_reply(utils::string::to_hex_string(data)); + } + + void write_memory(const connection_handler& connection, gdb_stub_handler& handler, + const std::string_view payload) + { + const auto [info, hex_data] = split_string(payload, ':'); + + size_t size{}; + uint64_t address{}; + rt_assert(sscanf_s(std::string(info).c_str(), "%" PRIx64 ",%zx", &address, &size) == 2); + + if (size > 0x1000) + { + 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()); + if (!res) + { + connection.send_reply("E01"); + return; + } + + connection.send_reply("OK"); + } + + std::string decode_x_memory(const std::string_view payload) + { + std::string result{}; + result.reserve(payload.size()); + + bool xor_next = false; + + for (auto value : payload) + { + if (xor_next) + { + value ^= 0x20; + xor_next = false; + } + else if (value == '}') + { + xor_next = true; + continue; + } + + result.push_back(value); + } + + return result; + } + + void write_x_memory(const connection_handler& connection, gdb_stub_handler& handler, + const std::string_view payload) + { + const auto [info, encoded_data] = split_string(payload, ':'); + + size_t size{}; + uint64_t address{}; + rt_assert(sscanf_s(std::string(info).c_str(), "%" PRIx64 ",%zx", &address, &size) == 2); + + if (size > 0x1000) + { + 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()); + if (!res) + { + connection.send_reply("E01"); + return; + } + + connection.send_reply("OK"); + } + void handle_command(const connection_handler& connection, async_handler& async, gdb_stub_handler& handler, const uint8_t command, const std::string_view data) { @@ -187,14 +395,34 @@ namespace gdb_stub handle_v_packet(connection, data); break; - // TODO case 'g': - case 'm': - case 'p': + read_registers(connection, handler); + break; + case 'G': - case 'M': + write_registers(connection, handler, data); + break; + + case 'p': + read_single_register(connection, handler, std::string(data)); + break; + case 'P': + write_single_register(connection, handler, data); + break; + + case 'm': + read_memory(connection, handler, std::string(data)); + break; + + case 'M': + write_memory(connection, handler, data); + break; + case 'X': + write_x_memory(connection, handler, data); + break; + default: connection.send_reply({}); break; From cc252447d52434af019d094cff82993e955c270f Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 18 Jan 2025 12:39:29 +0100 Subject: [PATCH 14/20] Multiple fixes --- src/common/utils/string.hpp | 58 ++++++++++++------- src/gdb-stub/async_handler.cpp | 3 + src/gdb-stub/gdb_stub.cpp | 4 +- .../debugging/x64_gdb_stub_handler.hpp | 3 +- 4 files changed, 45 insertions(+), 23 deletions(-) diff --git a/src/common/utils/string.hpp b/src/common/utils/string.hpp index 13bf4ab0..7a3cb30c 100644 --- a/src/common/utils/string.hpp +++ b/src/common/utils/string.hpp @@ -2,7 +2,6 @@ #include #include #include -#include #include #include @@ -47,49 +46,66 @@ namespace utils::string return to_lower(std::move(str)); } - template - requires(std::is_integral_v) - std::string to_hex_string(const Integer& i) + inline char to_nibble(std::byte value, const bool uppercase = false) { - std::stringstream stream{}; - stream << std::hex << i; - return stream.str(); + value = value & static_cast(0xF); + + if (value <= static_cast(9)) + { + return static_cast('0' + static_cast(value)); + } + + return static_cast((uppercase ? 'A' : 'a') + (static_cast(value) - 0xA)); } - inline std::string to_hex_string(const void* data, const size_t size) + inline std::pair to_hex(const std::byte value, const bool uppercase = false) { - std::stringstream stream{}; - stream << std::hex; + return {to_nibble(value >> 4, uppercase), to_nibble(value, uppercase)}; + } + + inline std::string to_hex_string(const void* data, const size_t size, const bool uppercase = false) + { + std::string result{}; + result.reserve(size * 2); for (size_t i = 0; i < size; ++i) { - const auto value = static_cast(data)[i]; - stream << value; + const auto value = static_cast(data)[i]; + const auto [high, low] = to_hex(value, uppercase); + result.push_back(high); + result.push_back(low); } - return stream.str(); + return result; } - inline std::string to_hex_string(const std::span data) + template + requires(std::is_integral_v) + std::string to_hex_string(const Integer& i, const bool uppercase = false) { - return to_hex_string(data.data(), data.size()); + return to_hex_string(&i, sizeof(Integer), uppercase); } - inline uint8_t parse_nibble(const char nibble) + inline std::string to_hex_string(const std::span data, const bool uppercase = false) + { + return to_hex_string(data.data(), data.size(), uppercase); + } + + inline std::byte parse_nibble(const char nibble) { const auto lower = char_to_lower(nibble); if (lower >= '0' && lower <= '9') { - return static_cast(lower - '0'); + return static_cast(lower - '0'); } if (lower >= 'a' && lower <= 'f') { - return static_cast(lower - 'a'); + return static_cast(lower - 'a'); } - return 0; + return static_cast(0); } inline std::vector from_hex_string(const std::string_view str) @@ -97,13 +113,13 @@ namespace utils::string const auto size = str.size() / 2; std::vector data{}; - data.resize(size); + data.reserve(size); for (size_t i = 0; i < size; ++i) { const auto high = parse_nibble(str[i * 2 + 0]); const auto low = parse_nibble(str[i * 2 + 1]); - const auto value = static_cast((high << 4) | low); + const auto value = (high << 4) | low; data.push_back(value); } diff --git a/src/gdb-stub/async_handler.cpp b/src/gdb-stub/async_handler.cpp index 4af41429..9560a3b3 100644 --- a/src/gdb-stub/async_handler.cpp +++ b/src/gdb-stub/async_handler.cpp @@ -9,6 +9,9 @@ namespace gdb_stub : handler_(std::move(h)) { this->stop_ = false; + this->runner_ = std::thread([this] { + this->work(); // + }); } async_handler::~async_handler() diff --git a/src/gdb-stub/gdb_stub.cpp b/src/gdb-stub/gdb_stub.cpp index 0ff6053b..ad643428 100644 --- a/src/gdb-stub/gdb_stub.cpp +++ b/src/gdb-stub/gdb_stub.cpp @@ -212,7 +212,7 @@ namespace gdb_stub const std::string& payload) { size_t reg{}; - rt_assert(sscanf_s(payload.c_str(), "%zx", ®) == 3); + rt_assert(sscanf_s(payload.c_str(), "%zx", ®) == 1); std::vector data{}; data.resize(handler.get_max_register_size()); @@ -368,10 +368,12 @@ namespace gdb_stub async.run(); process_action(connection, handler.run()); async.pause(); + connection.send_reply("S05"); break; case 's': process_action(connection, handler.singlestep()); + connection.send_reply("S05"); break; case 'q': diff --git a/src/windows-emulator/debugging/x64_gdb_stub_handler.hpp b/src/windows-emulator/debugging/x64_gdb_stub_handler.hpp index e5e147cc..fa4a15a7 100644 --- a/src/windows-emulator/debugging/x64_gdb_stub_handler.hpp +++ b/src/windows-emulator/debugging/x64_gdb_stub_handler.hpp @@ -103,7 +103,8 @@ class x64_gdb_stub_handler : public gdb_stub::gdb_stub_handler size_t get_max_register_size() override { - return 256 / 8; + // return 256 / 8; + return 64 / 8; } bool read_register(const size_t reg, void* data, const size_t max_length) override From 81fda5f8afb338b2fe85542a634b1a1fb5ba9909 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 18 Jan 2025 12:49:10 +0100 Subject: [PATCH 15/20] Remove mini-gdbstub dependency --- deps/CMakeLists.txt | 1 - deps/mini-gdbstub | 1 - deps/mini-gdbstub.cmake | 11 ----------- src/windows-emulator/CMakeLists.txt | 1 - 4 files changed, 14 deletions(-) delete mode 160000 deps/mini-gdbstub delete mode 100644 deps/mini-gdbstub.cmake diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 168d6006..19217d54 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -11,6 +11,5 @@ target_include_directories(reflect INTERFACE ########################################## -include(mini-gdbstub.cmake) include(googletest.cmake) include(zlib.cmake) diff --git a/deps/mini-gdbstub b/deps/mini-gdbstub deleted file mode 160000 index 632ebd38..00000000 --- a/deps/mini-gdbstub +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 632ebd38921da6aa2cb5cc77c19646df6b48366f diff --git a/deps/mini-gdbstub.cmake b/deps/mini-gdbstub.cmake deleted file mode 100644 index b4ed7b63..00000000 --- a/deps/mini-gdbstub.cmake +++ /dev/null @@ -1,11 +0,0 @@ -file(GLOB_RECURSE SRC_FILES CONFIGURE_DEPENDS - mini-gdbstub/lib/*.c -) - -list(SORT SRC_FILES) - -add_library(mini-gdbstub ${SRC_FILES}) - -target_include_directories(mini-gdbstub PUBLIC - "${CMAKE_CURRENT_LIST_DIR}/mini-gdbstub/include" -) diff --git a/src/windows-emulator/CMakeLists.txt b/src/windows-emulator/CMakeLists.txt index b2ffe79c..90cb3af8 100644 --- a/src/windows-emulator/CMakeLists.txt +++ b/src/windows-emulator/CMakeLists.txt @@ -14,7 +14,6 @@ target_precompile_headers(windows-emulator PRIVATE std_include.hpp) target_link_libraries(windows-emulator PRIVATE unicorn-emulator - mini-gdbstub ) target_link_libraries(windows-emulator PUBLIC From b34ef0e5468551dbc661b94550b9e9a724d703e5 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 18 Jan 2025 12:56:57 +0100 Subject: [PATCH 16/20] Rename interface --- src/gdb-stub/gdb_stub.cpp | 34 +++++++++---------- src/gdb-stub/gdb_stub.hpp | 12 +++---- .../debugging/win_x64_gdb_stub_handler.hpp | 8 ++--- .../debugging/x64_gdb_stub_handler.hpp | 10 +++--- 4 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/gdb-stub/gdb_stub.cpp b/src/gdb-stub/gdb_stub.cpp index ad643428..cca0d1aa 100644 --- a/src/gdb-stub/gdb_stub.cpp +++ b/src/gdb-stub/gdb_stub.cpp @@ -50,7 +50,7 @@ namespace gdb_stub return {name, args}; } - void process_xfer(const connection_handler& connection, gdb_stub_handler& handler, + void process_xfer(const connection_handler& connection, debugging_handler& handler, const std::string_view payload) { auto [name, args] = split_string(payload, ':'); @@ -67,7 +67,7 @@ namespace gdb_stub } } - void process_query(const connection_handler& connection, gdb_stub_handler& handler, + void process_query(const connection_handler& connection, debugging_handler& handler, const std::string_view payload) { const auto [name, args] = split_string(payload, ':'); @@ -94,9 +94,9 @@ namespace gdb_stub } } - void process_action(const connection_handler& connection, const gdb_action action) + void process_action(const connection_handler& connection, const action a) { - if (action == gdb_action::shutdown) + if (a == action::shutdown) { connection.close(); } @@ -112,7 +112,7 @@ namespace gdb_stub return static_cast(type); } - bool change_breakpoint(gdb_stub_handler& handler, const bool set, const breakpoint_type type, + bool change_breakpoint(debugging_handler& handler, const bool set, const breakpoint_type type, const uint64_t address, const size_t size) { if (set) @@ -123,8 +123,8 @@ namespace gdb_stub return handler.delete_breakpoint(type, address, size); } - void handle_breakpoint(const connection_handler& connection, gdb_stub_handler& handler, const std::string& data, - const bool set) + void handle_breakpoint(const connection_handler& connection, debugging_handler& handler, + const std::string& data, const bool set) { uint32_t type{}; uint64_t addr{}; @@ -152,7 +152,7 @@ namespace gdb_stub } } - void read_registers(const connection_handler& connection, gdb_stub_handler& handler) + void read_registers(const connection_handler& connection, debugging_handler& handler) { std::string response{}; std::vector data{}; @@ -177,7 +177,7 @@ namespace gdb_stub connection.send_reply(response); } - void write_registers(const connection_handler& connection, gdb_stub_handler& handler, + void write_registers(const connection_handler& connection, debugging_handler& handler, const std::string_view payload) { const auto data = utils::string::from_hex_string(payload); @@ -208,7 +208,7 @@ namespace gdb_stub connection.send_reply("OK"); } - void read_single_register(const connection_handler& connection, gdb_stub_handler& handler, + void read_single_register(const connection_handler& connection, debugging_handler& handler, const std::string& payload) { size_t reg{}; @@ -229,7 +229,7 @@ namespace gdb_stub } } - void write_single_register(const connection_handler& connection, gdb_stub_handler& handler, + void write_single_register(const connection_handler& connection, debugging_handler& handler, const std::string_view payload) { const auto [reg, hex_data] = split_string(payload, '='); @@ -252,7 +252,7 @@ namespace gdb_stub connection.send_reply("OK"); } - void read_memory(const connection_handler& connection, gdb_stub_handler& handler, const std::string& payload) + void read_memory(const connection_handler& connection, debugging_handler& handler, const std::string& payload) { uint64_t address{}; size_t size{}; @@ -277,7 +277,7 @@ namespace gdb_stub connection.send_reply(utils::string::to_hex_string(data)); } - void write_memory(const connection_handler& connection, gdb_stub_handler& handler, + void write_memory(const connection_handler& connection, debugging_handler& handler, const std::string_view payload) { const auto [info, hex_data] = split_string(payload, ':'); @@ -331,7 +331,7 @@ namespace gdb_stub return result; } - void write_x_memory(const connection_handler& connection, gdb_stub_handler& handler, + void write_x_memory(const connection_handler& connection, debugging_handler& handler, const std::string_view payload) { const auto [info, encoded_data] = split_string(payload, ':'); @@ -359,7 +359,7 @@ namespace gdb_stub connection.send_reply("OK"); } - void handle_command(const connection_handler& connection, async_handler& async, gdb_stub_handler& handler, + void handle_command(const connection_handler& connection, async_handler& async, debugging_handler& handler, const uint8_t command, const std::string_view data) { switch (command) @@ -431,7 +431,7 @@ namespace gdb_stub } } - void process_packet(const connection_handler& connection, async_handler& async, gdb_stub_handler& handler, + void process_packet(const connection_handler& connection, async_handler& async, debugging_handler& handler, const std::string_view packet) { connection.send_raw_data("+"); @@ -451,7 +451,7 @@ namespace gdb_stub } } - bool run_gdb_stub(const network::address& bind_address, gdb_stub_handler& handler) + bool run_gdb_stub(const network::address& bind_address, debugging_handler& handler) { auto client = accept_client(bind_address); if (!client) diff --git a/src/gdb-stub/gdb_stub.hpp b/src/gdb-stub/gdb_stub.hpp index 40ce53d6..9a71b7b2 100644 --- a/src/gdb-stub/gdb_stub.hpp +++ b/src/gdb-stub/gdb_stub.hpp @@ -4,7 +4,7 @@ namespace gdb_stub { - enum class gdb_action : uint8_t + enum class action : uint8_t { none, resume, @@ -21,12 +21,12 @@ namespace gdb_stub END, }; - struct gdb_stub_handler + struct debugging_handler { - virtual ~gdb_stub_handler() = default; + virtual ~debugging_handler() = default; - virtual gdb_action run() = 0; - virtual gdb_action singlestep() = 0; + virtual action run() = 0; + virtual action singlestep() = 0; virtual size_t get_register_count() = 0; virtual size_t get_max_register_size() = 0; @@ -45,5 +45,5 @@ namespace gdb_stub virtual std::string get_target_description() = 0; }; - bool run_gdb_stub(const network::address& bind_address, gdb_stub_handler& handler); + 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 df9d1261..96b87212 100644 --- a/src/windows-emulator/debugging/win_x64_gdb_stub_handler.hpp +++ b/src/windows-emulator/debugging/win_x64_gdb_stub_handler.hpp @@ -12,7 +12,7 @@ class win_x64_gdb_stub_handler : public x64_gdb_stub_handler { } - gdb_stub::gdb_action run() override + gdb_stub::action run() override { try { @@ -23,10 +23,10 @@ class win_x64_gdb_stub_handler : public x64_gdb_stub_handler this->win_emu_->log.error("%s\n", e.what()); } - return gdb_stub::gdb_action::resume; + return gdb_stub::action::resume; } - gdb_stub::gdb_action singlestep() override + gdb_stub::action singlestep() override { try { @@ -37,7 +37,7 @@ class win_x64_gdb_stub_handler : public x64_gdb_stub_handler this->win_emu_->log.error("%s\n", e.what()); } - return gdb_stub::gdb_action::resume; + return gdb_stub::action::resume; } std::string get_target_description() override diff --git a/src/windows-emulator/debugging/x64_gdb_stub_handler.hpp b/src/windows-emulator/debugging/x64_gdb_stub_handler.hpp index fa4a15a7..0937edeb 100644 --- a/src/windows-emulator/debugging/x64_gdb_stub_handler.hpp +++ b/src/windows-emulator/debugging/x64_gdb_stub_handler.hpp @@ -58,7 +58,7 @@ struct std::hash } }; -class x64_gdb_stub_handler : public gdb_stub::gdb_stub_handler +class x64_gdb_stub_handler : public gdb_stub::debugging_handler { public: x64_gdb_stub_handler(x64_emulator& emu) @@ -68,7 +68,7 @@ class x64_gdb_stub_handler : public gdb_stub::gdb_stub_handler ~x64_gdb_stub_handler() override = default; - gdb_stub::gdb_action run() override + gdb_stub::action run() override { try { @@ -79,10 +79,10 @@ class x64_gdb_stub_handler : public gdb_stub::gdb_stub_handler puts(e.what()); } - return gdb_stub::gdb_action::resume; + return gdb_stub::action::resume; } - gdb_stub::gdb_action singlestep() override + gdb_stub::action singlestep() override { try { @@ -93,7 +93,7 @@ class x64_gdb_stub_handler : public gdb_stub::gdb_stub_handler puts(e.what()); } - return gdb_stub::gdb_action::resume; + return gdb_stub::action::resume; } size_t get_register_count() override From 3dbd954c60e746b2e13ff426124101143e30d591 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 18 Jan 2025 15:58:51 +0100 Subject: [PATCH 17/20] Fix includes --- src/gdb-stub/gdb_stub.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/gdb-stub/gdb_stub.cpp b/src/gdb-stub/gdb_stub.cpp index cca0d1aa..e0c7f0ad 100644 --- a/src/gdb-stub/gdb_stub.cpp +++ b/src/gdb-stub/gdb_stub.cpp @@ -1,16 +1,15 @@ #include "gdb_stub.hpp" -#include -#include - -#include "checksum.hpp" -#include "async_handler.hpp" -#include "connection_handler.hpp" -#include "utils/string.hpp" - #include #include +#include +#include +#include + +#include "async_handler.hpp" +#include "connection_handler.hpp" + using namespace std::literals; namespace gdb_stub From a0932e6803ec08a960252cca240a34451f59a842 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 18 Jan 2025 15:59:13 +0100 Subject: [PATCH 18/20] Align register behaviour with implementation on main branch --- src/windows-emulator/debugging/x64_gdb_stub_handler.hpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/windows-emulator/debugging/x64_gdb_stub_handler.hpp b/src/windows-emulator/debugging/x64_gdb_stub_handler.hpp index 0937edeb..f80996cd 100644 --- a/src/windows-emulator/debugging/x64_gdb_stub_handler.hpp +++ b/src/windows-emulator/debugging/x64_gdb_stub_handler.hpp @@ -113,7 +113,8 @@ class x64_gdb_stub_handler : public gdb_stub::debugging_handler { if (reg >= gdb_registers.size()) { - return false; + // TODO: Fix + return true; } this->emu_->read_register(gdb_registers[reg], data, max_length); @@ -121,7 +122,8 @@ class x64_gdb_stub_handler : public gdb_stub::debugging_handler } catch (...) { - return false; + // TODO: Fix + return true; } } @@ -131,7 +133,8 @@ class x64_gdb_stub_handler : public gdb_stub::debugging_handler { if (reg >= gdb_registers.size()) { - return false; + // TODO: Fix + return true; } this->emu_->write_register(gdb_registers[reg], data, size); From 4c5257098c7a73021b8824194c050fb39eb60174 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 18 Jan 2025 15:59:52 +0100 Subject: [PATCH 19/20] Add logging line for later --- src/gdb-stub/gdb_stub.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gdb-stub/gdb_stub.cpp b/src/gdb-stub/gdb_stub.cpp index e0c7f0ad..80f63a66 100644 --- a/src/gdb-stub/gdb_stub.cpp +++ b/src/gdb-stub/gdb_stub.cpp @@ -361,6 +361,8 @@ namespace gdb_stub void handle_command(const connection_handler& connection, async_handler& async, debugging_handler& handler, 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': From 9fc37fa3ef57840d2debdf1fde79434d4a6f47ab Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 18 Jan 2025 19:36:12 +0100 Subject: [PATCH 20/20] Small cleanup and utils --- src/common/utils/string.hpp | 23 +++++++++++++++++++++++ src/gdb-stub/gdb_stub.cpp | 16 ++-------------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/common/utils/string.hpp b/src/common/utils/string.hpp index 7a3cb30c..2b5100b8 100644 --- a/src/common/utils/string.hpp +++ b/src/common/utils/string.hpp @@ -79,6 +79,29 @@ namespace utils::string return result; } + template + requires(std::is_integral_v) + std::string to_hex_number(const Integer& i, const bool uppercase = false) + { + std::string res{}; + res.reserve(sizeof(i) * 2); + + const std::span data{reinterpret_cast(&i), sizeof(i)}; + + for (const auto value : data) + { + const auto [high, low] = to_hex(value, uppercase); + res.insert(res.begin(), {high, low}); + } + + while (res.size() > 1 && res.front() == '0') + { + res.erase(res.begin()); + } + + return res; + } + template requires(std::is_integral_v) std::string to_hex_string(const Integer& i, const bool uppercase = false) diff --git a/src/gdb-stub/gdb_stub.cpp b/src/gdb-stub/gdb_stub.cpp index 80f63a66..0ebabd8c 100644 --- a/src/gdb-stub/gdb_stub.cpp +++ b/src/gdb-stub/gdb_stub.cpp @@ -242,13 +242,7 @@ namespace gdb_stub const auto res = register_size <= data.size() && // handler.write_register(register_index, data.data(), register_size); - if (!res) - { - connection.send_reply("E01"); - return; - } - - connection.send_reply("OK"); + connection.send_reply(res ? "OK" : "E01"); } void read_memory(const connection_handler& connection, debugging_handler& handler, const std::string& payload) @@ -295,13 +289,7 @@ namespace gdb_stub data.resize(size); const auto res = handler.write_memory(address, data.data(), data.size()); - if (!res) - { - connection.send_reply("E01"); - return; - } - - connection.send_reply("OK"); + connection.send_reply(res ? "OK" : "E01"); } std::string decode_x_memory(const std::string_view payload)