2021-07-20 09:29:00 +01:00
|
|
|
/*
|
2021-10-04 20:59:10 +01:00
|
|
|
* Copyright (c) Atmosphère-NX
|
2021-07-20 09:29:00 +01:00
|
|
|
*
|
|
|
|
* 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"
|
2021-07-22 08:51:28 +01:00
|
|
|
#include "dmnt2_software_breakpoint.hpp"
|
|
|
|
#include "dmnt2_hardware_breakpoint.hpp"
|
|
|
|
#include "dmnt2_hardware_watchpoint.hpp"
|
2021-07-20 09:29:00 +01:00
|
|
|
|
|
|
|
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:
|
2021-10-05 01:12:32 +01:00
|
|
|
os::NativeHandle m_debug_handle{os::InvalidNativeHandle};
|
2021-07-20 09:29:00 +01:00
|
|
|
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{};
|
2021-07-22 08:51:28 +01:00
|
|
|
u64 m_thread_id_override{};
|
|
|
|
u64 m_continue_thread_id{};
|
2021-11-02 00:18:13 +00:00
|
|
|
u64 m_preferred_debug_break_thread_id{};
|
2021-07-20 09:29:00 +01:00
|
|
|
GdbSignal m_last_signal{};
|
2021-07-22 08:51:28 +01:00
|
|
|
bool m_stepping{false};
|
2021-07-27 13:34:51 +01:00
|
|
|
bool m_use_hardware_single_step{false};
|
2021-07-20 09:29:00 +01:00
|
|
|
bool m_thread_valid[ThreadCountMax]{};
|
|
|
|
u64 m_thread_ids[ThreadCountMax]{};
|
|
|
|
osdbg::ThreadInfo m_thread_infos[ThreadCountMax]{};
|
|
|
|
svc::DebugInfoCreateProcess m_create_process_info{};
|
2021-07-22 08:51:28 +01:00
|
|
|
SoftwareBreakPointManager m_software_breakpoints;
|
|
|
|
HardwareBreakPointManager m_hardware_breakpoints;
|
|
|
|
HardwareWatchPointManager m_hardware_watchpoints;
|
|
|
|
BreakPointManager &m_step_breakpoints;
|
2021-07-20 09:29:00 +01:00
|
|
|
ModuleDefinition m_module_definitions[ModuleCountMax]{};
|
|
|
|
size_t m_module_count{};
|
|
|
|
size_t m_main_module{};
|
2021-11-01 19:05:47 +00:00
|
|
|
u64 m_process_alias_address{};
|
|
|
|
u64 m_process_alias_size{};
|
|
|
|
u64 m_process_heap_address{};
|
|
|
|
u64 m_process_heap_size{};
|
|
|
|
u64 m_process_aslr_address{};
|
|
|
|
u64 m_process_aslr_size{};
|
|
|
|
u64 m_process_stack_address{};
|
|
|
|
u64 m_process_stack_size{};
|
|
|
|
ncm::ProgramLocation m_program_location{};
|
|
|
|
cfg::OverrideStatus m_process_override_status{};
|
|
|
|
bool m_is_application{false};
|
2021-07-20 09:29:00 +01:00
|
|
|
public:
|
2021-07-27 13:34:51 +01:00
|
|
|
DebugProcess() : m_software_breakpoints(this), m_hardware_breakpoints(this), m_hardware_watchpoints(this), m_step_breakpoints(m_software_breakpoints) {
|
|
|
|
if (svc::IsKernelMesosphere()) {
|
|
|
|
uint64_t value = 0;
|
|
|
|
m_use_hardware_single_step = R_SUCCEEDED(::ams::svc::GetInfo(std::addressof(value), ::ams::svc::InfoType_MesosphereMeta, ::ams::svc::InvalidHandle, ::ams::svc::MesosphereMetaInfo_IsSingleStepEnabled)) && value != 0;
|
|
|
|
}
|
|
|
|
}
|
2021-07-20 09:29:00 +01:00
|
|
|
~DebugProcess() { this->Detach(); }
|
|
|
|
|
2021-10-05 01:12:32 +01:00
|
|
|
os::NativeHandle GetHandle() const { return m_debug_handle; }
|
2021-07-20 09:29:00 +01:00
|
|
|
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(); }
|
2021-11-01 06:25:35 +00:00
|
|
|
uintptr_t GetModuleBaseAddress(size_t ix) const { return m_module_definitions[ix].GetAddress(); }
|
|
|
|
uintptr_t GetModuleSize(size_t ix) const { return m_module_definitions[ix].GetSize(); }
|
2021-07-20 09:29:00 +01:00
|
|
|
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; }
|
|
|
|
|
2021-11-02 00:18:13 +00:00
|
|
|
u64 GetPreferredDebuggerBreakThreadId() { return m_preferred_debug_break_thread_id; }
|
|
|
|
|
2021-07-20 09:29:00 +01:00
|
|
|
void SetLastThreadId(u64 tid) {
|
|
|
|
m_last_thread_id = tid;
|
|
|
|
m_thread_id_override = tid;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetThreadIdOverride(u64 tid) {
|
2021-11-02 00:18:13 +00:00
|
|
|
m_thread_id_override = tid;
|
|
|
|
m_preferred_debug_break_thread_id = tid;
|
2021-07-20 09:29:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void SetDebugBreaked() {
|
|
|
|
m_status = ProcessStatus_DebugBreak;
|
|
|
|
}
|
2021-11-01 19:05:47 +00:00
|
|
|
|
|
|
|
u64 GetAliasRegionAddress() const { return m_process_alias_address; }
|
|
|
|
u64 GetAliasRegionSize() const { return m_process_alias_size; }
|
|
|
|
u64 GetHeapRegionAddress() const { return m_process_heap_address; }
|
|
|
|
u64 GetHeapRegionSize() const { return m_process_heap_size; }
|
|
|
|
u64 GetAslrRegionAddress() const { return m_process_aslr_address; }
|
|
|
|
u64 GetAslrRegionSize() const { return m_process_aslr_size; }
|
|
|
|
u64 GetStackRegionAddress() const { return m_process_stack_address; }
|
|
|
|
u64 GetStackRegionSize() const { return m_process_stack_size; }
|
|
|
|
|
|
|
|
const ncm::ProgramLocation &GetProgramLocation() const { return m_program_location; }
|
|
|
|
const cfg::OverrideStatus &GetOverrideStatus() const { return m_process_override_status; }
|
|
|
|
|
|
|
|
bool IsApplication() const { return m_is_application; }
|
2021-07-20 09:29:00 +01:00
|
|
|
public:
|
2021-11-01 06:57:28 +00:00
|
|
|
Result Attach(os::ProcessId process_id, bool start_process = false);
|
2021-07-20 09:29:00 +01:00
|
|
|
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);
|
|
|
|
|
2021-11-01 01:32:23 +00:00
|
|
|
Result QueryMemory(svc::MemoryInfo *out, uintptr_t address);
|
|
|
|
|
2021-07-22 08:51:28 +01:00
|
|
|
Result Continue();
|
|
|
|
Result Continue(u64 thread_id);
|
|
|
|
Result Step();
|
|
|
|
Result Step(u64 thread_id);
|
|
|
|
void ClearStep();
|
|
|
|
|
|
|
|
Result Break();
|
|
|
|
|
2021-07-23 13:29:39 +01:00
|
|
|
Result Terminate();
|
|
|
|
|
2021-07-22 08:51:28 +01:00
|
|
|
Result SetBreakPoint(uintptr_t address, size_t size, bool is_step);
|
|
|
|
Result ClearBreakPoint(uintptr_t address, size_t size);
|
|
|
|
|
|
|
|
Result SetHardwareBreakPoint(uintptr_t address, size_t size, bool is_step);
|
|
|
|
Result ClearHardwareBreakPoint(uintptr_t address, size_t size);
|
|
|
|
|
|
|
|
Result SetWatchPoint(u64 address, u64 size, bool read, bool write);
|
|
|
|
Result ClearWatchPoint(u64 address, u64 size);
|
|
|
|
Result GetWatchPointInfo(u64 address, bool &read, bool &write);
|
|
|
|
|
|
|
|
static bool IsValidWatchPoint(u64 address, u64 size);
|
|
|
|
|
2021-07-20 09:29:00 +01:00
|
|
|
Result GetThreadCurrentCore(u32 *out, u64 thread_id);
|
|
|
|
|
|
|
|
Result GetProcessDebugEvent(svc::DebugEventInfo *out);
|
2021-07-22 08:51:28 +01:00
|
|
|
|
|
|
|
void GetBranchTarget(svc::ThreadContext &ctx, u64 thread_id, u64 ¤t_pc, u64 &target);
|
2021-07-22 14:01:07 +01:00
|
|
|
|
|
|
|
Result CollectModules();
|
2021-11-02 00:18:13 +00:00
|
|
|
|
|
|
|
void GetThreadName(char *dst, u64 thread_id) const;
|
2021-07-20 09:29:00 +01:00
|
|
|
private:
|
|
|
|
Result Start();
|
|
|
|
|
2021-11-01 19:05:47 +00:00
|
|
|
void CollectProcessInfo();
|
|
|
|
|
2021-07-20 09:29:00 +01:00
|
|
|
s32 ThreadCreate(u64 thread_id);
|
|
|
|
void ThreadExit(u64 thread_id);
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|