diff options
| author | 2018-06-06 05:20:47 +0100 | |
|---|---|---|
| committer | 2018-06-06 00:20:47 -0400 | |
| commit | 39fb3e362cf71d0542a5807c97039cd474ef3716 (patch) | |
| tree | e54ad38fba3c7b2b32e5e408e9e7eb8cb6ffe6b0 /src/core/gdbstub/gdbstub.cpp | |
| parent | Merge pull request #516 from Subv/f2i_r (diff) | |
| download | yuzu-39fb3e362cf71d0542a5807c97039cd474ef3716.tar.gz yuzu-39fb3e362cf71d0542a5807c97039cd474ef3716.tar.xz yuzu-39fb3e362cf71d0542a5807c97039cd474ef3716.zip | |
GDB Stub Improvements (#508)
* GDB Stub should work now.
* Applied clang-format.
* Replaced htonll with swap64.
* Tidy up.
Diffstat (limited to 'src/core/gdbstub/gdbstub.cpp')
| -rw-r--r-- | src/core/gdbstub/gdbstub.cpp | 170 |
1 files changed, 144 insertions, 26 deletions
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]; | |||
| 137 | static u32 command_length; | 141 | static u32 command_length; |
| 138 | 142 | ||
| 139 | static u32 latest_signal = 0; | 143 | static u32 latest_signal = 0; |
| 140 | static bool step_break = false; | ||
| 141 | static bool memory_break = false; | 144 | static bool memory_break = false; |
| 142 | 145 | ||
| 146 | static 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. |
| 145 | static u16 gdbstub_port = 24689; | 150 | static u16 gdbstub_port = 24689; |
| 146 | 151 | ||
| 147 | static bool halt_loop = true; | 152 | static bool halt_loop = true; |
| 148 | static bool step_loop = false; | 153 | static bool step_loop = false; |
| 154 | static 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; | |||
| 165 | static std::map<u64, Breakpoint> breakpoints_read; | 171 | static std::map<u64, Breakpoint> breakpoints_read; |
| 166 | static std::map<u64, Breakpoint> breakpoints_write; | 172 | static std::map<u64, Breakpoint> breakpoints_write; |
| 167 | 173 | ||
| 174 | static 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 | |||
| 187 | static 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 | |||
| 205 | static 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. |
| 492 | static void HandleSetThread() { | 563 | static 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. | ||
| 587 | static 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 | */ |
| 506 | static void SendSignal(u32 signal) { | 606 | static 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() { | |||
| 734 | void Break(bool is_memory_break) { | 843 | void 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) { | |||
| 744 | static void Step() { | 853 | static 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. | ||
| 751 | bool IsMemoryBreak() { | 860 | bool 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. |
| 760 | static void Continue() { | 869 | static 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() { | |||
| 1079 | void SetCpuStepFlag(bool is_step) { | 1190 | void SetCpuStepFlag(bool is_step) { |
| 1080 | step_loop = is_step; | 1191 | step_loop = is_step; |
| 1081 | } | 1192 | } |
| 1193 | |||
| 1194 | void 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 |