Renamed GdbRsp directory to Gdb

This commit is contained in:
Nav
2022-03-31 21:52:46 +01:00
parent 01d52bb130
commit 2aa240a680
57 changed files with 64 additions and 64 deletions

View File

@@ -0,0 +1,45 @@
#include "CommandPacket.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/ResponsePacket.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/OkResponsePacket.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/TargetStopped.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/DebugServer/Gdb/Signal.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
namespace Bloom::DebugServer::Gdb::CommandPackets
{
using ResponsePackets::ResponsePacket;
using ResponsePackets::OkResponsePacket;
using ResponsePackets::TargetStopped;
using ResponsePackets::ErrorResponsePacket;
using Exceptions::Exception;
void CommandPacket::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) {
auto packetData = this->getData();
auto packetString = std::string(packetData.begin(), packetData.end());
if (packetString[0] == '?') {
// Status report
debugSession.connection.writePacket(TargetStopped(Signal::TRAP));
} else if (packetString[0] == 'D') {
// Detach packet - there's not really anything we need to do here, so just respond with an OK
debugSession.connection.writePacket(OkResponsePacket());
} else if (packetString.find("qAttached") == 0) {
Logger::debug("Handling qAttached");
debugSession.connection.writePacket(ResponsePacket({1}));
} else {
Logger::debug("Unknown GDB RSP packet: " + packetString + " - returning empty response");
// Respond with an empty packet
debugSession.connection.writePacket(ResponsePacket({0}));
}
}
}

View File

@@ -0,0 +1,51 @@
#pragma once
#include <vector>
#include <memory>
#include "src/DebugServer/Gdb/Packet.hpp"
#include "src/DebugServer/Gdb/DebugSession.hpp"
#include "src/TargetController/TargetControllerConsole.hpp"
namespace Bloom::DebugServer::Gdb::CommandPackets
{
/**
* GDB RSP command packets are sent to the server, from the GDB client. These packets carry instructions that the
* server is expected to carry out. Upon completion, the server is expected to respond to the client with
* a ResponsePacket.
*
* For some command packets, we define a specific data structure by extending this CommandPacket class. These
* classes extend the data structure to include fields for data which may be specific to the command.
* They also implement additional methods that allow us to easily access the additional data. An example
* of this would be the SupportedFeaturesQuery class. It extends the CommandPacket class and provides access
* to additional data fields that are specific to the command (in this case, a set of GDB features reported to be
* supported by the GDB client).
*
* Typically, command packets that require specific data structures are handled in a dedicated handler method
* in the GdbRspDebugServer. This is done by double dispatching the packet object to the appropriate handler.
* See CommandPacket::dispatchToHandler(), GdbRspDebugServer::serve() and the overloads
* for GdbRspDebugServer::handleGdbPacket() for more on this.
*
* Some command packets are so simple they do not require a dedicated data structure. An example of this is
* the halt reason packet, which contains nothing more than an ? character in the packet body. These packets are
* typically handled in the generic GdbRspDebugServer::handleGdbPacket(CommandPacket&) method.
*
* See the Packet class for information on how the raw packets are formatted.
*/
class CommandPacket: public Packet
{
public:
explicit CommandPacket(const std::vector<unsigned char>& rawPacket): Packet(rawPacket) {}
/**
* Should handle the command for the current active debug session.
*
* @param debugSession
* The current active debug session.
*
* @param targetControllerConsole
*/
virtual void handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole);
};
}

View File

@@ -0,0 +1,33 @@
#include "ContinueExecution.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
namespace Bloom::DebugServer::Gdb::CommandPackets
{
using ResponsePackets::ErrorResponsePacket;
using Exceptions::Exception;
void ContinueExecution::init() {
if (this->data.size() > 1) {
this->fromProgramCounter = static_cast<std::uint32_t>(
std::stoi(std::string(this->data.begin(), this->data.end()), nullptr, 16)
);
}
}
void ContinueExecution::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) {
Logger::debug("Handling ContinueExecution packet");
try {
targetControllerConsole.continueTargetExecution(this->fromProgramCounter);
debugSession.waitingForBreak = true;
} catch (const Exception& exception) {
Logger::error("Failed to continue execution on target - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
}
}
}

View File

@@ -0,0 +1,36 @@
#pragma once
#include <cstdint>
#include <optional>
#include "CommandPacket.hpp"
namespace Bloom::DebugServer::Gdb::CommandPackets
{
/**
* The ContinueExecution class implements a structure for "c" packets. These packets instruct the server
* to continue execution on the target.
*
* See @link https://sourceware.org/gdb/onlinedocs/gdb/Packets.html#Packets for more on this.
*/
class ContinueExecution: public CommandPacket
{
public:
/**
* The "c" packet can contain an address which defines the point from which the execution should be resumed on
* the target.
*
* Although the packet *can* contain this address, it is not required, hence the optional.
*/
std::optional<std::uint32_t> fromProgramCounter;
explicit ContinueExecution(const std::vector<unsigned char>& rawPacket): CommandPacket(rawPacket) {
init();
}
void handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) override;
private:
void init();
};
}

View File

@@ -0,0 +1,29 @@
#include "InterruptExecution.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/TargetStopped.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/DebugServer/Gdb/Signal.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
namespace Bloom::DebugServer::Gdb::CommandPackets
{
using ResponsePackets::TargetStopped;
using ResponsePackets::ErrorResponsePacket;
using Exceptions::Exception;
void InterruptExecution::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) {
Logger::debug("Handling InterruptExecution packet");
try {
targetControllerConsole.stopTargetExecution();
debugSession.connection.writePacket(TargetStopped(Signal::INTERRUPTED));
debugSession.waitingForBreak = false;
} catch (const Exception& exception) {
Logger::error("Failed to interrupt execution - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
}
}
}

View File

@@ -0,0 +1,22 @@
#pragma once
#include "CommandPacket.hpp"
namespace Bloom::DebugServer::Gdb::CommandPackets
{
/**
* The InterruptException class represents interrupt command packets. Upon receiving an interrupt packet, the
* server is expected to interrupt execution on the target.
*
* Technically, interrupts are not sent by the client in the form of a typical GDP RSP packet. Instead, they're
* just sent as a single byte from the client. We fake the packet on our end, to save us the headache of dealing
* with this inconsistency. We do this in Connection::readRawPackets().
*/
class InterruptExecution: public CommandPacket
{
public:
explicit InterruptExecution(const std::vector<unsigned char>& rawPacket): CommandPacket(rawPacket) {}
void handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) override;
};
}

View File

@@ -0,0 +1,96 @@
#include "ReadRegisters.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/TargetStopped.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/Targets/TargetRegister.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
namespace Bloom::DebugServer::Gdb::CommandPackets
{
using Targets::TargetRegister;
using Targets::TargetRegisterDescriptors;
using ResponsePackets::ResponsePacket;
using ResponsePackets::ErrorResponsePacket;
using Exceptions::Exception;
void ReadRegisters::init() {
if (this->data.size() >= 2 && this->data.front() == 'p') {
// This command packet is requesting a specific register
this->registerNumber = static_cast<size_t>(
std::stoi(std::string(this->data.begin() + 1, this->data.end()))
);
}
}
void ReadRegisters::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) {
Logger::debug("Handling ReadRegisters packet");
try {
const auto& targetDescriptor = debugSession.targetDescriptor;
auto descriptors = TargetRegisterDescriptors();
if (this->registerNumber.has_value()) {
Logger::debug("Reading register number: " + std::to_string(this->registerNumber.value()));
descriptors.insert(
targetDescriptor.getTargetRegisterDescriptorFromNumber(this->registerNumber.value())
);
} else {
// Read all target registers mapped to a GDB register
for (const auto& registerNumber : targetDescriptor.getRegisterNumbers()) {
descriptors.insert(targetDescriptor.getTargetRegisterDescriptorFromNumber(registerNumber));
}
}
auto registerSet = targetControllerConsole.readRegisters(descriptors);
/*
* Sort each register by their respective GDB register number - this will leave us with a collection of
* registers in the order expected by the GDB client.
*/
std::sort(
registerSet.begin(),
registerSet.end(),
[this, &targetDescriptor] (const TargetRegister& registerA, const TargetRegister& registerB) {
return targetDescriptor.getRegisterNumberFromTargetRegisterDescriptor(registerA.descriptor) <
targetDescriptor.getRegisterNumberFromTargetRegisterDescriptor(registerB.descriptor);
}
);
/*
* Finally, reverse the register values (as they're all currently in MSB, but GDB expects them in LSB), ensure
* that each register value size matches the size in the associated GDB register descriptor, implode the
* values, convert to hexadecimal form and send to the GDB client.
*/
auto registers = std::vector<unsigned char>();
for (auto& reg : registerSet) {
std::reverse(reg.value.begin(), reg.value.end());
const auto gdbRegisterNumber = targetDescriptor.getRegisterNumberFromTargetRegisterDescriptor(
reg.descriptor
).value();
const auto& gdbRegisterDescriptor = targetDescriptor.getRegisterDescriptorFromNumber(gdbRegisterNumber);
if (reg.value.size() < gdbRegisterDescriptor.size) {
reg.value.insert(reg.value.end(), (gdbRegisterDescriptor.size - reg.value.size()), 0x00);
}
registers.insert(registers.end(), reg.value.begin(), reg.value.end());
}
auto responseRegisters = Packet::dataToHex(registers);
debugSession.connection.writePacket(
ResponsePacket(std::vector<unsigned char>(responseRegisters.begin(), responseRegisters.end()))
);
} catch (const Exception& exception) {
Logger::error("Failed to read general registers - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
}
}
}

View File

@@ -0,0 +1,37 @@
#pragma once
#include <optional>
#include "CommandPacket.hpp"
#include "src/DebugServer/Gdb/RegisterDescriptor.hpp"
namespace Bloom::DebugServer::Gdb::CommandPackets
{
/**
* The ReadRegisters class implements a structure for "g" and "p" command packets. In response to these
* packets, the server is expected to send register values for all registers (for "g" packets) or for a single
* register (for "p" packets).
*/
class ReadRegisters: public CommandPacket
{
public:
/**
* "p" packets include a register number to indicate which register is requested for reading. When this is set,
* the server is expected to respond with only the value of the requested register.
*
* If the register number is not supplied (as is the case with "g" packets), the server is expected to respond
* with values for all registers.
*/
std::optional<GdbRegisterNumberType> registerNumber;
explicit ReadRegisters(const std::vector<unsigned char>& rawPacket): CommandPacket(rawPacket) {
init();
};
void handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) override;
private:
void init();
};
}

View File

@@ -0,0 +1,64 @@
#include "RemoveBreakpoint.hpp"
#include <QtCore/QString>
#include "src/DebugServer/Gdb/ResponsePackets/OkResponsePacket.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/Targets/TargetBreakpoint.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
namespace Bloom::DebugServer::Gdb::CommandPackets
{
using Targets::TargetBreakpoint;
using ResponsePackets::OkResponsePacket;
using ResponsePackets::ErrorResponsePacket;
using Exceptions::Exception;
void RemoveBreakpoint::init() {
if (data.size() < 6) {
throw Exception("Unexpected RemoveBreakpoint packet size");
}
// z0 = SW breakpoint, z1 = HW breakpoint
this->type = (data[1] == 0) ? BreakpointType::SOFTWARE_BREAKPOINT : (data[1] == 1) ?
BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN;
auto packetData = QString::fromLocal8Bit(
reinterpret_cast<const char*>(this->data.data() + 2),
static_cast<int>(this->data.size() - 2)
);
auto packetSegments = packetData.split(",");
if (packetSegments.size() < 3) {
throw Exception("Unexpected number of packet segments in RemoveBreakpoint packet");
}
bool conversionStatus = true;
this->address = packetSegments.at(1).toUInt(&conversionStatus, 16);
if (!conversionStatus) {
throw Exception("Failed to convert address hex value from RemoveBreakpoint packet.");
}
}
void RemoveBreakpoint::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) {
Logger::debug("Removing breakpoint at address " + std::to_string(this->address));
try {
auto breakpoint = TargetBreakpoint();
breakpoint.address = this->address;
targetControllerConsole.removeBreakpoint(breakpoint);
debugSession.connection.writePacket(OkResponsePacket());
} catch (const Exception& exception) {
Logger::error("Failed to remove breakpoint on target - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
}
}
}

View File

@@ -0,0 +1,38 @@
#pragma once
#include <cstdint>
#include <string>
#include <set>
#include "CommandPacket.hpp"
#include "src/DebugServer/Gdb/BreakpointType.hpp"
namespace Bloom::DebugServer::Gdb::CommandPackets
{
/**
* The RemoveBreakpoint class implements the structure for "z" command packets. Upon receiving this command, the
* server is expected to remove a breakpoint at the specified address.
*/
class RemoveBreakpoint: public CommandPacket
{
public:
/**
* Breakpoint type (Software or Hardware)
*/
BreakpointType type = BreakpointType::UNKNOWN;
/**
* Address at which the breakpoint should be located.
*/
std::uint32_t address = 0;
explicit RemoveBreakpoint(const std::vector<unsigned char>& rawPacket): CommandPacket(rawPacket) {
this->init();
};
void handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) override;
private:
void init();
};
}

View File

@@ -0,0 +1,64 @@
#include "SetBreakpoint.hpp"
#include <QtCore/QString>
#include "src/DebugServer/Gdb/ResponsePackets/OkResponsePacket.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/Targets/TargetBreakpoint.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
namespace Bloom::DebugServer::Gdb::CommandPackets
{
using Targets::TargetBreakpoint;
using ResponsePackets::OkResponsePacket;
using ResponsePackets::ErrorResponsePacket;
using Exceptions::Exception;
void SetBreakpoint::init() {
if (data.size() < 6) {
throw Exception("Unexpected SetBreakpoint packet size");
}
// Z0 = SW breakpoint, Z1 = HW breakpoint
this->type = (data[1] == 0) ? BreakpointType::SOFTWARE_BREAKPOINT : (data[1] == 1) ?
BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN;
auto packetData = QString::fromLocal8Bit(
reinterpret_cast<const char*>(this->data.data() + 2),
static_cast<int>(this->data.size() - 2)
);
auto packetSegments = packetData.split(",");
if (packetSegments.size() < 3) {
throw Exception("Unexpected number of packet segments in SetBreakpoint packet");
}
bool conversionStatus = true;
this->address = packetSegments.at(1).toUInt(&conversionStatus, 16);
if (!conversionStatus) {
throw Exception("Failed to convert address hex value from SetBreakpoint packet.");
}
}
void SetBreakpoint::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) {
Logger::debug("Handling SetBreakpoint packet");
try {
auto breakpoint = TargetBreakpoint();
breakpoint.address = this->address;
targetControllerConsole.setBreakpoint(breakpoint);
debugSession.connection.writePacket(OkResponsePacket());
} catch (const Exception& exception) {
Logger::error("Failed to set breakpoint on target - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
}
}
}

View File

@@ -0,0 +1,38 @@
#pragma once
#include <cstdint>
#include <string>
#include <set>
#include "CommandPacket.hpp"
#include "src/DebugServer/Gdb/BreakpointType.hpp"
namespace Bloom::DebugServer::Gdb::CommandPackets
{
/**
* The SetBreakpoint class implements the structure for "Z" command packets. Upon receiving this command, the
* server is expected to set a breakpoint at the specified address.
*/
class SetBreakpoint: public CommandPacket
{
public:
/**
* Breakpoint type (Software or Hardware)
*/
BreakpointType type = BreakpointType::UNKNOWN;
/**
* Address at which the breakpoint should be located.
*/
std::uint32_t address = 0;
explicit SetBreakpoint(const std::vector<unsigned char>& rawPacket): CommandPacket(rawPacket) {
this->init();
};
void handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) override;
private:
void init();
};
}

View File

