/* SPDX-License-Identifier: Unlicense */ #pragma once #include #include #include #include namespace GDBRemote { enum class FeatureSupportMark: uint8_t { kNone = 0, kPlus, kMinus, kQuestion, }; enum class FeatureName: int { kUnknown = 0, kMultiprocess, kSoftwareBreak, kHardwareBreak, kQRelocInsn, kForkEvents, kVForkEvents, kExecEvents, kVContSuppurted, kQThreadEvents, kNoResumed, kMemoryTagging, kPacketSize, }; struct Feature { const FeatureName name{}; const FeatureSupportMark mark{}; const bool has_value{}; const int value{}; const std::string raw{}; }; enum class PacketType: int { kNone = 0, kQuerySupported, kQueryHaltReason, kQueryC, kQueryFThreadInfo, kQuerySThreadInfo, kQueryRTOSThreadInfo, kQueryAttached, kQueryTraceStatus, kQueryRcmd, kQStartNoAckMode, kSetThreadForCont, kSetThreadForOps, kVMustReplyEmpty, kEnableExtendedMode, kInterrupt, kContinue, kContinueAskSupported, kReadGeneralRegisters, kWriteGeneralRegisters, kReadRegister, kWriteRegister, kReadMemory, kWriteMemory, kStep, kSetBreakpoint, kDeleteBreakpoint, }; struct PacketData { virtual ~PacketData() {}; protected: PacketData(const PacketData&) = delete; PacketData(PacketData&&) = delete; PacketData() = default; }; struct PacketDataSupportedFeatures: public PacketData { std::vector features{}; virtual ~PacketDataSupportedFeatures() {} }; struct PacketDataReadMemory: public PacketData { PacketDataReadMemory(uint32_t a_offset, uint32_t a_length) : offset(a_offset), length(a_length) {} uint32_t offset{}, length{}; virtual ~PacketDataReadMemory() {} }; struct PacketDataWriteMemory: public PacketData { PacketDataWriteMemory(uint32_t a_offset, uint32_t a_length, std::vector&& a_data) : offset(a_offset), length(a_length), data(std::move(a_data)) {} uint32_t offset{}, length{}; std::vector data{}; virtual ~PacketDataWriteMemory() {} }; struct PacketDataRcmd: public PacketData { PacketDataRcmd(std::string&& a_data): data(std::move(a_data)) {} std::string data{}; virtual ~PacketDataRcmd() {} }; enum class BreakpointType: uint32_t { kMin = 0, kSoftwareBreakpoint = 0, kHardwareBreakpoint = 1, kWriteWatchpoint = 2, kReadWatchpoint = 3, kAccessWatchpoint = 4, kUnsupported // Keep it last and unassigned }; struct PacketDataBreakpoint: public PacketData { PacketDataBreakpoint(BreakpointType a_type, uint32_t a_offset, uint32_t a_length) : type(a_type), offset(a_offset), length(a_length) {} BreakpointType type{}; uint32_t offset{}, length{}; virtual ~PacketDataBreakpoint() {} }; struct PacketDataWriteGeneralRegisters: public PacketData { PacketDataWriteGeneralRegisters(std::vector&& regs): registers(std::move(regs)) {} std::vector registers{}; virtual ~PacketDataWriteGeneralRegisters() {} }; struct PacketDataReadRegister: public PacketData { PacketDataReadRegister(uint32_t a_reg_id): reg_id(a_reg_id) {} uint32_t reg_id{}; virtual ~PacketDataReadRegister() {} }; struct PacketDataWriteRegister: public PacketData { PacketDataWriteRegister(uint32_t a_reg_id, uint32_t a_value) : reg_id(a_reg_id), value(a_value) {} uint32_t reg_id{}, value{}; virtual ~PacketDataWriteRegister() {} }; struct Packet { const PacketType type{}; const std::unique_ptr data{nullptr}; /** Convert raw packet data into a Packet * * Packet data is the data extracted by ExchangeContext::Consume function. */ static Packet Parse(std::string packet_data); /** None packet creator */ static Packet None() { return Packet{}; } /** Stringify PacketType for debugging purposes */ static const char* PacketTypeToString(PacketType type) { switch (type) { case PacketType::kNone: return "None"; case PacketType::kQuerySupported: return "qSupported"; case PacketType::kQueryHaltReason: return "?"; case PacketType::kQueryC: return "qC"; case PacketType::kQueryFThreadInfo: return "qfThreadInfo"; case PacketType::kQuerySThreadInfo: return "qsThreadInfo"; case PacketType::kQueryRTOSThreadInfo: return "qL"; case PacketType::kQueryAttached: return "qAttached"; case PacketType::kQueryTraceStatus: return "qTStatus"; case PacketType::kQueryRcmd: return "qRcmd"; case PacketType::kQStartNoAckMode: return "QStartNoAckMode"; case PacketType::kSetThreadForCont: return "Hc"; case PacketType::kSetThreadForOps: return "Hg"; case PacketType::kVMustReplyEmpty: return "vMustReplyEmpty"; case PacketType::kEnableExtendedMode: return "!"; case PacketType::kInterrupt: return "vCtrlC"; case PacketType::kContinue: return "c"; case PacketType::kContinueAskSupported: return "vCont?"; case PacketType::kReadGeneralRegisters: return "g"; case PacketType::kWriteGeneralRegisters: return "G"; case PacketType::kReadRegister: return "p"; case PacketType::kWriteRegister: return "P"; case PacketType::kReadMemory: return "m"; case PacketType::kWriteMemory: return "M"; case PacketType::kStep: return "s"; case PacketType::kSetBreakpoint: return "Z"; case PacketType::kDeleteBreakpoint: return "z"; } return ""; } }; struct ExchangeResult { const std::string packet{}; const std::string ack{}; ExchangeResult(std::string a_packet, std::string a_response) : packet(a_packet), ack(a_response) {} static std::unique_ptr Nak(std::string data=std::string{}) { return std::make_unique(data, std::string{"-"}); } static std::unique_ptr Ack(std::string data=std::string{}) { return std::make_unique(data, std::string{"+"}); } }; class ExchangeContext { public: /** Consume next byte from input stream from GDB client * * Returns packet data and acknowledge response in case if a valid packet * fully received. * * Returns nullptr if is in progress. */ std::unique_ptr Consume(uint8_t byte); /** Creates raw packet from packet data to be sent into socket directly * * It is not static nor const because after sending a packet an * ExchangeContext must be set into awaiting acknowledge response state * (i.e. '+' or '-'). Otherwise we will be unable to resend packet in case * if '-' negative acknowledge is received. */ std::string WrapDataToSend(std::string packet=std::string{}); /** Returns last recognized packet * * For debugging purposes. */ const std::string GetLastPacket() { return _last_packet; } private: enum class ParsingState { kIdle, kPacketData, kPacketDataBinSecond, kChecksum1, kChecksum2, }; void transit(ParsingState); static const char* parsingStateToString(ParsingState state); ParsingState _parsing_state{ParsingState::kIdle}; std::string _packet_data{}; std::string _last_packet{}; uint8_t _checksum{}; uint8_t _given_checksum_first_byte{}; }; }