mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	Implement gdbstub
This commit is contained in:
		
							parent
							
								
									addef06081
								
							
						
					
					
						commit
						31dee93e84
					
				
					 18 changed files with 1174 additions and 9 deletions
				
			
		|  | @ -30,6 +30,8 @@ | ||||||
| 
 | 
 | ||||||
| #include "video_core/video_core.h" | #include "video_core/video_core.h" | ||||||
| 
 | 
 | ||||||
|  | #include "core/gdbstub/gdbstub.h" | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| static void PrintHelp() | static void PrintHelp() | ||||||
| { | { | ||||||
|  | @ -72,6 +74,7 @@ int main(int argc, char **argv) { | ||||||
|     Config config; |     Config config; | ||||||
|     log_filter.ParseFilterString(Settings::values.log_filter); |     log_filter.ParseFilterString(Settings::values.log_filter); | ||||||
| 
 | 
 | ||||||
|  |     GDBStub::SetServerPort(static_cast<u32>(Settings::values.gdbstub_port)); | ||||||
| 
 | 
 | ||||||
|     EmuWindow_GLFW* emu_window = new EmuWindow_GLFW; |     EmuWindow_GLFW* emu_window = new EmuWindow_GLFW; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -75,6 +75,10 @@ void Config::ReadValues() { | ||||||
| 
 | 
 | ||||||
|     // Miscellaneous
 |     // Miscellaneous
 | ||||||
|     Settings::values.log_filter = glfw_config->Get("Miscellaneous", "log_filter", "*:Info"); |     Settings::values.log_filter = glfw_config->Get("Miscellaneous", "log_filter", "*:Info"); | ||||||
|  | 
 | ||||||
|  |     // GDBStubebugging
 | ||||||
|  |     Settings::values.use_gdbstub = glfw_config->GetBoolean("Debugging", "use_gdbstub", false); | ||||||
|  |     Settings::values.gdbstub_port = glfw_config->GetInteger("Debugging", "gdbstub_port", 24689); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Config::Reload() { | void Config::Reload() { | ||||||
|  |  | ||||||
|  | @ -66,6 +66,11 @@ region_value = | ||||||
| # A filter which removes logs below a certain logging level. | # A filter which removes logs below a certain logging level. | ||||||
| # Examples: *:Debug Kernel.SVC:Trace Service.*:Critical | # Examples: *:Debug Kernel.SVC:Trace Service.*:Critical | ||||||
| log_filter = *:Info | log_filter = *:Info | ||||||
|  | 
 | ||||||
|  | [Debugging] | ||||||
|  | # Port for listening to GDB connections. | ||||||
|  | use_gdbstub=false | ||||||
|  | gdbstub_port=24689 | ||||||
| )"; | )"; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -62,6 +62,11 @@ void Config::ReadValues() { | ||||||
|     qt_config->beginGroup("Miscellaneous"); |     qt_config->beginGroup("Miscellaneous"); | ||||||
|     Settings::values.log_filter = qt_config->value("log_filter", "*:Info").toString().toStdString(); |     Settings::values.log_filter = qt_config->value("log_filter", "*:Info").toString().toStdString(); | ||||||
|     qt_config->endGroup(); |     qt_config->endGroup(); | ||||||
|  | 
 | ||||||
|  |     qt_config->beginGroup("Debugging"); | ||||||
|  |     Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool(); | ||||||
|  |     Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt(); | ||||||
|  |     qt_config->endGroup(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Config::SaveValues() { | void Config::SaveValues() { | ||||||
|  | @ -97,6 +102,11 @@ void Config::SaveValues() { | ||||||
|     qt_config->beginGroup("Miscellaneous"); |     qt_config->beginGroup("Miscellaneous"); | ||||||
|     qt_config->setValue("log_filter", QString::fromStdString(Settings::values.log_filter)); |     qt_config->setValue("log_filter", QString::fromStdString(Settings::values.log_filter)); | ||||||
|     qt_config->endGroup(); |     qt_config->endGroup(); | ||||||
|  | 
 | ||||||
|  |     qt_config->beginGroup("Debugging"); | ||||||
|  |     qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub); | ||||||
|  |     qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port); | ||||||
|  |     qt_config->endGroup(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Config::Reload() { | void Config::Reload() { | ||||||
|  |  | ||||||
|  | @ -48,6 +48,8 @@ | ||||||
| 
 | 
 | ||||||
| #include "video_core/video_core.h" | #include "video_core/video_core.h" | ||||||
| 
 | 
 | ||||||
|  | #include "core/gdbstub/gdbstub.h" | ||||||
|  | 
 | ||||||
| GMainWindow::GMainWindow() : emu_thread(nullptr) | GMainWindow::GMainWindow() : emu_thread(nullptr) | ||||||
| { | { | ||||||
|     Pica::g_debug_context = Pica::DebugContext::Construct(); |     Pica::g_debug_context = Pica::DebugContext::Construct(); | ||||||
|  | @ -143,6 +145,11 @@ GMainWindow::GMainWindow() : emu_thread(nullptr) | ||||||
| 
 | 
 | ||||||
|     game_list->LoadInterfaceLayout(settings); |     game_list->LoadInterfaceLayout(settings); | ||||||
| 
 | 
 | ||||||
|  |     ui.action_Use_Gdbstub->setChecked(Settings::values.use_gdbstub); | ||||||
|  |     SetGdbstubEnabled(ui.action_Use_Gdbstub->isChecked()); | ||||||
|  | 
 | ||||||
|  |     GDBStub::SetServerPort(static_cast<u32>(Settings::values.gdbstub_port)); | ||||||
|  | 
 | ||||||
|     ui.action_Use_Hardware_Renderer->setChecked(Settings::values.use_hw_renderer); |     ui.action_Use_Hardware_Renderer->setChecked(Settings::values.use_hw_renderer); | ||||||
|     SetHardwareRendererEnabled(ui.action_Use_Hardware_Renderer->isChecked()); |     SetHardwareRendererEnabled(ui.action_Use_Hardware_Renderer->isChecked()); | ||||||
| 
 | 
 | ||||||
|  | @ -175,6 +182,7 @@ GMainWindow::GMainWindow() : emu_thread(nullptr) | ||||||
|     connect(ui.action_Stop, SIGNAL(triggered()), this, SLOT(OnStopGame())); |     connect(ui.action_Stop, SIGNAL(triggered()), this, SLOT(OnStopGame())); | ||||||
|     connect(ui.action_Use_Hardware_Renderer, SIGNAL(triggered(bool)), this, SLOT(SetHardwareRendererEnabled(bool))); |     connect(ui.action_Use_Hardware_Renderer, SIGNAL(triggered(bool)), this, SLOT(SetHardwareRendererEnabled(bool))); | ||||||
|     connect(ui.action_Use_Shader_JIT, SIGNAL(triggered(bool)), this, SLOT(SetShaderJITEnabled(bool))); |     connect(ui.action_Use_Shader_JIT, SIGNAL(triggered(bool)), this, SLOT(SetShaderJITEnabled(bool))); | ||||||
|  |     connect(ui.action_Use_Gdbstub, SIGNAL(triggered(bool)), this, SLOT(SetGdbstubEnabled(bool))); | ||||||
|     connect(ui.action_Single_Window_Mode, SIGNAL(triggered(bool)), this, SLOT(ToggleWindowMode())); |     connect(ui.action_Single_Window_Mode, SIGNAL(triggered(bool)), this, SLOT(ToggleWindowMode())); | ||||||
|     connect(ui.action_Hotkeys, SIGNAL(triggered()), this, SLOT(OnOpenHotkeysDialog())); |     connect(ui.action_Hotkeys, SIGNAL(triggered()), this, SLOT(OnOpenHotkeysDialog())); | ||||||
| 
 | 
 | ||||||
|  | @ -445,6 +453,10 @@ void GMainWindow::SetHardwareRendererEnabled(bool enabled) { | ||||||
|     VideoCore::g_hw_renderer_enabled = enabled; |     VideoCore::g_hw_renderer_enabled = enabled; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void GMainWindow::SetGdbstubEnabled(bool enabled) { | ||||||
|  |     GDBStub::ToggleServer(enabled); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void GMainWindow::SetShaderJITEnabled(bool enabled) { | void GMainWindow::SetShaderJITEnabled(bool enabled) { | ||||||
|     VideoCore::g_shader_jit_enabled = enabled; |     VideoCore::g_shader_jit_enabled = enabled; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -99,6 +99,7 @@ private slots: | ||||||
|     void OnConfigure(); |     void OnConfigure(); | ||||||
|     void OnDisplayTitleBars(bool); |     void OnDisplayTitleBars(bool); | ||||||
|     void SetHardwareRendererEnabled(bool); |     void SetHardwareRendererEnabled(bool); | ||||||
|  |     void SetGdbstubEnabled(bool); | ||||||
|     void SetShaderJITEnabled(bool); |     void SetShaderJITEnabled(bool); | ||||||
|     void ToggleWindowMode(); |     void ToggleWindowMode(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -75,6 +75,7 @@ | ||||||
|     <addaction name="separator"/> |     <addaction name="separator"/> | ||||||
|     <addaction name="action_Use_Hardware_Renderer"/> |     <addaction name="action_Use_Hardware_Renderer"/> | ||||||
|     <addaction name="action_Use_Shader_JIT"/> |     <addaction name="action_Use_Shader_JIT"/> | ||||||
|  |     <addaction name="action_Use_Gdbstub"/> | ||||||
|     <addaction name="action_Configure"/> |     <addaction name="action_Configure"/> | ||||||
|    </widget> |    </widget> | ||||||
|    <widget class="QMenu" name="menu_View"> |    <widget class="QMenu" name="menu_View"> | ||||||
|  | @ -170,6 +171,14 @@ | ||||||
|     <string>Use Shader JIT</string> |     <string>Use Shader JIT</string> | ||||||
|    </property> |    </property> | ||||||
|   </action> |   </action> | ||||||
|  |   <action name="action_Use_Gdbstub"> | ||||||
|  |     <property name="checkable"> | ||||||
|  |       <bool>true</bool> | ||||||
|  |     </property> | ||||||
|  |     <property name="text"> | ||||||
|  |       <string>Use Gdbstub</string> | ||||||
|  |     </property> | ||||||
|  |   </action> | ||||||
|   <action name="action_Configure"> |   <action name="action_Configure"> | ||||||
|    <property name="text"> |    <property name="text"> | ||||||
|     <string>Configure ...</string> |     <string>Configure ...</string> | ||||||
|  |  | ||||||
|  | @ -29,6 +29,7 @@ namespace Log { | ||||||
|         SUB(Debug, Emulated) \ |         SUB(Debug, Emulated) \ | ||||||
|         SUB(Debug, GPU) \ |         SUB(Debug, GPU) \ | ||||||
|         SUB(Debug, Breakpoint) \ |         SUB(Debug, Breakpoint) \ | ||||||
|  |         SUB(Debug, GDBStub) \ | ||||||
|         CLS(Kernel) \ |         CLS(Kernel) \ | ||||||
|         SUB(Kernel, SVC) \ |         SUB(Kernel, SVC) \ | ||||||
|         CLS(Service) \ |         CLS(Service) \ | ||||||
|  |  | ||||||
|  | @ -43,6 +43,7 @@ enum class Class : ClassType { | ||||||
|     Debug_Emulated,             ///< Debug messages from the emulated programs
 |     Debug_Emulated,             ///< Debug messages from the emulated programs
 | ||||||
|     Debug_GPU,                  ///< GPU debugging tools
 |     Debug_GPU,                  ///< GPU debugging tools
 | ||||||
|     Debug_Breakpoint,           ///< Logging breakpoints and watchpoints
 |     Debug_Breakpoint,           ///< Logging breakpoints and watchpoints
 | ||||||
|  |     Debug_GDBStub,              ///< GDB Stub
 | ||||||
|     Kernel,                     ///< The HLE implementation of the CTR kernel
 |     Kernel,                     ///< The HLE implementation of the CTR kernel
 | ||||||
|     Kernel_SVC,                 ///< Kernel system calls
 |     Kernel_SVC,                 ///< Kernel system calls
 | ||||||
|     Service,                    ///< HLE implementation of system services. Each major service
 |     Service,                    ///< HLE implementation of system services. Each major service
 | ||||||
|  |  | ||||||
|  | @ -22,6 +22,7 @@ set(SRCS | ||||||
|             file_sys/archive_systemsavedata.cpp |             file_sys/archive_systemsavedata.cpp | ||||||
|             file_sys/disk_archive.cpp |             file_sys/disk_archive.cpp | ||||||
|             file_sys/ivfc_archive.cpp |             file_sys/ivfc_archive.cpp | ||||||
|  |             gdbstub/gdbstub.cpp | ||||||
|             hle/config_mem.cpp |             hle/config_mem.cpp | ||||||
|             hle/hle.cpp |             hle/hle.cpp | ||||||
|             hle/applets/applet.cpp |             hle/applets/applet.cpp | ||||||
|  | @ -149,6 +150,7 @@ set(HEADERS | ||||||
|             file_sys/disk_archive.h |             file_sys/disk_archive.h | ||||||
|             file_sys/file_backend.h |             file_sys/file_backend.h | ||||||
|             file_sys/ivfc_archive.h |             file_sys/ivfc_archive.h | ||||||
|  |             gdbstub/gdbstub.h | ||||||
|             hle/config_mem.h |             hle/config_mem.h | ||||||
|             hle/function_wrappers.h |             hle/function_wrappers.h | ||||||
|             hle/hle.h |             hle/hle.h | ||||||
|  |  | ||||||
|  | @ -23,6 +23,8 @@ | ||||||
| #include "core/arm/skyeye_common/armsupp.h" | #include "core/arm/skyeye_common/armsupp.h" | ||||||
| #include "core/arm/skyeye_common/vfp/vfp.h" | #include "core/arm/skyeye_common/vfp/vfp.h" | ||||||
| 
 | 
 | ||||||
|  | #include "core/gdbstub/gdbstub.h" | ||||||
|  | 
 | ||||||
| Common::Profiling::TimingCategory profile_execute("DynCom::Execute"); | Common::Profiling::TimingCategory profile_execute("DynCom::Execute"); | ||||||
| Common::Profiling::TimingCategory profile_decode("DynCom::Decode"); | Common::Profiling::TimingCategory profile_decode("DynCom::Decode"); | ||||||
| 
 | 
 | ||||||
|  | @ -3548,6 +3550,7 @@ static int InterpreterTranslate(ARMul_State* cpu, int& bb_start, u32 addr) { | ||||||
|             CITRA_IGNORE_EXIT(-1); |             CITRA_IGNORE_EXIT(-1); | ||||||
|         } |         } | ||||||
|         inst_base = arm_instruction_trans[idx](inst, idx); |         inst_base = arm_instruction_trans[idx](inst, idx); | ||||||
|  | 
 | ||||||
| translated: | translated: | ||||||
|         phys_addr += inst_size; |         phys_addr += inst_size; | ||||||
| 
 | 
 | ||||||
|  | @ -3580,6 +3583,8 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { | ||||||
|     Common::Profiling::ScopeTimer timer_execute(profile_execute); |     Common::Profiling::ScopeTimer timer_execute(profile_execute); | ||||||
|     MICROPROFILE_SCOPE(DynCom_Execute); |     MICROPROFILE_SCOPE(DynCom_Execute); | ||||||
| 
 | 
 | ||||||
|  |     int breakpoint_offset = -1; | ||||||
|  | 
 | ||||||
|     #undef RM |     #undef RM | ||||||
|     #undef RS |     #undef RS | ||||||
| 
 | 
 | ||||||
|  | @ -3604,15 +3609,27 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { | ||||||
|     #define INC_PC(l)   ptr += sizeof(arm_inst) + l |     #define INC_PC(l)   ptr += sizeof(arm_inst) + l | ||||||
|     #define INC_PC_STUB ptr += sizeof(arm_inst) |     #define INC_PC_STUB ptr += sizeof(arm_inst) | ||||||
| 
 | 
 | ||||||
|  | #define GDB_BP_CHECK \ | ||||||
|  |     cpu->Cpsr &= ~(1 << 5); \ | ||||||
|  |     cpu->Cpsr |= cpu->TFlag << 5; \ | ||||||
|  |     if (GDBStub::g_server_enabled) { \ | ||||||
|  |         if (GDBStub::IsMemoryBreak() || PC == breakpoint_offset) { \ | ||||||
|  |             GDBStub::Break(); \ | ||||||
|  |             goto END; \ | ||||||
|  |         } \ | ||||||
|  |     } | ||||||
|  | 
 | ||||||
| // GCC and Clang have a C++ extension to support a lookup table of labels. Otherwise, fallback to a
 | // GCC and Clang have a C++ extension to support a lookup table of labels. Otherwise, fallback to a
 | ||||||
| // clunky switch statement.
 | // clunky switch statement.
 | ||||||
| #if defined __GNUC__ || defined __clang__ | #if defined __GNUC__ || defined __clang__ | ||||||
| #define GOTO_NEXT_INST \ | #define GOTO_NEXT_INST \ | ||||||
|  |     GDB_BP_CHECK; \ | ||||||
|     if (num_instrs >= cpu->NumInstrsToExecute) goto END; \ |     if (num_instrs >= cpu->NumInstrsToExecute) goto END; \ | ||||||
|     num_instrs++; \ |     num_instrs++; \ | ||||||
|     goto *InstLabel[inst_base->idx] |     goto *InstLabel[inst_base->idx] | ||||||
| #else | #else | ||||||
| #define GOTO_NEXT_INST \ | #define GOTO_NEXT_INST \ | ||||||
|  |     GDB_BP_CHECK; \ | ||||||
|     if (num_instrs >= cpu->NumInstrsToExecute) goto END; \ |     if (num_instrs >= cpu->NumInstrsToExecute) goto END; \ | ||||||
|     num_instrs++; \ |     num_instrs++; \ | ||||||
|     switch(inst_base->idx) { \ |     switch(inst_base->idx) { \ | ||||||
|  | @ -3878,6 +3895,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { | ||||||
|     unsigned int addr; |     unsigned int addr; | ||||||
|     unsigned int num_instrs = 0; |     unsigned int num_instrs = 0; | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|     int ptr; |     int ptr; | ||||||
| 
 | 
 | ||||||
|     LOAD_NZCVT; |     LOAD_NZCVT; | ||||||
|  | @ -3903,6 +3921,11 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { | ||||||
|                 goto END; |                 goto END; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         // Find breakpoint if one exists within the block
 | ||||||
|  |         if (GDBStub::g_server_enabled && GDBStub::IsConnected()) { | ||||||
|  |             breakpoint_offset = GDBStub::GetNextBreakpointFromAddress(cpu->Reg[15], GDBStub::BreakpointType::Execute); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         inst_base = (arm_inst *)&inst_buf[ptr]; |         inst_base = (arm_inst *)&inst_buf[ptr]; | ||||||
|         GOTO_NEXT_INST; |         GOTO_NEXT_INST; | ||||||
|     } |     } | ||||||
|  | @ -4454,7 +4477,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { | ||||||
|             ldst_inst* inst_cream = (ldst_inst*)inst_base->component; |             ldst_inst* inst_cream = (ldst_inst*)inst_base->component; | ||||||
|             inst_cream->get_addr(cpu, inst_cream->inst, addr); |             inst_cream->get_addr(cpu, inst_cream->inst, addr); | ||||||
| 
 | 
 | ||||||
|             cpu->Reg[BITS(inst_cream->inst, 12, 15)] = Memory::Read8(addr); |             cpu->Reg[BITS(inst_cream->inst, 12, 15)] = cpu->ReadMemory8(addr); | ||||||
| 
 | 
 | ||||||
|             if (BITS(inst_cream->inst, 12, 15) == 15) { |             if (BITS(inst_cream->inst, 12, 15) == 15) { | ||||||
|                 INC_PC(sizeof(ldst_inst)); |                 INC_PC(sizeof(ldst_inst)); | ||||||
|  | @ -4472,7 +4495,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { | ||||||
|             ldst_inst* inst_cream = (ldst_inst*)inst_base->component; |             ldst_inst* inst_cream = (ldst_inst*)inst_base->component; | ||||||
|             inst_cream->get_addr(cpu, inst_cream->inst, addr); |             inst_cream->get_addr(cpu, inst_cream->inst, addr); | ||||||
| 
 | 
 | ||||||
|             cpu->Reg[BITS(inst_cream->inst, 12, 15)] = Memory::Read8(addr); |             cpu->Reg[BITS(inst_cream->inst, 12, 15)] = cpu->ReadMemory8(addr); | ||||||
| 
 | 
 | ||||||
|             if (BITS(inst_cream->inst, 12, 15) == 15) { |             if (BITS(inst_cream->inst, 12, 15) == 15) { | ||||||
|                 INC_PC(sizeof(ldst_inst)); |                 INC_PC(sizeof(ldst_inst)); | ||||||
|  | @ -4531,7 +4554,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { | ||||||
| 
 | 
 | ||||||
|             cpu->SetExclusiveMemoryAddress(read_addr); |             cpu->SetExclusiveMemoryAddress(read_addr); | ||||||
| 
 | 
 | ||||||
|             RD = Memory::Read8(read_addr); |             RD = cpu->ReadMemory8(read_addr); | ||||||
|             if (inst_cream->Rd == 15) { |             if (inst_cream->Rd == 15) { | ||||||
|                 INC_PC(sizeof(generic_arm_inst)); |                 INC_PC(sizeof(generic_arm_inst)); | ||||||
|                 goto DISPATCH; |                 goto DISPATCH; | ||||||
|  | @ -4604,7 +4627,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { | ||||||
|         if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { |         if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { | ||||||
|             ldst_inst* inst_cream = (ldst_inst*)inst_base->component; |             ldst_inst* inst_cream = (ldst_inst*)inst_base->component; | ||||||
|             inst_cream->get_addr(cpu, inst_cream->inst, addr); |             inst_cream->get_addr(cpu, inst_cream->inst, addr); | ||||||
|             unsigned int value = Memory::Read8(addr); |             unsigned int value = cpu->ReadMemory8(addr); | ||||||
|             if (BIT(value, 7)) { |             if (BIT(value, 7)) { | ||||||
|                 value |= 0xffffff00; |                 value |= 0xffffff00; | ||||||
|             } |             } | ||||||
|  | @ -6027,7 +6050,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { | ||||||
|             ldst_inst* inst_cream = (ldst_inst*)inst_base->component; |             ldst_inst* inst_cream = (ldst_inst*)inst_base->component; | ||||||
|             inst_cream->get_addr(cpu, inst_cream->inst, addr); |             inst_cream->get_addr(cpu, inst_cream->inst, addr); | ||||||
|             unsigned int value = cpu->Reg[BITS(inst_cream->inst, 12, 15)] & 0xff; |             unsigned int value = cpu->Reg[BITS(inst_cream->inst, 12, 15)] & 0xff; | ||||||
|             Memory::Write8(addr, value); |             cpu->WriteMemory8(addr, value); | ||||||
|         } |         } | ||||||
|         cpu->Reg[15] += cpu->GetInstructionSize(); |         cpu->Reg[15] += cpu->GetInstructionSize(); | ||||||
|         INC_PC(sizeof(ldst_inst)); |         INC_PC(sizeof(ldst_inst)); | ||||||
|  | @ -6040,7 +6063,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { | ||||||
|             ldst_inst* inst_cream = (ldst_inst*)inst_base->component; |             ldst_inst* inst_cream = (ldst_inst*)inst_base->component; | ||||||
|             inst_cream->get_addr(cpu, inst_cream->inst, addr); |             inst_cream->get_addr(cpu, inst_cream->inst, addr); | ||||||
|             unsigned int value = cpu->Reg[BITS(inst_cream->inst, 12, 15)] & 0xff; |             unsigned int value = cpu->Reg[BITS(inst_cream->inst, 12, 15)] & 0xff; | ||||||
|             Memory::Write8(addr, value); |             cpu->WriteMemory8(addr, value); | ||||||
|         } |         } | ||||||
|         cpu->Reg[15] += cpu->GetInstructionSize(); |         cpu->Reg[15] += cpu->GetInstructionSize(); | ||||||
|         INC_PC(sizeof(ldst_inst)); |         INC_PC(sizeof(ldst_inst)); | ||||||
|  | @ -6091,7 +6114,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { | ||||||
| 
 | 
 | ||||||
|             if (cpu->IsExclusiveMemoryAccess(write_addr)) { |             if (cpu->IsExclusiveMemoryAccess(write_addr)) { | ||||||
|                 cpu->UnsetExclusiveMemoryAddress(); |                 cpu->UnsetExclusiveMemoryAddress(); | ||||||
|                 Memory::Write8(write_addr, cpu->Reg[inst_cream->Rm]); |                 cpu->WriteMemory8(write_addr, cpu->Reg[inst_cream->Rm]); | ||||||
|                 RD = 0; |                 RD = 0; | ||||||
|             } else { |             } else { | ||||||
|                 // Failed to write due to mutex access
 |                 // Failed to write due to mutex access
 | ||||||
|  | @ -6250,8 +6273,8 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { | ||||||
|         if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { |         if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { | ||||||
|             swp_inst* inst_cream = (swp_inst*)inst_base->component; |             swp_inst* inst_cream = (swp_inst*)inst_base->component; | ||||||
|             addr = RN; |             addr = RN; | ||||||
|             unsigned int value = Memory::Read8(addr); |             unsigned int value = cpu->ReadMemory8(addr); | ||||||
|             Memory::Write8(addr, (RM & 0xFF)); |             cpu->WriteMemory8(addr, (RM & 0xFF)); | ||||||
|             RD = value; |             RD = value; | ||||||
|         } |         } | ||||||
|         cpu->Reg[15] += cpu->GetInstructionSize(); |         cpu->Reg[15] += cpu->GetInstructionSize(); | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ | ||||||
| #include "core/memory.h" | #include "core/memory.h" | ||||||
| #include "core/arm/skyeye_common/armstate.h" | #include "core/arm/skyeye_common/armstate.h" | ||||||
| #include "core/arm/skyeye_common/vfp/vfp.h" | #include "core/arm/skyeye_common/vfp/vfp.h" | ||||||
|  | #include "core/gdbstub/gdbstub.h" | ||||||
| 
 | 
 | ||||||
| ARMul_State::ARMul_State(PrivilegeMode initial_mode) | ARMul_State::ARMul_State(PrivilegeMode initial_mode) | ||||||
| { | { | ||||||
|  | @ -185,8 +186,25 @@ void ARMul_State::ResetMPCoreCP15Registers() | ||||||
|     CP15[CP15_TLB_DEBUG_CONTROL] = 0x00000000; |     CP15[CP15_TLB_DEBUG_CONTROL] = 0x00000000; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void CheckMemoryBreakpoint(u32 address, GDBStub::BreakpointType type) | ||||||
|  | { | ||||||
|  |     if (GDBStub::g_server_enabled && GDBStub::CheckBreakpoint(address, type)) { | ||||||
|  |         LOG_DEBUG(Debug, "Found memory breakpoint @ %08x", address); | ||||||
|  |         GDBStub::Break(true); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | u8 ARMul_State::ReadMemory8(u32 address) const | ||||||
|  | { | ||||||
|  |     CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Read); | ||||||
|  | 
 | ||||||
|  |     return Memory::Read8(address); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| u16 ARMul_State::ReadMemory16(u32 address) const | u16 ARMul_State::ReadMemory16(u32 address) const | ||||||
| { | { | ||||||
|  |     CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Read); | ||||||
|  | 
 | ||||||
|     u16 data = Memory::Read16(address); |     u16 data = Memory::Read16(address); | ||||||
| 
 | 
 | ||||||
|     if (InBigEndianMode()) |     if (InBigEndianMode()) | ||||||
|  | @ -197,6 +215,8 @@ u16 ARMul_State::ReadMemory16(u32 address) const | ||||||
| 
 | 
 | ||||||
| u32 ARMul_State::ReadMemory32(u32 address) const | u32 ARMul_State::ReadMemory32(u32 address) const | ||||||
| { | { | ||||||
|  |     CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Read); | ||||||
|  | 
 | ||||||
|     u32 data = Memory::Read32(address); |     u32 data = Memory::Read32(address); | ||||||
| 
 | 
 | ||||||
|     if (InBigEndianMode()) |     if (InBigEndianMode()) | ||||||
|  | @ -207,6 +227,8 @@ u32 ARMul_State::ReadMemory32(u32 address) const | ||||||
| 
 | 
 | ||||||
| u64 ARMul_State::ReadMemory64(u32 address) const | u64 ARMul_State::ReadMemory64(u32 address) const | ||||||
| { | { | ||||||
|  |     CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Read); | ||||||
|  | 
 | ||||||
|     u64 data = Memory::Read64(address); |     u64 data = Memory::Read64(address); | ||||||
| 
 | 
 | ||||||
|     if (InBigEndianMode()) |     if (InBigEndianMode()) | ||||||
|  | @ -215,8 +237,17 @@ u64 ARMul_State::ReadMemory64(u32 address) const | ||||||
|     return data; |     return data; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void ARMul_State::WriteMemory8(u32 address, u8 data) | ||||||
|  | { | ||||||
|  |     CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Write); | ||||||
|  | 
 | ||||||
|  |     Memory::Write8(address, data); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void ARMul_State::WriteMemory16(u32 address, u16 data) | void ARMul_State::WriteMemory16(u32 address, u16 data) | ||||||
| { | { | ||||||
|  |     CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Write); | ||||||
|  | 
 | ||||||
|     if (InBigEndianMode()) |     if (InBigEndianMode()) | ||||||
|         data = Common::swap16(data); |         data = Common::swap16(data); | ||||||
| 
 | 
 | ||||||
|  | @ -225,6 +256,8 @@ void ARMul_State::WriteMemory16(u32 address, u16 data) | ||||||
| 
 | 
 | ||||||
| void ARMul_State::WriteMemory32(u32 address, u32 data) | void ARMul_State::WriteMemory32(u32 address, u32 data) | ||||||
| { | { | ||||||
|  |     CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Write); | ||||||
|  | 
 | ||||||
|     if (InBigEndianMode()) |     if (InBigEndianMode()) | ||||||
|         data = Common::swap32(data); |         data = Common::swap32(data); | ||||||
| 
 | 
 | ||||||
|  | @ -233,6 +266,8 @@ void ARMul_State::WriteMemory32(u32 address, u32 data) | ||||||
| 
 | 
 | ||||||
| void ARMul_State::WriteMemory64(u32 address, u64 data) | void ARMul_State::WriteMemory64(u32 address, u64 data) | ||||||
| { | { | ||||||
|  |     CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Write); | ||||||
|  | 
 | ||||||
|     if (InBigEndianMode()) |     if (InBigEndianMode()) | ||||||
|         data = Common::swap64(data); |         data = Common::swap64(data); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -153,9 +153,11 @@ public: | ||||||
| 
 | 
 | ||||||
|     // Reads/writes data in big/little endian format based on the
 |     // Reads/writes data in big/little endian format based on the
 | ||||||
|     // state of the E (endian) bit in the APSR.
 |     // state of the E (endian) bit in the APSR.
 | ||||||
|  |     u8 ReadMemory8(u32 address) const; | ||||||
|     u16 ReadMemory16(u32 address) const; |     u16 ReadMemory16(u32 address) const; | ||||||
|     u32 ReadMemory32(u32 address) const; |     u32 ReadMemory32(u32 address) const; | ||||||
|     u64 ReadMemory64(u32 address) const; |     u64 ReadMemory64(u32 address) const; | ||||||
|  |     void WriteMemory8(u32 address, u8 data); | ||||||
|     void WriteMemory16(u32 address, u16 data); |     void WriteMemory16(u32 address, u16 data); | ||||||
|     void WriteMemory32(u32 address, u32 data); |     void WriteMemory32(u32 address, u32 data); | ||||||
|     void WriteMemory64(u32 address, u64 data); |     void WriteMemory64(u32 address, u64 data); | ||||||
|  |  | ||||||
|  | @ -13,6 +13,8 @@ | ||||||
| #include "core/hle/kernel/thread.h" | #include "core/hle/kernel/thread.h" | ||||||
| #include "core/hw/hw.h" | #include "core/hw/hw.h" | ||||||
| 
 | 
 | ||||||
|  | #include "core/gdbstub/gdbstub.h" | ||||||
|  | 
 | ||||||
| namespace Core { | namespace Core { | ||||||
| 
 | 
 | ||||||
| ARM_Interface*     g_app_core = nullptr;  ///< ARM11 application core
 | ARM_Interface*     g_app_core = nullptr;  ///< ARM11 application core
 | ||||||
|  | @ -20,6 +22,21 @@ ARM_Interface*     g_sys_core = nullptr;  ///< ARM11 system (OS) core | ||||||
| 
 | 
 | ||||||
| /// Run the core CPU loop
 | /// Run the core CPU loop
 | ||||||
| void RunLoop(int tight_loop) { | void RunLoop(int tight_loop) { | ||||||
|  |     if (GDBStub::g_server_enabled) { | ||||||
|  |         GDBStub::HandlePacket(); | ||||||
|  | 
 | ||||||
|  |         // If the loop is halted and we want to step, use a tiny (1) number of instructions to execute.
 | ||||||
|  |         // Otherwise get out of the loop function.
 | ||||||
|  |         if (GDBStub::GetCpuHaltFlag()) { | ||||||
|  |             if (GDBStub::GetCpuStepFlag()) { | ||||||
|  |                 GDBStub::SetCpuStepFlag(false); | ||||||
|  |                 tight_loop = 1; | ||||||
|  |             } else { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     // If we don't have a currently active thread then don't execute instructions,
 |     // If we don't have a currently active thread then don't execute instructions,
 | ||||||
|     // instead advance to the next event and try to yield to the next thread
 |     // instead advance to the next event and try to yield to the next thread
 | ||||||
|     if (Kernel::GetCurrentThread() == nullptr) { |     if (Kernel::GetCurrentThread() == nullptr) { | ||||||
|  |  | ||||||
							
								
								
									
										940
									
								
								src/core/gdbstub/gdbstub.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										940
									
								
								src/core/gdbstub/gdbstub.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,940 @@ | ||||||
|  | // Copyright 2013 Dolphin Emulator Project
 | ||||||
|  | // Licensed under GPLv2+
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | // Originally written by Sven Peter <sven@fail0verflow.com> for anergistic.
 | ||||||
|  | 
 | ||||||
|  | #include <csignal> | ||||||
|  | #include <cstdarg> | ||||||
|  | #include <cstdio> | ||||||
|  | #include <cstring> | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <map> | ||||||
|  | #include <numeric> | ||||||
|  | 
 | ||||||
|  | #ifdef _MSC_VER | ||||||
|  | #include <WinSock2.h> | ||||||
|  | #include <ws2tcpip.h> | ||||||
|  | #include <common/x64/abi.h> | ||||||
|  | #include <io.h> | ||||||
|  | #include <iphlpapi.h> | ||||||
|  | #define SHUT_RDWR 2 | ||||||
|  | #else | ||||||
|  | #include <sys/select.h> | ||||||
|  | #include <sys/socket.h> | ||||||
|  | #include <sys/un.h> | ||||||
|  | #include <netinet/in.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #include "common/logging/log.h" | ||||||
|  | #include "common/string_util.h" | ||||||
|  | #include <core/arm/arm_interface.h> | ||||||
|  | #include "core/core.h" | ||||||
|  | #include "core/memory.h" | ||||||
|  | #include "gdbstub.h" | ||||||
|  | 
 | ||||||
|  | const int GDB_BUFFER_SIZE = 10000; | ||||||
|  | 
 | ||||||
|  | const char GDB_STUB_START = '$'; | ||||||
|  | const char GDB_STUB_END = '#'; | ||||||
|  | const char GDB_STUB_ACK = '+'; | ||||||
|  | const char GDB_STUB_NACK = '-'; | ||||||
|  | 
 | ||||||
|  | #ifndef SIGTRAP | ||||||
|  | const u32 SIGTRAP = 5; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #ifndef SIGTERM | ||||||
|  | const u32 SIGTERM = 15; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #ifndef MSG_WAITALL | ||||||
|  | const u32 MSG_WAITALL = 8; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | const u32 R0_REGISTER = 0; | ||||||
|  | const u32 R15_REGISTER = 15; | ||||||
|  | const u32 CSPR_REGISTER = 25; | ||||||
|  | 
 | ||||||
|  | namespace GDBStub { | ||||||
|  | 
 | ||||||
|  | static int gdbserver_socket = -1; | ||||||
|  | 
 | ||||||
|  | static u8 command_buffer[GDB_BUFFER_SIZE]; | ||||||
|  | static u32 command_length; | ||||||
|  | 
 | ||||||
|  | static u32 latest_signal = 0; | ||||||
|  | static u32 send_signal = 0; | ||||||
|  | static u32 step_break = 0; | ||||||
|  | static bool memory_break = false; | ||||||
|  | 
 | ||||||
|  | // 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; | ||||||
|  | std::atomic<bool> g_server_enabled(false); | ||||||
|  | 
 | ||||||
|  | #ifdef _WIN32 | ||||||
|  | WSADATA InitData; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | struct Breakpoint { | ||||||
|  |     bool active; | ||||||
|  |     PAddr addr; | ||||||
|  |     u32 len; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static std::map<u32, Breakpoint> breakpoints_execute; | ||||||
|  | static std::map<u32, Breakpoint> breakpoints_read; | ||||||
|  | static std::map<u32, Breakpoint> breakpoints_write; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Turns hex string character into the equivalent byte. | ||||||
|  |  * | ||||||
|  |  * @param hex Input hex character to be turned into byte. | ||||||
|  |  */ | ||||||
|  | static u8 HexCharToValue(u8 hex) { | ||||||
|  |     if (hex >= '0' && hex <= '9') { | ||||||
|  |         return hex - '0'; | ||||||
|  |     } else if (hex >= 'a' && hex <= 'f') { | ||||||
|  |         return hex - 'a' + 0xA; | ||||||
|  |     } else if (hex >= 'A' && hex <= 'F') { | ||||||
|  |         return hex - 'A' + 0xA; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     LOG_ERROR(Debug_GDBStub, "Invalid nibble: %c (%02x)\n", hex, hex); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Turn nibble of byte into hex string character. | ||||||
|  |  * | ||||||
|  |  * @param n Nibble to be turned into hex character. | ||||||
|  |  */ | ||||||
|  | static u8 NibbleToHex(u8 n) { | ||||||
|  |     n &= 0xF; | ||||||
|  |     if (n < 0xA) { | ||||||
|  |         return '0' + n; | ||||||
|  |     } else { | ||||||
|  |         return 'A' + n - 0xA; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Converts input array of u8 bytes into their equivalent hex string characters. | ||||||
|  |  * | ||||||
|  |  * @param dest Pointer to buffer to store output hex string characters. | ||||||
|  |  * @param src Pointer to array of u8 bytes. | ||||||
|  |  * @param len Length of src array. | ||||||
|  |  */ | ||||||
|  | static void MemToHex(u8* dest, u8* src, u32 len) { | ||||||
|  |     while (len-- > 0) { | ||||||
|  |         u8 tmp = *src++; | ||||||
|  |         *dest++ = NibbleToHex(tmp >> 4); | ||||||
|  |         *dest++ = NibbleToHex(tmp); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Converts input hex string characters into an array of equivalent of u8 bytes. | ||||||
|  |  * | ||||||
|  |  * @param dest Pointer to buffer to store u8 bytes. | ||||||
|  |  * @param src Pointer to array of output hex string characters. | ||||||
|  |  * @param len Length of src array. | ||||||
|  |  */ | ||||||
|  | static void HexToMem(u8* dest, u8* src, u32 len) { | ||||||
|  |     while (len-- > 0) { | ||||||
|  |         *dest++ = (HexCharToValue(src[0]) << 4) | HexCharToValue(src[1]); | ||||||
|  |         src += 2; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Convert a u32 into a hex string. | ||||||
|  |  * | ||||||
|  |  * @param dest Pointer to buffer to store output hex string characters. | ||||||
|  |  */ | ||||||
|  | static void IntToHex(u8* dest, u32 v) { | ||||||
|  |     for (int i = 0; i < 8; i += 2) { | ||||||
|  |         dest[i + 1] = NibbleToHex(v >> (4 * i)); | ||||||
|  |         dest[i] = NibbleToHex(v >> (4 * (i + 1))); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Convert a hex string into a u32. | ||||||
|  |  * | ||||||
|  |  * @param src Pointer to hex string. | ||||||
|  |  */ | ||||||
|  | static u32 HexToInt(u8* src) { | ||||||
|  |     u32 output = 0; | ||||||
|  | 
 | ||||||
|  |     for (int i = 0; i < 8; i += 2) { | ||||||
|  |         output = (output << 4) | HexCharToValue(src[7 - i - 1]); | ||||||
|  |         output = (output << 4) | HexCharToValue(src[7 - i]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return output; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Read a byte from the gdb client.
 | ||||||
|  | static u8 ReadByte() { | ||||||
|  |     u8 c; | ||||||
|  |     size_t received_size = recv(gdbserver_socket, reinterpret_cast<char*>(&c), 1, MSG_WAITALL); | ||||||
|  |     if (received_size != 1) { | ||||||
|  |         LOG_ERROR(Debug_GDBStub, "recv failed : %ld", received_size); | ||||||
|  |         Deinit(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return c; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Calculate the checksum of the current command buffer.
 | ||||||
|  | static u8 CalculateChecksum(u8 *buffer, u32 length) { | ||||||
|  |     return static_cast<u8>(std::accumulate(buffer, buffer + length, 0, std::plus<u8>())); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get the list of breakpoints for a given breakpoint type. | ||||||
|  |  * | ||||||
|  |  * @param type Type of breakpoint list. | ||||||
|  |  */ | ||||||
|  | static std::map<u32, Breakpoint>& GetBreakpointList(BreakpointType type) { | ||||||
|  |     switch (type) { | ||||||
|  |     case BreakpointType::Execute: | ||||||
|  |         return breakpoints_execute; | ||||||
|  |     case BreakpointType::Read: | ||||||
|  |         return breakpoints_read; | ||||||
|  |     case BreakpointType::Write: | ||||||
|  |         return breakpoints_write; | ||||||
|  |     default: | ||||||
|  |         return breakpoints_read; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Remove the breakpoint from the given address of the specified type. | ||||||
|  |  * | ||||||
|  |  * @param type Type of breakpoint. | ||||||
|  |  * @param addr Address of breakpoint. | ||||||
|  |  */ | ||||||
|  | static void RemoveBreakpoint(BreakpointType type, PAddr addr) { | ||||||
|  |     std::map<u32, Breakpoint>& p = GetBreakpointList(type); | ||||||
|  | 
 | ||||||
|  |     auto bp = p.find(addr); | ||||||
|  |     if (bp != p.end()) { | ||||||
|  |         LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: %08x bytes at %08x of type %d\n", bp->second.len, bp->second.addr, type); | ||||||
|  |         p.erase(addr); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | PAddr GetNextBreakpointFromAddress(PAddr addr, BreakpointType type) { | ||||||
|  |     std::map<u32, Breakpoint>& p = GetBreakpointList(type); | ||||||
|  |     auto next_breakpoint = p.lower_bound(addr); | ||||||
|  |     u32 breakpoint = -1; | ||||||
|  | 
 | ||||||
|  |     if (next_breakpoint != p.end()) | ||||||
|  |         breakpoint = next_breakpoint->first; | ||||||
|  | 
 | ||||||
|  |     return breakpoint; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool CheckBreakpoint(PAddr addr, BreakpointType type) { | ||||||
|  |     if (!IsConnected()) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     std::map<u32, Breakpoint>& p = GetBreakpointList(type); | ||||||
|  | 
 | ||||||
|  |     auto bp = p.find(addr); | ||||||
|  |     if (bp != p.end()) { | ||||||
|  |         u32 len = bp->second.len; | ||||||
|  | 
 | ||||||
|  |         // IDA Pro defaults to 4-byte breakpoints for all non-hardware breakpoints
 | ||||||
|  |         // no matter if it's a 4-byte or 2-byte instruction. When you execute a
 | ||||||
|  |         // Thumb instruction with a 4-byte breakpoint set, it will set a breakpoint on
 | ||||||
|  |         // two instructions instead of the single instruction you placed the breakpoint
 | ||||||
|  |         // on. So, as a way to make sure that execution breakpoints are only breaking
 | ||||||
|  |         // on the instruction that was specified, set the length of an execution
 | ||||||
|  |         // breakpoint to 1. This should be fine since the CPU should never begin executing
 | ||||||
|  |         // an instruction anywhere except the beginning of the instruction.
 | ||||||
|  |         if (type == BreakpointType::Execute) { | ||||||
|  |             len = 1; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) { | ||||||
|  |             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); | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Send packet to gdb client. | ||||||
|  |  * | ||||||
|  |  * @param packet Packet to be sent to client. | ||||||
|  |  */ | ||||||
|  | static void SendPacket(const char packet) { | ||||||
|  |     size_t sent_size = send(gdbserver_socket, &packet, 1, 0); | ||||||
|  |     if (sent_size != 1) { | ||||||
|  |         LOG_ERROR(Debug_GDBStub, "send failed"); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Send reply to gdb client. | ||||||
|  |  * | ||||||
|  |  * @param reply Reply to be sent to client. | ||||||
|  |  */ | ||||||
|  | static void SendReply(const char* reply) { | ||||||
|  |     if (!IsConnected()) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     memset(command_buffer, 0, sizeof(command_buffer)); | ||||||
|  | 
 | ||||||
|  |     command_length = strlen(reply); | ||||||
|  |     if (command_length + 4 > sizeof(command_buffer)) { | ||||||
|  |         LOG_ERROR(Debug_GDBStub, "command_buffer overflow in SendReply"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     memcpy(command_buffer + 1, reply, command_length); | ||||||
|  | 
 | ||||||
|  |     u8 checksum = CalculateChecksum(command_buffer, command_length + 1); | ||||||
|  |     command_buffer[0] = GDB_STUB_START; | ||||||
|  |     command_buffer[command_length + 1] = GDB_STUB_END; | ||||||
|  |     command_buffer[command_length + 2] = NibbleToHex(checksum >> 4); | ||||||
|  |     command_buffer[command_length + 3] = NibbleToHex(checksum); | ||||||
|  | 
 | ||||||
|  |     u8* ptr = command_buffer; | ||||||
|  |     u32 left = command_length + 4; | ||||||
|  |     while (left > 0) { | ||||||
|  |         int sent_size = send(gdbserver_socket, reinterpret_cast<char*>(ptr), left, 0); | ||||||
|  |         if (sent_size < 0) { | ||||||
|  |             LOG_ERROR(Debug_GDBStub, "gdb: send failed"); | ||||||
|  |             return Deinit(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         left -= sent_size; | ||||||
|  |         ptr += sent_size; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Handle query command from gdb client.
 | ||||||
|  | static void HandleQuery() { | ||||||
|  |     LOG_DEBUG(Debug_GDBStub, "gdb: query '%s'\n", command_buffer + 1); | ||||||
|  | 
 | ||||||
|  |     if (!strcmp(reinterpret_cast<const char*>(command_buffer + 1), "TStatus")) { | ||||||
|  |         SendReply("T0"); | ||||||
|  |     } else { | ||||||
|  |         SendReply(""); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// 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"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     SendReply("E01"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Create and send signal packet.
 | ||||||
|  | static void HandleSignal() { | ||||||
|  |     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))); | ||||||
|  | 
 | ||||||
|  |     LOG_DEBUG(Debug_GDBStub, "Response: %s", buffer.c_str()); | ||||||
|  | 
 | ||||||
|  |     SendReply(buffer.c_str()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Set signal and send packet to client through HandleSignal if signal flag is set using SendSignal. | ||||||
|  |  * | ||||||
|  |  * @param signal Signal to be sent to client. | ||||||
|  |  */ | ||||||
|  | int SendSignal(u32 signal) { | ||||||
|  |     if (gdbserver_socket == -1) { | ||||||
|  |         return 1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     latest_signal = signal; | ||||||
|  | 
 | ||||||
|  |     if (send_signal) { | ||||||
|  |         HandleSignal(); | ||||||
|  |         send_signal = 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Read command from gdb client.
 | ||||||
|  | static void ReadCommand() { | ||||||
|  |     command_length = 0; | ||||||
|  |     memset(command_buffer, 0, sizeof(command_buffer)); | ||||||
|  | 
 | ||||||
|  |     u8 c = ReadByte(); | ||||||
|  |     if (c == '+') { | ||||||
|  |         //ignore ack
 | ||||||
|  |         return; | ||||||
|  |     } else if (c == 0x03) { | ||||||
|  |         LOG_INFO(Debug_GDBStub, "gdb: found break command\n"); | ||||||
|  |         halt_loop = true; | ||||||
|  |         send_signal = 1; | ||||||
|  |         SendSignal(SIGTRAP); | ||||||
|  |         return; | ||||||
|  |     } else if (c != GDB_STUB_START) { | ||||||
|  |         LOG_DEBUG(Debug_GDBStub, "gdb: read invalid byte %02x\n", c); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     while ((c = ReadByte()) != GDB_STUB_END) { | ||||||
|  |         command_buffer[command_length++] = c; | ||||||
|  |         if (command_length == sizeof(command_buffer)) { | ||||||
|  |             LOG_ERROR(Debug_GDBStub, "gdb: command_buffer overflow\n"); | ||||||
|  |             SendPacket(GDB_STUB_NACK); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     u8 checksum_received = HexCharToValue(ReadByte()) << 4; | ||||||
|  |     checksum_received |= HexCharToValue(ReadByte()); | ||||||
|  | 
 | ||||||
|  |     u8 checksum_calculated = CalculateChecksum(command_buffer, command_length); | ||||||
|  | 
 | ||||||
|  |     if (checksum_received != checksum_calculated) { | ||||||
|  |         LOG_ERROR(Debug_GDBStub, "gdb: invalid checksum: calculated %02x and read %02x for $%s# (length: %d)\n", | ||||||
|  |             checksum_calculated, checksum_received, command_buffer, command_length); | ||||||
|  | 
 | ||||||
|  |         command_length = 0; | ||||||
|  | 
 | ||||||
|  |         SendPacket(GDB_STUB_NACK); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     SendPacket(GDB_STUB_ACK); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Check if there is data to be read from the gdb client.
 | ||||||
|  | static bool IsDataAvailable() { | ||||||
|  |     if (!IsConnected()) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fd_set fd_socket; | ||||||
|  | 
 | ||||||
|  |     FD_ZERO(&fd_socket); | ||||||
|  |     FD_SET(gdbserver_socket, &fd_socket); | ||||||
|  | 
 | ||||||
|  |     struct timeval t; | ||||||
|  |     t.tv_sec = 0; | ||||||
|  |     t.tv_usec = 0; | ||||||
|  | 
 | ||||||
|  |     if (select(gdbserver_socket + 1, &fd_socket, nullptr, nullptr, &t) < 0) { | ||||||
|  |         LOG_ERROR(Debug_GDBStub, "select failed"); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return FD_ISSET(gdbserver_socket, &fd_socket); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Send requested register to gdb client.
 | ||||||
|  | static void ReadRegister() { | ||||||
|  |     static u8 reply[64]; | ||||||
|  |     memset(reply, 0, sizeof(reply)); | ||||||
|  | 
 | ||||||
|  |     u32 id = HexCharToValue(command_buffer[1]); | ||||||
|  |     if (command_buffer[2] != '\0') { | ||||||
|  |         id <<= 4; | ||||||
|  |         id |= HexCharToValue(command_buffer[2]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (id >= R0_REGISTER && id <= R15_REGISTER) { | ||||||
|  |         IntToHex(reply, Core::g_app_core->GetReg(id)); | ||||||
|  |     } else if (id == CSPR_REGISTER) { | ||||||
|  |         IntToHex(reply, Core::g_app_core->GetCPSR()); | ||||||
|  |     } else { | ||||||
|  |         return SendReply("E01"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     SendReply(reinterpret_cast<char*>(reply)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Send all registers to the gdb client.
 | ||||||
|  | static void ReadRegisters() { | ||||||
|  |     static u8 buffer[GDB_BUFFER_SIZE - 4]; | ||||||
|  |     memset(buffer, 0, sizeof(buffer)); | ||||||
|  | 
 | ||||||
|  |     u8* bufptr = buffer; | ||||||
|  |     for (int i = 0; i <= CSPR_REGISTER; i++) { | ||||||
|  |         if (i <= R15_REGISTER) { | ||||||
|  |             IntToHex(bufptr + i * 8, Core::g_app_core->GetReg(i)); | ||||||
|  |         } else if (i == CSPR_REGISTER) { | ||||||
|  |             IntToHex(bufptr + i * 8, Core::g_app_core->GetCPSR()); | ||||||
|  |         } else { | ||||||
|  |             IntToHex(bufptr + i * 8, 0); | ||||||
|  |             IntToHex(bufptr + (i + 1) * 8, 0); | ||||||
|  |             i++; // These registers seem to be all 64bit instead of 32bit, so skip two instead of one
 | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     SendReply(reinterpret_cast<char*>(buffer)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Modify data of register specified by gdb client.
 | ||||||
|  | static void WriteRegister() { | ||||||
|  |     u8* buffer_ptr = command_buffer + 3; | ||||||
|  | 
 | ||||||
|  |     u32 id = HexCharToValue(command_buffer[1]); | ||||||
|  |     if (command_buffer[2] != '=') { | ||||||
|  |         ++buffer_ptr; | ||||||
|  |         id <<= 4; | ||||||
|  |         id |= HexCharToValue(command_buffer[2]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (id >= R0_REGISTER && id <= R15_REGISTER) { | ||||||
|  |         Core::g_app_core->SetReg(id, HexToInt(buffer_ptr)); | ||||||
|  |     } else if (id == CSPR_REGISTER) { | ||||||
|  |         Core::g_app_core->SetCPSR(HexToInt(buffer_ptr)); | ||||||
|  |     } else { | ||||||
|  |         return SendReply("E01"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     SendReply("OK"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Modify all registers with data received from the client.
 | ||||||
|  | static void WriteRegisters() { | ||||||
|  |     u8* buffer_ptr = command_buffer + 1; | ||||||
|  | 
 | ||||||
|  |     if (command_buffer[0] != 'G') | ||||||
|  |         return SendReply("E01"); | ||||||
|  | 
 | ||||||
|  |     for (int i = 0; i <= CSPR_REGISTER; i++) { | ||||||
|  |         if (i <= R15_REGISTER) { | ||||||
|  |             Core::g_app_core->SetReg(i, HexToInt(buffer_ptr + i * 8)); | ||||||
|  |         } else if (i == CSPR_REGISTER) { | ||||||
|  |             Core::g_app_core->SetCPSR(HexToInt(buffer_ptr + i * 8)); | ||||||
|  |         } else { | ||||||
|  |             i++; // These registers seem to be all 64bit instead of 32bit, so skip two instead of one
 | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     SendReply("OK"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Read location in memory specified by gdb client.
 | ||||||
|  | static void ReadMemory() { | ||||||
|  |     static u8 reply[GDB_BUFFER_SIZE - 4]; | ||||||
|  | 
 | ||||||
|  |     int i = 1; | ||||||
|  |     PAddr addr = 0; | ||||||
|  |     while (command_buffer[i] != ',') { | ||||||
|  |         addr = (addr << 4) | HexCharToValue(command_buffer[i++]); | ||||||
|  |     } | ||||||
|  |     i++; | ||||||
|  | 
 | ||||||
|  |     u32 len = 0; | ||||||
|  |     while (i < command_length) { | ||||||
|  |         len = (len << 4) | HexCharToValue(command_buffer[i++]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (len * 2 > sizeof(reply)) { | ||||||
|  |         SendReply("E01"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     u8* data = Memory::GetPointer(addr); | ||||||
|  |     if (!data) { | ||||||
|  |         return SendReply("E0"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     MemToHex(reply, data, len); | ||||||
|  |     reply[len * 2] = '\0'; | ||||||
|  |     SendReply(reinterpret_cast<char*>(reply)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Modify location in memory with data received from the gdb client.
 | ||||||
|  | static void WriteMemory() { | ||||||
|  |     int i = 1; | ||||||
|  |     PAddr addr = 0; | ||||||
|  |     while (command_buffer[i] != ',') { | ||||||
|  |         addr = (addr << 4) | HexCharToValue(command_buffer[i++]); | ||||||
|  |     } | ||||||
|  |     i++; | ||||||
|  | 
 | ||||||
|  |     u32 len = 0; | ||||||
|  |     while (command_buffer[i] != ':') { | ||||||
|  |         len = (len << 4) | HexCharToValue(command_buffer[i++]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     u8* dst = Memory::GetPointer(addr); | ||||||
|  |     if (!dst) { | ||||||
|  |         return SendReply("E00"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     HexToMem(dst, command_buffer + i + 1, len); | ||||||
|  |     SendReply("OK"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Break(bool is_memory_break) { | ||||||
|  |     if (!halt_loop) { | ||||||
|  |         halt_loop = true; | ||||||
|  |         send_signal = 1; | ||||||
|  |         SendSignal(SIGTRAP); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     memory_break = is_memory_break; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Tell the CPU that it should perform a single step.
 | ||||||
|  | static void Step() { | ||||||
|  |     step_loop = true; | ||||||
|  |     halt_loop = true; | ||||||
|  |     send_signal = 1; | ||||||
|  |     step_break = 1; | ||||||
|  |     SendSignal(SIGTRAP); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool IsMemoryBreak() { | ||||||
|  |     if (IsConnected()) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return memory_break; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Tell the CPU to continue executing.
 | ||||||
|  | static void Continue() { | ||||||
|  |     memory_break = false; | ||||||
|  |     step_break = 0; | ||||||
|  |     step_loop = false; | ||||||
|  |     halt_loop = false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Commit breakpoint to list of breakpoints. | ||||||
|  |  * | ||||||
|  |  * @param type Type of breakpoint. | ||||||
|  |  * @param addr Address of breakpoint. | ||||||
|  |  * @param len Length of breakpoint. | ||||||
|  |  */ | ||||||
|  | bool CommitBreakpoint(BreakpointType type, PAddr addr, u32 len) { | ||||||
|  |     std::map<u32, Breakpoint>& p = GetBreakpointList(type); | ||||||
|  | 
 | ||||||
|  |     Breakpoint breakpoint; | ||||||
|  |     breakpoint.active = true; | ||||||
|  |     breakpoint.addr = addr; | ||||||
|  |     breakpoint.len = len; | ||||||
|  |     p.insert({ addr, breakpoint }); | ||||||
|  | 
 | ||||||
|  |     LOG_DEBUG(Debug_GDBStub, "gdb: added %d breakpoint: %08x bytes at %08x\n", type, breakpoint.len, breakpoint.addr); | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Handle add breakpoint command from gdb client.
 | ||||||
|  | static void AddBreakpoint() { | ||||||
|  |     BreakpointType type; | ||||||
|  | 
 | ||||||
|  |     u8 type_id = HexCharToValue(command_buffer[1]); | ||||||
|  |     switch (type_id) { | ||||||
|  |     case 0: | ||||||
|  |     case 1: | ||||||
|  |         type = BreakpointType::Execute; | ||||||
|  |         break; | ||||||
|  |     case 2: | ||||||
|  |         type = BreakpointType::Write; | ||||||
|  |         break; | ||||||
|  |     case 3: | ||||||
|  |         type = BreakpointType::Read; | ||||||
|  |         break; | ||||||
|  |     case 4: | ||||||
|  |         type = BreakpointType::Access; | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         return SendReply("E01"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     int i = 3; | ||||||
|  |     PAddr addr = 0; | ||||||
|  |     while (command_buffer[i] != ',') { | ||||||
|  |         addr = addr << 4 | HexCharToValue(command_buffer[i++]); | ||||||
|  |     } | ||||||
|  |     i++; | ||||||
|  | 
 | ||||||
|  |     u32 len = 0; | ||||||
|  |     while (i < command_length) { | ||||||
|  |         len = len << 4 | HexCharToValue(command_buffer[i++]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (type == BreakpointType::Access) { | ||||||
|  |         // Access is made up of Read and Write types, so add both breakpoints
 | ||||||
|  |         type = BreakpointType::Read; | ||||||
|  | 
 | ||||||
|  |         if (!CommitBreakpoint(type, addr, len)) { | ||||||
|  |             return SendReply("E02"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         type = BreakpointType::Write; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!CommitBreakpoint(type, addr, len)) { | ||||||
|  |         return SendReply("E02"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     SendReply("OK"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Handle remove breakpoint command from gdb client.
 | ||||||
|  | static void RemoveBreakpoint() { | ||||||
|  |     BreakpointType type; | ||||||
|  | 
 | ||||||
|  |     u8 type_id = HexCharToValue(command_buffer[1]); | ||||||
|  |     switch (type_id) { | ||||||
|  |     case 0: | ||||||
|  |     case 1: | ||||||
|  |         type = BreakpointType::Execute; | ||||||
|  |         break; | ||||||
|  |     case 2: | ||||||
|  |         type = BreakpointType::Write; | ||||||
|  |         break; | ||||||
|  |     case 3: | ||||||
|  |         type = BreakpointType::Read; | ||||||
|  |         break; | ||||||
|  |     case 4: | ||||||
|  |         type = BreakpointType::Access; | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         return SendReply("E01"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     int i = 3; | ||||||
|  |     PAddr addr = 0; | ||||||
|  |     while (command_buffer[i] != ',') { | ||||||
|  |         addr = (addr << 4) | HexCharToValue(command_buffer[i++]); | ||||||
|  |     } | ||||||
|  |     i++; | ||||||
|  | 
 | ||||||
|  |     u32 len = 0; | ||||||
|  |     while (i < command_length) { | ||||||
|  |         len = (len << 4) | HexCharToValue(command_buffer[i++]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (type == BreakpointType::Access) { | ||||||
|  |         // Access is made up of Read and Write types, so add both breakpoints
 | ||||||
|  |         type = BreakpointType::Read; | ||||||
|  |         RemoveBreakpoint(type, addr); | ||||||
|  | 
 | ||||||
|  |         type = BreakpointType::Write; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     RemoveBreakpoint(type, addr); | ||||||
|  |     SendReply("OK"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void HandlePacket() { | ||||||
|  |     if (!IsConnected()) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!IsDataAvailable()) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ReadCommand(); | ||||||
|  |     if (command_length == 0) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     LOG_DEBUG(Debug_GDBStub, "Packet: %s", command_buffer); | ||||||
|  | 
 | ||||||
|  |     switch (command_buffer[0]) { | ||||||
|  |     case 'q': | ||||||
|  |         HandleQuery(); | ||||||
|  |         break; | ||||||
|  |     case 'H': | ||||||
|  |         HandleSetThread(); | ||||||
|  |         break; | ||||||
|  |     case '?': | ||||||
|  |         HandleSignal(); | ||||||
|  |         break; | ||||||
|  |     case 'k': | ||||||
|  |         Deinit(); | ||||||
|  |         LOG_INFO(Debug_GDBStub, "killed by gdb"); | ||||||
|  |         return; | ||||||
|  |     case 'g': | ||||||
|  |         ReadRegisters(); | ||||||
|  |         break; | ||||||
|  |     case 'G': | ||||||
|  |         WriteRegisters(); | ||||||
|  |         break; | ||||||
|  |     case 'p': | ||||||
|  |         ReadRegister(); | ||||||
|  |         break; | ||||||
|  |     case 'P': | ||||||
|  |         WriteRegister(); | ||||||
|  |         break; | ||||||
|  |     case 'm': | ||||||
|  |         ReadMemory(); | ||||||
|  |         break; | ||||||
|  |     case 'M': | ||||||
|  |         WriteMemory(); | ||||||
|  |         break; | ||||||
|  |     case 's': | ||||||
|  |         Step(); | ||||||
|  |         return; | ||||||
|  |     case 'C': | ||||||
|  |     case 'c': | ||||||
|  |         Continue(); | ||||||
|  |         return; | ||||||
|  |     case 'z': | ||||||
|  |         RemoveBreakpoint(); | ||||||
|  |         break; | ||||||
|  |     case 'Z': | ||||||
|  |         AddBreakpoint(); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         SendReply(""); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void SetServerPort(u16 port) { | ||||||
|  |     gdbstub_port = port; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ToggleServer(bool status) { | ||||||
|  |     if (status) { | ||||||
|  |         g_server_enabled = status; | ||||||
|  | 
 | ||||||
|  |         // Start server
 | ||||||
|  |         if (!IsConnected() && Core::g_sys_core != nullptr) { | ||||||
|  |             Init(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         // Stop server
 | ||||||
|  |         if (IsConnected()) { | ||||||
|  |             Deinit(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         g_server_enabled = status; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Init(u16 port) { | ||||||
|  |     if (!g_server_enabled) { | ||||||
|  |         // Set the halt loop to false in case the user enabled the gdbstub mid-execution.
 | ||||||
|  |         // This way the CPU can still execute normally.
 | ||||||
|  |         halt_loop = false; | ||||||
|  |         step_loop = false; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Setup initial gdbstub status
 | ||||||
|  |     halt_loop = true; | ||||||
|  |     step_loop = false; | ||||||
|  | 
 | ||||||
|  |     breakpoints_execute.clear(); | ||||||
|  |     breakpoints_read.clear(); | ||||||
|  |     breakpoints_write.clear(); | ||||||
|  | 
 | ||||||
|  |     // Start gdb server
 | ||||||
|  |     LOG_INFO(Debug_GDBStub, "Starting GDB server on port %d...", port); | ||||||
|  | 
 | ||||||
|  |     sockaddr_in saddr_server = {}; | ||||||
|  |     saddr_server.sin_family = AF_INET; | ||||||
|  |     saddr_server.sin_port = htons(port); | ||||||
|  |     saddr_server.sin_addr.s_addr = INADDR_ANY; | ||||||
|  | 
 | ||||||
|  | #ifdef _WIN32 | ||||||
|  |     WSAStartup(MAKEWORD(2, 2), &InitData); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |     int tmpsock = socket(PF_INET, SOCK_STREAM, 0); | ||||||
|  |     if (tmpsock == -1) { | ||||||
|  |         LOG_ERROR(Debug_GDBStub, "Failed to create gdb socket"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const sockaddr* server_addr = reinterpret_cast<const sockaddr*>(&saddr_server); | ||||||
|  |     socklen_t server_addrlen = sizeof(saddr_server); | ||||||
|  |     if (bind(tmpsock, server_addr, server_addrlen) < 0) { | ||||||
|  |         LOG_ERROR(Debug_GDBStub, "Failed to bind gdb socket"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (listen(tmpsock, 1) < 0) { | ||||||
|  |         LOG_ERROR(Debug_GDBStub, "Failed to listen to gdb socket"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Wait for gdb to connect
 | ||||||
|  |     LOG_INFO(Debug_GDBStub, "Waiting for gdb to connect...\n"); | ||||||
|  |     sockaddr_in saddr_client; | ||||||
|  |     sockaddr* client_addr = reinterpret_cast<sockaddr*>(&saddr_client); | ||||||
|  |     socklen_t client_addrlen = sizeof(saddr_client); | ||||||
|  |     gdbserver_socket = accept(tmpsock, client_addr, &client_addrlen); | ||||||
|  |     if (gdbserver_socket < 0) { | ||||||
|  |         // In the case that we couldn't start the server for whatever reason, just start CPU execution like normal.
 | ||||||
|  |         halt_loop = false; | ||||||
|  |         step_loop = false; | ||||||
|  | 
 | ||||||
|  |         LOG_ERROR(Debug_GDBStub, "Failed to accept gdb client"); | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         LOG_INFO(Debug_GDBStub, "Client connected.\n"); | ||||||
|  |         saddr_client.sin_addr.s_addr = ntohl(saddr_client.sin_addr.s_addr); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Clean up temporary socket if it's still alive at this point.
 | ||||||
|  |     if (tmpsock != -1) { | ||||||
|  |         shutdown(tmpsock, SHUT_RDWR); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Init() { | ||||||
|  |     Init(gdbstub_port); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Deinit() { | ||||||
|  |     if (!g_server_enabled) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     LOG_INFO(Debug_GDBStub, "Stopping GDB ..."); | ||||||
|  |     if (gdbserver_socket != -1) { | ||||||
|  |         shutdown(gdbserver_socket, SHUT_RDWR); | ||||||
|  |         gdbserver_socket = -1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | #ifdef _WIN32 | ||||||
|  |     WSACleanup(); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |     LOG_INFO(Debug_GDBStub, "GDB stopped."); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool IsConnected() { | ||||||
|  |     return g_server_enabled && gdbserver_socket != -1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool GetCpuHaltFlag() { | ||||||
|  |     return halt_loop; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool GetCpuStepFlag() { | ||||||
|  |     return step_loop; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void SetCpuStepFlag(bool is_step) { | ||||||
|  |     step_loop = is_step; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }; | ||||||
							
								
								
									
										89
									
								
								src/core/gdbstub/gdbstub.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								src/core/gdbstub/gdbstub.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,89 @@ | ||||||
|  | // Copyright 2013 Dolphin Emulator Project
 | ||||||
|  | // Licensed under GPLv2+
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | // Originally written by Sven Peter <sven@fail0verflow.com> for anergistic.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | #include <atomic> | ||||||
|  | 
 | ||||||
|  | namespace GDBStub { | ||||||
|  | 
 | ||||||
|  | /// Breakpoint Method
 | ||||||
|  | enum class BreakpointType { | ||||||
|  |     None,     ///< None
 | ||||||
|  |     Execute,  ///< Execution Breakpoint
 | ||||||
|  |     Read,     ///< Read Breakpoint
 | ||||||
|  |     Write,    ///< Write Breakpoint
 | ||||||
|  |     Access    ///< Access (R/W) Breakpoint
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /// If set to false, the server will never be started and no gdbstub-related functions will be executed.
 | ||||||
|  | extern std::atomic<bool> g_server_enabled; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Set the port the gdbstub should use to listen for connections. | ||||||
|  |  * | ||||||
|  |  * @param port Port to listen for connection | ||||||
|  |  */ | ||||||
|  | void SetServerPort(u16 port); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Set the g_server_enabled flag and start or stop the server if possible. | ||||||
|  |  * | ||||||
|  |  * @param status Set the server to enabled or disabled. | ||||||
|  |  */ | ||||||
|  | void ToggleServer(bool status); | ||||||
|  | 
 | ||||||
|  | /// Start the gdbstub server.
 | ||||||
|  | void Init(); | ||||||
|  | 
 | ||||||
|  | /// Stop gdbstub server.
 | ||||||
|  | void Deinit(); | ||||||
|  | 
 | ||||||
|  | /// Returns true if there is an active socket connection.
 | ||||||
|  | bool IsConnected(); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Signal to the gdbstub server that it should halt CPU execution. | ||||||
|  |  * | ||||||
|  |  * @param is_memory_break If true, the break resulted from a memory breakpoint. | ||||||
|  |  */ | ||||||
|  | void Break(bool is_memory_break = false); | ||||||
|  | 
 | ||||||
|  | /// Determine if there was a memory breakpoint.
 | ||||||
|  | bool IsMemoryBreak(); | ||||||
|  | 
 | ||||||
|  | /// Read and handle packet from gdb client.
 | ||||||
|  | void HandlePacket(); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get the nearest breakpoint of the specified type at the given address. | ||||||
|  |  * | ||||||
|  |  * @param addr Address to search from. | ||||||
|  |  * @param type Type of breakpoint. | ||||||
|  |  */ | ||||||
|  | PAddr GetNextBreakpointFromAddress(u32 addr, GDBStub::BreakpointType type); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Check if a breakpoint of the specified type exists at the given address. | ||||||
|  |  * | ||||||
|  |  * @param addr Address of breakpoint. | ||||||
|  |  * @param type Type of breakpoint. | ||||||
|  |  */ | ||||||
|  | bool CheckBreakpoint(u32 addr, GDBStub::BreakpointType type); | ||||||
|  | 
 | ||||||
|  | // If set to true, the CPU will halt at the beginning of the next CPU loop.
 | ||||||
|  | bool GetCpuHaltFlag(); | ||||||
|  | 
 | ||||||
|  | // If set to true and the CPU is halted, the CPU will step one instruction.
 | ||||||
|  | bool GetCpuStepFlag(); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * When set to true, the CPU will step one instruction when the CPU is halted next. | ||||||
|  |  * | ||||||
|  |  * @param is_step | ||||||
|  |  */ | ||||||
|  | void SetCpuStepFlag(bool is_step); | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -6,6 +6,7 @@ | ||||||
| 
 | 
 | ||||||
| #include <string> | #include <string> | ||||||
| #include <array> | #include <array> | ||||||
|  | #include <common/file_util.h> | ||||||
| 
 | 
 | ||||||
| namespace Settings { | namespace Settings { | ||||||
| 
 | 
 | ||||||
|  | @ -60,6 +61,10 @@ struct Values { | ||||||
|     float bg_blue; |     float bg_blue; | ||||||
| 
 | 
 | ||||||
|     std::string log_filter; |     std::string log_filter; | ||||||
|  | 
 | ||||||
|  |     // Debugging
 | ||||||
|  |     bool use_gdbstub; | ||||||
|  |     u16 gdbstub_port; | ||||||
| } extern values; | } extern values; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -12,6 +12,8 @@ | ||||||
| 
 | 
 | ||||||
| #include "video_core/video_core.h" | #include "video_core/video_core.h" | ||||||
| 
 | 
 | ||||||
|  | #include "core/gdbstub/gdbstub.h" | ||||||
|  | 
 | ||||||
| namespace System { | namespace System { | ||||||
| 
 | 
 | ||||||
| void Init(EmuWindow* emu_window) { | void Init(EmuWindow* emu_window) { | ||||||
|  | @ -22,9 +24,13 @@ void Init(EmuWindow* emu_window) { | ||||||
|     Kernel::Init(); |     Kernel::Init(); | ||||||
|     HLE::Init(); |     HLE::Init(); | ||||||
|     VideoCore::Init(emu_window); |     VideoCore::Init(emu_window); | ||||||
|  | 
 | ||||||
|  |     GDBStub::Init(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Shutdown() { | void Shutdown() { | ||||||
|  |     GDBStub::Deinit(); | ||||||
|  | 
 | ||||||
|     VideoCore::Shutdown(); |     VideoCore::Shutdown(); | ||||||
|     HLE::Shutdown(); |     HLE::Shutdown(); | ||||||
|     Kernel::Shutdown(); |     Kernel::Shutdown(); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue