mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu.git
				synced 2025-05-12 00:45:25 +00:00 
			
		
		
		
	GDB Stub Improvements (#508)
* GDB Stub should work now. * Applied clang-format. * Replaced htonll with swap64. * Tidy up.
This commit is contained in:
		
							parent
							
								
									5fb99e6a16
								
							
						
					
					
						commit
						39fb3e362c
					
				| @ -35,6 +35,17 @@ LoadDll LoadDll::g_load_dll; | ||||
|         }                                                                                          \ | ||||
|     } while (0) | ||||
| 
 | ||||
| static void CodeHook(uc_engine* uc, uint64_t address, uint32_t size, void* user_data) { | ||||
|     GDBStub::BreakpointAddress bkpt = | ||||
|         GDBStub::GetNextBreakpointFromAddress(address, GDBStub::BreakpointType::Execute); | ||||
|     if (GDBStub::IsMemoryBreak() || | ||||
|         (bkpt.type != GDBStub::BreakpointType::None && address == bkpt.address)) { | ||||
|         auto core = static_cast<ARM_Unicorn*>(user_data); | ||||
|         core->RecordBreak(bkpt); | ||||
|         uc_emu_stop(uc); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void InterruptHook(uc_engine* uc, u32 intNo, void* user_data) { | ||||
|     u32 esr{}; | ||||
|     CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr)); | ||||
| @ -67,6 +78,10 @@ ARM_Unicorn::ARM_Unicorn() { | ||||
|     uc_hook hook{}; | ||||
|     CHECKED(uc_hook_add(uc, &hook, UC_HOOK_INTR, (void*)InterruptHook, this, 0, -1)); | ||||
|     CHECKED(uc_hook_add(uc, &hook, UC_HOOK_MEM_INVALID, (void*)UnmappedMemoryHook, this, 0, -1)); | ||||
|     if (GDBStub::IsServerEnabled()) { | ||||
|         CHECKED(uc_hook_add(uc, &hook, UC_HOOK_CODE, (void*)CodeHook, this, 0, -1)); | ||||
|         last_bkpt_hit = false; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| ARM_Unicorn::~ARM_Unicorn() { | ||||
| @ -155,7 +170,11 @@ void ARM_Unicorn::SetTlsAddress(VAddr base) { | ||||
| } | ||||
| 
 | ||||
| void ARM_Unicorn::Run() { | ||||
|     if (GDBStub::IsServerEnabled()) { | ||||
|         ExecuteInstructions(std::max(4000000, 0)); | ||||
|     } else { | ||||
|         ExecuteInstructions(std::max(CoreTiming::GetDowncount(), 0)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void ARM_Unicorn::Step() { | ||||
| @ -168,6 +187,18 @@ void ARM_Unicorn::ExecuteInstructions(int num_instructions) { | ||||
|     MICROPROFILE_SCOPE(ARM_Jit); | ||||
|     CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions)); | ||||
|     CoreTiming::AddTicks(num_instructions); | ||||
|     if (GDBStub::IsServerEnabled()) { | ||||
|         if (last_bkpt_hit) { | ||||
|             uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address); | ||||
|         } | ||||
|         Kernel::Thread* thread = Kernel::GetCurrentThread(); | ||||
|         SaveContext(thread->context); | ||||
|         if (last_bkpt_hit) { | ||||
|             last_bkpt_hit = false; | ||||
|             GDBStub::Break(); | ||||
|         } | ||||
|         GDBStub::SendTrap(thread, 5); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void ARM_Unicorn::SaveContext(ARM_Interface::ThreadContext& ctx) { | ||||
| @ -233,3 +264,8 @@ void ARM_Unicorn::PrepareReschedule() { | ||||
| } | ||||
| 
 | ||||
| void ARM_Unicorn::ClearInstructionCache() {} | ||||
| 
 | ||||
| void ARM_Unicorn::RecordBreak(GDBStub::BreakpointAddress bkpt) { | ||||
|     last_bkpt = bkpt; | ||||
|     last_bkpt_hit = true; | ||||
| } | ||||
|  | ||||
| @ -7,6 +7,7 @@ | ||||
| #include <unicorn/unicorn.h> | ||||
| #include "common/common_types.h" | ||||
| #include "core/arm/arm_interface.h" | ||||
| #include "core/gdbstub/gdbstub.h" | ||||
| 
 | ||||
| class ARM_Unicorn final : public ARM_Interface { | ||||
| public: | ||||
| @ -35,7 +36,10 @@ public: | ||||
|     void Step() override; | ||||
|     void ClearInstructionCache() override; | ||||
|     void PageTableChanged() override{}; | ||||
|     void RecordBreak(GDBStub::BreakpointAddress bkpt); | ||||
| 
 | ||||
| private: | ||||
|     uc_engine* uc{}; | ||||
|     GDBStub::BreakpointAddress last_bkpt{}; | ||||
|     bool last_bkpt_hit; | ||||
| }; | ||||
|  | ||||
| @ -32,9 +32,13 @@ | ||||
| 
 | ||||
| #include "common/logging/log.h" | ||||
| #include "common/string_util.h" | ||||
| #include "common/swap.h" | ||||
| #include "core/arm/arm_interface.h" | ||||
| #include "core/core.h" | ||||
| #include "core/core_cpu.h" | ||||
| #include "core/gdbstub/gdbstub.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/scheduler.h" | ||||
| #include "core/loader/loader.h" | ||||
| #include "core/memory.h" | ||||
| 
 | ||||
| @ -137,15 +141,17 @@ static u8 command_buffer[GDB_BUFFER_SIZE]; | ||||
| static u32 command_length; | ||||
| 
 | ||||
| static u32 latest_signal = 0; | ||||
| static bool step_break = false; | ||||
| static bool memory_break = false; | ||||
| 
 | ||||
| static Kernel::Thread* current_thread = nullptr; | ||||
| 
 | ||||
| // Binding to a port within the reserved ports range (0-1023) requires root permissions,
 | ||||
| // so default to a port outside of that range.
 | ||||
| static u16 gdbstub_port = 24689; | ||||
| 
 | ||||
| static bool halt_loop = true; | ||||
| static bool step_loop = false; | ||||
| static bool send_trap = false; | ||||
| 
 | ||||
| // If set to false, the server will never be started and no
 | ||||
| // gdbstub-related functions will be executed.
 | ||||
| @ -165,6 +171,53 @@ static std::map<u64, Breakpoint> breakpoints_execute; | ||||
| static std::map<u64, Breakpoint> breakpoints_read; | ||||
| static std::map<u64, Breakpoint> breakpoints_write; | ||||
| 
 | ||||
| static Kernel::Thread* FindThreadById(int id) { | ||||
|     for (int core = 0; core < Core::NUM_CPU_CORES; core++) { | ||||
|         auto threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); | ||||
|         for (auto thread : threads) { | ||||
|             if (thread->GetThreadId() == id) { | ||||
|                 current_thread = thread.get(); | ||||
|                 return current_thread; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return nullptr; | ||||
| } | ||||
| 
 | ||||
| static u64 RegRead(int id, Kernel::Thread* thread = nullptr) { | ||||
|     if (!thread) { | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     if (id < SP_REGISTER) { | ||||
|         return thread->context.cpu_registers[id]; | ||||
|     } else if (id == SP_REGISTER) { | ||||
|         return thread->context.sp; | ||||
|     } else if (id == PC_REGISTER) { | ||||
|         return thread->context.pc; | ||||
|     } else if (id == CPSR_REGISTER) { | ||||
|         return thread->context.cpsr; | ||||
|     } else { | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void RegWrite(int id, u64 val, Kernel::Thread* thread = nullptr) { | ||||
|     if (!thread) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (id < SP_REGISTER) { | ||||
|         thread->context.cpu_registers[id] = val; | ||||
|     } else if (id == SP_REGISTER) { | ||||
|         thread->context.sp = val; | ||||
|     } else if (id == PC_REGISTER) { | ||||
|         thread->context.pc = val; | ||||
|     } else if (id == CPSR_REGISTER) { | ||||
|         thread->context.cpsr = val; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Turns hex string character into the equivalent byte. | ||||
|  * | ||||
| @ -193,7 +246,7 @@ static u8 NibbleToHex(u8 n) { | ||||
|     if (n < 0xA) { | ||||
|         return '0' + n; | ||||
|     } else { | ||||
|         return 'A' + n - 0xA; | ||||
|         return 'a' + n - 0xA; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -439,6 +492,8 @@ static void SendReply(const char* reply) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     NGLOG_DEBUG(Debug_GDBStub, "Reply: {}", reply); | ||||
| 
 | ||||
|     memset(command_buffer, 0, sizeof(command_buffer)); | ||||
| 
 | ||||
|     command_length = static_cast<u32>(strlen(reply)); | ||||
| @ -483,6 +538,22 @@ static void HandleQuery() { | ||||
|     } else if (strncmp(query, "Xfer:features:read:target.xml:", | ||||
|                        strlen("Xfer:features:read:target.xml:")) == 0) { | ||||
|         SendReply(target_xml); | ||||
|     } else if (strncmp(query, "Offsets", strlen("Offsets")) == 0) { | ||||
|         std::string buffer = fmt::format("TextSeg={:0x}", Memory::PROCESS_IMAGE_VADDR); | ||||
|         SendReply(buffer.c_str()); | ||||
|     } else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) { | ||||
|         std::string val = "m"; | ||||
|         for (int core = 0; core < Core::NUM_CPU_CORES; core++) { | ||||
|             auto threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); | ||||
|             for (auto thread : threads) { | ||||
|                 val += fmt::format("{:x}", thread->GetThreadId()); | ||||
|                 val += ","; | ||||
|             } | ||||
|         } | ||||
|         val.pop_back(); | ||||
|         SendReply(val.c_str()); | ||||
|     } else if (strncmp(query, "sThreadInfo", strlen("sThreadInfo")) == 0) { | ||||
|         SendReply("l"); | ||||
|     } else { | ||||
|         SendReply(""); | ||||
|     } | ||||
| @ -490,11 +561,40 @@ static void HandleQuery() { | ||||
| 
 | ||||
| /// Handle set thread command from gdb client.
 | ||||
| static void HandleSetThread() { | ||||
|     if (memcmp(command_buffer, "Hg0", 3) == 0 || memcmp(command_buffer, "Hc-1", 4) == 0 || | ||||
|         memcmp(command_buffer, "Hc0", 4) == 0 || memcmp(command_buffer, "Hc1", 4) == 0) { | ||||
|         return SendReply("OK"); | ||||
|     if (memcmp(command_buffer, "Hc", 2) == 0 || memcmp(command_buffer, "Hg", 2) == 0) { | ||||
|         int thread_id = -1; | ||||
|         if (command_buffer[2] != '-') { | ||||
|             thread_id = static_cast<int>(HexToInt( | ||||
|                 command_buffer + 2, | ||||
|                 command_length - 2 /*strlen(reinterpret_cast<char*>(command_buffer) + 2)*/)); | ||||
|         } | ||||
|         if (thread_id >= 1) { | ||||
|             current_thread = FindThreadById(thread_id); | ||||
|         } | ||||
|         if (!current_thread) { | ||||
|             thread_id = 1; | ||||
|             current_thread = FindThreadById(thread_id); | ||||
|         } | ||||
|         if (current_thread) { | ||||
|             SendReply("OK"); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|     SendReply("E01"); | ||||
| } | ||||
| 
 | ||||
| /// Handle thread alive command from gdb client.
 | ||||
| static void HandleThreadAlive() { | ||||
|     int thread_id = static_cast<int>( | ||||
|         HexToInt(command_buffer + 1, | ||||
|                  command_length - 1 /*strlen(reinterpret_cast<char*>(command_buffer) + 1)*/)); | ||||
|     if (thread_id == 0) { | ||||
|         thread_id = 1; | ||||
|     } | ||||
|     if (FindThreadById(thread_id)) { | ||||
|         SendReply("OK"); | ||||
|         return; | ||||
|     } | ||||
|     SendReply("E01"); | ||||
| } | ||||
| 
 | ||||
| @ -503,15 +603,24 @@ static void HandleSetThread() { | ||||
|  * | ||||
|  * @param signal Signal to be sent to client. | ||||
|  */ | ||||
| static void SendSignal(u32 signal) { | ||||
| static void SendSignal(Kernel::Thread* thread, u32 signal, bool full = true) { | ||||
|     if (gdbserver_socket == -1) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     latest_signal = signal; | ||||
| 
 | ||||
|     std::string buffer = fmt::format("T{:02x}", latest_signal); | ||||
|     NGLOG_DEBUG(Debug_GDBStub, "Response: {}", buffer); | ||||
|     std::string buffer; | ||||
|     if (full) { | ||||
|         buffer = fmt::format("T{:02x}{:02x}:{:016x};{:02x}:{:016x};", latest_signal, PC_REGISTER, | ||||
|                              Common::swap64(RegRead(PC_REGISTER, thread)), SP_REGISTER, | ||||
|                              Common::swap64(RegRead(SP_REGISTER, thread))); | ||||
|     } else { | ||||
|         buffer = fmt::format("T{:02x};", latest_signal); | ||||
|     } | ||||
| 
 | ||||
|     buffer += fmt::format("thread:{:x};", thread->GetThreadId()); | ||||
| 
 | ||||
|     SendReply(buffer.c_str()); | ||||
| } | ||||
| 
 | ||||
| @ -527,7 +636,7 @@ static void ReadCommand() { | ||||
|     } else if (c == 0x03) { | ||||
|         NGLOG_INFO(Debug_GDBStub, "gdb: found break command"); | ||||
|         halt_loop = true; | ||||
|         SendSignal(SIGTRAP); | ||||
|         SendSignal(current_thread, SIGTRAP); | ||||
|         return; | ||||
|     } else if (c != GDB_STUB_START) { | ||||
|         NGLOG_DEBUG(Debug_GDBStub, "gdb: read invalid byte {:02X}", c); | ||||
| @ -598,11 +707,11 @@ static void ReadRegister() { | ||||
|     } | ||||
| 
 | ||||
|     if (id <= SP_REGISTER) { | ||||
|         LongToGdbHex(reply, Core::CurrentArmInterface().GetReg(static_cast<int>(id))); | ||||
|         LongToGdbHex(reply, RegRead(id, current_thread)); | ||||
|     } else if (id == PC_REGISTER) { | ||||
|         LongToGdbHex(reply, Core::CurrentArmInterface().GetPC()); | ||||
|         LongToGdbHex(reply, RegRead(id, current_thread)); | ||||
|     } else if (id == CPSR_REGISTER) { | ||||
|         IntToGdbHex(reply, Core::CurrentArmInterface().GetCPSR()); | ||||
|         IntToGdbHex(reply, (u32)RegRead(id, current_thread)); | ||||
|     } else { | ||||
|         return SendReply("E01"); | ||||
|     } | ||||
| @ -618,16 +727,16 @@ static void ReadRegisters() { | ||||
|     u8* bufptr = buffer; | ||||
| 
 | ||||
|     for (int reg = 0; reg <= SP_REGISTER; reg++) { | ||||
|         LongToGdbHex(bufptr + reg * 16, Core::CurrentArmInterface().GetReg(reg)); | ||||
|         LongToGdbHex(bufptr + reg * 16, RegRead(reg, current_thread)); | ||||
|     } | ||||
| 
 | ||||
|     bufptr += (32 * 16); | ||||
| 
 | ||||
|     LongToGdbHex(bufptr, Core::CurrentArmInterface().GetPC()); | ||||
|     LongToGdbHex(bufptr, RegRead(PC_REGISTER, current_thread)); | ||||
| 
 | ||||
|     bufptr += 16; | ||||
| 
 | ||||
|     IntToGdbHex(bufptr, Core::CurrentArmInterface().GetCPSR()); | ||||
|     IntToGdbHex(bufptr, (u32)RegRead(CPSR_REGISTER, current_thread)); | ||||
| 
 | ||||
|     bufptr += 8; | ||||
| 
 | ||||
| @ -646,11 +755,11 @@ static void WriteRegister() { | ||||
|     } | ||||
| 
 | ||||
|     if (id <= SP_REGISTER) { | ||||
|         Core::CurrentArmInterface().SetReg(id, GdbHexToLong(buffer_ptr)); | ||||
|         RegWrite(id, GdbHexToLong(buffer_ptr), current_thread); | ||||
|     } else if (id == PC_REGISTER) { | ||||
|         Core::CurrentArmInterface().SetPC(GdbHexToLong(buffer_ptr)); | ||||
|         RegWrite(id, GdbHexToLong(buffer_ptr), current_thread); | ||||
|     } else if (id == CPSR_REGISTER) { | ||||
|         Core::CurrentArmInterface().SetCPSR(GdbHexToInt(buffer_ptr)); | ||||
|         RegWrite(id, GdbHexToInt(buffer_ptr), current_thread); | ||||
|     } else { | ||||
|         return SendReply("E01"); | ||||
|     } | ||||
| @ -667,11 +776,11 @@ static void WriteRegisters() { | ||||
| 
 | ||||
|     for (int i = 0, reg = 0; reg <= CPSR_REGISTER; i++, reg++) { | ||||
|         if (reg <= SP_REGISTER) { | ||||
|             Core::CurrentArmInterface().SetReg(reg, GdbHexToLong(buffer_ptr + i * 16)); | ||||
|             RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread); | ||||
|         } else if (reg == PC_REGISTER) { | ||||
|             Core::CurrentArmInterface().SetPC(GdbHexToLong(buffer_ptr + i * 16)); | ||||
|             RegWrite(PC_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread); | ||||
|         } else if (reg == CPSR_REGISTER) { | ||||
|             Core::CurrentArmInterface().SetCPSR(GdbHexToInt(buffer_ptr + i * 16)); | ||||
|             RegWrite(CPSR_REGISTER, GdbHexToInt(buffer_ptr + i * 16), current_thread); | ||||
|         } else { | ||||
|             UNIMPLEMENTED(); | ||||
|         } | ||||
| @ -734,7 +843,7 @@ static void WriteMemory() { | ||||
| void Break(bool is_memory_break) { | ||||
|     if (!halt_loop) { | ||||
|         halt_loop = true; | ||||
|         SendSignal(SIGTRAP); | ||||
|         send_trap = true; | ||||
|     } | ||||
| 
 | ||||
|     memory_break = is_memory_break; | ||||
| @ -744,10 +853,10 @@ void Break(bool is_memory_break) { | ||||
| static void Step() { | ||||
|     step_loop = true; | ||||
|     halt_loop = true; | ||||
|     step_break = true; | ||||
|     SendSignal(SIGTRAP); | ||||
|     send_trap = true; | ||||
| } | ||||
| 
 | ||||
| /// Tell the CPU if we hit a memory breakpoint.
 | ||||
| bool IsMemoryBreak() { | ||||
|     if (IsConnected()) { | ||||
|         return false; | ||||
| @ -759,7 +868,6 @@ bool IsMemoryBreak() { | ||||
| /// Tell the CPU to continue executing.
 | ||||
| static void Continue() { | ||||
|     memory_break = false; | ||||
|     step_break = false; | ||||
|     step_loop = false; | ||||
|     halt_loop = false; | ||||
| } | ||||
| @ -898,7 +1006,7 @@ void HandlePacket() { | ||||
|         HandleSetThread(); | ||||
|         break; | ||||
|     case '?': | ||||
|         SendSignal(latest_signal); | ||||
|         SendSignal(current_thread, latest_signal); | ||||
|         break; | ||||
|     case 'k': | ||||
|         Shutdown(); | ||||
| @ -935,6 +1043,9 @@ void HandlePacket() { | ||||
|     case 'Z': | ||||
|         AddBreakpoint(); | ||||
|         break; | ||||
|     case 'T': | ||||
|         HandleThreadAlive(); | ||||
|         break; | ||||
|     default: | ||||
|         SendReply(""); | ||||
|         break; | ||||
| @ -1079,4 +1190,11 @@ bool GetCpuStepFlag() { | ||||
| void SetCpuStepFlag(bool is_step) { | ||||
|     step_loop = is_step; | ||||
| } | ||||
| 
 | ||||
| void SendTrap(Kernel::Thread* thread, int trap) { | ||||
|     if (send_trap) { | ||||
|         send_trap = false; | ||||
|         SendSignal(thread, trap); | ||||
|     } | ||||
| } | ||||
| }; // namespace GDBStub
 | ||||
|  | ||||
| @ -7,6 +7,7 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/kernel/thread.h" | ||||
| 
 | ||||
| namespace GDBStub { | ||||
| 
 | ||||
| @ -91,4 +92,12 @@ bool GetCpuStepFlag(); | ||||
|  * @param is_step | ||||
|  */ | ||||
| void SetCpuStepFlag(bool is_step); | ||||
| 
 | ||||
| /**
 | ||||
|  * Send trap signal from thread back to the gdbstub server. | ||||
|  * | ||||
|  * @param thread Sending thread. | ||||
|  * @param trap Trap no. | ||||
|  */ | ||||
| void SendTrap(Kernel::Thread* thread, int trap); | ||||
| } // namespace GDBStub
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Hedges
						Hedges