diff --git a/stratosphere/dmnt.gen2/source/dmnt2_debug_process.cpp b/stratosphere/dmnt.gen2/source/dmnt2_debug_process.cpp index 813a13e43..01ef99bef 100644 --- a/stratosphere/dmnt.gen2/source/dmnt2_debug_process.cpp +++ b/stratosphere/dmnt.gen2/source/dmnt2_debug_process.cpp @@ -194,6 +194,16 @@ namespace ams::dmnt { /* Truncate module name. */ module_name[ModuleDefinition::PathLengthMax - 1] = 0; + + /* Set default module name start. */ + module.SetNameStart(0); + + /* Ignore leading directories. */ + for (size_t i = 0; i < static_cast(module_path.path_length); ++i) { + if (module_name[i] == '/' || module_name[i] == '\\') { + module.SetNameStart(i + 1); + } + } } } @@ -228,6 +238,11 @@ namespace ams::dmnt { return svc::WriteDebugProcessMemory(m_debug_handle, reinterpret_cast(src), address, size); } + Result DebugProcess::QueryMemory(svc::MemoryInfo *out, uintptr_t address) { + svc::PageInfo dummy; + return svc::QueryDebugProcessMemory(out, std::addressof(dummy), m_debug_handle, address); + } + Result DebugProcess::Continue() { AMS_DMNT2_GDB_LOG_DEBUG("DebugProcess::Continue() all\n"); diff --git a/stratosphere/dmnt.gen2/source/dmnt2_debug_process.hpp b/stratosphere/dmnt.gen2/source/dmnt2_debug_process.hpp index d991420b0..f0c027e6f 100644 --- a/stratosphere/dmnt.gen2/source/dmnt2_debug_process.hpp +++ b/stratosphere/dmnt.gen2/source/dmnt2_debug_process.hpp @@ -117,6 +117,8 @@ namespace ams::dmnt { Result ReadMemory(void *dst, uintptr_t address, size_t size); Result WriteMemory(const void *src, uintptr_t address, size_t size); + Result QueryMemory(svc::MemoryInfo *out, uintptr_t address); + Result Continue(); Result Continue(u64 thread_id); Result Step(); diff --git a/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.cpp b/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.cpp index 7030178d0..d88c9a6e1 100644 --- a/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.cpp +++ b/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.cpp @@ -772,6 +772,7 @@ namespace ams::dmnt { AnnexBufferContents_Processes, AnnexBufferContents_Threads, AnnexBufferContents_Libraries, + AnnexBufferContents_MemoryMap, }; constinit AnnexBufferContents g_annex_buffer_contents = AnnexBufferContents_Invalid; @@ -1856,6 +1857,7 @@ namespace ams::dmnt { AppendReply(m_reply_packet, ";augmented-libraries-svr4-read+"); AppendReply(m_reply_packet, ";qXfer:threads:read+"); AppendReply(m_reply_packet, ";qXfer:exec-file:read+"); + AppendReply(m_reply_packet, ";qXfer:memory-map:read+"); AppendReply(m_reply_packet, ";swbreak+"); AppendReply(m_reply_packet, ";hwbreak+"); AppendReply(m_reply_packet, ";vContSupported+"); @@ -1882,6 +1884,8 @@ namespace ams::dmnt { } } else if (ParsePrefix(m_receive_packet, "libraries:read::")) { this->qXferLibrariesRead(); + } else if (ParsePrefix(m_receive_packet, "memory-map:read::")) { + this->qXferMemoryMapRead(); } else if (ParsePrefix(m_receive_packet, "exec-file:read:")) { SetReply(m_reply_packet, "l%s", m_debug_process.GetProcessName()); } else { @@ -1962,7 +1966,7 @@ namespace ams::dmnt { const char *module_name = m_debug_process.GetModuleName(i); const auto name_len = std::strlen(module_name); - if (name_len > 4 && std::strcmp(module_name + name_len - 4, ".elf") != 0 && std::strcmp(module_name + name_len - 4, ".nss") != 0) { + if (name_len < 5 || (std::strcmp(module_name + name_len - 4, ".elf") != 0 && std::strcmp(module_name + name_len - 4, ".nss") != 0)) { AppendReply(g_annex_buffer, "", module_name, m_debug_process.GetBaseAddress(i)); } else { AppendReply(g_annex_buffer, "", module_name, m_debug_process.GetBaseAddress(i)); @@ -1978,6 +1982,73 @@ namespace ams::dmnt { GetAnnexBufferContents(m_reply_packet, offset, length); } + void GdbServerImpl::qXferMemoryMapRead() { + /* Handle the qXfer. */ + u32 offset, length; + + /* Parse offset/length. */ + ParseOffsetLength(m_receive_packet, offset, length); + + /* Acquire access to the annex buffer. */ + std::scoped_lock lk(g_annex_buffer_lock); + + /* If doing a fresh read, generate the module list. */ + if (offset == 0 || g_annex_buffer_contents != AnnexBufferContents_MemoryMap) { + /* Set header. */ + SetReply(g_annex_buffer, "\n"); + + /* Iterate over all mappings. */ + uintptr_t cur_addr = 0; + svc::MemoryInfo prev_info = {}; + size_t prev_size = 0; + while (true) { + /* Get current mapping. */ + svc::MemoryInfo mem_info; + if (R_FAILED(m_debug_process.QueryMemory(std::addressof(mem_info), cur_addr))) { + break; + } + + /* If the mapping is present, add it. */ + if (mem_info.state != svc::MemoryState_Free && mem_info.state != svc::MemoryState_Inaccessible && mem_info.permission != svc::MemoryPermission_None) { + if (prev_size != 0 && mem_info.state == prev_info.state && mem_info.permission == prev_info.permission && mem_info.attribute == prev_info.attribute && mem_info.base_address == prev_info.base_address + prev_size) { + prev_size += mem_info.size; + } else { + if (prev_size != 0) { + AppendReply(g_annex_buffer, "", prev_info.base_address, prev_size); + } + + prev_info = mem_info; + prev_size = mem_info.size; + } + } else { + if (prev_size != 0) { + AppendReply(g_annex_buffer, "", prev_info.base_address, prev_size); + + prev_size = 0; + } + } + + const uintptr_t next_address = mem_info.base_address + mem_info.size; + if (next_address <= cur_addr) { + break; + } + + cur_addr = next_address; + } + + if (prev_size != 0) { + AppendReply(g_annex_buffer, "", prev_info.base_address, prev_size); + } + + AppendReply(g_annex_buffer, ""); + + g_annex_buffer_contents = AnnexBufferContents_MemoryMap; + } + + /* Copy out the module list. */ + GetAnnexBufferContents(m_reply_packet, offset, length); + } + void GdbServerImpl::qXferOsdataRead() { /* Handle the qXfer. */ u32 offset, length; diff --git a/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.hpp b/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.hpp index 0e5de4770..ef467a7d1 100644 --- a/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.hpp +++ b/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.hpp @@ -102,6 +102,7 @@ namespace ams::dmnt { void qXfer(); void qXferFeaturesRead(); void qXferLibrariesRead(); + void qXferMemoryMapRead(); void qXferOsdataRead(); bool qXferThreadsRead(); diff --git a/stratosphere/dmnt.gen2/source/dmnt2_module_definition.hpp b/stratosphere/dmnt.gen2/source/dmnt2_module_definition.hpp index 43bc1afcc..91324a596 100644 --- a/stratosphere/dmnt.gen2/source/dmnt2_module_definition.hpp +++ b/stratosphere/dmnt.gen2/source/dmnt2_module_definition.hpp @@ -26,6 +26,7 @@ namespace ams::dmnt { char m_name[PathLengthMax]; u64 m_address; u64 m_size; + size_t m_name_start; public: constexpr ModuleDefinition() : m_name(), m_address(), m_size() { /* ... */ } constexpr ~ModuleDefinition() { /* ... */ } @@ -34,9 +35,10 @@ namespace ams::dmnt { constexpr ModuleDefinition &operator=(const ModuleDefinition &rhs) = default; constexpr void Reset() { - m_name[0] = 0; - m_address = 0; - m_size = 0; + m_name[0] = 0; + m_address = 0; + m_size = 0; + m_name_start = 0; } constexpr bool operator==(const ModuleDefinition &rhs) const { @@ -48,7 +50,7 @@ namespace ams::dmnt { } constexpr char *GetNameBuffer() { return m_name; } - constexpr const char *GetName() const { return m_name; } + constexpr const char *GetName() const { return m_name + m_name_start; } constexpr u64 GetAddress() const { return m_address; } constexpr u64 GetSize() const { return m_size; } @@ -57,6 +59,10 @@ namespace ams::dmnt { m_address = address; m_size = size; } + + constexpr void SetNameStart(size_t offset) { + m_name_start = offset; + } }; } \ No newline at end of file