mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-11-15 01:26:34 +00:00
dmnt: refactor to use process accessor
This commit is contained in:
parent
cf27c8a7a2
commit
eb6d18329e
7 changed files with 785 additions and 205 deletions
312
stratosphere/dmnt.gen2/source/dmnt2_debug_process.cpp
Normal file
312
stratosphere/dmnt.gen2/source/dmnt2_debug_process.cpp
Normal file
|
@ -0,0 +1,312 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "dmnt2_debug_log.hpp"
|
||||||
|
#include "dmnt2_debug_process.hpp"
|
||||||
|
|
||||||
|
namespace ams::dmnt {
|
||||||
|
|
||||||
|
Result DebugProcess::Attach(os::ProcessId process_id) {
|
||||||
|
/* Attach to the process. */
|
||||||
|
R_TRY(svc::DebugActiveProcess(std::addressof(m_debug_handle), process_id.value));
|
||||||
|
|
||||||
|
/* Collect initial information. */
|
||||||
|
R_TRY(this->Start());
|
||||||
|
|
||||||
|
/* Get the attached modules. */
|
||||||
|
R_TRY(this->CollectModules());
|
||||||
|
|
||||||
|
/* Get our process id. */
|
||||||
|
u64 pid_value = 0;
|
||||||
|
svc::GetProcessId(std::addressof(pid_value), m_debug_handle);
|
||||||
|
|
||||||
|
m_process_id = { pid_value };
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugProcess::Detach() {
|
||||||
|
if (m_is_valid) {
|
||||||
|
R_ABORT_UNLESS(svc::CloseHandle(m_debug_handle));
|
||||||
|
m_debug_handle = svc::InvalidHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_is_valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DebugProcess::Start() {
|
||||||
|
/* Process the initial debug events. */
|
||||||
|
s32 num_threads = 0;
|
||||||
|
bool attached = false;
|
||||||
|
while (num_threads == 0 || !attached) {
|
||||||
|
/* Wait for debug events to be available. */
|
||||||
|
s32 dummy_index;
|
||||||
|
R_ABORT_UNLESS(svc::WaitSynchronization(std::addressof(dummy_index), std::addressof(m_debug_handle), 1, svc::WaitInfinite));
|
||||||
|
|
||||||
|
/* Get debug event. */
|
||||||
|
svc::DebugEventInfo d;
|
||||||
|
R_ABORT_UNLESS(svc::GetDebugEvent(std::addressof(d), m_debug_handle));
|
||||||
|
|
||||||
|
/* Handle the debug event. */
|
||||||
|
switch (d.type) {
|
||||||
|
case svc::DebugEvent_CreateProcess:
|
||||||
|
{
|
||||||
|
/* Set our create process info. */
|
||||||
|
m_create_process_info = d.info.create_process;
|
||||||
|
|
||||||
|
/* Cache our bools. */
|
||||||
|
m_is_64_bit = (m_create_process_info.flags & svc::CreateProcessFlag_Is64Bit);
|
||||||
|
m_is_64_bit_address_space = (m_create_process_info.flags & svc::CreateProcessFlag_AddressSpaceMask) == svc::CreateProcessFlag_AddressSpace64Bit;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case svc::DebugEvent_CreateThread:
|
||||||
|
{
|
||||||
|
++num_threads;
|
||||||
|
|
||||||
|
if (const s32 index = this->ThreadCreate(d.thread_id); index >= 0) {
|
||||||
|
const Result result = osdbg::InitializeThreadInfo(std::addressof(m_thread_infos[index]), m_debug_handle, std::addressof(m_create_process_info), std::addressof(d.info.create_thread));
|
||||||
|
|
||||||
|
if (R_FAILED(result)) {
|
||||||
|
AMS_DMNT2_GDB_LOG_WARN("DebugProcess::Start: InitializeThreadInfo(%lx) failed: %08x\n", d.thread_id, result.GetValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case svc::DebugEvent_ExitThread:
|
||||||
|
{
|
||||||
|
--num_threads;
|
||||||
|
this->ThreadExit(d.thread_id);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case svc::DebugEvent_Exception:
|
||||||
|
{
|
||||||
|
if (d.info.exception.type == svc::DebugException_DebuggerAttached) {
|
||||||
|
attached = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set ourselves as valid. */
|
||||||
|
m_is_valid = true;
|
||||||
|
this->SetDebugBreaked();
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 DebugProcess::ThreadCreate(u64 thread_id) {
|
||||||
|
for (size_t i = 0; i < ThreadCountMax; ++i) {
|
||||||
|
if (!m_thread_valid[i]) {
|
||||||
|
m_thread_valid[i] = true;
|
||||||
|
m_thread_ids[i] = thread_id;
|
||||||
|
|
||||||
|
this->SetLastThreadId(thread_id);
|
||||||
|
this->SetLastSignal(GdbSignal_BreakpointTrap);
|
||||||
|
|
||||||
|
++m_thread_count;
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugProcess::ThreadExit(u64 thread_id) {
|
||||||
|
for (size_t i = 0; i < ThreadCountMax; ++i) {
|
||||||
|
if (m_thread_valid[i] && m_thread_ids[i] == thread_id) {
|
||||||
|
m_thread_valid[i] = false;
|
||||||
|
m_thread_ids[i] = 0;
|
||||||
|
|
||||||
|
this->SetLastThreadId(thread_id);
|
||||||
|
this->SetLastSignal(GdbSignal_BreakpointTrap);
|
||||||
|
|
||||||
|
--m_thread_count;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DebugProcess::CollectModules() {
|
||||||
|
/* Traverse the address space, looking for modules. */
|
||||||
|
uintptr_t address = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
/* Query the current address. */
|
||||||
|
svc::MemoryInfo memory_info;
|
||||||
|
svc::PageInfo page_info;
|
||||||
|
if (R_SUCCEEDED(svc::QueryDebugProcessMemory(std::addressof(memory_info), std::addressof(page_info), m_debug_handle, address))) {
|
||||||
|
if (memory_info.perm == svc::MemoryPermission_ReadExecute && (memory_info.state == svc::MemoryState_Code || memory_info.state == svc::MemoryState_AliasCode)) {
|
||||||
|
/* Check that we can add the module. */
|
||||||
|
AMS_ABORT_UNLESS(m_module_count < ModuleCountMax);
|
||||||
|
|
||||||
|
/* Get module definition. */
|
||||||
|
auto &module = m_module_definitions[m_module_count++];
|
||||||
|
|
||||||
|
/* Set module address/size. */
|
||||||
|
module.SetAddressSize(memory_info.addr, memory_info.size);
|
||||||
|
|
||||||
|
/* Get module name buffer. */
|
||||||
|
char *module_name = module.GetNameBuffer();
|
||||||
|
module_name[0] = 0;
|
||||||
|
|
||||||
|
/* Read module path. */
|
||||||
|
struct {
|
||||||
|
u32 zero;
|
||||||
|
s32 path_length;
|
||||||
|
char path[ModuleDefinition::PathLengthMax];
|
||||||
|
} module_path;
|
||||||
|
if (R_SUCCEEDED(this->ReadMemory(std::addressof(module_path), memory_info.addr + memory_info.size, sizeof(module_path)))) {
|
||||||
|
if (module_path.zero == 0 && module_path.path_length == util::Strnlen(module_path.path, sizeof(module_path.path))) {
|
||||||
|
std::memcpy(module_name, module_path.path, ModuleDefinition::PathLengthMax);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Truncate module name. */
|
||||||
|
module_name[ModuleDefinition::PathLengthMax - 1] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if we're done. */
|
||||||
|
const uintptr_t next_address = memory_info.addr + memory_info.size;
|
||||||
|
if (memory_info.state == svc::MemoryState_Inaccessible) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (next_address <= address) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
address = next_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DebugProcess::GetThreadContext(svc::ThreadContext *out, u64 thread_id, u32 flags) {
|
||||||
|
return svc::GetDebugThreadContext(out, m_debug_handle, thread_id, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DebugProcess::SetThreadContext(const svc::ThreadContext *ctx, u64 thread_id, u32 flags) {
|
||||||
|
return svc::SetDebugThreadContext(m_debug_handle, thread_id, ctx, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DebugProcess::ReadMemory(void *dst, uintptr_t address, size_t size) {
|
||||||
|
return svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(dst), m_debug_handle, address, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DebugProcess::WriteMemory(const void *src, uintptr_t address, size_t size) {
|
||||||
|
return svc::WriteDebugProcessMemory(m_debug_handle, reinterpret_cast<uintptr_t>(src), address, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DebugProcess::GetThreadCurrentCore(u32 *out, u64 thread_id) {
|
||||||
|
u64 dummy_value;
|
||||||
|
u32 val32 = 0;
|
||||||
|
|
||||||
|
R_TRY(svc::GetDebugThreadParam(std::addressof(dummy_value), std::addressof(val32), m_debug_handle, thread_id, svc::DebugThreadParam_CurrentCore));
|
||||||
|
|
||||||
|
*out = val32;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DebugProcess::GetProcessDebugEvent(svc::DebugEventInfo *out) {
|
||||||
|
/* Get the event. */
|
||||||
|
R_TRY(svc::GetDebugEvent(out, m_debug_handle));
|
||||||
|
|
||||||
|
/* Process the event. */
|
||||||
|
switch (out->type) {
|
||||||
|
case svc::DebugEvent_CreateProcess:
|
||||||
|
{
|
||||||
|
/* Set our create process info. */
|
||||||
|
m_create_process_info = out->info.create_process;
|
||||||
|
|
||||||
|
/* Cache our bools. */
|
||||||
|
m_is_64_bit = (m_create_process_info.flags & svc::CreateProcessFlag_Is64Bit);
|
||||||
|
m_is_64_bit_address_space = (m_create_process_info.flags & svc::CreateProcessFlag_AddressSpaceMask) == svc::CreateProcessFlag_AddressSpace64Bit;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case svc::DebugEvent_CreateThread:
|
||||||
|
{
|
||||||
|
if (const s32 index = this->ThreadCreate(out->thread_id); index >= 0) {
|
||||||
|
const Result result = osdbg::InitializeThreadInfo(std::addressof(m_thread_infos[index]), m_debug_handle, std::addressof(m_create_process_info), std::addressof(out->info.create_thread));
|
||||||
|
|
||||||
|
if (R_FAILED(result)) {
|
||||||
|
AMS_DMNT2_GDB_LOG_WARN("DebugProcess::GetProcessDebugEvent: InitializeThreadInfo(%lx) failed: %08x\n", out->thread_id, result.GetValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case svc::DebugEvent_ExitThread:
|
||||||
|
{
|
||||||
|
this->ThreadExit(out->thread_id);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out->flags & svc::DebugEventFlag_Stopped) {
|
||||||
|
this->SetDebugBreaked();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 DebugProcess::GetLastThreadId() {
|
||||||
|
/* Select our first valid thread id. */
|
||||||
|
if (m_last_thread_id == 0) {
|
||||||
|
for (size_t i = 0; i < ThreadCountMax; ++i) {
|
||||||
|
if (m_thread_valid[i]) {
|
||||||
|
SetLastThreadId(m_thread_ids[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_last_thread_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DebugProcess::GetThreadList(s32 *out_count, u64 *out_thread_ids, size_t max_count) {
|
||||||
|
s32 count = 0;
|
||||||
|
for (size_t i = 0; i < ThreadCountMax; ++i) {
|
||||||
|
if (m_thread_valid[i]) {
|
||||||
|
if (count < static_cast<s32>(max_count)) {
|
||||||
|
out_thread_ids[count++] = m_thread_ids[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_count = count;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DebugProcess::GetThreadInfoList(s32 *out_count, osdbg::ThreadInfo **out_infos, size_t max_count) {
|
||||||
|
s32 count = 0;
|
||||||
|
for (size_t i = 0; i < ThreadCountMax; ++i) {
|
||||||
|
if (m_thread_valid[i]) {
|
||||||
|
if (count < static_cast<s32>(max_count)) {
|
||||||
|
out_infos[count++] = std::addressof(m_thread_infos[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_count = count;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
117
stratosphere/dmnt.gen2/source/dmnt2_debug_process.hpp
Normal file
117
stratosphere/dmnt.gen2/source/dmnt2_debug_process.hpp
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "dmnt2_gdb_signal.hpp"
|
||||||
|
#include "dmnt2_module_definition.hpp"
|
||||||
|
|
||||||
|
namespace ams::dmnt {
|
||||||
|
|
||||||
|
class DebugProcess {
|
||||||
|
public:
|
||||||
|
static constexpr size_t ThreadCountMax = 0x100;
|
||||||
|
static constexpr size_t ModuleCountMax = 0x60;
|
||||||
|
|
||||||
|
enum ProcessStatus {
|
||||||
|
ProcessStatus_DebugBreak,
|
||||||
|
ProcessStatus_Running,
|
||||||
|
ProcessStatus_Exited,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ContinueMode {
|
||||||
|
ContinueMode_Stopped,
|
||||||
|
ContinueMode_Continue,
|
||||||
|
ContinueMode_Step,
|
||||||
|
};
|
||||||
|
private:
|
||||||
|
svc::Handle m_debug_handle{svc::InvalidHandle};
|
||||||
|
s32 m_thread_count{0};
|
||||||
|
bool m_is_valid{false};
|
||||||
|
bool m_is_64_bit{false};
|
||||||
|
bool m_is_64_bit_address_space{false};
|
||||||
|
ProcessStatus m_status{ProcessStatus_DebugBreak};
|
||||||
|
os::ProcessId m_process_id{os::InvalidProcessId};
|
||||||
|
u64 m_last_thread_id{};
|
||||||
|
u64 m_thread_id_override;
|
||||||
|
GdbSignal m_last_signal{};
|
||||||
|
bool m_thread_valid[ThreadCountMax]{};
|
||||||
|
u64 m_thread_ids[ThreadCountMax]{};
|
||||||
|
osdbg::ThreadInfo m_thread_infos[ThreadCountMax]{};
|
||||||
|
svc::DebugInfoCreateProcess m_create_process_info{};
|
||||||
|
ModuleDefinition m_module_definitions[ModuleCountMax]{};
|
||||||
|
size_t m_module_count{};
|
||||||
|
size_t m_main_module{};
|
||||||
|
public:
|
||||||
|
constexpr DebugProcess() = default;
|
||||||
|
~DebugProcess() { this->Detach(); }
|
||||||
|
|
||||||
|
svc::Handle GetHandle() const { return m_debug_handle; }
|
||||||
|
bool IsValid() const { return m_is_valid; }
|
||||||
|
bool Is64Bit() const { return m_is_64_bit; }
|
||||||
|
bool Is64BitAddressSpace() const { return m_is_64_bit_address_space; }
|
||||||
|
size_t GetModuleCount() const { return m_module_count; }
|
||||||
|
size_t GetMainModuleIndex() const { return m_main_module; }
|
||||||
|
const char *GetModuleName(size_t ix) const { return m_module_definitions[ix].GetName(); }
|
||||||
|
uintptr_t GetBaseAddress(size_t ix) const { return m_module_definitions[ix].GetAddress(); }
|
||||||
|
ProcessStatus GetStatus() const { return m_status; }
|
||||||
|
os::ProcessId GetProcessId() const { return m_process_id; }
|
||||||
|
|
||||||
|
const char *GetProcessName() const { return m_create_process_info.name; }
|
||||||
|
|
||||||
|
void SetLastSignal(GdbSignal signal) { m_last_signal = signal; }
|
||||||
|
GdbSignal GetLastSignal() const { return m_last_signal; }
|
||||||
|
|
||||||
|
Result GetThreadList(s32 *out_count, u64 *out_thread_ids, size_t max_count);
|
||||||
|
Result GetThreadInfoList(s32 *out_count, osdbg::ThreadInfo **out_infos, size_t max_count);
|
||||||
|
|
||||||
|
u64 GetLastThreadId();
|
||||||
|
u64 GetThreadIdOverride() { this->GetLastThreadId(); return m_thread_id_override; }
|
||||||
|
|
||||||
|
void SetLastThreadId(u64 tid) {
|
||||||
|
m_last_thread_id = tid;
|
||||||
|
m_thread_id_override = tid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetThreadIdOverride(u64 tid) {
|
||||||
|
m_thread_id_override = tid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetDebugBreaked() {
|
||||||
|
m_status = ProcessStatus_DebugBreak;
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
Result Attach(os::ProcessId process_id);
|
||||||
|
void Detach();
|
||||||
|
|
||||||
|
Result GetThreadContext(svc::ThreadContext *out, u64 thread_id, u32 flags);
|
||||||
|
Result SetThreadContext(const svc::ThreadContext *ctx, u64 thread_id, u32 flags);
|
||||||
|
|
||||||
|
Result ReadMemory(void *dst, uintptr_t address, size_t size);
|
||||||
|
Result WriteMemory(const void *src, uintptr_t address, size_t size);
|
||||||
|
|
||||||
|
Result GetThreadCurrentCore(u32 *out, u64 thread_id);
|
||||||
|
|
||||||
|
Result GetProcessDebugEvent(svc::DebugEventInfo *out);
|
||||||
|
private:
|
||||||
|
Result Start();
|
||||||
|
|
||||||
|
s32 ThreadCreate(u64 thread_id);
|
||||||
|
void ThreadExit(u64 thread_id);
|
||||||
|
|
||||||
|
Result CollectModules();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -25,7 +25,7 @@ namespace ams::dmnt {
|
||||||
constexpr size_t ServerThreadStackSize = util::AlignUp(4 * GdbPacketBufferSize + os::MemoryPageSize, os::ThreadStackAlignment);
|
constexpr size_t ServerThreadStackSize = util::AlignUp(4 * GdbPacketBufferSize + os::MemoryPageSize, os::ThreadStackAlignment);
|
||||||
|
|
||||||
alignas(os::ThreadStackAlignment) constinit u8 g_server_thread_stack[ServerThreadStackSize];
|
alignas(os::ThreadStackAlignment) constinit u8 g_server_thread_stack[ServerThreadStackSize];
|
||||||
alignas(os::ThreadStackAlignment) constinit u8 g_events_thread_stack[16_KB];
|
alignas(os::ThreadStackAlignment) constinit u8 g_events_thread_stack[util::AlignUp(2 * GdbPacketBufferSize + os::MemoryPageSize, os::ThreadStackAlignment)];
|
||||||
|
|
||||||
constinit os::ThreadType g_server_thread;
|
constinit os::ThreadType g_server_thread;
|
||||||
|
|
||||||
|
|
|
@ -349,7 +349,106 @@ namespace ams::dmnt {
|
||||||
AMS_DMNT2_GDB_LOG_DEBUG("Offset/Length %x/%x\n", offset, length);
|
AMS_DMNT2_GDB_LOG_DEBUG("Offset/Length %x/%x\n", offset, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
constinit char g_process_list_buffer[0x2000];
|
void SetGdbRegister32(char *dst, u32 value) {
|
||||||
|
if (value != 0) {
|
||||||
|
AppendReply(dst, "%08x", util::ConvertToBigEndian(value));
|
||||||
|
} else {
|
||||||
|
AppendReply(dst, "0*\"00");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetGdbRegister64(char *dst, u64 value) {
|
||||||
|
if (value != 0) {
|
||||||
|
AppendReply(dst, "%016lx", util::ConvertToBigEndian(value));
|
||||||
|
} else {
|
||||||
|
AppendReply(dst, "0*,");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetGdbRegister128(char *dst, u128 value) {
|
||||||
|
if (value != 0) {
|
||||||
|
AppendReply(dst, "%016lx%016lx", util::ConvertToBigEndian(static_cast<u64>(value >> 0)), util::ConvertToBigEndian(static_cast<u64>(value >> BITSIZEOF(u64))));
|
||||||
|
} else {
|
||||||
|
AppendReply(dst, "0*<");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetGdbRegisterPacket(char *dst, const svc::ThreadContext &thread_context, bool is_64_bit) {
|
||||||
|
/* Clear packet. */
|
||||||
|
dst[0] = 0;
|
||||||
|
|
||||||
|
if (is_64_bit) {
|
||||||
|
/* Copy general purpose registers. */
|
||||||
|
for (size_t i = 0; i < util::size(thread_context.r); ++i) {
|
||||||
|
SetGdbRegister64(dst, thread_context.r[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy special registers. */
|
||||||
|
SetGdbRegister64(dst, thread_context.fp);
|
||||||
|
SetGdbRegister64(dst, thread_context.lr);
|
||||||
|
SetGdbRegister64(dst, thread_context.sp);
|
||||||
|
SetGdbRegister64(dst, thread_context.pc);
|
||||||
|
|
||||||
|
SetGdbRegister32(dst, thread_context.pstate);
|
||||||
|
|
||||||
|
/* Copy FPU registers. */
|
||||||
|
for (size_t i = 0; i < util::size(thread_context.v); ++i) {
|
||||||
|
SetGdbRegister128(dst, thread_context.v[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetGdbRegister32(dst, thread_context.fpsr);
|
||||||
|
SetGdbRegister32(dst, thread_context.fpcr);
|
||||||
|
} else {
|
||||||
|
/* Copy general purpose registers. */
|
||||||
|
for (size_t i = 0; i < 15; ++i) {
|
||||||
|
SetGdbRegister32(dst, thread_context.r[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy special registers. */
|
||||||
|
SetGdbRegister32(dst, thread_context.pc);
|
||||||
|
SetGdbRegister32(dst, thread_context.pstate);
|
||||||
|
|
||||||
|
/* Copy FPU registers. */
|
||||||
|
for (size_t i = 0; i < util::size(thread_context.v); ++i) {
|
||||||
|
SetGdbRegister128(dst, thread_context.v[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const u32 fpscr = (thread_context.fpsr & 0xF80000FF) | (thread_context.fpcr & 0x07FFFF00);
|
||||||
|
SetGdbRegister32(dst, fpscr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constinit os::SdkMutex g_annex_buffer_lock;
|
||||||
|
constinit char g_annex_buffer[0x8000];
|
||||||
|
|
||||||
|
enum AnnexBufferContents {
|
||||||
|
AnnexBufferContents_Invalid,
|
||||||
|
AnnexBufferContents_Processes,
|
||||||
|
AnnexBufferContents_Threads,
|
||||||
|
AnnexBufferContents_Libraries,
|
||||||
|
};
|
||||||
|
|
||||||
|
constinit AnnexBufferContents g_annex_buffer_contents = AnnexBufferContents_Invalid;
|
||||||
|
|
||||||
|
void GetAnnexBufferContents(char *dst, u32 offset, u32 length) {
|
||||||
|
const u32 annex_len = std::strlen(g_annex_buffer);
|
||||||
|
if (offset <= annex_len) {
|
||||||
|
if (offset + length < annex_len) {
|
||||||
|
dst[0] = 'm';
|
||||||
|
std::memcpy(dst + 1, g_annex_buffer + offset, length);
|
||||||
|
dst[1 + length] = 0;
|
||||||
|
} else {
|
||||||
|
const auto size = annex_len - offset;
|
||||||
|
|
||||||
|
dst[0] = 'l';
|
||||||
|
std::memcpy(dst + 1, g_annex_buffer + offset, size);
|
||||||
|
dst[1 + size] = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dst[0] = '1';
|
||||||
|
dst[1] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
constinit os::SdkMutex g_event_request_lock;
|
constinit os::SdkMutex g_event_request_lock;
|
||||||
constinit os::SdkMutex g_event_lock;
|
constinit os::SdkMutex g_event_lock;
|
||||||
|
@ -358,7 +457,7 @@ namespace ams::dmnt {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GdbServerImpl::GdbServerImpl(int socket, void *stack, size_t stack_size) : m_socket(socket), m_session(socket), m_packet_io(), m_state(State::Initial), m_event(os::EventClearMode_AutoClear) {
|
GdbServerImpl::GdbServerImpl(int socket, void *stack, size_t stack_size) : m_socket(socket), m_session(socket), m_packet_io(), m_state(State::Initial), m_debug_process(), m_event(os::EventClearMode_AutoClear) {
|
||||||
/* Create and start the events thread. */
|
/* Create and start the events thread. */
|
||||||
R_ABORT_UNLESS(os::CreateThread(std::addressof(m_events_thread), DebugEventsThreadEntry, this, stack, stack_size, os::HighestThreadPriority - 1));
|
R_ABORT_UNLESS(os::CreateThread(std::addressof(m_events_thread), DebugEventsThreadEntry, this, stack, stack_size, os::HighestThreadPriority - 1));
|
||||||
os::StartThread(std::addressof(m_events_thread));
|
os::StartThread(std::addressof(m_events_thread));
|
||||||
|
@ -388,8 +487,8 @@ namespace ams::dmnt {
|
||||||
m_state = State::Destroyed;
|
m_state = State::Destroyed;
|
||||||
|
|
||||||
/* Detach. */
|
/* Detach. */
|
||||||
if (m_attached) {
|
if (this->HasDebugProcess()) {
|
||||||
R_ABORT_UNLESS(svc::CloseHandle(m_debug_handle));
|
m_debug_process.Detach();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -408,37 +507,18 @@ namespace ams::dmnt {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set ourselves as unattached. */
|
/* Detach. */
|
||||||
m_attached = false;
|
m_debug_process.Detach();
|
||||||
|
|
||||||
/* If we have a process id, attach. */
|
/* If we have a process id, attach. */
|
||||||
if (R_FAILED(svc::DebugActiveProcess(std::addressof(m_debug_handle), m_process_id.value))) {
|
if (R_FAILED(m_debug_process.Attach(m_process_id))) {
|
||||||
AMS_DMNT2_GDB_LOG_DEBUG("Failed to attach to %016lx\n", m_process_id.value);
|
AMS_DMNT2_GDB_LOG_DEBUG("Failed to attach to %016lx\n", m_process_id.value);
|
||||||
m_debug_handle = svc::InvalidHandle;
|
|
||||||
g_event_done_cv.Signal();
|
g_event_done_cv.Signal();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the process id. */
|
/* Set our process id. */
|
||||||
if (R_FAILED(svc::GetProcessId(std::addressof(m_process_id.value), m_debug_handle))) {
|
m_process_id = m_debug_process.GetProcessId();
|
||||||
AMS_DMNT2_GDB_LOG_DEBUG("Failed to get process id for debug handle\n");
|
|
||||||
R_ABORT_UNLESS(svc::CloseHandle(m_debug_handle));
|
|
||||||
m_debug_handle = svc::InvalidHandle;
|
|
||||||
g_event_done_cv.Signal();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set ourselves as attached. */
|
|
||||||
m_attached = true;
|
|
||||||
|
|
||||||
/* Get the create process event. */
|
|
||||||
{
|
|
||||||
svc::DebugEventInfo d;
|
|
||||||
R_ABORT_UNLESS(svc::GetDebugEvent(std::addressof(d), m_debug_handle));
|
|
||||||
AMS_ABORT_UNLESS(d.type == svc::DebugEvent_CreateProcess);
|
|
||||||
|
|
||||||
m_create_process_info = d.info.create_process;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Signal that we're done attaching. */
|
/* Signal that we're done attaching. */
|
||||||
g_event_done_cv.Signal();
|
g_event_done_cv.Signal();
|
||||||
|
@ -450,9 +530,9 @@ namespace ams::dmnt {
|
||||||
g_event_lock.Lock();
|
g_event_lock.Lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clear our process ids. */
|
/* Clear our process id and detach. */
|
||||||
m_process_id = os::InvalidProcessId;
|
m_process_id = os::InvalidProcessId;
|
||||||
m_target_process_id = os::InvalidProcessId;
|
m_debug_process.Detach();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -469,11 +549,12 @@ namespace ams::dmnt {
|
||||||
std::scoped_lock lk(g_event_lock);
|
std::scoped_lock lk(g_event_lock);
|
||||||
|
|
||||||
s32 dummy = -1;
|
s32 dummy = -1;
|
||||||
return svc::WaitSynchronization(std::addressof(dummy), std::addressof(m_debug_handle), 1, TimeSpan::FromMilliSeconds(20).GetNanoSeconds());
|
svc::Handle handle = m_debug_process.GetHandle();
|
||||||
|
return svc::WaitSynchronization(std::addressof(dummy), std::addressof(handle), 1, TimeSpan::FromMilliSeconds(20).GetNanoSeconds());
|
||||||
}();
|
}();
|
||||||
|
|
||||||
/* Check if we're killed. */
|
/* Check if we're killed. */
|
||||||
if (m_killed) {
|
if (m_killed || !m_debug_process.IsValid()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -484,7 +565,7 @@ namespace ams::dmnt {
|
||||||
|
|
||||||
/* Try to get the event. */
|
/* Try to get the event. */
|
||||||
svc::DebugEventInfo d;
|
svc::DebugEventInfo d;
|
||||||
if (R_FAILED(svc::GetDebugEvent(std::addressof(d), m_debug_handle))) {
|
if (R_FAILED(m_debug_process.GetProcessDebugEvent(std::addressof(d)))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -501,9 +582,12 @@ namespace ams::dmnt {
|
||||||
/* Set the signal. */
|
/* Set the signal. */
|
||||||
SetReply(m_reply_packet, "T%02X", static_cast<u32>(signal));
|
SetReply(m_reply_packet, "T%02X", static_cast<u32>(signal));
|
||||||
|
|
||||||
|
/* Get the last thread id. */
|
||||||
|
const u64 thread_id = m_debug_process.GetLastThreadId();
|
||||||
|
|
||||||
/* Get the thread context. */
|
/* Get the thread context. */
|
||||||
svc::ThreadContext thread_context = {};
|
svc::ThreadContext thread_context = {};
|
||||||
svc::GetDebugThreadContext(std::addressof(thread_context), m_debug_handle, m_thread_id, svc::ThreadContextFlag_General | svc::ThreadContextFlag_Control);
|
m_debug_process.GetThreadContext(std::addressof(thread_context), thread_id, svc::ThreadContextFlag_General | svc::ThreadContextFlag_Control);
|
||||||
|
|
||||||
/* Add important registers. */
|
/* Add important registers. */
|
||||||
/* TODO: aarch32 */
|
/* TODO: aarch32 */
|
||||||
|
@ -528,13 +612,12 @@ namespace ams::dmnt {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add the thread id. */
|
/* Add the thread id. */
|
||||||
AppendReply(m_reply_packet, ";thread:p%lx.%lx", m_target_process_id.value, m_thread_id);
|
AppendReply(m_reply_packet, ";thread:p%lx.%lx", m_process_id.value, thread_id);
|
||||||
|
|
||||||
/* Add the thread core. */
|
/* Add the thread core. */
|
||||||
{
|
{
|
||||||
u64 dummy;
|
|
||||||
u32 core = 0;
|
u32 core = 0;
|
||||||
svc::GetDebugThreadParam(std::addressof(dummy), std::addressof(core), m_debug_handle, m_thread_id, svc::DebugThreadParam_CurrentCore);
|
m_debug_process.GetThreadCurrentCore(std::addressof(core), thread_id);
|
||||||
|
|
||||||
AppendReply(m_reply_packet, ";core:%u;", core);
|
AppendReply(m_reply_packet, ";core:%u;", core);
|
||||||
}
|
}
|
||||||
|
@ -614,73 +697,54 @@ namespace ams::dmnt {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GdbServerImpl::Hg() {
|
void GdbServerImpl::Hg() {
|
||||||
|
bool success = false;
|
||||||
|
s64 thread_id;
|
||||||
if (const char *dot = std::strchr(m_receive_packet, '.'); dot != nullptr) {
|
if (const char *dot = std::strchr(m_receive_packet, '.'); dot != nullptr) {
|
||||||
m_thread_id = DecodeHex(dot + 1);
|
thread_id = std::strcmp(dot + 1, "-1") == 0 ? -1 : static_cast<s64>(DecodeHex(dot + 1));
|
||||||
AMS_DMNT2_GDB_LOG_DEBUG("Set thread id = %lx\n", m_thread_id);
|
AMS_DMNT2_GDB_LOG_DEBUG("Set thread id = %lx\n", thread_id);
|
||||||
|
|
||||||
/* TODO: Validate thread exists for the process? */
|
u64 thread_ids[DebugProcess::ThreadCountMax];
|
||||||
|
s32 num_threads;
|
||||||
|
if (R_SUCCEEDED(m_debug_process.GetThreadList(std::addressof(num_threads), thread_ids, util::size(thread_ids)))) {
|
||||||
|
if (thread_id == 0) {
|
||||||
|
thread_id = thread_ids[0];
|
||||||
|
}
|
||||||
|
for (auto i = 0; i < num_threads; ++i) {
|
||||||
|
if (thread_id == -1 || static_cast<u64>(thread_id) == thread_ids[i]) {
|
||||||
|
svc::ThreadContext context;
|
||||||
|
if (R_SUCCEEDED(m_debug_process.GetThreadContext(std::addressof(context), thread_ids[i], svc::ThreadContextFlag_Control))) {
|
||||||
|
success = true;
|
||||||
|
if (thread_id != -1) {
|
||||||
|
m_debug_process.SetThreadIdOverride(thread_ids[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SetReplyOk(m_reply_packet);
|
if (success) {
|
||||||
|
SetReplyOk(m_reply_packet);
|
||||||
|
} else {
|
||||||
|
SetReplyError(m_reply_packet, "E01");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GdbServerImpl::g() {
|
bool GdbServerImpl::g() {
|
||||||
/* TODO: aarch32 */
|
/* Get thread id. */
|
||||||
|
u64 thread_id = m_debug_process.GetThreadIdOverride();
|
||||||
|
if (thread_id == 0 || thread_id == static_cast<u64>(-1)) {
|
||||||
|
thread_id = m_debug_process.GetLastThreadId();
|
||||||
|
}
|
||||||
|
|
||||||
/* Get thread context. */
|
/* Get thread context. */
|
||||||
svc::ThreadContext thread_context;
|
svc::ThreadContext thread_context;
|
||||||
if (R_FAILED(svc::GetDebugThreadContext(std::addressof(thread_context), m_debug_handle, m_thread_id, svc::ThreadContextFlag_All))) {
|
if (R_FAILED(m_debug_process.GetThreadContext(std::addressof(thread_context), thread_id, svc::ThreadContextFlag_All))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copy general purpose registers. */
|
/* Populate reply packet. */
|
||||||
for (size_t i = 0; i < util::size(thread_context.r); ++i) {
|
SetGdbRegisterPacket(m_reply_packet, thread_context, m_debug_process.Is64Bit());
|
||||||
if (thread_context.r[i] != 0) {
|
|
||||||
AppendReply(m_reply_packet, "%016lx", util::ConvertToBigEndian(thread_context.r[i]));
|
|
||||||
} else {
|
|
||||||
AppendReply(m_reply_packet, "0*,");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copy special registers. */
|
|
||||||
if (thread_context.fp != 0) {
|
|
||||||
AppendReply(m_reply_packet, "%016lx", util::ConvertToBigEndian(thread_context.fp));
|
|
||||||
} else {
|
|
||||||
AppendReply(m_reply_packet, "0*,");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (thread_context.lr != 0) {
|
|
||||||
AppendReply(m_reply_packet, "%016lx", util::ConvertToBigEndian(thread_context.lr));
|
|
||||||
} else {
|
|
||||||
AppendReply(m_reply_packet, "0*,");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (thread_context.sp != 0) {
|
|
||||||
AppendReply(m_reply_packet, "%016lx", util::ConvertToBigEndian(thread_context.sp));
|
|
||||||
} else {
|
|
||||||
AppendReply(m_reply_packet, "0*,");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (thread_context.pc != 0) {
|
|
||||||
AppendReply(m_reply_packet, "%016lx", util::ConvertToBigEndian(thread_context.pc));
|
|
||||||
} else {
|
|
||||||
AppendReply(m_reply_packet, "0*,");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (thread_context.pstate != 0) {
|
|
||||||
AppendReply(m_reply_packet, "%08x", util::ConvertToBigEndian(thread_context.pstate));
|
|
||||||
} else {
|
|
||||||
AppendReply(m_reply_packet, "0*\"0");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copy FPU registers. */
|
|
||||||
for (size_t i = 0; i < util::size(thread_context.v); ++i) {
|
|
||||||
if (thread_context.v[i] != 0) {
|
|
||||||
AppendReply(m_reply_packet, "%016lx%016lx", util::ConvertToBigEndian(static_cast<u64>(thread_context.v[i] >> 0)), util::ConvertToBigEndian(static_cast<u64>(thread_context.v[i] >> BITSIZEOF(u64))));
|
|
||||||
} else {
|
|
||||||
AppendReply(m_reply_packet, "0*<");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -705,7 +769,7 @@ namespace ams::dmnt {
|
||||||
|
|
||||||
/* Read the memory. */
|
/* Read the memory. */
|
||||||
/* TODO: Detect partial readability? */
|
/* TODO: Detect partial readability? */
|
||||||
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(m_buffer), m_debug_handle, address, length))) {
|
if (R_FAILED(m_debug_process.ReadMemory(m_buffer, address, length))) {
|
||||||
SetReplyError(m_reply_packet, "E01");
|
SetReplyError(m_reply_packet, "E01");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -727,34 +791,21 @@ namespace ams::dmnt {
|
||||||
/* Get the process id. */
|
/* Get the process id. */
|
||||||
if (const u64 process_id = DecodeHex(m_receive_packet); process_id != 0) {
|
if (const u64 process_id = DecodeHex(m_receive_packet); process_id != 0) {
|
||||||
/* Set our process id. */
|
/* Set our process id. */
|
||||||
m_process_id = { process_id };
|
m_process_id = { process_id };
|
||||||
m_target_process_id = { process_id };
|
|
||||||
|
|
||||||
/* Wait for us to be attached. */
|
/* Wait for us to be attached. */
|
||||||
bool attached;
|
|
||||||
{
|
{
|
||||||
std::scoped_lock lk(g_event_request_lock);
|
std::scoped_lock lk(g_event_request_lock);
|
||||||
g_event_request_cv.Signal();
|
g_event_request_cv.Signal();
|
||||||
if (!g_event_done_cv.TimedWait(g_event_request_lock, TimeSpan::FromSeconds(2))) {
|
if (!g_event_done_cv.TimedWait(g_event_request_lock, TimeSpan::FromSeconds(2))) {
|
||||||
m_event.Signal();
|
m_event.Signal();
|
||||||
}
|
}
|
||||||
|
|
||||||
attached = m_attached;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we're attached, send a stop reply packet. */
|
/* If we're attached, send a stop reply packet. */
|
||||||
if (attached) {
|
if (m_debug_process.IsValid()) {
|
||||||
/* Set our initial thread id. */
|
|
||||||
{
|
|
||||||
s32 dummy;
|
|
||||||
u64 thread_id = 0;
|
|
||||||
R_ABORT_UNLESS(svc::GetThreadList(std::addressof(dummy), std::addressof(thread_id), 1, m_debug_handle));
|
|
||||||
|
|
||||||
m_thread_id = thread_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set the stop reply packet. */
|
/* Set the stop reply packet. */
|
||||||
this->SetStopReplyPacket(GdbSignal_Signal0);
|
this->SetStopReplyPacket(m_debug_process.GetLastSignal());
|
||||||
} else {
|
} else {
|
||||||
SetReplyError(m_reply_packet, "E01");
|
SetReplyError(m_reply_packet, "E01");
|
||||||
}
|
}
|
||||||
|
@ -782,11 +833,6 @@ namespace ams::dmnt {
|
||||||
|
|
||||||
void GdbServerImpl::qAttached() {
|
void GdbServerImpl::qAttached() {
|
||||||
if (this->HasDebugProcess()) {
|
if (this->HasDebugProcess()) {
|
||||||
/* Parse/Save the requested process id */
|
|
||||||
if (const char *colon = std::strchr(m_receive_packet, ':'); colon != nullptr) {
|
|
||||||
m_target_process_id.value = DecodeHex(colon + 1);
|
|
||||||
AMS_DMNT2_GDB_LOG_DEBUG("Queried for attached to %016lx\n", m_target_process_id.value);
|
|
||||||
}
|
|
||||||
SetReply(m_reply_packet, "1");
|
SetReply(m_reply_packet, "1");
|
||||||
} else {
|
} else {
|
||||||
SetReplyError(m_reply_packet, "E01");
|
SetReplyError(m_reply_packet, "E01");
|
||||||
|
@ -795,20 +841,8 @@ namespace ams::dmnt {
|
||||||
|
|
||||||
void GdbServerImpl::qC() {
|
void GdbServerImpl::qC() {
|
||||||
if (this->HasDebugProcess()) {
|
if (this->HasDebugProcess()) {
|
||||||
/* Potentially get our thread id. */
|
|
||||||
if (m_thread_id == 0) {
|
|
||||||
s32 dummy;
|
|
||||||
u64 thread_id = 0;
|
|
||||||
if (R_FAILED(svc::GetThreadList(std::addressof(dummy), std::addressof(thread_id), 1, m_debug_handle))) {
|
|
||||||
SetReplyError(m_reply_packet, "E01");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_thread_id = thread_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Send the thread id. */
|
/* Send the thread id. */
|
||||||
SetReply(m_reply_packet, "QCp%lx.%lx", m_target_process_id.value, m_thread_id);
|
SetReply(m_reply_packet, "QCp%lx.%lx", m_process_id.value, m_debug_process.GetLastThreadId());
|
||||||
} else {
|
} else {
|
||||||
SetReplyError(m_reply_packet, "E01");
|
SetReplyError(m_reply_packet, "E01");
|
||||||
}
|
}
|
||||||
|
@ -822,9 +856,11 @@ namespace ams::dmnt {
|
||||||
AppendReply(m_reply_packet, ";multiprocess+");
|
AppendReply(m_reply_packet, ";multiprocess+");
|
||||||
AppendReply(m_reply_packet, ";qXfer:osdata:read+");
|
AppendReply(m_reply_packet, ";qXfer:osdata:read+");
|
||||||
AppendReply(m_reply_packet, ";qXfer:features:read+");
|
AppendReply(m_reply_packet, ";qXfer:features:read+");
|
||||||
|
AppendReply(m_reply_packet, ";qXfer:libraries:read+");
|
||||||
AppendReply(m_reply_packet, ";qXfer:libraries-svr4:read+");
|
AppendReply(m_reply_packet, ";qXfer:libraries-svr4:read+");
|
||||||
AppendReply(m_reply_packet, ";augmented-libraries-svr4-read+");
|
AppendReply(m_reply_packet, ";augmented-libraries-svr4-read+");
|
||||||
AppendReply(m_reply_packet, ";qXfer:threads:read+");
|
AppendReply(m_reply_packet, ";qXfer:threads:read+");
|
||||||
|
AppendReply(m_reply_packet, ";qXfer:exec-file:read+");
|
||||||
AppendReply(m_reply_packet, ";swbreak+");
|
AppendReply(m_reply_packet, ";swbreak+");
|
||||||
AppendReply(m_reply_packet, ";hwbreak+");
|
AppendReply(m_reply_packet, ";hwbreak+");
|
||||||
AppendReply(m_reply_packet, ";vContSupported+");
|
AppendReply(m_reply_packet, ";vContSupported+");
|
||||||
|
@ -844,11 +880,15 @@ namespace ams::dmnt {
|
||||||
/* Process. */
|
/* Process. */
|
||||||
if (ParsePrefix(m_receive_packet, "features:read:")) {
|
if (ParsePrefix(m_receive_packet, "features:read:")) {
|
||||||
this->qXferFeaturesRead();
|
this->qXferFeaturesRead();
|
||||||
} else if (ParsePrefix(m_receive_packet, "threads:read:")) {
|
} else if (ParsePrefix(m_receive_packet, "threads:read::")) {
|
||||||
if (!this->qXferThreadsRead()) {
|
if (!this->qXferThreadsRead()) {
|
||||||
m_killed = true;
|
m_killed = true;
|
||||||
SetReplyError(m_reply_packet, "E01");
|
SetReplyError(m_reply_packet, "E01");
|
||||||
}
|
}
|
||||||
|
} else if (ParsePrefix(m_receive_packet, "libraries:read::")) {
|
||||||
|
this->qXferLibrariesRead();
|
||||||
|
} else if (ParsePrefix(m_receive_packet, "exec-file:read:")) {
|
||||||
|
SetReply(m_reply_packet, "l%s", m_debug_process.GetProcessName());
|
||||||
} else {
|
} else {
|
||||||
AMS_DMNT2_GDB_LOG_DEBUG("Not Implemented qxfer: %s\n", m_receive_packet);
|
AMS_DMNT2_GDB_LOG_DEBUG("Not Implemented qxfer: %s\n", m_receive_packet);
|
||||||
SetReplyError(m_reply_packet, "E01");
|
SetReplyError(m_reply_packet, "E01");
|
||||||
|
@ -865,7 +905,6 @@ namespace ams::dmnt {
|
||||||
ParseOffsetLength(m_receive_packet, offset, length);
|
ParseOffsetLength(m_receive_packet, offset, length);
|
||||||
|
|
||||||
/* Send the desired xml. */
|
/* Send the desired xml. */
|
||||||
/* TODO: Detection of debug-process as 64 bit or not. */
|
|
||||||
std::strncpy(m_reply_packet, (this->Is64Bit() ? TargetXmlAarch64 : TargetXmlAarch32) + offset, length);
|
std::strncpy(m_reply_packet, (this->Is64Bit() ? TargetXmlAarch64 : TargetXmlAarch32) + offset, length);
|
||||||
m_reply_packet[length] = 0;
|
m_reply_packet[length] = 0;
|
||||||
m_reply_packet += std::strlen(m_reply_packet);
|
m_reply_packet += std::strlen(m_reply_packet);
|
||||||
|
@ -907,6 +946,37 @@ namespace ams::dmnt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GdbServerImpl::qXferLibrariesRead() {
|
||||||
|
/* 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_Libraries) {
|
||||||
|
/* Set header. */
|
||||||
|
SetReply(g_annex_buffer, "<library-list>");
|
||||||
|
|
||||||
|
/* Get the module list. */
|
||||||
|
for (size_t i = 0; i < m_debug_process.GetModuleCount(); ++i) {
|
||||||
|
AMS_DMNT2_GDB_LOG_DEBUG("Module[%zu]: %p, %s\n", i, reinterpret_cast<void *>(m_debug_process.GetBaseAddress(i)), m_debug_process.GetModuleName(i));
|
||||||
|
|
||||||
|
AppendReply(g_annex_buffer, "<library name=\"%s\"><segment address=\"0x%lx\" /></library>", m_debug_process.GetModuleName(i), m_debug_process.GetBaseAddress(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
AppendReply(g_annex_buffer, "</library-list>");
|
||||||
|
|
||||||
|
g_annex_buffer_contents = AnnexBufferContents_Libraries;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy out the module list. */
|
||||||
|
GetAnnexBufferContents(m_reply_packet, offset, length);
|
||||||
|
}
|
||||||
|
|
||||||
void GdbServerImpl::qXferOsdataRead() {
|
void GdbServerImpl::qXferOsdataRead() {
|
||||||
/* Handle the qXfer. */
|
/* Handle the qXfer. */
|
||||||
u32 offset, length;
|
u32 offset, length;
|
||||||
|
@ -915,13 +985,16 @@ namespace ams::dmnt {
|
||||||
/* Parse offset/length. */
|
/* Parse offset/length. */
|
||||||
ParseOffsetLength(m_receive_packet, 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 process list. */
|
/* If doing a fresh read, generate the process list. */
|
||||||
if (offset == 0) {
|
if (offset == 0 || g_annex_buffer_contents != AnnexBufferContents_Processes) {
|
||||||
/* Clear the process list buffer. */
|
/* Clear the process list buffer. */
|
||||||
g_process_list_buffer[0] = 0;
|
g_annex_buffer[0] = 0;
|
||||||
|
|
||||||
/* Set header. */
|
/* Set header. */
|
||||||
SetReply(g_process_list_buffer, "<?xml version=\"1.0\"?>\n<!DOCTYPE target SYSTEM \"osdata.dtd\">\n<osdata type=\"processes\">\n");
|
SetReply(g_annex_buffer, "<?xml version=\"1.0\"?>\n<!DOCTYPE target SYSTEM \"osdata.dtd\">\n<osdata type=\"processes\">\n");
|
||||||
|
|
||||||
/* Get all processes. */
|
/* Get all processes. */
|
||||||
{
|
{
|
||||||
|
@ -941,32 +1014,19 @@ namespace ams::dmnt {
|
||||||
R_ABORT_UNLESS(svc::GetDebugEvent(std::addressof(d), handle));
|
R_ABORT_UNLESS(svc::GetDebugEvent(std::addressof(d), handle));
|
||||||
AMS_ABORT_UNLESS(d.type == svc::DebugEvent_CreateProcess);
|
AMS_ABORT_UNLESS(d.type == svc::DebugEvent_CreateProcess);
|
||||||
|
|
||||||
AppendReply(g_process_list_buffer, "<item>\n<column name=\"pid\">%lu</column>\n<column name=\"command\">%s</column>\n</item>\n", d.info.create_process.process_id, d.info.create_process.name);
|
AppendReply(g_annex_buffer, "<item>\n<column name=\"pid\">%lu</column>\n<column name=\"command\">%s</column>\n</item>\n", d.info.create_process.process_id, d.info.create_process.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set footer. */
|
/* Set footer. */
|
||||||
AppendReply(g_process_list_buffer, "</osdata>");
|
AppendReply(g_annex_buffer, "</osdata>");
|
||||||
|
|
||||||
|
g_annex_buffer_contents = AnnexBufferContents_Processes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copy out the process list. */
|
/* Copy out the process list. */
|
||||||
const u32 annex_len = std::strlen(g_process_list_buffer);
|
GetAnnexBufferContents(m_reply_packet, offset, length);
|
||||||
if (offset <= annex_len) {
|
|
||||||
if (offset + length < annex_len) {
|
|
||||||
m_reply_packet[0] = 'm';
|
|
||||||
std::memcpy(m_reply_packet + 1, g_process_list_buffer + offset, length);
|
|
||||||
m_reply_packet[1 + length] = 0;
|
|
||||||
} else {
|
|
||||||
const auto size = annex_len - offset;
|
|
||||||
|
|
||||||
m_reply_packet[0] = 'l';
|
|
||||||
std::memcpy(m_reply_packet + 1, g_process_list_buffer + offset, size);
|
|
||||||
m_reply_packet[1 + size] = 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
SetReply(m_reply_packet, "l");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
AMS_DMNT2_GDB_LOG_DEBUG("Not Implemented qxfer:osdata:read: %s\n", m_receive_packet);
|
AMS_DMNT2_GDB_LOG_DEBUG("Not Implemented qxfer:osdata:read: %s\n", m_receive_packet);
|
||||||
SetReplyError(m_reply_packet, "E01");
|
SetReplyError(m_reply_packet, "E01");
|
||||||
|
@ -974,54 +1034,63 @@ namespace ams::dmnt {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GdbServerImpl::qXferThreadsRead() {
|
bool GdbServerImpl::qXferThreadsRead() {
|
||||||
/* Set header. */
|
/* Handle the qXfer. */
|
||||||
SetReply(m_reply_packet, "l<threads>");
|
u32 offset, length;
|
||||||
|
|
||||||
/* Get thread list. */
|
/* Parse offset/length. */
|
||||||
s32 num_threads;
|
ParseOffsetLength(m_receive_packet, offset, length);
|
||||||
u64 thread_ids[0x80];
|
|
||||||
if (R_FAILED(svc::GetThreadList(std::addressof(num_threads), thread_ids, util::size(thread_ids), m_debug_handle))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cap thread limit at 128. */
|
/* Acquire access to the annex buffer. */
|
||||||
if (num_threads > 0x80) {
|
std::scoped_lock lk(g_annex_buffer_lock);
|
||||||
num_threads = 0x80;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add all threads. */
|
/* If doing a fresh read, generate the thread list. */
|
||||||
for (s32 i = 0; i < num_threads; ++i) {
|
if (offset == 0 || g_annex_buffer_contents != AnnexBufferContents_Threads) {
|
||||||
const auto thread_id = thread_ids[num_threads - 1 - i];
|
/* Set header. */
|
||||||
|
SetReply(g_annex_buffer, "<threads>");
|
||||||
|
|
||||||
/* Check that we can get the thread context. */
|
/* Get the thread list. */
|
||||||
svc::ThreadContext thread_context;
|
u64 thread_ids[DebugProcess::ThreadCountMax];
|
||||||
if (R_FAILED(svc::GetDebugThreadContext(std::addressof(thread_context), m_debug_handle, thread_id, svc::ThreadContextFlag_All))) {
|
s32 num_threads;
|
||||||
continue;
|
if (R_SUCCEEDED(m_debug_process.GetThreadList(std::addressof(num_threads), thread_ids, util::size(thread_ids)))) {
|
||||||
}
|
for (auto i = 0; i < num_threads; ++i) {
|
||||||
|
/* Check that we can get the thread context. */
|
||||||
|
{
|
||||||
|
svc::ThreadContext dummy_context;
|
||||||
|
if (R_FAILED(m_debug_process.GetThreadContext(std::addressof(dummy_context), thread_ids[i], svc::ThreadContextFlag_All))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Get the thread core. */
|
/* Get the thread core. */
|
||||||
u32 core = 0;
|
u32 core = 0;
|
||||||
{
|
m_debug_process.GetThreadCurrentCore(std::addressof(core), thread_ids[i]);
|
||||||
u64 dummy;
|
|
||||||
u32 val32;
|
/* TODO: `name=\"%s\"`? */
|
||||||
if (R_SUCCEEDED(svc::GetDebugThreadParam(std::addressof(dummy), std::addressof(val32), m_debug_handle, m_thread_id, svc::DebugThreadParam_CurrentCore))) {
|
AppendReply(g_annex_buffer, "<thread id=\"p%lx.%lx\" core=\"%u\"/>", m_process_id.value, thread_ids[i], core);
|
||||||
core = val32;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: `name=\"%s\"`? */
|
AppendReply(g_annex_buffer, "</threads>");
|
||||||
AppendReply(m_reply_packet, "<thread id=\"p%lx.%lx\" core=\"%u\"/>", m_target_process_id.value, thread_id, core);
|
|
||||||
|
g_annex_buffer_contents = AnnexBufferContents_Threads;
|
||||||
}
|
}
|
||||||
|
|
||||||
AppendReply(m_reply_packet, "</threads>");
|
/* Copy out the threads list. */
|
||||||
|
GetAnnexBufferContents(m_reply_packet, offset, length);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GdbServerImpl::QuestionMark() {
|
void GdbServerImpl::QuestionMark() {
|
||||||
/* TODO */
|
if (m_debug_process.IsValid()) {
|
||||||
AMS_DMNT2_GDB_LOG_DEBUG("Not Implemented QuestionMark\n");
|
if (m_debug_process.GetLastThreadId() == 0) {
|
||||||
SetReply(m_reply_packet, "W00");
|
SetReply(m_reply_packet, "X01");
|
||||||
|
} else {
|
||||||
|
this->SetStopReplyPacket(m_debug_process.GetLastSignal());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SetReply(m_reply_packet, "W00");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -16,6 +16,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "dmnt2_gdb_packet_io.hpp"
|
#include "dmnt2_gdb_packet_io.hpp"
|
||||||
|
#include "dmnt2_gdb_signal.hpp"
|
||||||
|
#include "dmnt2_debug_process.hpp"
|
||||||
|
|
||||||
namespace ams::dmnt {
|
namespace ams::dmnt {
|
||||||
|
|
||||||
|
@ -27,19 +29,6 @@ namespace ams::dmnt {
|
||||||
Exited,
|
Exited,
|
||||||
Destroyed,
|
Destroyed,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum GdbSignal {
|
|
||||||
GdbSignal_Signal0 = 0,
|
|
||||||
GdbSignal_Interrupt = 2,
|
|
||||||
GdbSignal_IllegalInstruction = 4,
|
|
||||||
GdbSignal_BreakpointTrap = 5,
|
|
||||||
GdbSignal_EmulationTrap = 7,
|
|
||||||
GdbSignal_ArithmeticException = 8,
|
|
||||||
GdbSignal_Killed = 9,
|
|
||||||
GdbSignal_BusError = 10,
|
|
||||||
GdbSignal_SegmentationFault = 11,
|
|
||||||
GdbSignal_BadSystemCall = 12,
|
|
||||||
};
|
|
||||||
private:
|
private:
|
||||||
int m_socket;
|
int m_socket;
|
||||||
HtcsSession m_session;
|
HtcsSession m_session;
|
||||||
|
@ -48,14 +37,10 @@ namespace ams::dmnt {
|
||||||
char *m_reply_packet{nullptr};
|
char *m_reply_packet{nullptr};
|
||||||
char m_buffer[GdbPacketBufferSize / 2];
|
char m_buffer[GdbPacketBufferSize / 2];
|
||||||
bool m_killed{false};
|
bool m_killed{false};
|
||||||
svc::Handle m_debug_handle{svc::InvalidHandle};
|
|
||||||
os::ThreadType m_events_thread;
|
os::ThreadType m_events_thread;
|
||||||
State m_state;
|
State m_state;
|
||||||
bool m_attached{false};
|
DebugProcess m_debug_process;
|
||||||
u64 m_thread_id{0};
|
|
||||||
os::ProcessId m_process_id{os::InvalidProcessId};
|
os::ProcessId m_process_id{os::InvalidProcessId};
|
||||||
os::ProcessId m_target_process_id{os::InvalidProcessId};
|
|
||||||
svc::DebugInfoCreateProcess m_create_process_info;
|
|
||||||
os::Event m_event;
|
os::Event m_event;
|
||||||
public:
|
public:
|
||||||
GdbServerImpl(int socket, void *thread_stack, size_t stack_size);
|
GdbServerImpl(int socket, void *thread_stack, size_t stack_size);
|
||||||
|
@ -68,9 +53,9 @@ namespace ams::dmnt {
|
||||||
void SendPacket(bool *out_break, const char *src) { return m_packet_io.SendPacket(out_break, src, std::addressof(m_session)); }
|
void SendPacket(bool *out_break, const char *src) { return m_packet_io.SendPacket(out_break, src, std::addressof(m_session)); }
|
||||||
char *ReceivePacket(bool *out_break, char *dst, size_t size) { return m_packet_io.ReceivePacket(out_break, dst, size, std::addressof(m_session)); }
|
char *ReceivePacket(bool *out_break, char *dst, size_t size) { return m_packet_io.ReceivePacket(out_break, dst, size, std::addressof(m_session)); }
|
||||||
private:
|
private:
|
||||||
bool HasDebugProcess() const { return m_debug_handle != svc::InvalidHandle; }
|
bool HasDebugProcess() const { return m_debug_process.IsValid(); }
|
||||||
bool Is64Bit() const { return (m_create_process_info.flags & svc::CreateProcessFlag_Is64Bit) == svc::CreateProcessFlag_Is64Bit; }
|
bool Is64Bit() const { return m_debug_process.Is64Bit(); }
|
||||||
bool Is64BitAddressSpace() const { return (m_create_process_info.flags & svc::CreateProcessFlag_AddressSpaceMask) == svc::CreateProcessFlag_AddressSpace64Bit; }
|
bool Is64BitAddressSpace() const { return m_debug_process.Is64BitAddressSpace(); }
|
||||||
private:
|
private:
|
||||||
static void DebugEventsThreadEntry(void *arg) { static_cast<GdbServerImpl *>(arg)->DebugEventsThread(); }
|
static void DebugEventsThreadEntry(void *arg) { static_cast<GdbServerImpl *>(arg)->DebugEventsThread(); }
|
||||||
void DebugEventsThread();
|
void DebugEventsThread();
|
||||||
|
@ -95,6 +80,7 @@ namespace ams::dmnt {
|
||||||
void qSupported();
|
void qSupported();
|
||||||
void qXfer();
|
void qXfer();
|
||||||
void qXferFeaturesRead();
|
void qXferFeaturesRead();
|
||||||
|
void qXferLibrariesRead();
|
||||||
void qXferOsdataRead();
|
void qXferOsdataRead();
|
||||||
bool qXferThreadsRead();
|
bool qXferThreadsRead();
|
||||||
|
|
||||||
|
|
34
stratosphere/dmnt.gen2/source/dmnt2_gdb_signal.hpp
Normal file
34
stratosphere/dmnt.gen2/source/dmnt2_gdb_signal.hpp
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::dmnt {
|
||||||
|
|
||||||
|
enum GdbSignal {
|
||||||
|
GdbSignal_Signal0 = 0,
|
||||||
|
GdbSignal_Interrupt = 2,
|
||||||
|
GdbSignal_IllegalInstruction = 4,
|
||||||
|
GdbSignal_BreakpointTrap = 5,
|
||||||
|
GdbSignal_EmulationTrap = 7,
|
||||||
|
GdbSignal_ArithmeticException = 8,
|
||||||
|
GdbSignal_Killed = 9,
|
||||||
|
GdbSignal_BusError = 10,
|
||||||
|
GdbSignal_SegmentationFault = 11,
|
||||||
|
GdbSignal_BadSystemCall = 12,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
62
stratosphere/dmnt.gen2/source/dmnt2_module_definition.hpp
Normal file
62
stratosphere/dmnt.gen2/source/dmnt2_module_definition.hpp
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::dmnt {
|
||||||
|
|
||||||
|
class ModuleDefinition {
|
||||||
|
NON_MOVEABLE(ModuleDefinition);
|
||||||
|
public:
|
||||||
|
static constexpr size_t PathLengthMax = 0x200;
|
||||||
|
private:
|
||||||
|
char m_name[PathLengthMax];
|
||||||
|
u64 m_address;
|
||||||
|
u64 m_size;
|
||||||
|
public:
|
||||||
|
constexpr ModuleDefinition() : m_name(), m_address(), m_size() { /* ... */ }
|
||||||
|
constexpr ~ModuleDefinition() { /* ... */ }
|
||||||
|
|
||||||
|
constexpr ModuleDefinition(const ModuleDefinition &rhs) = default;
|
||||||
|
constexpr ModuleDefinition &operator=(const ModuleDefinition &rhs) = default;
|
||||||
|
|
||||||
|
constexpr void Reset() {
|
||||||
|
m_name[0] = 0;
|
||||||
|
m_address = 0;
|
||||||
|
m_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool operator==(const ModuleDefinition &rhs) const {
|
||||||
|
return m_address == rhs.m_address && m_size == rhs.m_size && std::strcmp(m_name, rhs.m_name) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool operator!=(const ModuleDefinition &rhs) const {
|
||||||
|
return !(*this == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr char *GetNameBuffer() { return m_name; }
|
||||||
|
constexpr const char *GetName() const { return m_name; }
|
||||||
|
|
||||||
|
constexpr u64 GetAddress() const { return m_address; }
|
||||||
|
constexpr u64 GetSize() const { return m_size; }
|
||||||
|
|
||||||
|
constexpr void SetAddressSize(u64 address, u64 size) {
|
||||||
|
m_address = address;
|
||||||
|
m_size = size;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue