aboutsummaryrefslogtreecommitdiff
path: root/src/debug/GdbProto.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/debug/GdbProto.cpp')
-rw-r--r--src/debug/GdbProto.cpp389
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;
+}
+
+}
+
+}
+