#ifdef _WIN32 #include #include #include #endif #include #include #include #include #include #include #ifndef _WIN32 #include #include #include #endif #include "../Platform.h" #include "hexutil.h" #include "GdbProto.h" using namespace melonDS; 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; } } }