diff options
author | Lioncash <mathew1800@gmail.com> | 2020-11-27 16:50:48 +0100 |
---|---|---|
committer | Lioncash <mathew1800@gmail.com> | 2020-11-27 17:40:45 +0100 |
commit | 5bc4eabe36b7ef4dcd5ad8db1e944705655be432 (patch) | |
tree | 285ffb3801aa773cb3537f77153a9e432390f6b8 | |
parent | Merge pull request #5023 from lioncash/save-global (diff) | |
download | yuzu-5bc4eabe36b7ef4dcd5ad8db1e944705655be432.tar yuzu-5bc4eabe36b7ef4dcd5ad8db1e944705655be432.tar.gz yuzu-5bc4eabe36b7ef4dcd5ad8db1e944705655be432.tar.bz2 yuzu-5bc4eabe36b7ef4dcd5ad8db1e944705655be432.tar.lz yuzu-5bc4eabe36b7ef4dcd5ad8db1e944705655be432.tar.xz yuzu-5bc4eabe36b7ef4dcd5ad8db1e944705655be432.tar.zst yuzu-5bc4eabe36b7ef4dcd5ad8db1e944705655be432.zip |
Diffstat (limited to '')
-rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic_64.cpp | 11 | ||||
-rw-r--r-- | src/core/core.cpp | 7 | ||||
-rw-r--r-- | src/core/cpu_manager.cpp | 1 | ||||
-rw-r--r-- | src/core/gdbstub/gdbstub.cpp | 1397 | ||||
-rw-r--r-- | src/core/gdbstub/gdbstub.h | 114 | ||||
-rw-r--r-- | src/core/loader/deconstructed_rom_directory.cpp | 3 | ||||
-rw-r--r-- | src/core/loader/kip.cpp | 3 | ||||
-rw-r--r-- | src/core/loader/nro.cpp | 6 | ||||
-rw-r--r-- | src/core/loader/nso.cpp | 5 | ||||
-rw-r--r-- | src/core/settings.cpp | 17 | ||||
-rw-r--r-- | src/core/settings.h | 8 | ||||
-rw-r--r-- | src/yuzu/configuration/config.cpp | 3 | ||||
-rw-r--r-- | src/yuzu/configuration/configure_dialog.cpp | 3 | ||||
-rw-r--r-- | src/yuzu/configuration/configure_per_game.cpp | 2 | ||||
-rw-r--r-- | src/yuzu/configuration/configure_profile_manager.cpp | 2 | ||||
-rw-r--r-- | src/yuzu/configuration/configure_system.cpp | 12 | ||||
-rw-r--r-- | src/yuzu/configuration/configure_ui.cpp | 3 | ||||
-rw-r--r-- | src/yuzu/main.cpp | 36 | ||||
-rw-r--r-- | src/yuzu_cmd/yuzu.cpp | 9 | ||||
-rw-r--r-- | src/yuzu_tester/yuzu.cpp | 7 |
21 files changed, 58 insertions, 1593 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index e370fd225..66de33799 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -135,8 +135,6 @@ add_library(core STATIC frontend/framebuffer_layout.cpp frontend/framebuffer_layout.h frontend/input.h - gdbstub/gdbstub.cpp - gdbstub/gdbstub.h hardware_interrupt_manager.cpp hardware_interrupt_manager.h hle/ipc.h diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 9f170a224..5c2060d78 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -14,7 +14,6 @@ #include "core/arm/dynarmic/arm_exclusive_monitor.h" #include "core/core.h" #include "core/core_timing.h" -#include "core/gdbstub/gdbstub.h" #include "core/hardware_properties.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/scheduler.h" @@ -96,16 +95,6 @@ public: case Dynarmic::A64::Exception::Yield: return; case Dynarmic::A64::Exception::Breakpoint: - if (GDBStub::IsServerEnabled()) { - parent.jit->HaltExecution(); - parent.SetPC(pc); - Kernel::Thread* const thread = parent.system.CurrentScheduler().GetCurrentThread(); - parent.SaveContext(thread->GetContext64()); - GDBStub::Break(); - GDBStub::SendTrap(thread, 5); - return; - } - [[fallthrough]]; default: ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", static_cast<std::size_t>(exception), pc, MemoryReadCode(pc)); diff --git a/src/core/core.cpp b/src/core/core.cpp index 7ca3652af..f4bbc9ec3 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -25,7 +25,6 @@ #include "core/file_sys/sdmc_factory.h" #include "core/file_sys/vfs_concat.h" #include "core/file_sys/vfs_real.h" -#include "core/gdbstub/gdbstub.h" #include "core/hardware_interrupt_manager.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/kernel.h" @@ -186,11 +185,8 @@ struct System::Impl { } service_manager = std::make_shared<Service::SM::ServiceManager>(kernel); - services = std::make_unique<Service::Services>(service_manager, system); - GDBStub::DeferStart(); - - interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system); + interrupt_manager = std::make_unique<Hardware::InterruptManager>(system); // Initialize time manager, which must happen after kernel is created time_manager.Initialize(); @@ -297,7 +293,6 @@ struct System::Impl { } // Shutdown emulation session - GDBStub::Shutdown(); services.reset(); service_manager.reset(); cheat_engine.reset(); diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 100e90d82..eeeb6e8df 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -10,7 +10,6 @@ #include "core/core.h" #include "core/core_timing.h" #include "core/cpu_manager.h" -#include "core/gdbstub/gdbstub.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/physical_core.h" #include "core/hle/kernel/scheduler.h" diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp deleted file mode 100644 index 97ee65464..000000000 --- a/src/core/gdbstub/gdbstub.cpp +++ /dev/null @@ -1,1397 +0,0 @@ -// 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 <algorithm> -#include <atomic> -#include <climits> -#include <csignal> -#include <cstdarg> -#include <cstdio> -#include <cstring> -#include <map> -#include <numeric> -#include <fcntl.h> - -#ifdef _WIN32 -#include <winsock2.h> -// winsock2.h needs to be included first to prevent winsock.h being included by other includes -#include <io.h> -#include <iphlpapi.h> -#include <ws2tcpip.h> -#define SHUT_RDWR 2 -#else -#include <netinet/in.h> -#include <sys/select.h> -#include <sys/socket.h> -#include <sys/un.h> -#include <unistd.h> -#endif - -#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/gdbstub/gdbstub.h" -#include "core/hle/kernel/memory/page_table.h" -#include "core/hle/kernel/process.h" -#include "core/hle/kernel/scheduler.h" -#include "core/loader/loader.h" -#include "core/memory.h" - -namespace GDBStub { -namespace { -constexpr int GDB_BUFFER_SIZE = 10000; - -constexpr char GDB_STUB_START = '$'; -constexpr char GDB_STUB_END = '#'; -constexpr char GDB_STUB_ACK = '+'; -constexpr char GDB_STUB_NACK = '-'; - -#ifndef SIGTRAP -constexpr u32 SIGTRAP = 5; -#endif - -#ifndef SIGTERM -constexpr u32 SIGTERM = 15; -#endif - -#ifndef MSG_WAITALL -constexpr u32 MSG_WAITALL = 8; -#endif - -constexpr u32 LR_REGISTER = 30; -constexpr u32 SP_REGISTER = 31; -constexpr u32 PC_REGISTER = 32; -constexpr u32 PSTATE_REGISTER = 33; -constexpr u32 UC_ARM64_REG_Q0 = 34; -constexpr u32 FPCR_REGISTER = 66; - -// For sample XML files see the GDB source /gdb/features -// GDB also wants the l character at the start -// This XML defines what the registers are for this specific ARM device -constexpr char target_xml[] = - R"(l<?xml version="1.0"?> -<!DOCTYPE target SYSTEM "gdb-target.dtd"> -<target version="1.0"> - <feature name="org.gnu.gdb.aarch64.core"> - <reg name="x0" bitsize="64"/> - <reg name="x1" bitsize="64"/> - <reg name="x2" bitsize="64"/> - <reg name="x3" bitsize="64"/> - <reg name="x4" bitsize="64"/> - <reg name="x5" bitsize="64"/> - <reg name="x6" bitsize="64"/> - <reg name="x7" bitsize="64"/> - <reg name="x8" bitsize="64"/> - <reg name="x9" bitsize="64"/> - <reg name="x10" bitsize="64"/> - <reg name="x11" bitsize="64"/> - <reg name="x12" bitsize="64"/> - <reg name="x13" bitsize="64"/> - <reg name="x14" bitsize="64"/> - <reg name="x15" bitsize="64"/> - <reg name="x16" bitsize="64"/> - <reg name="x17" bitsize="64"/> - <reg name="x18" bitsize="64"/> - <reg name="x19" bitsize="64"/> - <reg name="x20" bitsize="64"/> - <reg name="x21" bitsize="64"/> - <reg name="x22" bitsize="64"/> - <reg name="x23" bitsize="64"/> - <reg name="x24" bitsize="64"/> - <reg name="x25" bitsize="64"/> - <reg name="x26" bitsize="64"/> - <reg name="x27" bitsize="64"/> - <reg name="x28" bitsize="64"/> - <reg name="x29" bitsize="64"/> - <reg name="x30" bitsize="64"/> - <reg name="sp" bitsize="64" type="data_ptr"/> - - <reg name="pc" bitsize="64" type="code_ptr"/> - - <flags id="pstate_flags" size="4"> - <field name="SP" start="0" end="0"/> - <field name="" start="1" end="1"/> - <field name="EL" start="2" end="3"/> - <field name="nRW" start="4" end="4"/> - <field name="" start="5" end="5"/> - <field name="F" start="6" end="6"/> - <field name="I" start="7" end="7"/> - <field name="A" start="8" end="8"/> - <field name="D" start="9" end="9"/> - - <field name="IL" start="20" end="20"/> - <field name="SS" start="21" end="21"/> - - <field name="V" start="28" end="28"/> - <field name="C" start="29" end="29"/> - <field name="Z" start="30" end="30"/> - <field name="N" start="31" end="31"/> - </flags> - <reg name="pstate" bitsize="32" type="pstate_flags"/> - </feature> - <feature name="org.gnu.gdb.aarch64.fpu"> - </feature> -</target> -)"; - -int gdbserver_socket = -1; -bool defer_start = false; - -u8 command_buffer[GDB_BUFFER_SIZE]; -u32 command_length; - -u32 latest_signal = 0; -bool memory_break = false; - -Kernel::Thread* current_thread = nullptr; -u32 current_core = 0; - -// Binding to a port within the reserved ports range (0-1023) requires root permissions, -// so default to a port outside of that range. -u16 gdbstub_port = 24689; - -bool halt_loop = true; -bool step_loop = false; -bool send_trap = false; - -// If set to false, the server will never be started and no -// gdbstub-related functions will be executed. -std::atomic<bool> server_enabled(false); - -#ifdef _WIN32 -WSADATA InitData; -#endif - -struct Breakpoint { - bool active; - VAddr addr; - u64 len; - std::array<u8, 4> inst; -}; - -using BreakpointMap = std::map<VAddr, Breakpoint>; -BreakpointMap breakpoints_execute; -BreakpointMap breakpoints_read; -BreakpointMap breakpoints_write; - -struct Module { - std::string name; - VAddr beg; - VAddr end; -}; - -std::vector<Module> modules; -} // Anonymous namespace - -void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext) { - Module module; - if (add_elf_ext) { - Common::SplitPath(name, nullptr, &module.name, nullptr); - module.name += ".elf"; - } else { - module.name = std::move(name); - } - module.beg = beg; - module.end = end; - modules.push_back(std::move(module)); -} - -static Kernel::Thread* FindThreadById(s64 id) { - const auto& threads = Core::System::GetInstance().GlobalScheduler().GetThreadList(); - for (auto& thread : threads) { - if (thread->GetThreadID() == static_cast<u64>(id)) { - current_core = thread->GetProcessorID(); - return thread.get(); - } - } - return nullptr; -} - -static u64 RegRead(std::size_t id, Kernel::Thread* thread = nullptr) { - if (!thread) { - return 0; - } - - const auto& thread_context = thread->GetContext64(); - - 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 == PSTATE_REGISTER) { - return thread_context.pstate; - } else if (id > PSTATE_REGISTER && id < FPCR_REGISTER) { - return thread_context.vector_registers[id - UC_ARM64_REG_Q0][0]; - } else { - return 0; - } -} - -static void RegWrite(std::size_t id, u64 val, Kernel::Thread* thread = nullptr) { - if (!thread) { - return; - } - - auto& thread_context = thread->GetContext64(); - - 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 == PSTATE_REGISTER) { - thread_context.pstate = static_cast<u32>(val); - } else if (id > PSTATE_REGISTER && id < FPCR_REGISTER) { - thread_context.vector_registers[id - (PSTATE_REGISTER + 1)][0] = val; - } -} - -static u128 FpuRead(std::size_t id, Kernel::Thread* thread = nullptr) { - if (!thread) { - return u128{0}; - } - - auto& thread_context = thread->GetContext64(); - - if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) { - return thread_context.vector_registers[id - UC_ARM64_REG_Q0]; - } else if (id == FPCR_REGISTER) { - return u128{thread_context.fpcr, 0}; - } else { - return u128{0}; - } -} - -static void FpuWrite(std::size_t id, u128 val, Kernel::Thread* thread = nullptr) { - if (!thread) { - return; - } - - auto& thread_context = thread->GetContext64(); - - if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) { - thread_context.vector_registers[id - UC_ARM64_REG_Q0] = val; - } else if (id == FPCR_REGISTER) { - thread_context.fpcr = static_cast<u32>(val[0]); - } -} - -/** - * 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 static_cast<u8>(hex - '0'); - } else if (hex >= 'a' && hex <= 'f') { - return static_cast<u8>(hex - 'a' + 0xA); - } else if (hex >= 'A' && hex <= 'F') { - return static_cast<u8>(hex - 'A' + 0xA); - } - - LOG_ERROR(Debug_GDBStub, "Invalid nibble: {} ({:02X})", 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 static_cast<u8>('0' + n); - } else { - return static_cast<u8>('a' + n - 0xA); - } -} - -/** - * Converts input hex string characters into an array of equivalent of u8 bytes. - * - * @param src Pointer to array of output hex string characters. - * @param len Length of src array. - */ -static u32 HexToInt(const u8* src, std::size_t len) { - u32 output = 0; - while (len-- > 0) { - output = (output << 4) | HexCharToValue(src[0]); - src++; - } - return output; -} - -/** - * Converts input hex string characters into an array of equivalent of u8 bytes. - * - * @param src Pointer to array of output hex string characters. - * @param len Length of src array. - */ -static u64 HexToLong(const u8* src, std::size_t len) { - u64 output = 0; - while (len-- > 0) { - output = (output << 4) | HexCharToValue(src[0]); - src++; - } - return output; -} - -/** - * 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 MemToGdbHex(u8* dest, const u8* src, std::size_t len) { - while (len-- > 0) { - const u8 tmp = *src++; - *dest++ = NibbleToHex(static_cast<u8>(tmp >> 4)); - *dest++ = NibbleToHex(tmp); - } -} - -/** - * Converts input gdb-formatted 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 GdbHexToMem(u8* dest, const u8* src, std::size_t len) { - while (len-- > 0) { - *dest++ = static_cast<u8>((HexCharToValue(src[0]) << 4) | HexCharToValue(src[1])); - src += 2; - } -} - -/** - * Convert a u32 into a gdb-formatted hex string. - * - * @param dest Pointer to buffer to store output hex string characters. - * @param v Value to convert. - */ -static void IntToGdbHex(u8* dest, u32 v) { - for (int i = 0; i < 8; i += 2) { - dest[i + 1] = NibbleToHex(static_cast<u8>(v >> (4 * i))); - dest[i] = NibbleToHex(static_cast<u8>(v >> (4 * (i + 1)))); - } -} - -/** - * Convert a u64 into a gdb-formatted hex string. - * - * @param dest Pointer to buffer to store output hex string characters. - * @param v Value to convert. - */ -static void LongToGdbHex(u8* dest, u64 v) { - for (int i = 0; i < 16; i += 2) { - dest[i + 1] = NibbleToHex(static_cast<u8>(v >> (4 * i))); - dest[i] = NibbleToHex(static_cast<u8>(v >> (4 * (i + 1)))); - } -} - -/** - * Convert a gdb-formatted hex string into a u32. - * - * @param src Pointer to hex string. - */ -static u32 GdbHexToInt(const 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; -} - -/** - * Convert a gdb-formatted hex string into a u64. - * - * @param src Pointer to hex string. - */ -static u64 GdbHexToLong(const u8* src) { - u64 output = 0; - - for (int i = 0; i < 16; i += 2) { - output = (output << 4) | HexCharToValue(src[15 - i - 1]); - output = (output << 4) | HexCharToValue(src[15 - i]); - } - - return output; -} - -/** - * Convert a gdb-formatted hex string into a u128. - * - * @param src Pointer to hex string. - */ -static u128 GdbHexToU128(const u8* src) { - u128 output; - - for (int i = 0; i < 16; i += 2) { - output[0] = (output[0] << 4) | HexCharToValue(src[15 - i - 1]); - output[0] = (output[0] << 4) | HexCharToValue(src[15 - i]); - } - - for (int i = 0; i < 16; i += 2) { - output[1] = (output[1] << 4) | HexCharToValue(src[16 + 15 - i - 1]); - output[1] = (output[1] << 4) | HexCharToValue(src[16 + 15 - i]); - } - - return output; -} - -/// Read a byte from the gdb client. -static u8 ReadByte() { - u8 c; - std::size_t received_size = recv(gdbserver_socket, reinterpret_cast<char*>(&c), 1, MSG_WAITALL); - if (received_size != 1) { - LOG_ERROR(Debug_GDBStub, "recv failed: {}", received_size); - Shutdown(); - } - - return c; -} - -/// Calculate the checksum of the current command buffer. -static u8 CalculateChecksum(const u8* buffer, std::size_t length) { - return static_cast<u8>(std::accumulate(buffer, buffer + length, u8{0}, - [](u8 lhs, u8 rhs) { return u8(lhs + rhs); })); -} - -/** - * Get the map of breakpoints for a given breakpoint type. - * - * @param type Type of breakpoint map. - */ -static BreakpointMap& GetBreakpointMap(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, VAddr addr) { - BreakpointMap& p = GetBreakpointMap(type); - - const auto bp = p.find(addr); - if (bp == p.end()) { - return; - } - - LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: {:016X} bytes at {:016X} of type {}", - bp->second.len, bp->second.addr, static_cast<int>(type)); - - if (type == BreakpointType::Execute) { - auto& system = Core::System::GetInstance(); - system.Memory().WriteBlock(bp->second.addr, bp->second.inst.data(), bp->second.inst.size()); - system.InvalidateCpuInstructionCaches(); - } - p.erase(addr); -} - -BreakpointAddress GetNextBreakpointFromAddress(VAddr addr, BreakpointType type) { - const BreakpointMap& p = GetBreakpointMap(type); - const auto next_breakpoint = p.lower_bound(addr); - BreakpointAddress breakpoint; - - if (next_breakpoint != p.end()) { - breakpoint.address = next_breakpoint->first; - breakpoint.type = type; - } else { - breakpoint.address = 0; - breakpoint.type = BreakpointType::None; - } - - return breakpoint; -} - -bool CheckBreakpoint(VAddr addr, BreakpointType type) { - if (!IsConnected()) { - return false; - } - - const BreakpointMap& p = GetBreakpointMap(type); - const auto bp = p.find(addr); - - if (bp == p.end()) { - return false; - } - - u64 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 {} @ {:016X}, range: {:016X}" - " - {:016X} ({:X} bytes)", - static_cast<int>(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) { - std::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; - } - - LOG_DEBUG(Debug_GDBStub, "Reply: {}", reply); - - memset(command_buffer, 0, sizeof(command_buffer)); - - command_length = static_cast<u32>(strlen(reply)); - if (command_length + 4 > sizeof(command_buffer)) { - LOG_ERROR(Debug_GDBStub, "command_buffer overflow in SendReply"); - return; - } - - memcpy(command_buffer + 1, reply, command_length); - - const 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(static_cast<u8>(checksum >> 4)); - command_buffer[command_length + 3] = NibbleToHex(checksum); - - u8* ptr = command_buffer; - u32 left = command_length + 4; - while (left > 0) { - const auto sent_size = send(gdbserver_socket, reinterpret_cast<char*>(ptr), left, 0); - if (sent_size < 0) { - LOG_ERROR(Debug_GDBStub, "gdb: send failed"); - return Shutdown(); - } - - left -= static_cast<u32>(sent_size); - ptr += sent_size; - } -} - -/// Handle query command from gdb client. -static void HandleQuery() { - LOG_DEBUG(Debug_GDBStub, "gdb: query '{}'", command_buffer + 1); - - const char* query = reinterpret_cast<const char*>(command_buffer + 1); - - if (strcmp(query, "TStatus") == 0) { - SendReply("T0"); - } else if (strncmp(query, "Supported", strlen("Supported")) == 0) { - // PacketSize needs to be large enough for target xml - std::string buffer = "PacketSize=2000;qXfer:features:read+;qXfer:threads:read+"; - if (!modules.empty()) { - buffer += ";qXfer:libraries:read+"; - } - SendReply(buffer.c_str()); - } 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) { - const VAddr base_address = - Core::System::GetInstance().CurrentProcess()->PageTable().GetCodeRegionStart(); - std::string buffer = fmt::format("TextSeg={:0x}", base_address); - SendReply(buffer.c_str()); - } else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) { - std::string val = "m"; - const auto& threads = Core::System::GetInstance().GlobalScheduler().GetThreadList(); - for (const auto& thread : threads) { - val += fmt::format("{:x},", thread->GetThreadID()); - } - val.pop_back(); - SendReply(val.c_str()); - } else if (strncmp(query, "sThreadInfo", strlen("sThreadInfo")) == 0) { - SendReply("l"); - } else if (strncmp(query, "Xfer:threads:read", strlen("Xfer:threads:read")) == 0) { - std::string buffer; - buffer += "l<?xml version=\"1.0\"?>"; - buffer += "<threads>"; - const auto& threads = Core::System::GetInstance().GlobalScheduler().GetThreadList(); - for (const auto& thread : threads) { - buffer += - fmt::format(R"*(<thread id="{:x}" core="{:d}" name="Thread {:x}"></thread>)*", - thread->GetThreadID(), thread->GetProcessorID(), thread->GetThreadID()); - } - buffer += "</threads>"; - SendReply(buffer.c_str()); - } else if (strncmp(query, "Xfer:libraries:read", strlen("Xfer:libraries:read")) == 0) { - std::string buffer; - buffer += "l<?xml version=\"1.0\"?>"; - buffer += "<library-list>"; - for (const auto& module : modules) { - buffer += - fmt::format(R"*("<library name = "{}"><segment address = "0x{:x}"/></library>)*", - module.name, module.beg); - } - buffer += "</library-list>"; - SendReply(buffer.c_str()); - } else { - SendReply(""); - } -} - -/// Handle set thread command from gdb client. -static void HandleSetThread() { - int thread_id = -1; - if (command_buffer[2] != '-') { - thread_id = static_cast<int>(HexToInt(command_buffer + 2, command_length - 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)); - if (thread_id == 0) { - thread_id = 1; - } - if (FindThreadById(thread_id)) { - SendReply("OK"); - return; - } - SendReply("E01"); -} - -/** - * Send signal packet to client. - * - * @param signal Signal to be sent to client. - */ -static void SendSignal(Kernel::Thread* thread, u32 signal, bool full = true) { - if (gdbserver_socket == -1) { - return; - } - - latest_signal = signal; - - if (!thread) { - full = false; - } - - std::string buffer; - if (full) { - buffer = fmt::format("T{:02x}{:02x}:{:016x};{:02x}:{:016x};{:02x}:{:016x}", latest_signal, - PC_REGISTER, Common::swap64(RegRead(PC_REGISTER, thread)), SP_REGISTER, - Common::swap64(RegRead(SP_REGISTER, thread)), LR_REGISTER, - Common::swap64(RegRead(LR_REGISTER, thread))); - } else { - buffer = fmt::format("T{:02x}", latest_signal); - } - - if (thread) { - buffer += fmt::format(";thread:{:x};", thread->GetThreadID()); - } - - SendReply(buffer.c_str()); -} - -/// 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"); - halt_loop = true; - SendSignal(current_thread, SIGTRAP); - return; - } else if (c != GDB_STUB_START) { - LOG_DEBUG(Debug_GDBStub, "gdb: read invalid byte {:02X}", c); - return; - } - - while ((c = ReadByte()) != GDB_STUB_END) { - if (command_length >= sizeof(command_buffer)) { - LOG_ERROR(Debug_GDBStub, "gdb: command_buffer overflow"); - SendPacket(GDB_STUB_NACK); - return; - } - command_buffer[command_length++] = c; - } - - auto checksum_received = static_cast<u32>(HexCharToValue(ReadByte()) << 4); - checksum_received |= static_cast<u32>(HexCharToValue(ReadByte())); - - const u32 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 ${}# (length: {})", - 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(static_cast<u32>(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) != 0; -} - -/// 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 <= SP_REGISTER) { - LongToGdbHex(reply, RegRead(id, current_thread)); - } else if (id == PC_REGISTER) { - LongToGdbHex(reply, RegRead(id, current_thread)); - } else if (id == PSTATE_REGISTER) { - IntToGdbHex(reply, static_cast<u32>(RegRead(id, current_thread))); - } else if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) { - u128 r = FpuRead(id, current_thread); - LongToGdbHex(reply, r[0]); - LongToGdbHex(reply + 16, r[1]); - } else if (id == FPCR_REGISTER) { - u128 r = FpuRead(id, current_thread); - IntToGdbHex(reply, static_cast<u32>(r[0])); - } else if (id == FPCR_REGISTER + 1) { - u128 r = FpuRead(id, current_thread); - IntToGdbHex(reply, static_cast<u32>(r[0] >> 32)); - } - - 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 (u32 reg = 0; reg <= SP_REGISTER; reg++) { - LongToGdbHex(bufptr + reg * 16, RegRead(reg, current_thread)); - } - - bufptr += 32 * 16; - - LongToGdbHex(bufptr, RegRead(PC_REGISTER, current_thread)); - - bufptr += 16; - - IntToGdbHex(bufptr, static_cast<u32>(RegRead(PSTATE_REGISTER, current_thread))); - - bufptr += 8; - - u128 r; - - for (u32 reg = UC_ARM64_REG_Q0; reg < FPCR_REGISTER; reg++) { - r = FpuRead(reg, current_thread); - LongToGdbHex(bufptr + reg * 32, r[0]); - LongToGdbHex(bufptr + reg * 32 + 16, r[1]); - } - - bufptr += 32 * 32; - - r = FpuRead(FPCR_REGISTER, current_thread); - IntToGdbHex(bufptr, static_cast<u32>(r[0])); - - bufptr += 8; - - SendReply(reinterpret_cast<char*>(buffer)); -} - -/// Modify data of register specified by gdb client. -static void WriteRegister() { - const 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 <= SP_REGISTER) { - RegWrite(id, GdbHexToLong(buffer_ptr), current_thread); - } else if (id == PC_REGISTER) { - RegWrite(id, GdbHexToLong(buffer_ptr), current_thread); - } else if (id == PSTATE_REGISTER) { - RegWrite(id, GdbHexToInt(buffer_ptr), current_thread); - } else if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) { - FpuWrite(id, GdbHexToU128(buffer_ptr), current_thread); - } else if (id == FPCR_REGISTER) { - } else if (id == FPCR_REGISTER + 1) { - } - - // Update ARM context, skipping scheduler - no running threads at this point - Core::System::GetInstance() - .ArmInterface(current_core) - .LoadContext(current_thread->GetContext64()); - - SendReply("OK"); -} - -/// Modify all registers with data received from the client. -static void WriteRegisters() { - const u8* buffer_ptr = command_buffer + 1; - - if (command_buffer[0] != 'G') - return SendReply("E01"); - - for (u32 i = 0, reg = 0; reg <= FPCR_REGISTER; i++, reg++) { - if (reg <= SP_REGISTER) { - RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread); - } else if (reg == PC_REGISTER) { - RegWrite(PC_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread); - } else if (reg == PSTATE_REGISTER) { - RegWrite(PSTATE_REGISTER, GdbHexToInt(buffer_ptr + i * 16), current_thread); - } else if (reg >= UC_ARM64_REG_Q0 && reg < FPCR_REGISTER) { - RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread); - } else if (reg == FPCR_REGISTER) { - RegWrite(FPCR_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread); - } else if (reg == FPCR_REGISTER + 1) { - RegWrite(FPCR_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread); - } - } - - // Update ARM context, skipping scheduler - no running threads at this point - Core::System::GetInstance() - .ArmInterface(current_core) - .LoadContext(current_thread->GetContext64()); - - SendReply("OK"); -} - -/// Read location in memory specified by gdb client. -static void ReadMemory() { - static u8 reply[GDB_BUFFER_SIZE - 4]; - - auto start_offset = command_buffer + 1; - const auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); - const VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset)); - - start_offset = addr_pos + 1; - const u64 len = - HexToLong(start_offset, static_cast<u64>((command_buffer + command_length) - start_offset)); - - LOG_DEBUG(Debug_GDBStub, "gdb: addr: {:016X} len: {:016X}", addr, len); - - if (len * 2 > sizeof(reply)) { - SendReply("E01"); - } - - auto& memory = Core::System::GetInstance().Memory(); - if (!memory.IsValidVirtualAddress(addr)) { - return SendReply("E00"); - } - - std::vector<u8> data(len); - memory.ReadBlock(addr, data.data(), len); - - MemToGdbHex(reply, data.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() { - auto start_offset = command_buffer + 1; - const auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); - const VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset)); - - start_offset = addr_pos + 1; - const auto len_pos = std::find(start_offset, command_buffer + command_length, ':'); - const u64 len = HexToLong(start_offset, static_cast<u64>(len_pos - start_offset)); - - auto& system = Core::System::GetInstance(); - auto& memory = system.Memory(); - if (!memory.IsValidVirtualAddress(addr)) { - return SendReply("E00"); - } - - std::vector<u8> data(len); - GdbHexToMem(data.data(), len_pos + 1, len); - memory.WriteBlock(addr, data.data(), len); - system.InvalidateCpuInstructionCaches(); - SendReply("OK"); -} - -void Break(bool is_memory_break) { - send_trap = true; - - memory_break = is_memory_break; -} - -/// Tell the CPU that it should perform a single step. -static void Step() { - if (command_length > 1) { - RegWrite(PC_REGISTER, GdbHexToLong(command_buffer + 1), current_thread); - // Update ARM context, skipping scheduler - no running threads at this point - Core::System::GetInstance() - .ArmInterface(current_core) - .LoadContext(current_thread->GetContext64()); - } - step_loop = true; - halt_loop = true; - send_trap = true; - Core::System::GetInstance().InvalidateCpuInstructionCaches(); -} - -/// Tell the CPU if we hit a memory breakpoint. -bool IsMemoryBreak() { - if (!IsConnected()) { - return false; - } - - return memory_break; -} - -/// Tell the CPU to continue executing. -static void Continue() { - memory_break = false; - step_loop = false; - halt_loop = false; - Core::System::GetInstance().InvalidateCpuInstructionCaches(); -} - -/** - * Commit breakpoint to list of breakpoints. - * - * @param type Type of breakpoint. - * @param addr Address of breakpoint. - * @param len Length of breakpoint. - */ -static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) { - BreakpointMap& p = GetBreakpointMap(type); - - Breakpoint breakpoint; - breakpoint.active = true; - breakpoint.addr = addr; - breakpoint.len = len; - - auto& system = Core::System::GetInstance(); - auto& memory = system.Memory(); - memory.ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size()); - - static constexpr std::array<u8, 4> btrap{0x00, 0x7d, 0x20, 0xd4}; - if (type == BreakpointType::Execute) { - memory.WriteBlock(addr, btrap.data(), btrap.size()); - system.InvalidateCpuInstructionCaches(); - } - p.insert({addr, breakpoint}); - - LOG_DEBUG(Debug_GDBStub, "gdb: added {} breakpoint: {:016X} bytes at {:016X}", - static_cast<int>(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"); - } - - auto start_offset = command_buffer + 3; - auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); - VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset)); - - start_offset = addr_pos + 1; - u64 len = - HexToLong(start_offset, static_cast<u64>((command_buffer + command_length) - start_offset)); - - 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"); - } - - auto start_offset = command_buffer + 3; - auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); - VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset)); - - 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()) { - if (defer_start) { - ToggleServer(true); - } - return; - } - - if (!IsDataAvailable()) { - return; - } - - ReadCommand(); - if (command_length == 0) { - return; - } - - LOG_DEBUG(Debug_GDBStub, "Packet: {}", command_buffer); - - switch (command_buffer[0]) { - case 'q': - HandleQuery(); - break; - case 'H': - HandleSetThread(); - break; - case '?': - SendSignal(current_thread, latest_signal); - break; - case 'k': - Shutdown(); - 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; - case 'T': - HandleThreadAlive(); - break; - default: - SendReply(""); - break; - } -} - -void SetServerPort(u16 port) { - gdbstub_port = port; -} - -void ToggleServer(bool status) { - if (status) { - server_enabled = status; - - // Start server - if (!IsConnected() && Core::System::GetInstance().IsPoweredOn()) { - Init(); - } - } else { - // Stop server - if (IsConnected()) { - Shutdown(); - } - - server_enabled = status; - } -} - -void DeferStart() { - defer_start = true; -} - -static void Init(u16 port) { - if (!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(); - - modules.clear(); - - // Start gdb server - LOG_INFO(Debug_GDBStub, "Starting GDB server on port {}...", 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 = static_cast<int>(socket(PF_INET, SOCK_STREAM, 0)); - if (tmpsock == -1) { - LOG_ERROR(Debug_GDBStub, "Failed to create gdb socket"); - } - - // Set socket to SO_REUSEADDR so it can always bind on the same port - int reuse_enabled = 1; - if (setsockopt(tmpsock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse_enabled, - sizeof(reuse_enabled)) < 0) { - LOG_ERROR(Debug_GDBStub, "Failed to set gdb socket option"); - } - - 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..."); - sockaddr_in saddr_client; - sockaddr* client_addr = reinterpret_cast<sockaddr*>(&saddr_client); - socklen_t client_addrlen = sizeof(saddr_client); - gdbserver_socket = static_cast<int>(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."); - 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 Shutdown() { - if (!server_enabled) { - return; - } - defer_start = false; - - 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 IsServerEnabled() { - return server_enabled; -} - -bool IsConnected() { - return IsServerEnabled() && gdbserver_socket != -1; -} - -bool GetCpuHaltFlag() { - return halt_loop; -} - -bool GetCpuStepFlag() { - return step_loop; -} - -void SetCpuStepFlag(bool is_step) { - step_loop = is_step; -} - -void SendTrap(Kernel::Thread* thread, int trap) { - if (!send_trap) { - return; - } - - current_thread = thread; - SendSignal(thread, trap); - - halt_loop = true; - send_trap = false; -} -}; // namespace GDBStub diff --git a/src/core/gdbstub/gdbstub.h b/src/core/gdbstub/gdbstub.h deleted file mode 100644 index 8fe3c320b..000000000 --- a/src/core/gdbstub/gdbstub.h +++ /dev/null @@ -1,114 +0,0 @@ -// 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 <string> -#include "common/common_types.h" -#include "core/hle/kernel/thread.h" - -namespace GDBStub { - -/// Breakpoint Method -enum class BreakpointType { - None, ///< None - Execute, ///< Execution Breakpoint - Read, ///< Read Breakpoint - Write, ///< Write Breakpoint - Access ///< Access (R/W) Breakpoint -}; - -struct BreakpointAddress { - VAddr address; - BreakpointType type; -}; - -/** - * Set the port the gdbstub should use to listen for connections. - * - * @param port Port to listen for connection - */ -void SetServerPort(u16 port); - -/** - * Starts or stops the server if possible. - * - * @param status Set the server to enabled or disabled. - */ -void ToggleServer(bool status); - -/// Start the gdbstub server. -void Init(); - -/** - * Defer initialization of the gdbstub to the first packet processing functions. - * This avoids a case where the gdbstub thread is frozen after initialization - * and fails to respond in time to packets. - */ -void DeferStart(); - -/// Stop gdbstub server. -void Shutdown(); - -/// Checks if the gdbstub server is enabled. -bool IsServerEnabled(); - -/// Returns true if there is an active socket connection. -bool IsConnected(); - -/// Register module. -void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext = true); - -/** - * 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. - */ -BreakpointAddress GetNextBreakpointFromAddress(VAddr 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(VAddr 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); - -/** - * 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 diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 2002dc4f2..79ebf11de 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -12,7 +12,6 @@ #include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/romfs_factory.h" -#include "core/gdbstub/gdbstub.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/memory/page_table.h" #include "core/hle/kernel/process.h" @@ -180,8 +179,6 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect next_load_addr = *tentative_next_load_addr; modules.insert_or_assign(load_addr, module); LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); - // Register module with GDBStub - GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false); } // Find the RomFS by searching for a ".romfs" file in this directory diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp index 2a905d3e4..e162c4ff0 100644 --- a/src/core/loader/kip.cpp +++ b/src/core/loader/kip.cpp @@ -5,7 +5,6 @@ #include <cstring> #include "core/file_sys/kernel_executable.h" #include "core/file_sys/program_metadata.h" -#include "core/gdbstub/gdbstub.h" #include "core/hle/kernel/code_set.h" #include "core/hle/kernel/memory/page_table.h" #include "core/hle/kernel/process.h" @@ -91,8 +90,6 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::Process& process, program_image.resize(PageAlignSize(kip->GetBSSOffset()) + kip->GetBSSSize()); codeset.DataSegment().size += kip->GetBSSSize(); - GDBStub::RegisterModule(kip->GetName(), base_address, base_address + program_image.size()); - codeset.memory = std::move(program_image); process.LoadModule(std::move(codeset), base_address); diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 5f4b3104b..ccf8cc153 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -14,10 +14,10 @@ #include "core/file_sys/control_metadata.h" #include "core/file_sys/romfs_factory.h" #include "core/file_sys/vfs_offset.h" -#include "core/gdbstub/gdbstub.h" #include "core/hle/kernel/code_set.h" #include "core/hle/kernel/memory/page_table.h" #include "core/hle/kernel/process.h" +#include "core/hle/kernel/thread.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/loader/nro.h" #include "core/loader/nso.h" @@ -197,10 +197,6 @@ static bool LoadNroImpl(Kernel::Process& process, const std::vector<u8>& data, codeset.memory = std::move(program_image); process.LoadModule(std::move(codeset), process.PageTable().GetCodeRegionStart()); - // Register module with GDBStub - GDBStub::RegisterModule(name, process.PageTable().GetCodeRegionStart(), - process.PageTable().GetCodeRegionEnd()); - return true; } diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index aa85c1a29..95b6f339a 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -14,10 +14,10 @@ #include "common/swap.h" #include "core/core.h" #include "core/file_sys/patch_manager.h" -#include "core/gdbstub/gdbstub.h" #include "core/hle/kernel/code_set.h" #include "core/hle/kernel/memory/page_table.h" #include "core/hle/kernel/process.h" +#include "core/hle/kernel/thread.h" #include "core/loader/nso.h" #include "core/memory.h" #include "core/settings.h" @@ -159,9 +159,6 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, Core::S codeset.memory = std::move(program_image); process.LoadModule(std::move(codeset), load_base); - // Register module with GDBStub - GDBStub::RegisterModule(file.GetName(), load_base, load_base); - return load_base + image_size; } diff --git a/src/core/settings.cpp b/src/core/settings.cpp index aadbc3932..e9997a263 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -4,9 +4,10 @@ #include <string_view> +#include "common/assert.h" #include "common/file_util.h" +#include "common/logging/log.h" #include "core/core.h" -#include "core/gdbstub/gdbstub.h" #include "core/hle/service/hid/hid.h" #include "core/settings.h" #include "video_core/renderer_base.h" @@ -31,13 +32,9 @@ std::string GetTimeZoneString() { return timezones[time_zone_index]; } -void Apply() { - GDBStub::SetServerPort(values.gdbstub_port); - GDBStub::ToggleServer(values.use_gdbstub); - - auto& system_instance = Core::System::GetInstance(); - if (system_instance.IsPoweredOn()) { - system_instance.Renderer().RefreshBaseSettings(); +void Apply(Core::System& system) { + if (system.IsPoweredOn()) { + system.Renderer().RefreshBaseSettings(); } Service::HID::ReloadInputDevices(); @@ -106,9 +103,9 @@ float Volume() { return values.volume.GetValue(); } -void RestoreGlobalState() { +void RestoreGlobalState(bool is_powered_on) { // If a game is running, DO NOT restore the global settings state - if (Core::System::GetInstance().IsPoweredOn()) { + if (is_powered_on) { return; } diff --git a/src/core/settings.h b/src/core/settings.h index 1143aba5d..3df611d5b 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -14,6 +14,10 @@ #include "common/common_types.h" #include "input_common/settings.h" +namespace Core { +class System; +} + namespace Settings { enum class RendererBackend { @@ -247,11 +251,11 @@ float Volume(); std::string GetTimeZoneString(); -void Apply(); +void Apply(Core::System& system); void LogSettings(); // Restore the global state of all applicable settings in the Values struct -void RestoreGlobalState(); +void RestoreGlobalState(bool is_powered_on); // Fixes settings that are known to cause issues with the emulator void Sanitize(); diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 3c423a271..8be9e93c3 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -7,6 +7,7 @@ #include <QSettings> #include "common/common_paths.h" #include "common/file_util.h" +#include "core/core.h" #include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/hid/controllers/npad.h" #include "input_common/main.h" @@ -1598,7 +1599,7 @@ void Config::Reload() { Settings::Sanitize(); // To apply default value changes SaveValues(); - Settings::Apply(); + Settings::Apply(Core::System::GetInstance()); } void Config::Save() { diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index 5041e0bf8..b33f8437a 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp @@ -5,6 +5,7 @@ #include <QHash> #include <QListWidgetItem> #include <QSignalBlocker> +#include "core/core.h" #include "core/settings.h" #include "ui_configure.h" #include "yuzu/configuration/config.h" @@ -54,7 +55,7 @@ void ConfigureDialog::ApplyConfiguration() { ui->debugTab->ApplyConfiguration(); ui->webTab->ApplyConfiguration(); ui->serviceTab->ApplyConfiguration(); - Settings::Apply(); + Settings::Apply(Core::System::GetInstance()); Settings::LogSettings(); } diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp index 8eac3bd9d..f598513df 100644 --- a/src/yuzu/configuration/configure_per_game.cpp +++ b/src/yuzu/configuration/configure_per_game.cpp @@ -57,7 +57,7 @@ void ConfigurePerGame::ApplyConfiguration() { ui->graphicsAdvancedTab->ApplyConfiguration(); ui->audioTab->ApplyConfiguration(); - Settings::Apply(); + Settings::Apply(Core::System::GetInstance()); Settings::LogSettings(); game_config->Save(); diff --git a/src/yuzu/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp index 6334c4c50..13d9a4757 100644 --- a/src/yuzu/configuration/configure_profile_manager.cpp +++ b/src/yuzu/configuration/configure_profile_manager.cpp @@ -180,7 +180,7 @@ void ConfigureProfileManager::ApplyConfiguration() { return; } - Settings::Apply(); + Settings::Apply(Core::System::GetInstance()); } void ConfigureProfileManager::SelectUser(const QModelIndex& index) { diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index 59a58d92c..6cf2032da 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp @@ -105,16 +105,18 @@ void ConfigureSystem::SetConfiguration() { void ConfigureSystem::ReadSystemSettings() {} void ConfigureSystem::ApplyConfiguration() { - // Allow setting custom RTC even if system is powered on, to allow in-game time to be fast - // forwared + auto& system = Core::System::GetInstance(); + + // Allow setting custom RTC even if system is powered on, + // to allow in-game time to be fast forwarded if (Settings::values.custom_rtc.UsingGlobal()) { if (ui->custom_rtc_checkbox->isChecked()) { Settings::values.custom_rtc.SetValue( std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch())); - if (Core::System::GetInstance().IsPoweredOn()) { + if (system.IsPoweredOn()) { const s64 posix_time{Settings::values.custom_rtc.GetValue()->count() + Service::Time::TimeManager::GetExternalTimeZoneOffset()}; - Core::System::GetInstance().GetTimeManager().UpdateLocalSystemClockTime(posix_time); + system.GetTimeManager().UpdateLocalSystemClockTime(posix_time); } } else { Settings::values.custom_rtc.SetValue(std::nullopt); @@ -197,7 +199,7 @@ void ConfigureSystem::ApplyConfiguration() { } } - Settings::Apply(); + Settings::Apply(system); } void ConfigureSystem::RefreshConsoleID() { diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp index dbe3f78c8..aed876008 100644 --- a/src/yuzu/configuration/configure_ui.cpp +++ b/src/yuzu/configuration/configure_ui.cpp @@ -9,6 +9,7 @@ #include <QDirIterator> #include "common/common_types.h" #include "common/file_util.h" +#include "core/core.h" #include "core/settings.h" #include "ui_configure_ui.h" #include "yuzu/configuration/configure_ui.h" @@ -84,7 +85,7 @@ void ConfigureUi::ApplyConfiguration() { UISettings::values.enable_screenshot_save_as = ui->enable_screenshot_save_as->isChecked(); Common::FS::GetUserPath(Common::FS::UserPath::ScreenshotsDir, ui->screenshot_path_edit->text().toStdString()); - Settings::Apply(); + Settings::Apply(Core::System::GetInstance()); } void ConfigureUi::RequestGameListUpdate() { diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 07fa85741..871ff4ae4 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -172,7 +172,7 @@ void GMainWindow::ShowTelemetryCallout() { "<br/><br/>Would you like to share your usage data with us?"); if (QMessageBox::question(this, tr("Telemetry"), telemetry_message) != QMessageBox::Yes) { Settings::values.enable_telemetry = false; - Settings::Apply(); + Settings::Apply(Core::System::GetInstance()); } } @@ -302,7 +302,7 @@ void GMainWindow::ControllerSelectorReconfigureControllers( emit ControllerSelectorReconfigureFinished(); // Don't forget to apply settings. - Settings::Apply(); + Settings::Apply(Core::System::GetInstance()); config->Save(); UpdateStatusButtons(); @@ -571,11 +571,11 @@ void GMainWindow::InitializeWidgets() { if (emulation_running) { return; } - bool is_async = !Settings::values.use_asynchronous_gpu_emulation.GetValue() || - Settings::values.use_multi_core.GetValue(); + const bool is_async = !Settings::values.use_asynchronous_gpu_emulation.GetValue() || + Settings::values.use_multi_core.GetValue(); Settings::values.use_asynchronous_gpu_emulation.SetValue(is_async); async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue()); - Settings::Apply(); + Settings::Apply(Core::System::GetInstance()); }); async_status_button->setText(tr("ASYNC")); async_status_button->setCheckable(true); @@ -590,12 +590,12 @@ void GMainWindow::InitializeWidgets() { return; } Settings::values.use_multi_core.SetValue(!Settings::values.use_multi_core.GetValue()); - bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue() || - Settings::values.use_multi_core.GetValue(); + const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue() || + Settings::values.use_multi_core.GetValue(); Settings::values.use_asynchronous_gpu_emulation.SetValue(is_async); async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue()); multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue()); - Settings::Apply(); + Settings::Apply(Core::System::GetInstance()); }); multicore_status_button->setText(tr("MULTICORE")); multicore_status_button->setCheckable(true); @@ -630,7 +630,7 @@ void GMainWindow::InitializeWidgets() { Settings::values.renderer_backend.SetValue(Settings::RendererBackend::OpenGL); } - Settings::Apply(); + Settings::Apply(Core::System::GetInstance()); }); #endif // HAS_VULKAN statusBar()->insertPermanentWidget(0, renderer_status_button); @@ -2130,14 +2130,14 @@ void GMainWindow::OnPauseGame() { } void GMainWindow::OnStopGame() { - Core::System& system{Core::System::GetInstance()}; + auto& system{Core::System::GetInstance()}; if (system.GetExitLock() && !ConfirmForceLockedExit()) { return; } ShutdownGame(); - Settings::RestoreGlobalState(); + Settings::RestoreGlobalState(system.IsPoweredOn()); UpdateStatusButtons(); } @@ -2312,10 +2312,11 @@ void GMainWindow::OnConfigurePerGame() { void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file_name) { const auto v_file = Core::GetGameFileFromPath(vfs, file_name); + const auto& system = Core::System::GetInstance(); ConfigurePerGame dialog(this, title_id); dialog.LoadFromFile(v_file); - auto result = dialog.exec(); + const auto result = dialog.exec(); if (result == QDialog::Accepted) { dialog.ApplyConfiguration(); @@ -2325,13 +2326,14 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file } // Do not cause the global config to write local settings into the config file - Settings::RestoreGlobalState(); + const bool is_powered_on = system.IsPoweredOn(); + Settings::RestoreGlobalState(is_powered_on); - if (!Core::System::GetInstance().IsPoweredOn()) { + if (!is_powered_on) { config->Save(); } } else { - Settings::RestoreGlobalState(); + Settings::RestoreGlobalState(system.IsPoweredOn()); } } @@ -2602,7 +2604,7 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det if (emu_thread) { ShutdownGame(); - Settings::RestoreGlobalState(); + Settings::RestoreGlobalState(Core::System::GetInstance().IsPoweredOn()); UpdateStatusButtons(); } } else { @@ -2774,7 +2776,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) { if (emu_thread != nullptr) { ShutdownGame(); - Settings::RestoreGlobalState(); + Settings::RestoreGlobalState(Core::System::GetInstance().IsPoweredOn()); UpdateStatusButtons(); } diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index ba6e89249..c2efe1ee6 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -25,7 +25,6 @@ #include "core/crypto/key_manager.h" #include "core/file_sys/registered_cache.h" #include "core/file_sys/vfs_real.h" -#include "core/gdbstub/gdbstub.h" #include "core/hle/kernel/process.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/loader/loader.h" @@ -174,13 +173,13 @@ int main(int argc, char** argv) { return -1; } + auto& system{Core::System::GetInstance()}; + InputCommon::InputSubsystem input_subsystem; + // Apply the command line arguments Settings::values.gdbstub_port = gdb_port; Settings::values.use_gdbstub = use_gdbstub; - Settings::Apply(); - - Core::System& system{Core::System::GetInstance()}; - InputCommon::InputSubsystem input_subsystem; + Settings::Apply(system); std::unique_ptr<EmuWindow_SDL2> emu_window; switch (Settings::values.renderer_backend.GetValue()) { diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp index ea94a6537..50bd7ae41 100644 --- a/src/yuzu_tester/yuzu.cpp +++ b/src/yuzu_tester/yuzu.cpp @@ -160,10 +160,12 @@ int main(int argc, char** argv) { return -1; } + Core::System& system{Core::System::GetInstance()}; + Settings::values.use_gdbstub = false; - Settings::Apply(); + Settings::Apply(system); - std::unique_ptr<EmuWindow_SDL2_Hide> emu_window{std::make_unique<EmuWindow_SDL2_Hide>()}; + const auto emu_window{std::make_unique<EmuWindow_SDL2_Hide>()}; bool finished = false; int return_value = 0; @@ -212,7 +214,6 @@ int main(int argc, char** argv) { return_value = -1; }; - Core::System& system{Core::System::GetInstance()}; system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); system.GetFileSystemController().CreateFactories(*system.GetFilesystem()); |