diff --git a/stratosphere/creport/source/creport_crash_report.cpp b/stratosphere/creport/source/creport_crash_report.cpp index 88b560b77..130b20671 100644 --- a/stratosphere/creport/source/creport_crash_report.cpp +++ b/stratosphere/creport/source/creport_crash_report.cpp @@ -1,10 +1,105 @@ #include #include "creport_crash_report.hpp" +#include "creport_debug_types.hpp" void CrashReport::BuildReport(u64 pid, bool has_extra_info) { this->has_extra_info = has_extra_info; if (OpenProcess(pid)) { - /* TODO: Actually generate report... */ + ProcessExceptions(); + + /* TODO: More stuff here (sub_7100002260)... */ Close(); } +} + +void CrashReport::ProcessExceptions() { + if (!IsOpen()) { + return; + } + + DebugEventInfo d; + while (R_SUCCEEDED(svcGetDebugEvent((u8 *)&d, this->debug_handle))) { + switch (d.type) { + case DebugEventType::AttachProcess: + HandleAttachProcess(d); + break; + case DebugEventType::Exception: + HandleException(d); + break; + case DebugEventType::AttachThread: + case DebugEventType::ExitProcess: + case DebugEventType::ExitThread: + default: + break; + } + } +} + +void CrashReport::HandleAttachProcess(DebugEventInfo &d) { + this->process_info = d.info.attach_process; + if (kernelAbove500() && IsApplication()) { + /* Parse out user data. */ + MemoryInfo mi; + u32 pi; + u64 address = this->process_info.user_exception_context_address; + u64 userdata_address = 0; + u64 userdata_size = 0; + + if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, this->debug_handle, address))) { + return; + } + + /* Must be read or read-write */ + if ((mi.perm | Perm_W) != Perm_Rw) { + return; + } + + /* Must have space for both userdata address and userdata size. */ + if (address < mi.addr || mi.addr + mi.size < address + 2 * sizeof(u64)) { + return; + } + + /* Read userdata address. */ + if (R_FAILED(svcReadDebugProcessMemory(&userdata_address, this->debug_handle, address, sizeof(userdata_address)))) { + return; + } + + /* Validate userdata address. */ + if (userdata_address == 0 || userdata_address & 0xFFF) { + return; + } + + /* Read userdata size. */ + if (R_FAILED(svcReadDebugProcessMemory(&userdata_size, this->debug_handle, address + sizeof(userdata_address), sizeof(userdata_size)))) { + return; + } + + /* Cap userdata size. */ + if (userdata_size > 0x1000) { + userdata_size = 0x1000; + } + + /* Assign. */ + this->userdata_5x_address = userdata_address; + this->userdata_5x_size = userdata_size; + } +} + +void CrashReport::HandleException(DebugEventInfo &d) { + switch (d.info.exception.type) { + case DebugExceptionType::UndefinedInstruction: + case DebugExceptionType::InstructionAbort: + case DebugExceptionType::DataAbort: + case DebugExceptionType::AlignmentFault: + case DebugExceptionType::UserBreak: + case DebugExceptionType::BadSvc: + case DebugExceptionType::UnknownNine: + /* TODO: Handle these exceptions...creport seems to discard all but the latest exception? */ + break; + case DebugExceptionType::DebuggerAttached: + case DebugExceptionType::BreakPoint: + case DebugExceptionType::DebuggerBreak: + default: + break; + } } \ No newline at end of file diff --git a/stratosphere/creport/source/creport_crash_report.hpp b/stratosphere/creport/source/creport_crash_report.hpp index 0ae64e50c..1aa96e4e6 100644 --- a/stratosphere/creport/source/creport_crash_report.hpp +++ b/stratosphere/creport/source/creport_crash_report.hpp @@ -2,16 +2,24 @@ #include +#include "creport_debug_types.hpp" + class CrashReport { private: Handle debug_handle; bool has_extra_info; Result result; + /* Attach Process Info. */ + AttachProcessInfo process_info; + u64 userdata_5x_address; + u64 userdata_5x_size; + public: - CrashReport() : debug_handle(INVALID_HANDLE), result(0x4A2) { } + CrashReport() : debug_handle(INVALID_HANDLE), result(0xC6A8), process_info({0}) { } void BuildReport(u64 pid, bool has_extra_info); + void ProcessExceptions(); Result GetResult() { return this->result; @@ -21,10 +29,25 @@ class CrashReport { return R_SUCCEEDED(svcDebugActiveProcess(&debug_handle, pid)); } + bool IsOpen() { + return this->debug_handle != INVALID_HANDLE; + } + void Close() { - if (debug_handle != INVALID_HANDLE) { + if (IsOpen()) { svcCloseHandle(debug_handle); debug_handle = INVALID_HANDLE; } } + + bool IsApplication() { + return (process_info.flags & 0x40) != 0; + } + + bool Is64Bit() { + return (process_info.flags & 0x01) != 0; + } + private: + void HandleAttachProcess(DebugEventInfo &d); + void HandleException(DebugEventInfo &d); }; \ No newline at end of file diff --git a/stratosphere/creport/source/creport_debug_types.hpp b/stratosphere/creport/source/creport_debug_types.hpp new file mode 100644 index 000000000..b0e752ff7 --- /dev/null +++ b/stratosphere/creport/source/creport_debug_types.hpp @@ -0,0 +1,86 @@ +#pragma once +#include + +struct AttachProcessInfo { + u64 title_id; + u64 process_id; + char name[0xC]; + u32 flags; + u64 user_exception_context_address; /* 5.0.0+ */ +}; + +struct AttachThreadInfo { + u64 thread_id; + u64 tls_address; + u64 entrypoint; +}; + +/* TODO: ExitProcessInfo */ +/* TODO: ExitThreadInfo */ + +enum class DebugExceptionType : u32 { + UndefinedInstruction = 0, + InstructionAbort = 1, + DataAbort = 2, + AlignmentFault = 3, + DebuggerAttached = 4, + BreakPoint = 5, + UserBreak = 6, + DebuggerBreak = 7, + BadSvc = 8, + UnknownNine = 9, +}; + +struct UndefinedInstructionInfo { + u32 insn; +}; + +struct DataAbortInfo { + u64 address; +}; + +struct AlignmentFaultInfo { + u64 address; +}; + +struct BadSvcInfo { + u32 id; +}; + +union SpecificExceptionInfo { + UndefinedInstructionInfo undefined_instruction; + DataAbortInfo data_abort; + AlignmentFaultInfo alignment_fault; + BadSvcInfo bad_svc; + u64 raw; +}; + +static_assert(sizeof(SpecificExceptionInfo) == sizeof(u64), "Bad SpecificExceptionInfo definition!"); + +struct ExceptionInfo { + DebugExceptionType type; + u64 address; + SpecificExceptionInfo specific; +}; + + +enum class DebugEventType : u32 { + AttachProcess = 0, + AttachThread = 1, + ExitProcess = 2, + ExitThread = 3, + Exception = 4 +}; + +union DebugInfo { + AttachProcessInfo attach_process; + AttachThreadInfo attach_thread; + ExceptionInfo exception; +}; + +struct DebugEventInfo { + DebugEventType type; + u32 flags; + u64 thread_id; + DebugInfo info; +}; \ No newline at end of file