diff options
Diffstat (limited to 'src/debug/GdbProto.cpp')
-rw-r--r-- | src/debug/GdbProto.cpp | 389 |
1 files changed, 389 insertions, 0 deletions
diff --git a/src/debug/GdbProto.cpp b/src/debug/GdbProto.cpp new file mode 100644 index 0000000..dc80364 --- /dev/null +++ b/src/debug/GdbProto.cpp @@ -0,0 +1,389 @@ + +#ifdef _WIN32 +#include <WS2tcpip.h> +#include <winsock.h> +#include <winsock2.h> +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <sys/types.h> +#include <errno.h> + +#ifndef _WIN32 +#include <poll.h> +#include <sys/select.h> +#include <sys/socket.h> +#endif + +#include "../Platform.h" +#include "hexutil.h" + +#include "GdbProto.h" + + +using Platform::Log; +using Platform::LogLevel; + +namespace Gdb +{ + +/* + * TODO commands to support: + * m M g G c s p P H + * ? D r + * qC qfThreadInfo qsThreadInfo + * z0 Z0 z1 Z1 z4 Z4 + * qCRC + * vAttach;addr + * vKill;pid + * qRcmd? qSupported? + */ +u8 Cmdbuf[GDBPROTO_BUFFER_CAPACITY]; +ssize_t Cmdlen; + +namespace Proto +{ + +u8 PacketBuf[GDBPROTO_BUFFER_CAPACITY]; +u8 RespBuf[GDBPROTO_BUFFER_CAPACITY+5]; + +ReadResult MsgRecv(int connfd, u8 cmd_dest[/*static GDBPROTO_BUFFER_CAPACITY*/]) +{ + static ssize_t dataoff = 0; + + ssize_t recv_total = dataoff; + ssize_t cksumoff = -1; + u8 sum = 0; + + bool first = true; + + //printf("--- dataoff=%zd\n", dataoff); + if (dataoff != 0) { + printf("--- got preexisting: %s\n", PacketBuf); + + ssize_t datastart = 0; + while (true) + { + if (PacketBuf[datastart] == '\x04') return ReadResult::Eof; + else if (PacketBuf[datastart] == '+' || PacketBuf[datastart] == '-') + { + /*if (PacketBuf[datastart] == '+') SendAck(connfd); + else SendNak(connfd);*/ + ++datastart; + continue; + } + else if (PacketBuf[datastart] == '$') + { + ++datastart; + break; + } + else + { + __builtin_trap(); + return ReadResult::Wut; + } + } + printf("--- datastart=%zd\n", datastart); + + for (ssize_t i = datastart; i < dataoff; ++i) + { + if (PacketBuf[i] == '#') + { + cksumoff = i + 1; + printf("--- cksumoff=%zd\n", cksumoff); + break; + } + + sum += PacketBuf[i]; + } + + if (cksumoff >= 0) + { + recv_total = dataoff - datastart + 1; + dataoff = cksumoff + 2 - datastart + 1; + cksumoff -= datastart - 1; + + memmove(&PacketBuf[1], &PacketBuf[datastart], recv_total); + PacketBuf[0] = '$'; + PacketBuf[recv_total] = 0; + + printf("=== cksumoff=%zi recv_total=%zi datastart=%zi dataoff=%zi\n==> %s\n", + cksumoff, recv_total, datastart, dataoff, PacketBuf); + //break; + } + } + + while (cksumoff < 0) + { + u8* pkt = &PacketBuf[dataoff]; + ssize_t n, blehoff = 0; + + memset(pkt, 0, sizeof(PacketBuf) - dataoff); + int flag = 0; +#if MOCKTEST + static bool FIRST = false; + if (FIRST) { + printf("%s", "[==>] TEST DONE\n"); + __builtin_trap(); + } + FIRST = true; + + const char* testinp1 = "+$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+;xmlRegisters=i386#77$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+;xmlRegisters=i386#77$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+;xmlRegisters=i386#77$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+;xmlRegisters=i386#77"; + const char* testinp2 = "+$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+;xmlRegisters=i386#77$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+;xmlRegisters=i386#77$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+;xmlRegisters=i386#77$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+;xmlRegisters=i386#77---+$vMustReplyEmpty#3a"; + + const char* testinp = testinp1; + + n = strlen(testinp); + memcpy(pkt, testinp, strlen(testinp)); +#else +#ifndef _WIN32 + if (first) flag |= MSG_DONTWAIT; + n = recv(connfd, pkt, sizeof(PacketBuf) - dataoff, flag); +#else + // fuck windows + n = recv(connfd, (char*)pkt, sizeof(PacketBuf) - dataoff, flag); +#endif +#endif + + if (n <= 0) + { + if (first) return ReadResult::NoPacket; + else + { + Log(LogLevel::Debug, "[GDB] recv() error %zi, errno=%d (%s)\n", n, errno, strerror(errno)); + return ReadResult::Eof; + } + } + + Log(LogLevel::Debug, "[GDB] recv() %zd bytes: '%s' (%02x)\n", n, pkt, pkt[0]); + first = false; + + do + { + if (dataoff == 0) + { + if (pkt[blehoff] == '\x04') return ReadResult::Eof; + else if (pkt[blehoff] == '\x03') return ReadResult::Break; + else if (pkt[blehoff] != '$') + { + ++blehoff; + --n; + } + else break; + + if (n == 0) goto next_outer; + } + } + while (true); + + if (blehoff > 0) + { + memmove(pkt, &pkt[blehoff], n - blehoff + 1); + n -= blehoff - 1; // ??? + } + + recv_total += n; + + Log(LogLevel::Debug, "[GDB] recv() after skipping: n=%zd, recv_total=%zd\n", n, recv_total); + + for (ssize_t i = (dataoff == 0) ? 1 : 0; i < n; ++i) + { + u8 v = pkt[i]; + if (v == '#') + { + cksumoff = dataoff + i + 1; + break; + } + + sum += pkt[i]; + } + + if (cksumoff < 0) + { + // oops, need more data + dataoff += n; + } + + next_outer:; + } + + u8 ck = (hex2nyb(PacketBuf[cksumoff+0]) << 4) + | hex2nyb(PacketBuf[cksumoff+1]); + + Log(LogLevel::Debug, "[GDB] got pkt, checksum: %02x vs %02x\n", ck, sum); + + if (ck != sum) + { + //__builtin_trap(); + return ReadResult::CksumErr; + } + + if (cksumoff + 2 > recv_total) + { + Log(LogLevel::Error, "[GDB] BIG MISTAKE: %zi > %zi which shouldn't happen!\n", cksumoff + 2, recv_total); + //__builtin_trap(); + return ReadResult::Wut; + } + else + { + Cmdlen = cksumoff - 2; + memcpy(Cmdbuf, &PacketBuf[1], Cmdlen); + Cmdbuf[Cmdlen] = 0; + + if (cksumoff + 2 < recv_total) { + // huh, we have the start of the next packet + dataoff = recv_total - (cksumoff + 2); + memmove(PacketBuf, &PacketBuf[cksumoff + 2], (size_t)dataoff); + PacketBuf[dataoff] = 0; + Log(LogLevel::Debug, "[GDB] got more: cksumoff=%zd, recvtotal=%zd, remain=%zd\n==> %s\n", cksumoff, recv_total, dataoff, PacketBuf); + } + else dataoff = 0; + } + + return ReadResult::CmdRecvd; +} + +int SendAck(int connfd) +{ + Log(LogLevel::Debug, "[GDB] send ack\n"); + u8 v = '+'; +#if MOCKTEST + return 1; +#endif + +#ifdef _WIN32 + // fuck windows + return send(connfd, (const char*)&v, 1, 0); +#else + return send(connfd, &v, 1, 0); +#endif +} + +int SendNak(int connfd) +{ + Log(LogLevel::Debug, "[GDB] send nak\n"); + u8 v = '-'; +#if MOCKTEST + return 1; +#endif + +#ifdef _WIN32 + // fuck windows + return send(connfd, (const char*)&v, 1, 0); +#else + return send(connfd, &v, 1, 0); +#endif +} + +int WaitAckBlocking(int connfd, u8* ackp, int to_ms) +{ +#if MOCKTEST + *ackp = '+'; + return 0; +#endif + +#ifdef _WIN32 + fd_set infd, outfd, errfd; + FD_ZERO(&infd); FD_ZERO(&outfd); FD_ZERO(&errfd); + FD_SET(connfd, &infd); + + struct timeval to; + to.tv_sec = to_ms / 1000; + to.tv_usec = (to_ms % 1000) * 1000; + + int r = select(connfd+1, &infd, &outfd, &errfd, &to); + + if (FD_ISSET(connfd, &errfd)) return -1; + else if (FD_ISSET(connfd, &infd)) + { + r = recv(connfd, (char*)ackp, 1, 0); + if (r < 0) return r; + return 0; + } + + return -1; +#else + struct pollfd pfd; + + pfd.fd = connfd; + pfd.events = POLLIN; + pfd.revents = 0; + + ssize_t r = (ssize_t)poll(&pfd, 1, to_ms); + if (r < 0) return r; + if (r == 0) return -1; + + if (pfd.revents & (POLLHUP|POLLERR)) return -69; + + r = recv(connfd, ackp, 1, 0); + if (r < 0) return r; + + return (r == 1) ? 0 : -1; +#endif +} + +int Resp(int connfd, const u8* data1, size_t len1, const u8* data2, size_t len2, bool noack) +{ + u8 cksum = 0; + int tries = 0; + + size_t totallen = len1 + len2; + + if (totallen >= GDBPROTO_BUFFER_CAPACITY) + { + Log(LogLevel::Error, "[GDB] packet with len %zu can't fit in buffer!\n", totallen); + return -42; + } + + RespBuf[0] = '$'; + for (size_t i = 0; i < len1; ++i) + { + cksum += data1[i]; + RespBuf[i+1] = data1[i]; + } + for (size_t i = 0; i < len2; ++i) + { + cksum += data2[i]; + RespBuf[len1+i+1] = data2[i]; + } + RespBuf[totallen+1] = '#'; + hexfmt8(&RespBuf[totallen+2], cksum); + RespBuf[totallen+4] = 0; + + do + { + ssize_t r; + u8 ack; + + Log(LogLevel::Debug, "[GDB] send resp: '%s'\n", RespBuf); +#if MOCKTEST + r = totallen+4; +#else +#ifdef _WIN32 + r = send(connfd, (const char*)RespBuf, totallen+4, 0); +#else + r = send(connfd, RespBuf, totallen+4, 0); +#endif +#endif + if (r < 0) return r; + + if (noack) break; + + r = WaitAckBlocking(connfd, &ack, 2000); + //Log(LogLevel::Debug, "[GDB] got ack: '%c'\n", ack); + if (r == 0 && ack == '+') break; + + ++tries; + } + while (tries < 3); + + return 0; +} + +} + +} + |