@@ -0,0 +1,34 @@
#include "StepExecution.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
namespace Bloom::DebugServer::Gdb::CommandPackets
{
using ResponsePackets::ErrorResponsePacket;
using Exceptions::Exception;
void StepExecution::init() {
if (this->data.size() > 1) {
this->fromProgramCounter = static_cast<std::uint32_t>(
std::stoi(std::string(this->data.begin(), this->data.end()), nullptr, 16)
);
}
}
void StepExecution::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) {
Logger::debug("Handling StepExecution packet");
try {
targetControllerConsole.stepTargetExecution(this->fromProgramCounter);
debugSession.waitingForBreak = true;
} catch (const Exception& exception) {
Logger::error("Failed to step execution on target - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
}
}
}

View File

@@ -0,0 +1,31 @@
#pragma once
#include <cstdint>
#include <optional>
#include "CommandPacket.hpp"
namespace Bloom::DebugServer::Gdb::CommandPackets
{
/**
* The StepExecution class implements the structure for "s" command packets. Upon receiving this command, the
* server is expected to step execution on the target.
*/
class StepExecution: public CommandPacket
{
public:
/**
* The address from which to begin the step.
*/
std::optional<std::size_t> fromProgramCounter;
explicit StepExecution(const std::vector<unsigned char>& rawPacket): CommandPacket(rawPacket) {
init();
};
void handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) override;
private:
void init();
};
}

View File

@@ -0,0 +1,72 @@
#include "SupportedFeaturesQuery.hpp"
#include <QtCore/QString>
#include "src/DebugServer/Gdb/Feature.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/SupportedFeaturesResponse.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
#include "src/DebugServer/Gdb/Exceptions/ClientNotSupported.hpp"
namespace Bloom::DebugServer::Gdb::CommandPackets
{
using ResponsePackets::SupportedFeaturesResponse;
using ResponsePackets::ErrorResponsePacket;
using Bloom::Exceptions::Exception;
using Gdb::Exceptions::ClientNotSupported;
void SupportedFeaturesQuery::init() {
/*
* For qSupported packets, supported and unsupported GDB features are reported in the packet
* data, where each GDB feature is separated by a semicolon.
*/
// The "qSupported:" prefix occupies 11 bytes
if (data.size() > 11) {
auto packetData = QString::fromLocal8Bit(
reinterpret_cast<const char*>(this->data.data() + 11),
static_cast<int>(this->data.size() - 11)
);
auto featureList = packetData.split(";");
auto gdbFeatureMapping = getGdbFeatureToNameMapping();
for (int i = 0; i < featureList.size(); i++) {
auto featureString = featureList.at(i);
// We only care about supported features. Supported features will precede a '+' character.
if (featureString[featureString.size() - 1] == '+') {
featureString.remove('+');
auto feature = gdbFeatureMapping.valueAt(featureString.toStdString());
if (feature.has_value()) {
this->supportedFeatures.insert(static_cast<decltype(feature)::value_type>(feature.value()));
}
}
}
}
}
void SupportedFeaturesQuery::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) {
Logger::debug("Handling QuerySupport packet");
if (!this->isFeatureSupported(Feature::HARDWARE_BREAKPOINTS)
&& !this->isFeatureSupported(Feature::SOFTWARE_BREAKPOINTS)
) {
// All GDB clients are expected to support breakpoints!
throw ClientNotSupported("GDB client does not support HW or SW breakpoints");
}
// Respond with a SupportedFeaturesResponse packet, listing all supported GDB features by Bloom
auto response = SupportedFeaturesResponse({
{Feature::SOFTWARE_BREAKPOINTS, std::nullopt},
{Feature::PACKET_SIZE, std::to_string(debugSession.connection.getMaxPacketSize())},
});
debugSession.connection.writePacket(response);
}
}

View File

