From 9acaf334102b8ab06e7f70244cde09defb37f7b4 Mon Sep 17 00:00:00 2001 From: WerWolv Date: Mon, 14 Jul 2025 16:25:25 +0200 Subject: [PATCH] impr: Allow GDB Provider to work without NoAckMode, added run length encoding support --- .../content/providers/gdb_provider.hpp | 1 + .../source/content/providers/gdb_provider.cpp | 189 ++++++++++++------ 2 files changed, 133 insertions(+), 57 deletions(-) diff --git a/plugins/builtin/include/content/providers/gdb_provider.hpp b/plugins/builtin/include/content/providers/gdb_provider.hpp index 437485cea..9fb0b76d5 100644 --- a/plugins/builtin/include/content/providers/gdb_provider.hpp +++ b/plugins/builtin/include/content/providers/gdb_provider.hpp @@ -56,6 +56,7 @@ namespace hex::plugin::builtin { std::string m_ipAddress; int m_port = 0; + std::mutex m_mutex; u64 m_size = 0; }; diff --git a/plugins/builtin/source/content/providers/gdb_provider.cpp b/plugins/builtin/source/content/providers/gdb_provider.cpp index 8d94dbcd1..543a22ece 100644 --- a/plugins/builtin/source/content/providers/gdb_provider.cpp +++ b/plugins/builtin/source/content/providers/gdb_provider.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -20,13 +21,10 @@ namespace hex::plugin::builtin { namespace gdb { namespace { - u8 calculateChecksum(const std::string &data) { u64 checksum = 0; - for (const auto &c : data) checksum += c; - return checksum & 0xFF; } @@ -35,91 +33,156 @@ namespace hex::plugin::builtin { } std::optional parsePacket(const std::string &packet) { - if (packet.length() < 4) + if (packet.length() < 4 || packet[0] != '$') return std::nullopt; - if (!packet.starts_with('$')) + size_t hashPos = packet.find('#'); + if (hashPos == std::string::npos || hashPos + 2 >= packet.size()) return std::nullopt; - if (packet[packet.length() - 3] != '#') - return std::nullopt; + std::string data = packet.substr(1, hashPos - 1); + std::string checksumStr = packet.substr(hashPos + 1, 2); - std::string data = packet.substr(1, packet.length() - 4); - std::string checksum = packet.substr(packet.length() - 2, 2); - - auto decodedChecksum = crypt::decode16(checksum); - if (checksum.length() != 2 || decodedChecksum.empty() || decodedChecksum[0] != calculateChecksum(data)) + auto decodedChecksum = crypt::decode16(checksumStr); + if (decodedChecksum.empty() || decodedChecksum[0] != calculateChecksum(data)) return std::nullopt; return data; } - } + void sendAck(const wolv::net::SocketClient &socket) { + socket.writeString("+"); + } + + char readCharacter(const wolv::net::SocketClient &socket) { + auto data = socket.readBytes(1); + return data.empty() ? '\0' : static_cast(data[0]); + } + + std::string sendReceivePackage(const wolv::net::SocketClient &socket, const std::string &packet) { + socket.writeString(packet); + + i32 retries = 20; + + std::string buffer; + while (true) { + char c = readCharacter(socket); + if (c == '+') { + // ACK response, continue reading + continue; + } else if (c == '$') { + // Start of response packet + buffer += c; + + // Read until the end of the packet + while (true) { + c = readCharacter(socket); + buffer += c; + if (c == '#') + break; + } + + // Read the checksum + buffer += readCharacter(socket); + buffer += readCharacter(socket); + break; + } else if (c == '-') { + // NAK response, retry sending the packet + socket.writeString(packet); + retries -= 1; + } else if (c == 0x00) { + // No response, wait a bit and retry receiving data + std::this_thread::sleep_for(10ms); + retries -= 1; + } + + if (retries <= 0) { + log::error("No response from GDB server after multiple retries"); + return {}; + } + } + + if (!buffer.empty()) + sendAck(socket); + + return buffer; + } + + std::vector decodeMemoryResponse(const std::string &response) { + std::string expanded; + size_t i = 0; + + while (i < response.size()) { + char c = response[i]; + + if (i + 2 < response.size() && response[i + 1] == '*') { + char repeatChar = response[i + 2]; + int repeatCount = static_cast(repeatChar) - 29; + + if (repeatCount <= 0) + return {}; + + expanded.append(repeatCount, c); + i += 3; + } else { + expanded.push_back(c); + i++; + } + } + + if (expanded.size() % 2 != 0) + return {}; + + std::vector decoded; + for (size_t j = 0; j < expanded.size(); j += 2) { + std::string byteStr = expanded.substr(j, 2); + try { + u8 byte = static_cast(std::stoul(byteStr, nullptr, 16)); + decoded.push_back(byte); + } catch (...) { + return { }; + } + } + + return decoded; + } - void sendAck(const wolv::net::SocketClient &socket) { - socket.writeString("+"); - } - void continueExecution(const wolv::net::SocketClient &socket) { - socket.writeString(createPacket("vCont;c")); } std::vector readMemory(const wolv::net::SocketClient &socket, u64 address, size_t size) { std::string packet = createPacket(hex::format("m{:X},{:X}", address, size)); - socket.writeString(packet); - - auto receivedPacket = socket.readString(size * 2 + 4); - - if (receivedPacket.empty()) - return {}; + std::string receivedPacket = sendReceivePackage(socket, packet); auto receivedData = parsePacket(receivedPacket); if (!receivedData.has_value()) return {}; + sendAck(socket); + if (receivedData->size() == 3 && receivedData->starts_with("E")) return {}; - auto data = crypt::decode16(receivedData.value()); - + auto data = decodeMemoryResponse(*receivedData); data.resize(size); - return data; } - void writeMemory(const wolv::net::SocketClient &socket, u64 address, const void *buffer, size_t size) { + bool writeMemory(const wolv::net::SocketClient &socket, u64 address, const void *buffer, size_t size) { std::vector bytes(size); std::memcpy(bytes.data(), buffer, size); - std::string byteString = crypt::encode16(bytes); std::string packet = createPacket(hex::format("M{:X},{:X}:{}", address, size, byteString)); - socket.writeString(packet); - - auto receivedPacket = socket.readString(6); - std::ignore = receivedPacket; - } - - bool enableNoAckMode(const wolv::net::SocketClient &socket) { - socket.writeString(createPacket("QStartNoAckMode")); - - auto ack = socket.readString(1); - - if (ack.empty() || ack[0] != '+') - return false; - - auto receivedPacket = socket.readString(6); - + std::string receivedPacket = sendReceivePackage(socket, packet); auto receivedData = parsePacket(receivedPacket); - - if (receivedData && *receivedData == "OK") { - sendAck(socket); - return true; - } else { + if (!receivedData.has_value() || *receivedData != "OK") return false; - } + + sendAck(socket); + return true; } } @@ -148,12 +211,20 @@ namespace hex::plugin::builtin { } void GDBProvider::readFromSource(u64 offset, void *buffer, size_t size) { + std::scoped_lock lock(m_mutex); + if (!m_socket.isConnected()) + return; + auto data = gdb::readMemory(m_socket, offset, size); if (!data.empty()) std::memcpy(buffer, &data[0], data.size()); } void GDBProvider::writeToSource(u64 offset, const void *buffer, size_t size) { + std::scoped_lock lock(m_mutex); + if (!m_socket.isConnected()) + return; + gdb::writeMemory(m_socket, offset, buffer, size); } @@ -184,15 +255,16 @@ namespace hex::plugin::builtin { } bool GDBProvider::open() { - m_socket = wolv::net::SocketClient(wolv::net::SocketClient::Type::TCP, true); + std::scoped_lock lock(m_mutex); + + CachedProvider::open(); + m_socket = wolv::net::SocketClient(wolv::net::SocketClient::Type::TCP, false); m_socket.connect(m_ipAddress, m_port); - if (!gdb::enableNoAckMode(m_socket)) { - m_socket.disconnect(); - return false; - } + + gdb::sendReceivePackage(m_socket, gdb::createPacket("!")); + gdb::sendReceivePackage(m_socket, gdb::createPacket("Hg0")); if (m_socket.isConnected()) { - gdb::continueExecution(m_socket); return true; } else { return false; @@ -200,6 +272,9 @@ namespace hex::plugin::builtin { } void GDBProvider::close() { + std::scoped_lock lock(m_mutex); + + CachedProvider::close(); m_socket.disconnect(); }