summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/arm/unicorn/arm_unicorn.cpp38
-rw-r--r--src/core/arm/unicorn/arm_unicorn.h4
-rw-r--r--src/core/gdbstub/gdbstub.cpp170
-rw-r--r--src/core/gdbstub/gdbstub.h9
4 files changed, 194 insertions, 27 deletions
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp
index c0cc62f03..ce6c5616d 100644
--- a/src/core/arm/unicorn/arm_unicorn.cpp
+++ b/src/core/arm/unicorn/arm_unicorn.cpp
@@ -35,6 +35,17 @@ LoadDll LoadDll::g_load_dll;
35 } \ 35 } \
36 } while (0) 36 } while (0)
37 37
38static void CodeHook(uc_engine* uc, uint64_t address, uint32_t size, void* user_data) {
39 GDBStub::BreakpointAddress bkpt =
40 GDBStub::GetNextBreakpointFromAddress(address, GDBStub::BreakpointType::Execute);
41 if (GDBStub::IsMemoryBreak() ||
42 (bkpt.type != GDBStub::BreakpointType::None && address == bkpt.address)) {
43 auto core = static_cast<ARM_Unicorn*>(user_data);
44 core->RecordBreak(bkpt);
45 uc_emu_stop(uc);
46 }
47}
48
38static void InterruptHook(uc_engine* uc, u32 intNo, void* user_data) { 49static void InterruptHook(uc_engine* uc, u32 intNo, void* user_data) {
39 u32 esr{}; 50 u32 esr{};
40 CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr)); 51 CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr));
@@ -67,6 +78,10 @@ ARM_Unicorn::ARM_Unicorn() {
67 uc_hook hook{}; 78 uc_hook hook{};
68 CHECKED(uc_hook_add(uc, &hook, UC_HOOK_INTR, (void*)InterruptHook, this, 0, -1)); 79 CHECKED(uc_hook_add(uc, &hook, UC_HOOK_INTR, (void*)InterruptHook, this, 0, -1));
69 CHECKED(uc_hook_add(uc, &hook, UC_HOOK_MEM_INVALID, (void*)UnmappedMemoryHook, this, 0, -1)); 80 CHECKED(uc_hook_add(uc, &hook, UC_HOOK_MEM_INVALID, (void*)UnmappedMemoryHook, this, 0, -1));
81 if (GDBStub::IsServerEnabled()) {
82 CHECKED(uc_hook_add(uc, &hook, UC_HOOK_CODE, (void*)CodeHook, this, 0, -1));
83 last_bkpt_hit = false;
84 }
70} 85}
71 86
72ARM_Unicorn::~ARM_Unicorn() { 87ARM_Unicorn::~ARM_Unicorn() {
@@ -155,7 +170,11 @@ void ARM_Unicorn::SetTlsAddress(VAddr base) {
155} 170}
156 171
157void ARM_Unicorn::Run() { 172void ARM_Unicorn::Run() {
158 ExecuteInstructions(std::max(CoreTiming::GetDowncount(), 0)); 173 if (GDBStub::IsServerEnabled()) {
174 ExecuteInstructions(std::max(4000000, 0));
175 } else {
176 ExecuteInstructions(std::max(CoreTiming::GetDowncount(), 0));
177 }
159} 178}
160 179
161void ARM_Unicorn::Step() { 180void ARM_Unicorn::Step() {
@@ -168,6 +187,18 @@ void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
168 MICROPROFILE_SCOPE(ARM_Jit); 187 MICROPROFILE_SCOPE(ARM_Jit);
169 CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions)); 188 CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions));
170 CoreTiming::AddTicks(num_instructions); 189 CoreTiming::AddTicks(num_instructions);
190 if (GDBStub::IsServerEnabled()) {
191 if (last_bkpt_hit) {
192 uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address);
193 }
194 Kernel::Thread* thread = Kernel::GetCurrentThread();
195 SaveContext(thread->context);
196 if (last_bkpt_hit) {
197 last_bkpt_hit = false;
198 GDBStub::Break();
199 }
200 GDBStub::SendTrap(thread, 5);
201 }
171} 202}
172 203
173void ARM_Unicorn::SaveContext(ARM_Interface::ThreadContext& ctx) { 204void ARM_Unicorn::SaveContext(ARM_Interface::ThreadContext& ctx) {
@@ -233,3 +264,8 @@ void ARM_Unicorn::PrepareReschedule() {
233} 264}
234 265
235void ARM_Unicorn::ClearInstructionCache() {} 266void ARM_Unicorn::ClearInstructionCache() {}
267
268void ARM_Unicorn::RecordBreak(GDBStub::BreakpointAddress bkpt) {
269 last_bkpt = bkpt;
270 last_bkpt_hit = true;
271}
diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h
index b99b58e4c..a482a2aa3 100644
--- a/src/core/arm/unicorn/arm_unicorn.h
+++ b/src/core/arm/unicorn/arm_unicorn.h
@@ -7,6 +7,7 @@
7#include <unicorn/unicorn.h> 7#include <unicorn/unicorn.h>
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/arm/arm_interface.h" 9#include "core/arm/arm_interface.h"
10#include "core/gdbstub/gdbstub.h"
10 11
11class ARM_Unicorn final : public ARM_Interface { 12class ARM_Unicorn final : public ARM_Interface {
12public: 13public:
@@ -35,7 +36,10 @@ public:
35 void Step() override; 36 void Step() override;
36 void ClearInstructionCache() override; 37 void ClearInstructionCache() override;
37 void PageTableChanged() override{}; 38 void PageTableChanged() override{};
39 void RecordBreak(GDBStub::BreakpointAddress bkpt);
38 40
39private: 41private:
40 uc_engine* uc{}; 42 uc_engine* uc{};
43 GDBStub::BreakpointAddress last_bkpt{};
44 bool last_bkpt_hit;
41}; 45};
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index 6c5a40ba8..2603192fe 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -32,9 +32,13 @@
32 32
33#include "common/logging/log.h" 33#include "common/logging/log.h"
34#include "common/string_util.h" 34#include "common/string_util.h"
35#include "common/swap.h"
35#include "core/arm/arm_interface.h" 36#include "core/arm/arm_interface.h"
36#include "core/core.h" 37#include "core/core.h"
38#include "core/core_cpu.h"
37#include "core/gdbstub/gdbstub.h" 39#include "core/gdbstub/gdbstub.h"
40#include "core/hle/kernel/kernel.h"
41#include "core/hle/kernel/scheduler.h"
38#include "core/loader/loader.h" 42#include "core/loader/loader.h"
39#include "core/memory.h" 43#include "core/memory.h"
40 44
@@ -137,15 +141,17 @@ static u8 command_buffer[GDB_BUFFER_SIZE];
137static u32 command_length; 141static u32 command_length;
138 142
139static u32 latest_signal = 0; 143static u32 latest_signal = 0;
140static bool step_break = false;
141static bool memory_break = false; 144static bool memory_break = false;
142 145
146static Kernel::Thread* current_thread = nullptr;
147
143// Binding to a port within the reserved ports range (0-1023) requires root permissions, 148// Binding to a port within the reserved ports range (0-1023) requires root permissions,
144// so default to a port outside of that range. 149// so default to a port outside of that range.
145static u16 gdbstub_port = 24689; 150static u16 gdbstub_port = 24689;
146 151
147static bool halt_loop = true; 152static bool halt_loop = true;
148static bool step_loop = false; 153static bool step_loop = false;
154static bool send_trap = false;
149 155
150// If set to false, the server will never be started and no 156// If set to false, the server will never be started and no
151// gdbstub-related functions will be executed. 157// gdbstub-related functions will be executed.
@@ -165,6 +171,53 @@ static std::map<u64, Breakpoint> breakpoints_execute;
165static std::map<u64, Breakpoint> breakpoints_read; 171static std::map<u64, Breakpoint> breakpoints_read;
166static std::map<u64, Breakpoint> breakpoints_write; 172static std::map<u64, Breakpoint> breakpoints_write;
167 173
174static Kernel::Thread* FindThreadById(int id) {
175 for (int core = 0; core < Core::NUM_CPU_CORES; core++) {
176 auto threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
177 for (auto thread : threads) {
178 if (thread->GetThreadId() == id) {
179 current_thread = thread.get();
180 return current_thread;
181 }
182 }
183 }
184 return nullptr;
185}
186
187static u64 RegRead(int id, Kernel::Thread* thread = nullptr) {
188 if (!thread) {
189 return 0;
190 }
191
192 if (id < SP_REGISTER) {
193 return thread->context.cpu_registers[id];
194 } else if (id == SP_REGISTER) {
195 return thread->context.sp;
196 } else if (id == PC_REGISTER) {
197 return thread->context.pc;
198 } else if (id == CPSR_REGISTER) {
199 return thread->context.cpsr;
200 } else {
201 return 0;
202 }
203}
204
205static void RegWrite(int id, u64 val, Kernel::Thread* thread = nullptr) {
206 if (!thread) {
207 return;
208 }
209
210 if (id < SP_REGISTER) {
211 thread->context.cpu_registers[id] = val;
212 } else if (id == SP_REGISTER) {
213 thread->context.sp = val;
214 } else if (id == PC_REGISTER) {
215 thread->context.pc = val;
216 } else if (id == CPSR_REGISTER) {
217 thread->context.cpsr = val;
218 }
219}
220
168/** 221/**
169 * Turns hex string character into the equivalent byte. 222 * Turns hex string character into the equivalent byte.
170 * 223 *
@@ -193,7 +246,7 @@ static u8 NibbleToHex(u8 n) {
193 if (n < 0xA) { 246 if (n < 0xA) {
194 return '0' + n; 247 return '0' + n;
195 } else { 248 } else {
196 return 'A' + n - 0xA; 249 return 'a' + n - 0xA;
197 } 250 }
198} 251}
199 252
@@ -439,6 +492,8 @@ static void SendReply(const char* reply) {
439 return; 492 return;
440 } 493 }
441 494
495 NGLOG_DEBUG(Debug_GDBStub, "Reply: {}", reply);
496
442 memset(command_buffer, 0, sizeof(command_buffer)); 497 memset(command_buffer, 0, sizeof(command_buffer));
443 498
444 command_length = static_cast<u32>(strlen(reply)); 499 command_length = static_cast<u32>(strlen(reply));
@@ -483,6 +538,22 @@ static void HandleQuery() {
483 } else if (strncmp(query, "Xfer:features:read:target.xml:", 538 } else if (strncmp(query, "Xfer:features:read:target.xml:",
484 strlen("Xfer:features:read:target.xml:")) == 0) { 539 strlen("Xfer:features:read:target.xml:")) == 0) {
485 SendReply(target_xml); 540 SendReply(target_xml);
541 } else if (strncmp(query, "Offsets", strlen("Offsets")) == 0) {
542 std::string buffer = fmt::format("TextSeg={:0x}", Memory::PROCESS_IMAGE_VADDR);
543 SendReply(buffer.c_str());
544 } else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) {
545 std::string val = "m";
546 for (int core = 0; core < Core::NUM_CPU_CORES; core++) {
547 auto threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
548 for (auto thread : threads) {
549 val += fmt::format("{:x}", thread->GetThreadId());
550 val += ",";
551 }
552 }
553 val.pop_back();
554 SendReply(val.c_str());
555 } else if (strncmp(query, "sThreadInfo", strlen("sThreadInfo")) == 0) {
556 SendReply("l");
486 } else { 557 } else {
487 SendReply(""); 558 SendReply("");
488 } 559 }
@@ -490,11 +561,40 @@ static void HandleQuery() {
490 561
491/// Handle set thread command from gdb client. 562/// Handle set thread command from gdb client.
492static void HandleSetThread() { 563static void HandleSetThread() {
493 if (memcmp(command_buffer, "Hg0", 3) == 0 || memcmp(command_buffer, "Hc-1", 4) == 0 || 564 if (memcmp(command_buffer, "Hc", 2) == 0 || memcmp(command_buffer, "Hg", 2) == 0) {
494 memcmp(command_buffer, "Hc0", 4) == 0 || memcmp(command_buffer, "Hc1", 4) == 0) { 565 int thread_id = -1;
495 return SendReply("OK"); 566 if (command_buffer[2] != '-') {
567 thread_id = static_cast<int>(HexToInt(
568 command_buffer + 2,
569 command_length - 2 /*strlen(reinterpret_cast<char*>(command_buffer) + 2)*/));
570 }
571 if (thread_id >= 1) {
572 current_thread = FindThreadById(thread_id);
573 }
574 if (!current_thread) {
575 thread_id = 1;
576 current_thread = FindThreadById(thread_id);
577 }
578 if (current_thread) {
579 SendReply("OK");
580 return;
581 }
496 } 582 }
583 SendReply("E01");
584}
497 585
586/// Handle thread alive command from gdb client.
587static void HandleThreadAlive() {
588 int thread_id = static_cast<int>(
589 HexToInt(command_buffer + 1,
590 command_length - 1 /*strlen(reinterpret_cast<char*>(command_buffer) + 1)*/));
591 if (thread_id == 0) {
592 thread_id = 1;
593 }
594 if (FindThreadById(thread_id)) {
595 SendReply("OK");
596 return;
597 }
498 SendReply("E01"); 598 SendReply("E01");
499} 599}
500 600
@@ -503,15 +603,24 @@ static void HandleSetThread() {
503 * 603 *
504 * @param signal Signal to be sent to client. 604 * @param signal Signal to be sent to client.
505 */ 605 */
506static void SendSignal(u32 signal) { 606static void SendSignal(Kernel::Thread* thread, u32 signal, bool full = true) {
507 if (gdbserver_socket == -1) { 607 if (gdbserver_socket == -1) {
508 return; 608 return;
509 } 609 }
510 610
511 latest_signal = signal; 611 latest_signal = signal;
512 612
513 std::string buffer = fmt::format("T{:02x}", latest_signal); 613 std::string buffer;
514 NGLOG_DEBUG(Debug_GDBStub, "Response: {}", buffer); 614 if (full) {
615 buffer = fmt::format("T{:02x}{:02x}:{:016x};{:02x}:{:016x};", latest_signal, PC_REGISTER,
616 Common::swap64(RegRead(PC_REGISTER, thread)), SP_REGISTER,
617 Common::swap64(RegRead(SP_REGISTER, thread)));
618 } else {
619 buffer = fmt::format("T{:02x};", latest_signal);
620 }
621
622 buffer += fmt::format("thread:{:x};", thread->GetThreadId());
623
515 SendReply(buffer.c_str()); 624 SendReply(buffer.c_str());
516} 625}
517 626
@@ -527,7 +636,7 @@ static void ReadCommand() {
527 } else if (c == 0x03) { 636 } else if (c == 0x03) {
528 NGLOG_INFO(Debug_GDBStub, "gdb: found break command"); 637 NGLOG_INFO(Debug_GDBStub, "gdb: found break command");
529 halt_loop = true; 638 halt_loop = true;
530 SendSignal(SIGTRAP); 639 SendSignal(current_thread, SIGTRAP);
531 return; 640 return;
532 } else if (c != GDB_STUB_START) { 641 } else if (c != GDB_STUB_START) {
533 NGLOG_DEBUG(Debug_GDBStub, "gdb: read invalid byte {:02X}", c); 642 NGLOG_DEBUG(Debug_GDBStub, "gdb: read invalid byte {:02X}", c);
@@ -598,11 +707,11 @@ static void ReadRegister() {
598 } 707 }
599 708
600 if (id <= SP_REGISTER) { 709 if (id <= SP_REGISTER) {
601 LongToGdbHex(reply, Core::CurrentArmInterface().GetReg(static_cast<int>(id))); 710 LongToGdbHex(reply, RegRead(id, current_thread));
602 } else if (id == PC_REGISTER) { 711 } else if (id == PC_REGISTER) {
603 LongToGdbHex(reply, Core::CurrentArmInterface().GetPC()); 712 LongToGdbHex(reply, RegRead(id, current_thread));
604 } else if (id == CPSR_REGISTER) { 713 } else if (id == CPSR_REGISTER) {
605 IntToGdbHex(reply, Core::CurrentArmInterface().GetCPSR()); 714 IntToGdbHex(reply, (u32)RegRead(id, current_thread));
606 } else { 715 } else {
607 return SendReply("E01"); 716 return SendReply("E01");
608 } 717 }
@@ -618,16 +727,16 @@ static void ReadRegisters() {
618 u8* bufptr = buffer; 727 u8* bufptr = buffer;
619 728
620 for (int reg = 0; reg <= SP_REGISTER; reg++) { 729 for (int reg = 0; reg <= SP_REGISTER; reg++) {
621 LongToGdbHex(bufptr + reg * 16, Core::CurrentArmInterface().GetReg(reg)); 730 LongToGdbHex(bufptr + reg * 16, RegRead(reg, current_thread));
622 } 731 }
623 732
624 bufptr += (32 * 16); 733 bufptr += (32 * 16);
625 734
626 LongToGdbHex(bufptr, Core::CurrentArmInterface().GetPC()); 735 LongToGdbHex(bufptr, RegRead(PC_REGISTER, current_thread));
627 736
628 bufptr += 16; 737 bufptr += 16;
629 738
630 IntToGdbHex(bufptr, Core::CurrentArmInterface().GetCPSR()); 739 IntToGdbHex(bufptr, (u32)RegRead(CPSR_REGISTER, current_thread));
631 740
632 bufptr += 8; 741 bufptr += 8;
633 742
@@ -646,11 +755,11 @@ static void WriteRegister() {
646 } 755 }
647 756
648 if (id <= SP_REGISTER) { 757 if (id <= SP_REGISTER) {
649 Core::CurrentArmInterface().SetReg(id, GdbHexToLong(buffer_ptr)); 758 RegWrite(id, GdbHexToLong(buffer_ptr), current_thread);
650 } else if (id == PC_REGISTER) { 759 } else if (id == PC_REGISTER) {
651 Core::CurrentArmInterface().SetPC(GdbHexToLong(buffer_ptr)); 760 RegWrite(id, GdbHexToLong(buffer_ptr), current_thread);
652 } else if (id == CPSR_REGISTER) { 761 } else if (id == CPSR_REGISTER) {
653 Core::CurrentArmInterface().SetCPSR(GdbHexToInt(buffer_ptr)); 762 RegWrite(id, GdbHexToInt(buffer_ptr), current_thread);
654 } else { 763 } else {
655 return SendReply("E01"); 764 return SendReply("E01");
656 } 765 }
@@ -667,11 +776,11 @@ static void WriteRegisters() {
667 776
668 for (int i = 0, reg = 0; reg <= CPSR_REGISTER; i++, reg++) { 777 for (int i = 0, reg = 0; reg <= CPSR_REGISTER; i++, reg++) {
669 if (reg <= SP_REGISTER) { 778 if (reg <= SP_REGISTER) {
670 Core::CurrentArmInterface().SetReg(reg, GdbHexToLong(buffer_ptr + i * 16)); 779 RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread);
671 } else if (reg == PC_REGISTER) { 780 } else if (reg == PC_REGISTER) {
672 Core::CurrentArmInterface().SetPC(GdbHexToLong(buffer_ptr + i * 16)); 781 RegWrite(PC_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread);
673 } else if (reg == CPSR_REGISTER) { 782 } else if (reg == CPSR_REGISTER) {
674 Core::CurrentArmInterface().SetCPSR(GdbHexToInt(buffer_ptr + i * 16)); 783 RegWrite(CPSR_REGISTER, GdbHexToInt(buffer_ptr + i * 16), current_thread);
675 } else { 784 } else {
676 UNIMPLEMENTED(); 785 UNIMPLEMENTED();
677 } 786 }
@@ -734,7 +843,7 @@ static void WriteMemory() {
734void Break(bool is_memory_break) { 843void Break(bool is_memory_break) {
735 if (!halt_loop) { 844 if (!halt_loop) {
736 halt_loop = true; 845 halt_loop = true;
737 SendSignal(SIGTRAP); 846 send_trap = true;
738 } 847 }
739 848
740 memory_break = is_memory_break; 849 memory_break = is_memory_break;
@@ -744,10 +853,10 @@ void Break(bool is_memory_break) {
744static void Step() { 853static void Step() {
745 step_loop = true; 854 step_loop = true;
746 halt_loop = true; 855 halt_loop = true;
747 step_break = true; 856 send_trap = true;
748 SendSignal(SIGTRAP);
749} 857}
750 858
859/// Tell the CPU if we hit a memory breakpoint.
751bool IsMemoryBreak() { 860bool IsMemoryBreak() {
752 if (IsConnected()) { 861 if (IsConnected()) {
753 return false; 862 return false;
@@ -759,7 +868,6 @@ bool IsMemoryBreak() {
759/// Tell the CPU to continue executing. 868/// Tell the CPU to continue executing.
760static void Continue() { 869static void Continue() {
761 memory_break = false; 870 memory_break = false;
762 step_break = false;
763 step_loop = false; 871 step_loop = false;
764 halt_loop = false; 872 halt_loop = false;
765} 873}
@@ -898,7 +1006,7 @@ void HandlePacket() {
898 HandleSetThread(); 1006 HandleSetThread();
899 break; 1007 break;
900 case '?': 1008 case '?':
901 SendSignal(latest_signal); 1009 SendSignal(current_thread, latest_signal);
902 break; 1010 break;
903 case 'k': 1011 case 'k':
904 Shutdown(); 1012 Shutdown();
@@ -935,6 +1043,9 @@ void HandlePacket() {
935 case 'Z': 1043 case 'Z':
936 AddBreakpoint(); 1044 AddBreakpoint();
937 break; 1045 break;
1046 case 'T':
1047 HandleThreadAlive();
1048 break;
938 default: 1049 default:
939 SendReply(""); 1050 SendReply("");
940 break; 1051 break;
@@ -1079,4 +1190,11 @@ bool GetCpuStepFlag() {
1079void SetCpuStepFlag(bool is_step) { 1190void SetCpuStepFlag(bool is_step) {
1080 step_loop = is_step; 1191 step_loop = is_step;
1081} 1192}
1193
1194void SendTrap(Kernel::Thread* thread, int trap) {
1195 if (send_trap) {
1196 send_trap = false;
1197 SendSignal(thread, trap);
1198 }
1199}
1082}; // namespace GDBStub 1200}; // namespace GDBStub
diff --git a/src/core/gdbstub/gdbstub.h b/src/core/gdbstub/gdbstub.h
index 201fca095..f2418c9e4 100644
--- a/src/core/gdbstub/gdbstub.h
+++ b/src/core/gdbstub/gdbstub.h
@@ -7,6 +7,7 @@
7#pragma once 7#pragma once
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/hle/kernel/thread.h"
10 11
11namespace GDBStub { 12namespace GDBStub {
12 13
@@ -91,4 +92,12 @@ bool GetCpuStepFlag();
91 * @param is_step 92 * @param is_step
92 */ 93 */
93void SetCpuStepFlag(bool is_step); 94void SetCpuStepFlag(bool is_step);
95
96/**
97 * Send trap signal from thread back to the gdbstub server.
98 *
99 * @param thread Sending thread.
100 * @param trap Trap no.
101 */
102void SendTrap(Kernel::Thread* thread, int trap);
94} // namespace GDBStub 103} // namespace GDBStub