summaryrefslogtreecommitdiff
path: root/src/core/gdbstub/gdbstub.cpp
diff options
context:
space:
mode:
authorGravatar bunnei2015-11-11 23:21:31 -0500
committerGravatar bunnei2015-11-11 23:21:31 -0500
commit43bb29edc5a07ee290a462dc72743d96eaadd70d (patch)
treec42780b313be023001c41c32a6f6090fe93c2afe /src/core/gdbstub/gdbstub.cpp
parentMerge pull request #1236 from Subv/log_overflow (diff)
parentFix bug with reading addresses and lengths (diff)
downloadyuzu-43bb29edc5a07ee290a462dc72743d96eaadd70d.tar.gz
yuzu-43bb29edc5a07ee290a462dc72743d96eaadd70d.tar.xz
yuzu-43bb29edc5a07ee290a462dc72743d96eaadd70d.zip
Merge pull request #1122 from polaris-/gdbstub
gdbstub implementation
Diffstat (limited to 'src/core/gdbstub/gdbstub.cpp')
-rw-r--r--src/core/gdbstub/gdbstub.cpp955
1 files changed, 955 insertions, 0 deletions
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
new file mode 100644
index 000000000..003ce4167
--- /dev/null
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -0,0 +1,955 @@
1// Copyright 2013 Dolphin Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5// Originally written by Sven Peter <sven@fail0verflow.com> for anergistic.
6
7#include <algorithm>
8#include <climits>
9#include <csignal>
10#include <cstdarg>
11#include <cstdio>
12#include <cstring>
13#include <fcntl.h>
14#include <map>
15#include <numeric>
16
17#ifdef _MSC_VER
18#include <WinSock2.h>
19#include <ws2tcpip.h>
20#include <common/x64/abi.h>
21#include <io.h>
22#include <iphlpapi.h>
23#define SHUT_RDWR 2
24#else
25#include <unistd.h>
26#include <sys/select.h>
27#include <sys/socket.h>
28#include <sys/un.h>
29#include <netinet/in.h>
30#endif
31
32#include "common/logging/log.h"
33#include "common/string_util.h"
34#include "core/core.h"
35#include "core/memory.h"
36#include "core/arm/arm_interface.h"
37#include "core/gdbstub/gdbstub.h"
38
39const int GDB_BUFFER_SIZE = 10000;
40
41const char GDB_STUB_START = '$';
42const char GDB_STUB_END = '#';
43const char GDB_STUB_ACK = '+';
44const char GDB_STUB_NACK = '-';
45
46#ifndef SIGTRAP
47const u32 SIGTRAP = 5;
48#endif
49
50#ifndef SIGTERM
51const u32 SIGTERM = 15;
52#endif
53
54#ifndef MSG_WAITALL
55const u32 MSG_WAITALL = 8;
56#endif
57
58const u32 R0_REGISTER = 0;
59const u32 R15_REGISTER = 15;
60const u32 CSPR_REGISTER = 25;
61const u32 FPSCR_REGISTER = 58;
62const u32 MAX_REGISTERS = 90;
63
64namespace GDBStub {
65
66static int gdbserver_socket = -1;
67
68static u8 command_buffer[GDB_BUFFER_SIZE];
69static u32 command_length;
70
71static u32 latest_signal = 0;
72static bool step_break = false;
73static bool memory_break = false;
74
75// Binding to a port within the reserved ports range (0-1023) requires root permissions,
76// so default to a port outside of that range.
77static u16 gdbstub_port = 24689;
78
79static bool halt_loop = true;
80static bool step_loop = false;
81std::atomic<bool> g_server_enabled(false);
82
83#ifdef _WIN32
84WSADATA InitData;
85#endif
86
87struct Breakpoint {
88 bool active;
89 PAddr addr;
90 u32 len;
91};
92
93static std::map<u32, Breakpoint> breakpoints_execute;
94static std::map<u32, Breakpoint> breakpoints_read;
95static std::map<u32, Breakpoint> breakpoints_write;
96
97/**
98 * Turns hex string character into the equivalent byte.
99 *
100 * @param hex Input hex character to be turned into byte.
101 */
102static u8 HexCharToValue(u8 hex) {
103 if (hex >= '0' && hex <= '9') {
104 return hex - '0';
105 } else if (hex >= 'a' && hex <= 'f') {
106 return hex - 'a' + 0xA;
107 } else if (hex >= 'A' && hex <= 'F') {
108 return hex - 'A' + 0xA;
109 }
110
111 LOG_ERROR(Debug_GDBStub, "Invalid nibble: %c (%02x)\n", hex, hex);
112 return 0;
113}
114
115/**
116 * Turn nibble of byte into hex string character.
117 *
118 * @param n Nibble to be turned into hex character.
119 */
120static u8 NibbleToHex(u8 n) {
121 n &= 0xF;
122 if (n < 0xA) {
123 return '0' + n;
124 } else {
125 return 'A' + n - 0xA;
126 }
127}
128
129/**
130* Converts input hex string characters into an array of equivalent of u8 bytes.
131*
132* @param dest Pointer to buffer to store u8 bytes.
133* @param src Pointer to array of output hex string characters.
134* @param len Length of src array.
135*/
136static u32 HexToInt(u8* src, u32 len) {
137 u32 output = 0;
138 while (len-- > 0) {
139 output = (output << 4) | HexCharToValue(src[0]);
140 src++;
141 }
142 return output;
143}
144
145/**
146 * Converts input array of u8 bytes into their equivalent hex string characters.
147 *
148 * @param dest Pointer to buffer to store output hex string characters.
149 * @param src Pointer to array of u8 bytes.
150 * @param len Length of src array.
151 */
152static void MemToGdbHex(u8* dest, u8* src, u32 len) {
153 while (len-- > 0) {
154 u8 tmp = *src++;
155 *dest++ = NibbleToHex(tmp >> 4);
156 *dest++ = NibbleToHex(tmp);
157 }
158}
159
160/**
161 * Converts input gdb-formatted hex string characters into an array of equivalent of u8 bytes.
162 *
163 * @param dest Pointer to buffer to store u8 bytes.
164 * @param src Pointer to array of output hex string characters.
165 * @param len Length of src array.
166 */
167static void GdbHexToMem(u8* dest, u8* src, u32 len) {
168 while (len-- > 0) {
169 *dest++ = (HexCharToValue(src[0]) << 4) | HexCharToValue(src[1]);
170 src += 2;
171 }
172}
173
174/**
175 * Convert a u32 into a gdb-formatted hex string.
176 *
177 * @param dest Pointer to buffer to store output hex string characters.
178 */
179static void IntToGdbHex(u8* dest, u32 v) {
180 for (int i = 0; i < 8; i += 2) {
181 dest[i + 1] = NibbleToHex(v >> (4 * i));
182 dest[i] = NibbleToHex(v >> (4 * (i + 1)));
183 }
184}
185
186/**
187 * Convert a gdb-formatted hex string into a u32.
188 *
189 * @param src Pointer to hex string.
190 */
191static u32 GdbHexToInt(u8* src) {
192 u32 output = 0;
193
194 for (int i = 0; i < 8; i += 2) {
195 output = (output << 4) | HexCharToValue(src[7 - i - 1]);
196 output = (output << 4) | HexCharToValue(src[7 - i]);
197 }
198
199 return output;
200}
201
202/// Read a byte from the gdb client.
203static u8 ReadByte() {
204 u8 c;
205 size_t received_size = recv(gdbserver_socket, reinterpret_cast<char*>(&c), 1, MSG_WAITALL);
206 if (received_size != 1) {
207 LOG_ERROR(Debug_GDBStub, "recv failed : %ld", received_size);
208 Shutdown();
209 }
210
211 return c;
212}
213
214/// Calculate the checksum of the current command buffer.
215static u8 CalculateChecksum(u8 *buffer, u32 length) {
216 return static_cast<u8>(std::accumulate(buffer, buffer + length, 0, std::plus<u8>()));
217}
218
219/**
220 * Get the list of breakpoints for a given breakpoint type.
221 *
222 * @param type Type of breakpoint list.
223 */
224static std::map<u32, Breakpoint>& GetBreakpointList(BreakpointType type) {
225 switch (type) {
226 case BreakpointType::Execute:
227 return breakpoints_execute;
228 case BreakpointType::Read:
229 return breakpoints_read;
230 case BreakpointType::Write:
231 return breakpoints_write;
232 default:
233 return breakpoints_read;
234 }
235}
236
237/**
238 * Remove the breakpoint from the given address of the specified type.
239 *
240 * @param type Type of breakpoint.
241 * @param addr Address of breakpoint.
242 */
243static void RemoveBreakpoint(BreakpointType type, PAddr addr) {
244 std::map<u32, Breakpoint>& p = GetBreakpointList(type);
245
246 auto bp = p.find(addr);
247 if (bp != p.end()) {
248 LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: %08x bytes at %08x of type %d\n", bp->second.len, bp->second.addr, type);
249 p.erase(addr);
250 }
251}
252
253BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, BreakpointType type) {
254 std::map<u32, Breakpoint>& p = GetBreakpointList(type);
255 auto next_breakpoint = p.lower_bound(addr);
256 BreakpointAddress breakpoint;
257
258 if (next_breakpoint != p.end()) {
259 breakpoint.address = next_breakpoint->first;
260 breakpoint.type = type;
261 } else {
262 breakpoint.address = 0;
263 breakpoint.type = BreakpointType::None;
264 }
265
266 return breakpoint;
267}
268
269bool CheckBreakpoint(PAddr addr, BreakpointType type) {
270 if (!IsConnected()) {
271 return false;
272 }
273
274 std::map<u32, Breakpoint>& p = GetBreakpointList(type);
275
276 auto bp = p.find(addr);
277 if (bp != p.end()) {
278 u32 len = bp->second.len;
279
280 // IDA Pro defaults to 4-byte breakpoints for all non-hardware breakpoints
281 // no matter if it's a 4-byte or 2-byte instruction. When you execute a
282 // Thumb instruction with a 4-byte breakpoint set, it will set a breakpoint on
283 // two instructions instead of the single instruction you placed the breakpoint
284 // on. So, as a way to make sure that execution breakpoints are only breaking
285 // on the instruction that was specified, set the length of an execution
286 // breakpoint to 1. This should be fine since the CPU should never begin executing
287 // an instruction anywhere except the beginning of the instruction.
288 if (type == BreakpointType::Execute) {
289 len = 1;
290 }
291
292 if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) {
293 LOG_DEBUG(Debug_GDBStub, "Found breakpoint type %d @ %08x, range: %08x - %08x (%d bytes)\n", type, addr, bp->second.addr, bp->second.addr + len, len);
294 return true;
295 }
296 }
297
298 return false;
299}
300
301/**
302 * Send packet to gdb client.
303 *
304 * @param packet Packet to be sent to client.
305 */
306static void SendPacket(const char packet) {
307 size_t sent_size = send(gdbserver_socket, &packet, 1, 0);
308 if (sent_size != 1) {
309 LOG_ERROR(Debug_GDBStub, "send failed");
310 }
311}
312
313/**
314 * Send reply to gdb client.
315 *
316 * @param reply Reply to be sent to client.
317 */
318static void SendReply(const char* reply) {
319 if (!IsConnected()) {
320 return;
321 }
322
323 memset(command_buffer, 0, sizeof(command_buffer));
324
325 command_length = strlen(reply);
326 if (command_length + 4 > sizeof(command_buffer)) {
327 LOG_ERROR(Debug_GDBStub, "command_buffer overflow in SendReply");
328 return;
329 }
330
331 memcpy(command_buffer + 1, reply, command_length);
332
333 u8 checksum = CalculateChecksum(command_buffer, command_length + 1);
334 command_buffer[0] = GDB_STUB_START;
335 command_buffer[command_length + 1] = GDB_STUB_END;
336 command_buffer[command_length + 2] = NibbleToHex(checksum >> 4);
337 command_buffer[command_length + 3] = NibbleToHex(checksum);
338
339 u8* ptr = command_buffer;
340 u32 left = command_length + 4;
341 while (left > 0) {
342 int sent_size = send(gdbserver_socket, reinterpret_cast<char*>(ptr), left, 0);
343 if (sent_size < 0) {
344 LOG_ERROR(Debug_GDBStub, "gdb: send failed");
345 return Shutdown();
346 }
347
348 left -= sent_size;
349 ptr += sent_size;
350 }
351}
352
353/// Handle query command from gdb client.
354static void HandleQuery() {
355 LOG_DEBUG(Debug_GDBStub, "gdb: query '%s'\n", command_buffer + 1);
356
357 if (!strcmp(reinterpret_cast<const char*>(command_buffer + 1), "TStatus")) {
358 SendReply("T0");
359 } else {
360 SendReply("");
361 }
362}
363
364/// Handle set thread command from gdb client.
365static void HandleSetThread() {
366 if (memcmp(command_buffer, "Hg0", 3) == 0 ||
367 memcmp(command_buffer, "Hc-1", 4) == 0 ||
368 memcmp(command_buffer, "Hc0", 4) == 0 ||
369 memcmp(command_buffer, "Hc1", 4) == 0) {
370 return SendReply("OK");
371 }
372
373 SendReply("E01");
374}
375
376/**
377 * Send signal packet to client.
378 *
379 * @param signal Signal to be sent to client.
380 */
381void SendSignal(u32 signal) {
382 if (gdbserver_socket == -1) {
383 return;
384 }
385
386 latest_signal = signal;
387
388 std::string buffer = Common::StringFromFormat("T%02x%02x:%08x;%02x:%08x;", latest_signal, 15, htonl(Core::g_app_core->GetPC()), 13, htonl(Core::g_app_core->GetReg(13)));
389 LOG_DEBUG(Debug_GDBStub, "Response: %s", buffer.c_str());
390 SendReply(buffer.c_str());
391}
392
393/// Read command from gdb client.
394static void ReadCommand() {
395 command_length = 0;
396 memset(command_buffer, 0, sizeof(command_buffer));
397
398 u8 c = ReadByte();
399 if (c == '+') {
400 //ignore ack
401 return;
402 } else if (c == 0x03) {
403 LOG_INFO(Debug_GDBStub, "gdb: found break command\n");
404 halt_loop = true;
405 SendSignal(SIGTRAP);
406 return;
407 } else if (c != GDB_STUB_START) {
408 LOG_DEBUG(Debug_GDBStub, "gdb: read invalid byte %02x\n", c);
409 return;
410 }
411
412 while ((c = ReadByte()) != GDB_STUB_END) {
413 if (command_length >= sizeof(command_buffer)) {
414 LOG_ERROR(Debug_GDBStub, "gdb: command_buffer overflow\n");
415 SendPacket(GDB_STUB_NACK);
416 return;
417 }
418 command_buffer[command_length++] = c;
419 }
420
421 u8 checksum_received = HexCharToValue(ReadByte()) << 4;
422 checksum_received |= HexCharToValue(ReadByte());
423
424 u8 checksum_calculated = CalculateChecksum(command_buffer, command_length);
425
426 if (checksum_received != checksum_calculated) {
427 LOG_ERROR(Debug_GDBStub, "gdb: invalid checksum: calculated %02x and read %02x for $%s# (length: %d)\n",
428 checksum_calculated, checksum_received, command_buffer, command_length);
429
430 command_length = 0;
431
432 SendPacket(GDB_STUB_NACK);
433 return;
434 }
435
436 SendPacket(GDB_STUB_ACK);
437}
438
439/// Check if there is data to be read from the gdb client.
440static bool IsDataAvailable() {
441 if (!IsConnected()) {
442 return false;
443 }
444
445 fd_set fd_socket;
446
447 FD_ZERO(&fd_socket);
448 FD_SET(gdbserver_socket, &fd_socket);
449
450 struct timeval t;
451 t.tv_sec = 0;
452 t.tv_usec = 0;
453
454 if (select(gdbserver_socket + 1, &fd_socket, nullptr, nullptr, &t) < 0) {
455 LOG_ERROR(Debug_GDBStub, "select failed");
456 return false;
457 }
458
459 return FD_ISSET(gdbserver_socket, &fd_socket);
460}
461
462/// Send requested register to gdb client.
463static void ReadRegister() {
464 static u8 reply[64];
465 memset(reply, 0, sizeof(reply));
466
467 u32 id = HexCharToValue(command_buffer[1]);
468 if (command_buffer[2] != '\0') {
469 id <<= 4;
470 id |= HexCharToValue(command_buffer[2]);
471 }
472
473 if (id >= R0_REGISTER && id <= R15_REGISTER) {
474 IntToGdbHex(reply, Core::g_app_core->GetReg(id));
475 } else if (id == CSPR_REGISTER) {
476 IntToGdbHex(reply, Core::g_app_core->GetCPSR());
477 } else if (id > CSPR_REGISTER && id < FPSCR_REGISTER) {
478 IntToGdbHex(reply, Core::g_app_core->GetVFPReg(id - CSPR_REGISTER - 1)); // VFP registers should start at 26, so one after CSPR_REGISTER
479 } else if (id == FPSCR_REGISTER) {
480 IntToGdbHex(reply, Core::g_app_core->GetVFPSystemReg(VFP_FPSCR)); // Get FPSCR
481 IntToGdbHex(reply + 8, 0);
482 } else {
483 return SendReply("E01");
484 }
485
486 SendReply(reinterpret_cast<char*>(reply));
487}
488
489/// Send all registers to the gdb client.
490static void ReadRegisters() {
491 static u8 buffer[GDB_BUFFER_SIZE - 4];
492 memset(buffer, 0, sizeof(buffer));
493
494 u8* bufptr = buffer;
495 for (int i = 0, reg = 0; i <= MAX_REGISTERS; i++, reg++) {
496 if (i <= R15_REGISTER) {
497 IntToGdbHex(bufptr + i * CHAR_BIT, Core::g_app_core->GetReg(reg));
498 } else if (i == CSPR_REGISTER) {
499 IntToGdbHex(bufptr + i * CHAR_BIT, Core::g_app_core->GetCPSR());
500 } else if (i < CSPR_REGISTER) {
501 IntToGdbHex(bufptr + i * CHAR_BIT, 0);
502 IntToGdbHex(bufptr + (i + 1) * CHAR_BIT, 0);
503 i++; // These registers seem to be all 64bit instead of 32bit, so skip two instead of one
504 reg++;
505 } else if (i > CSPR_REGISTER && i < MAX_REGISTERS) {
506 IntToGdbHex(bufptr + i * CHAR_BIT, Core::g_app_core->GetVFPReg(reg - CSPR_REGISTER - 1));
507 IntToGdbHex(bufptr + (i + 1) * CHAR_BIT, 0);
508 i++;
509 } else if (i == MAX_REGISTERS) {
510 IntToGdbHex(bufptr + i * CHAR_BIT, Core::g_app_core->GetVFPSystemReg(VFP_FPSCR));
511 }
512 }
513
514 SendReply(reinterpret_cast<char*>(buffer));
515}
516
517/// Modify data of register specified by gdb client.
518static void WriteRegister() {
519 u8* buffer_ptr = command_buffer + 3;
520
521 u32 id = HexCharToValue(command_buffer[1]);
522 if (command_buffer[2] != '=') {
523 ++buffer_ptr;
524 id <<= 4;
525 id |= HexCharToValue(command_buffer[2]);
526 }
527
528 if (id >= R0_REGISTER && id <= R15_REGISTER) {
529 Core::g_app_core->SetReg(id, GdbHexToInt(buffer_ptr));
530 } else if (id == CSPR_REGISTER) {
531 Core::g_app_core->SetCPSR(GdbHexToInt(buffer_ptr));
532 } else if (id > CSPR_REGISTER && id < FPSCR_REGISTER) {
533 Core::g_app_core->SetVFPReg(id - CSPR_REGISTER - 1, GdbHexToInt(buffer_ptr));
534 } else if (id == FPSCR_REGISTER) {
535 Core::g_app_core->SetVFPSystemReg(VFP_FPSCR, GdbHexToInt(buffer_ptr));
536 } else {
537 return SendReply("E01");
538 }
539
540 SendReply("OK");
541}
542
543/// Modify all registers with data received from the client.
544static void WriteRegisters() {
545 u8* buffer_ptr = command_buffer + 1;
546
547 if (command_buffer[0] != 'G')
548 return SendReply("E01");
549
550 for (int i = 0, reg = 0; i <= MAX_REGISTERS; i++, reg++) {
551 if (i <= R15_REGISTER) {
552 Core::g_app_core->SetReg(reg, GdbHexToInt(buffer_ptr + i * CHAR_BIT));
553 } else if (i == CSPR_REGISTER) {
554 Core::g_app_core->SetCPSR(GdbHexToInt(buffer_ptr + i * CHAR_BIT));
555 } else if (i < CSPR_REGISTER) {
556 i++; // These registers seem to be all 64bit instead of 32bit, so skip two instead of one
557 reg++;
558 } else if (i > CSPR_REGISTER && i < MAX_REGISTERS) {
559 Core::g_app_core->SetVFPReg(reg - CSPR_REGISTER - 1, GdbHexToInt(buffer_ptr + i * CHAR_BIT));
560 i++; // Skip padding
561 } else if (i == MAX_REGISTERS) {
562 Core::g_app_core->SetVFPSystemReg(VFP_FPSCR, GdbHexToInt(buffer_ptr + i * CHAR_BIT));
563 }
564 }
565
566 SendReply("OK");
567}
568
569/// Read location in memory specified by gdb client.
570static void ReadMemory() {
571 static u8 reply[GDB_BUFFER_SIZE - 4];
572
573 auto start_offset = command_buffer+1;
574 auto addr_pos = std::find(start_offset, command_buffer+command_length, ',');
575 PAddr addr = HexToInt(start_offset, addr_pos - start_offset);
576
577 start_offset = addr_pos+1;
578 u32 len = HexToInt(start_offset, (command_buffer + command_length) - start_offset);
579
580 LOG_DEBUG(Debug_GDBStub, "gdb: addr: %08x len: %08x\n", addr, len);
581
582 if (len * 2 > sizeof(reply)) {
583 SendReply("E01");
584 }
585
586 u8* data = Memory::GetPointer(addr);
587 if (!data) {
588 return SendReply("E0");
589 }
590
591 MemToGdbHex(reply, data, len);
592 reply[len * 2] = '\0';
593 SendReply(reinterpret_cast<char*>(reply));
594}
595
596/// Modify location in memory with data received from the gdb client.
597static void WriteMemory() {
598 auto start_offset = command_buffer+1;
599 auto addr_pos = std::find(start_offset, command_buffer+command_length, ',');
600 PAddr addr = HexToInt(start_offset, addr_pos - start_offset);
601
602 start_offset = addr_pos+1;
603 auto len_pos = std::find(start_offset, command_buffer+command_length, ':');
604 u32 len = HexToInt(start_offset, len_pos - start_offset);
605
606 u8* dst = Memory::GetPointer(addr);
607 if (!dst) {
608 return SendReply("E00");
609 }
610
611 GdbHexToMem(dst, len_pos + 1, len);
612 SendReply("OK");
613}
614
615void Break(bool is_memory_break) {
616 if (!halt_loop) {
617 halt_loop = true;
618 SendSignal(SIGTRAP);
619 }
620
621 memory_break = is_memory_break;
622}
623
624/// Tell the CPU that it should perform a single step.
625static void Step() {
626 step_loop = true;
627 halt_loop = true;
628 step_break = true;
629 SendSignal(SIGTRAP);
630}
631
632bool IsMemoryBreak() {
633 if (IsConnected()) {
634 return false;
635 }
636
637 return memory_break;
638}
639
640/// Tell the CPU to continue executing.
641static void Continue() {
642 memory_break = false;
643 step_break = false;
644 step_loop = false;
645 halt_loop = false;
646}
647
648/**
649 * Commit breakpoint to list of breakpoints.
650 *
651 * @param type Type of breakpoint.
652 * @param addr Address of breakpoint.
653 * @param len Length of breakpoint.
654 */
655bool CommitBreakpoint(BreakpointType type, PAddr addr, u32 len) {
656 std::map<u32, Breakpoint>& p = GetBreakpointList(type);
657
658 Breakpoint breakpoint;
659 breakpoint.active = true;
660 breakpoint.addr = addr;
661 breakpoint.len = len;
662 p.insert({ addr, breakpoint });
663
664 LOG_DEBUG(Debug_GDBStub, "gdb: added %d breakpoint: %08x bytes at %08x\n", type, breakpoint.len, breakpoint.addr);
665
666 return true;
667}
668
669/// Handle add breakpoint command from gdb client.
670static void AddBreakpoint() {
671 BreakpointType type;
672
673 u8 type_id = HexCharToValue(command_buffer[1]);
674 switch (type_id) {
675 case 0:
676 case 1:
677 type = BreakpointType::Execute;
678 break;
679 case 2:
680 type = BreakpointType::Write;
681 break;
682 case 3:
683 type = BreakpointType::Read;
684 break;
685 case 4:
686 type = BreakpointType::Access;
687 break;
688 default:
689 return SendReply("E01");
690 }
691
692 auto start_offset = command_buffer+3;
693 auto addr_pos = std::find(start_offset, command_buffer+command_length, ',');
694 PAddr addr = HexToInt(start_offset, addr_pos - start_offset);
695
696 start_offset = addr_pos+1;
697 u32 len = HexToInt(start_offset, (command_buffer + command_length) - start_offset);
698
699 if (type == BreakpointType::Access) {
700 // Access is made up of Read and Write types, so add both breakpoints
701 type = BreakpointType::Read;
702
703 if (!CommitBreakpoint(type, addr, len)) {
704 return SendReply("E02");
705 }
706
707 type = BreakpointType::Write;
708 }
709
710 if (!CommitBreakpoint(type, addr, len)) {
711 return SendReply("E02");
712 }
713
714 SendReply("OK");
715}
716
717/// Handle remove breakpoint command from gdb client.
718static void RemoveBreakpoint() {
719 BreakpointType type;
720
721 u8 type_id = HexCharToValue(command_buffer[1]);
722 switch (type_id) {
723 case 0:
724 case 1:
725 type = BreakpointType::Execute;
726 break;
727 case 2:
728 type = BreakpointType::Write;
729 break;
730 case 3:
731 type = BreakpointType::Read;
732 break;
733 case 4:
734 type = BreakpointType::Access;
735 break;
736 default:
737 return SendReply("E01");
738 }
739
740 auto start_offset = command_buffer+3;
741 auto addr_pos = std::find(start_offset, command_buffer+command_length, ',');
742 PAddr addr = HexToInt(start_offset, addr_pos - start_offset);
743
744 start_offset = addr_pos+1;
745 u32 len = HexToInt(start_offset, (command_buffer + command_length) - start_offset);
746
747 if (type == BreakpointType::Access) {
748 // Access is made up of Read and Write types, so add both breakpoints
749 type = BreakpointType::Read;
750 RemoveBreakpoint(type, addr);
751
752 type = BreakpointType::Write;
753 }
754
755 RemoveBreakpoint(type, addr);
756 SendReply("OK");
757}
758
759void HandlePacket() {
760 if (!IsConnected()) {
761 return;
762 }
763
764 if (!IsDataAvailable()) {
765 return;
766 }
767
768 ReadCommand();
769 if (command_length == 0) {
770 return;
771 }
772
773 LOG_DEBUG(Debug_GDBStub, "Packet: %s", command_buffer);
774
775 switch (command_buffer[0]) {
776 case 'q':
777 HandleQuery();
778 break;
779 case 'H':
780 HandleSetThread();
781 break;
782 case '?':
783 SendSignal(latest_signal);
784 break;
785 case 'k':
786 Shutdown();
787 LOG_INFO(Debug_GDBStub, "killed by gdb");
788 return;
789 case 'g':
790 ReadRegisters();
791 break;
792 case 'G':
793 WriteRegisters();
794 break;
795 case 'p':
796 ReadRegister();
797 break;
798 case 'P':
799 WriteRegister();
800 break;
801 case 'm':
802 ReadMemory();
803 break;
804 case 'M':
805 WriteMemory();
806 break;
807 case 's':
808 Step();
809 return;
810 case 'C':
811 case 'c':
812 Continue();
813 return;
814 case 'z':
815 RemoveBreakpoint();
816 break;
817 case 'Z':
818 AddBreakpoint();
819 break;
820 default:
821 SendReply("");
822 break;
823 }
824}
825
826void SetServerPort(u16 port) {
827 gdbstub_port = port;
828}
829
830void ToggleServer(bool status) {
831 if (status) {
832 g_server_enabled = status;
833
834 // Start server
835 if (!IsConnected() && Core::g_sys_core != nullptr) {
836 Init();
837 }
838 }
839 else {
840 // Stop server
841 if (IsConnected()) {
842 Shutdown();
843 }
844
845 g_server_enabled = status;
846 }
847}
848
849void Init(u16 port) {
850 if (!g_server_enabled) {
851 // Set the halt loop to false in case the user enabled the gdbstub mid-execution.
852 // This way the CPU can still execute normally.
853 halt_loop = false;
854 step_loop = false;
855 return;
856 }
857
858 // Setup initial gdbstub status
859 halt_loop = true;
860 step_loop = false;
861
862 breakpoints_execute.clear();
863 breakpoints_read.clear();
864 breakpoints_write.clear();
865
866 // Start gdb server
867 LOG_INFO(Debug_GDBStub, "Starting GDB server on port %d...", port);
868
869 sockaddr_in saddr_server = {};
870 saddr_server.sin_family = AF_INET;
871 saddr_server.sin_port = htons(port);
872 saddr_server.sin_addr.s_addr = INADDR_ANY;
873
874#ifdef _WIN32
875 WSAStartup(MAKEWORD(2, 2), &InitData);
876#endif
877
878 int tmpsock = socket(PF_INET, SOCK_STREAM, 0);
879 if (tmpsock == -1) {
880 LOG_ERROR(Debug_GDBStub, "Failed to create gdb socket");
881 }
882
883 const sockaddr* server_addr = reinterpret_cast<const sockaddr*>(&saddr_server);
884 socklen_t server_addrlen = sizeof(saddr_server);
885 if (bind(tmpsock, server_addr, server_addrlen) < 0) {
886 LOG_ERROR(Debug_GDBStub, "Failed to bind gdb socket");
887 }
888
889 if (listen(tmpsock, 1) < 0) {
890 LOG_ERROR(Debug_GDBStub, "Failed to listen to gdb socket");
891 }
892
893 // Wait for gdb to connect
894 LOG_INFO(Debug_GDBStub, "Waiting for gdb to connect...\n");
895 sockaddr_in saddr_client;
896 sockaddr* client_addr = reinterpret_cast<sockaddr*>(&saddr_client);
897 socklen_t client_addrlen = sizeof(saddr_client);
898 gdbserver_socket = accept(tmpsock, client_addr, &client_addrlen);
899 if (gdbserver_socket < 0) {
900 // In the case that we couldn't start the server for whatever reason, just start CPU execution like normal.
901 halt_loop = false;
902 step_loop = false;
903
904 LOG_ERROR(Debug_GDBStub, "Failed to accept gdb client");
905 }
906 else {
907 LOG_INFO(Debug_GDBStub, "Client connected.\n");
908 saddr_client.sin_addr.s_addr = ntohl(saddr_client.sin_addr.s_addr);
909 }
910
911 // Clean up temporary socket if it's still alive at this point.
912 if (tmpsock != -1) {
913 shutdown(tmpsock, SHUT_RDWR);
914 }
915}
916
917void Init() {
918 Init(gdbstub_port);
919}
920
921void Shutdown() {
922 if (!g_server_enabled) {
923 return;
924 }
925
926 LOG_INFO(Debug_GDBStub, "Stopping GDB ...");
927 if (gdbserver_socket != -1) {
928 shutdown(gdbserver_socket, SHUT_RDWR);
929 gdbserver_socket = -1;
930 }
931
932#ifdef _WIN32
933 WSACleanup();
934#endif
935
936 LOG_INFO(Debug_GDBStub, "GDB stopped.");
937}
938
939bool IsConnected() {
940 return g_server_enabled && gdbserver_socket != -1;
941}
942
943bool GetCpuHaltFlag() {
944 return halt_loop;
945}
946
947bool GetCpuStepFlag() {
948 return step_loop;
949}
950
951void SetCpuStepFlag(bool is_step) {
952 step_loop = is_step;
953}
954
955};