@@ -0,0 +1,44 @@
#pragma once
#include <string>
#include <set>
#include "CommandPacket.hpp"
#include "../Feature.hpp"
namespace Bloom::DebugServer::Gdb::CommandPackets
{
/**
* The SupportedFeaturesQuery command packet is a query from the GDB client, requesting a list of GDB features
* supported by the GDB server. The body of this packet also contains a list GDB features that are supported or
* unsupported by the GDB client.
*
* The command packet is identified by its 'qSupported' prefix in the command packet data. Following the prefix is
* a list of GDB features that are supported/unsupported by the client. For more info on this command
* packet, see the GDP RSP documentation.
*
* Responses to this command packet should take the form of a ResponsePackets::SupportedFeaturesResponse.
*/
class SupportedFeaturesQuery: public CommandPacket
{
public:
explicit SupportedFeaturesQuery(const std::vector<unsigned char>& rawPacket): CommandPacket(rawPacket) {
this->init();
};
[[nodiscard]] bool isFeatureSupported(const Feature& feature) const {
return this->supportedFeatures.find(feature) != this->supportedFeatures.end();
}
[[nodiscard]] const std::set<Feature>& getSupportedFeatures() const {
return this->supportedFeatures;
}
void handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) override;
private:
std::set<Feature> supportedFeatures;
void init();
};
}

View File

@@ -0,0 +1,77 @@
#include "WriteRegister.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/TargetStopped.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/OkResponsePacket.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/Targets/TargetRegister.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
namespace Bloom::DebugServer::Gdb::CommandPackets
{
using Targets::TargetRegister;
using Targets::TargetRegisterDescriptors;
using ResponsePackets::ResponsePacket;
using ResponsePackets::OkResponsePacket;
using ResponsePackets::ErrorResponsePacket;
using Exceptions::Exception;
void WriteRegister::init() {
// The P packet updates a single register
auto packet = std::string(this->data.begin(), this->data.end());
if (packet.size() < 4) {
throw Exception("Invalid P command packet - insufficient data in packet.");
}
if (packet.find('=') == std::string::npos) {
throw Exception("Invalid P command packet - unexpected format");
}
auto packetSegments = QString::fromStdString(packet).split("=");
this->registerNumber = static_cast<int>(packetSegments.front().mid(1).toUInt(nullptr, 16));
this->registerValue = Packet::hexToData(packetSegments.back().toStdString());
std::reverse(this->registerValue.begin(), this->registerValue.end());
}
void WriteRegister::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) {
Logger::debug("Handling WriteRegister packet");
try {
auto targetRegisterDescriptor = debugSession.targetDescriptor.getTargetRegisterDescriptorFromNumber(this->registerNumber);
const auto valueSize = this->registerValue.size();
if (valueSize > 0 && valueSize > targetRegisterDescriptor.size) {
// Attempt to trim the higher zero-value bytes from the register value, until we reach the correct size.
for (auto i = this->registerValue.size() - 1; i >= targetRegisterDescriptor.size; i--) {
if (this->registerValue.at(i) != 0x00) {
// If we reach a non-zero byte, we cannot trim anymore without changing the data
break;
}
this->registerValue.erase(this->registerValue.begin() + i);
}
if (this->registerValue.size() > targetRegisterDescriptor.size) {
const auto& gdbRegisterDescriptor = debugSession.targetDescriptor.getRegisterDescriptorFromNumber(this->registerNumber);
throw Exception("Cannot set value for " + gdbRegisterDescriptor.name
+ " - value size exceeds register size."
);
}
}
targetControllerConsole.writeRegisters({
TargetRegister(targetRegisterDescriptor, this->registerValue)
});
debugSession.connection.writePacket(OkResponsePacket());
} catch (const Exception& exception) {
Logger::error("Failed to write registers - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
}
}
}

View File

@@ -0,0 +1,29 @@
#pragma once
#include <optional>
#include "CommandPacket.hpp"
#include "src/Targets/TargetRegister.hpp"
namespace Bloom::DebugServer::Gdb::CommandPackets
{
/**
* The WriteRegisters class implements the structure for "P" packets. Upon receiving this packet,
* server is expected to update a register value to the target.
*/
class WriteRegister: public CommandPacket
{
public:
int registerNumber = 0;
std::vector<unsigned char> registerValue;
explicit WriteRegister(const std::vector<unsigned char>& rawPacket): CommandPacket(rawPacket) {
init();
};
void handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) override;
private:
void init();
};
}