From 766097d0b7ae24e5196a8c32130c59cc2e6dfb73 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 25 May 2019 13:32:34 -0700 Subject: [PATCH] creport: dump tls/name on crash (closes #310) --- .../creport/source/creport_code_info.cpp | 66 +++++++------- .../creport/source/creport_code_info.hpp | 6 +- .../creport/source/creport_crash_report.cpp | 91 ++++++++++--------- .../creport/source/creport_crash_report.hpp | 44 +++++---- .../creport/source/creport_debug_types.hpp | 2 +- stratosphere/creport/source/creport_main.cpp | 26 +++--- .../creport/source/creport_thread_info.cpp | 83 ++++++++++++----- .../creport/source/creport_thread_info.hpp | 18 ++-- 8 files changed, 195 insertions(+), 141 deletions(-) diff --git a/stratosphere/creport/source/creport_code_info.cpp b/stratosphere/creport/source/creport_code_info.cpp index f59aaeaf1..70d9d21af 100644 --- a/stratosphere/creport/source/creport_code_info.cpp +++ b/stratosphere/creport/source/creport_code_info.cpp @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + #include #include @@ -25,7 +25,7 @@ void CodeList::SaveToFile(FILE *f_report) { for (unsigned int i = 0; i < this->code_count; i++) { fprintf(f_report, " Code Region %02u:\n", i); fprintf(f_report, " Address: %016lx-%016lx\n", this->code_infos[i].start_address, this->code_infos[i].end_address); - if (this->code_infos[i].name[0]) { + if (this->code_infos[i].name[0]) { fprintf(f_report, " Name: %s\n", this->code_infos[i].name); } CrashReport::Memdump(f_report, " Build Id: ", this->code_infos[i].build_id, sizeof(this->code_infos[i].build_id)); @@ -34,17 +34,17 @@ void CodeList::SaveToFile(FILE *f_report) { void CodeList::ReadCodeRegionsFromThreadInfo(Handle debug_handle, const ThreadInfo *thread) { u64 code_base; - + /* Try to add the thread's PC. */ if (TryFindCodeRegion(debug_handle, thread->GetPC(), &code_base)) { AddCodeRegion(debug_handle, code_base); } - + /* Try to add the thread's LR. */ if (TryFindCodeRegion(debug_handle, thread->GetLR(), &code_base)) { AddCodeRegion(debug_handle, code_base); } - + /* Try to add all the addresses in the thread's stacktrace. */ for (u32 i = 0; i < thread->GetStackTraceSize(); i++) { if (TryFindCodeRegion(debug_handle, thread->GetStackTrace(i), &code_base)) { @@ -60,7 +60,7 @@ void CodeList::AddCodeRegion(u64 debug_handle, u64 code_address) { return; } } - + /* Add all contiguous code regions. */ u64 cur_ptr = code_address; while (this->code_count < max_code_count) { @@ -69,32 +69,32 @@ void CodeList::AddCodeRegion(u64 debug_handle, u64 code_address) { if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, cur_ptr))) { break; } - + if (mi.perm == Perm_Rx) { /* Parse CodeInfo. */ this->code_infos[this->code_count].start_address = mi.addr; - this->code_infos[this->code_count].end_address = mi.addr + mi.size; + this->code_infos[this->code_count].end_address = mi.addr + mi.size; GetCodeInfoName(debug_handle, mi.addr, mi.addr + mi.size, this->code_infos[this->code_count].name); GetCodeInfoBuildId(debug_handle, mi.addr + mi.size, this->code_infos[this->code_count].build_id); if (this->code_infos[this->code_count].name[0] == '\x00') { - snprintf(this->code_infos[this->code_count].name, 0x1F, "[%02x%02x%02x%02x]", this->code_infos[this->code_count].build_id[0], - this->code_infos[this->code_count].build_id[1], - this->code_infos[this->code_count].build_id[2], + snprintf(this->code_infos[this->code_count].name, 0x1F, "[%02x%02x%02x%02x]", this->code_infos[this->code_count].build_id[0], + this->code_infos[this->code_count].build_id[1], + this->code_infos[this->code_count].build_id[2], this->code_infos[this->code_count].build_id[3]); } this->code_count++; } - + /* If we're out of readable memory, we're done reading code. */ if (mi.type == MemType_Unmapped || mi.type == MemType_Reserved) { break; } - + /* Verify we're not getting stuck in an infinite loop. */ if (mi.size == 0 || U64_MAX - mi.size <= cur_ptr) { break; } - + cur_ptr += mi.size; } } @@ -105,37 +105,37 @@ bool CodeList::TryFindCodeRegion(Handle debug_handle, u64 guess, u64 *address) { if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, guess))) { return false; } - + if (mi.perm == Perm_Rw) { guess = mi.addr - 4; if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, guess))) { return false; } } - + if (mi.perm == Perm_R) { guess = mi.addr - 4; if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, guess))) { return false; } } - + if (mi.perm != Perm_Rx) { return false; } - + /* Iterate backwards until we find the memory before the code region. */ while (mi.addr > 0) { if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, guess))) { return false; } - + if (mi.type == MemType_Unmapped) { /* Code region will be at the end of the unmapped region preceding it. */ *address = mi.addr + mi.size; return true; } - + guess = mi.addr - 4; } return false; @@ -143,47 +143,47 @@ bool CodeList::TryFindCodeRegion(Handle debug_handle, u64 guess, u64 *address) { void CodeList::GetCodeInfoName(u64 debug_handle, u64 rx_address, u64 rodata_addr, char *name) { char name_in_proc[0x200]; - + /* Clear name. */ memset(name, 0, 0x20); - + /* Check whether this NSO *has* a name... */ { u64 rodata_start[0x20/sizeof(u64)]; MemoryInfo mi; u32 pi; u64 rw_address; - + /* Verify .rodata is read-only. */ if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, rodata_addr)) || mi.perm != Perm_R) { return; } - + /* rwdata is after rodata. */ rw_address = mi.addr + mi.size; - + /* Read start of .rodata. */ if (R_FAILED(svcReadDebugProcessMemory(rodata_start, debug_handle, rodata_addr, sizeof(rodata_start)))) { return; } - + /* Check if name section is present. */ if (rodata_start[0] == (rw_address - rx_address)) { return; } } - + /* Read name out of .rodata. */ if (R_FAILED(svcReadDebugProcessMemory(name_in_proc, debug_handle, rodata_addr + 8, sizeof(name_in_proc)))) { return; } - + /* Start after last slash in path. */ int ofs = strnlen(name_in_proc, sizeof(name_in_proc)); while (ofs >= 0 && name_in_proc[ofs] != '/' && name_in_proc[ofs] != '\\') { ofs--; } - + strncpy(name, name_in_proc + ofs + 1, 0x20); name[0x1F] = '\x00'; } @@ -191,22 +191,22 @@ void CodeList::GetCodeInfoName(u64 debug_handle, u64 rx_address, u64 rodata_addr void CodeList::GetCodeInfoBuildId(u64 debug_handle, u64 rodata_addr, u8 *build_id) { MemoryInfo mi; u32 pi; - + /* Clear build id. */ memset(build_id, 0, 0x20); - + /* Verify .rodata is read-only. */ if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, rodata_addr)) || mi.perm != Perm_R) { return; } - + /* We want to read the last two pages of .rodata. */ u8 last_pages[0x2000]; size_t last_pages_size = mi.size >= 0x2000 ? 0x2000 : 0x1000; if (R_FAILED(svcReadDebugProcessMemory(last_pages, debug_handle, mi.addr + mi.size - last_pages_size, last_pages_size))) { return; } - + /* Find GNU\x00 to locate start of Build ID. */ for (int ofs = last_pages_size - 0x24; ofs >= 0; ofs--) { if (memcmp(last_pages + ofs, "GNU\x00", 4) == 0) { diff --git a/stratosphere/creport/source/creport_code_info.hpp b/stratosphere/creport/source/creport_code_info.hpp index a46f9d75d..bd62a2c13 100644 --- a/stratosphere/creport/source/creport_code_info.hpp +++ b/stratosphere/creport/source/creport_code_info.hpp @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + #pragma once #include #include @@ -33,10 +33,10 @@ class CodeList { static const size_t max_code_count = 0x60; u32 code_count = 0; CodeInfo code_infos[max_code_count]; - + /* For pretty-printing. */ char address_str_buf[0x280]; - public: + public: void ReadCodeRegionsFromThreadInfo(Handle debug_handle, const ThreadInfo *thread); const char *GetFormattedAddressString(u64 address); void SaveToFile(FILE *f_report); diff --git a/stratosphere/creport/source/creport_crash_report.cpp b/stratosphere/creport/source/creport_crash_report.cpp index 1e389a3ad..df0031276 100644 --- a/stratosphere/creport/source/creport_crash_report.cpp +++ b/stratosphere/creport/source/creport_crash_report.cpp @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + #include #include #include @@ -28,21 +28,21 @@ void CrashReport::BuildReport(u64 pid, bool has_extra_info) { if (OpenProcess(pid)) { ProcessExceptions(); this->code_list.ReadCodeRegionsFromThreadInfo(this->debug_handle, &this->crashed_thread_info); - this->thread_list.ReadThreadsFromProcess(this->debug_handle, Is64Bit()); + this->thread_list.ReadThreadsFromProcess(this->thread_tls_map, this->debug_handle, Is64Bit()); this->crashed_thread_info.SetCodeList(&this->code_list); this->thread_list.SetCodeList(&this->code_list); - + if (IsApplication()) { ProcessDyingMessage(); } - + /* Real creport only does this if application, but there's no reason not to do it all the time. */ for (u32 i = 0; i < this->thread_list.GetThreadCount(); i++) { this->code_list.ReadCodeRegionsFromThreadInfo(this->debug_handle, this->thread_list.GetThreadInfo(i)); } - + /* Real creport builds the report here. We do it later. */ - + Close(); } } @@ -50,29 +50,29 @@ void CrashReport::BuildReport(u64 pid, bool has_extra_info) { FatalContext *CrashReport::GetFatalContext() { FatalContext *ctx = new FatalContext; *ctx = (FatalContext){0}; - + ctx->is_aarch32 = false; ctx->type = static_cast(this->exception_info.type); - + for (size_t i = 0; i < 29; i++) { ctx->aarch64_ctx.x[i] = this->crashed_thread_info.context.cpu_gprs[i].x; } ctx->aarch64_ctx.fp = this->crashed_thread_info.context.fp; ctx->aarch64_ctx.lr = this->crashed_thread_info.context.lr; ctx->aarch64_ctx.pc = this->crashed_thread_info.context.pc.x; - + ctx->aarch64_ctx.stack_trace_size = this->crashed_thread_info.stack_trace_size; for (size_t i = 0; i < ctx->aarch64_ctx.stack_trace_size; i++) { ctx->aarch64_ctx.stack_trace[i] = this->crashed_thread_info.stack_trace[i]; } - + if (this->code_list.code_count) { ctx->aarch64_ctx.start_address = this->code_list.code_infos[0].start_address; } - + /* For ams fatal... */ ctx->aarch64_ctx.afsr0 = this->process_info.title_id; - + return ctx; } @@ -80,7 +80,7 @@ void CrashReport::ProcessExceptions() { if (!IsOpen()) { return; } - + DebugEventInfo d; while (R_SUCCEEDED(svcGetDebugEvent((u8 *)&d, this->debug_handle))) { switch (d.type) { @@ -91,12 +91,16 @@ void CrashReport::ProcessExceptions() { HandleException(d); break; case DebugEventType::AttachThread: + HandleAttachThread(d); case DebugEventType::ExitProcess: case DebugEventType::ExitThread: default: break; } } + + /* Parse crashing thread info. */ + this->crashed_thread_info.ReadFromProcess(this->thread_tls_map, this->debug_handle, this->crashed_thread_id, Is64Bit()); } void CrashReport::HandleAttachProcess(DebugEventInfo &d) { @@ -106,31 +110,31 @@ void CrashReport::HandleAttachProcess(DebugEventInfo &d) { u64 address = this->process_info.user_exception_context_address; u64 userdata_address = 0; u64 userdata_size = 0; - + if (!IsAddressReadable(address, sizeof(userdata_address) + sizeof(userdata_size))) { 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 > sizeof(this->dying_message)) { userdata_size = sizeof(this->dying_message); } - + /* Assign. */ this->dying_message_address = userdata_address; this->dying_message_size = userdata_size; @@ -180,8 +184,11 @@ void CrashReport::HandleException(DebugEventInfo &d) { return; } this->exception_info = d.info.exception; - /* Parse crashing thread info. */ - this->crashed_thread_info.ReadFromProcess(this->debug_handle, d.thread_id, Is64Bit()); + this->crashed_thread_id = d.thread_id; +} + +void CrashReport::HandleAttachThread(DebugEventInfo &d) { + this->thread_tls_map[d.info.attach_thread.thread_id] = d.info.attach_thread.tls_address; } void CrashReport::ProcessDyingMessage() { @@ -189,7 +196,7 @@ void CrashReport::ProcessDyingMessage() { if ((GetRuntimeFirmwareVersion() < FirmwareVersion_500)) { return; } - + /* Validate the message address/size. */ if (this->dying_message_address == 0 || this->dying_message_address & 0xFFF) { return; @@ -197,36 +204,36 @@ void CrashReport::ProcessDyingMessage() { if (this->dying_message_size > sizeof(this->dying_message)) { return; } - + /* Validate that the report isn't garbage. */ if (!IsOpen() || !WasSuccessful()) { return; } - + if (!IsAddressReadable(this->dying_message_address, this->dying_message_size)) { return; } - + svcReadDebugProcessMemory(this->dying_message, this->debug_handle, this->dying_message_address, this->dying_message_size); } bool CrashReport::IsAddressReadable(u64 address, u64 size, MemoryInfo *o_mi) { MemoryInfo mi; u32 pi; - + if (o_mi == NULL) { o_mi = &mi; } - + if (R_FAILED(svcQueryDebugProcessMemory(o_mi, &pi, this->debug_handle, address))) { return false; } - + /* Must be read or read-write */ if ((o_mi->perm | Perm_W) != Perm_Rw) { return false; } - + /* Must have space for both userdata address and userdata size. */ if (address < o_mi->addr || o_mi->addr + o_mi->size < address + size) { return false; @@ -237,7 +244,7 @@ bool CrashReport::IsAddressReadable(u64 address, u64 size, MemoryInfo *o_mi) { bool CrashReport::GetCurrentTime(u64 *out) { *out = 0; - + /* Verify that pcv isn't dead. */ { bool has_time_service; @@ -254,7 +261,7 @@ bool CrashReport::GetCurrentTime(u64 *out) { return false; } } - + /* Try to get the current time. */ bool success = true; DoWithSmSession([&]() { @@ -268,7 +275,7 @@ bool CrashReport::GetCurrentTime(u64 *out) { } void CrashReport::EnsureReportDirectories() { - char path[FS_MAX_PATH]; + char path[FS_MAX_PATH]; strcpy(path, "sdmc:/atmosphere"); mkdir(path, S_IRWXU); strcat(path, "/crash_reports"); @@ -280,16 +287,16 @@ void CrashReport::EnsureReportDirectories() { void CrashReport::SaveReport() { /* Save the report to the SD card. */ char report_path[FS_MAX_PATH]; - + /* Ensure path exists. */ EnsureReportDirectories(); - + /* Get a timestamp. */ u64 timestamp; if (!GetCurrentTime(×tamp)) { timestamp = svcGetSystemTick(); } - + /* Open report file. */ snprintf(report_path, sizeof(report_path) - 1, "sdmc:/atmosphere/crash_reports/%011lu_%016lx.log", timestamp, process_info.title_id); FILE *f_report = fopen(report_path, "w"); @@ -298,7 +305,7 @@ void CrashReport::SaveReport() { } this->SaveToFile(f_report); fclose(f_report); - + /* Dump threads. */ snprintf(report_path, sizeof(report_path) - 1, "sdmc:/atmosphere/crash_reports/dumps/%011lu_%016lx_thread_info.bin", timestamp, process_info.title_id); f_report = fopen(report_path, "wb"); @@ -308,9 +315,9 @@ void CrashReport::SaveReport() { void CrashReport::SaveToFile(FILE *f_report) { char buf[0x10] = {0}; - fprintf(f_report, "Atmosphère Crash Report (v1.2):\n"); + fprintf(f_report, "Atmosphère Crash Report (v1.3):\n"); fprintf(f_report, "Result: 0x%X (2%03d-%04d)\n\n", this->result, R_MODULE(this->result), R_DESCRIPTION(this->result)); - + /* Process Info. */ memcpy(buf, this->process_info.name, sizeof(this->process_info.name)); fprintf(f_report, "Process Info:\n"); @@ -321,7 +328,7 @@ void CrashReport::SaveToFile(FILE *f_report) { if ((GetRuntimeFirmwareVersion() >= FirmwareVersion_500)) { fprintf(f_report, " User Exception Address: %s\n", this->code_list.GetFormattedAddressString(this->process_info.user_exception_context_address)); } - + fprintf(f_report, "Exception Info:\n"); fprintf(f_report, " Type: %s\n", GetDebugExceptionTypeStr(this->exception_info.type)); fprintf(f_report, " Address: %s\n", this->code_list.GetFormattedAddressString(this->exception_info.address)); @@ -331,7 +338,7 @@ void CrashReport::SaveToFile(FILE *f_report) { break; case DebugExceptionType::DataAbort: case DebugExceptionType::AlignmentFault: - if (this->exception_info.specific.raw != this->exception_info.address) { + if (this->exception_info.specific.raw != this->exception_info.address) { fprintf(f_report, " Fault Address: %s\n", this->code_list.GetFormattedAddressString(this->exception_info.specific.raw)); } break; @@ -346,10 +353,10 @@ void CrashReport::SaveToFile(FILE *f_report) { default: break; } - + fprintf(f_report, "Crashed Thread Info:\n"); this->crashed_thread_info.SaveToFile(f_report); - + if ((GetRuntimeFirmwareVersion() >= FirmwareVersion_500)) { if (this->dying_message_size) { fprintf(f_report, "Dying Message Info:\n"); diff --git a/stratosphere/creport/source/creport_crash_report.hpp b/stratosphere/creport/source/creport_crash_report.hpp index 790725208..7c7e2e706 100644 --- a/stratosphere/creport/source/creport_crash_report.hpp +++ b/stratosphere/creport/source/creport_crash_report.hpp @@ -13,12 +13,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + #pragma once #include #include #include +#include #include "creport_debug_types.hpp" #include "creport_thread_info.hpp" @@ -29,63 +30,67 @@ class CrashReport { Handle debug_handle = INVALID_HANDLE; bool has_extra_info; Result result = ResultCreportIncompleteReport; - - /* Attach Process Info. */ + + /* Attach Process Info. */ AttachProcessInfo process_info{}; u64 dying_message_address = 0; u64 dying_message_size = 0; u8 dying_message[0x1000]{}; - + static_assert(sizeof(dying_message) == 0x1000, "Incorrect definition for dying message!"); - + /* Exception Info. */ ExceptionInfo exception_info{}; + u64 crashed_thread_id = 0; ThreadInfo crashed_thread_info; - + /* Extra Info. */ CodeList code_list; ThreadList thread_list; - + + /* Meta, used for building list. */ + std::map thread_tls_map; + public: void BuildReport(u64 pid, bool has_extra_info); FatalContext *GetFatalContext(); void SaveReport(); - + bool IsAddressReadable(u64 address, u64 size, MemoryInfo *mi = NULL); - + static void Memdump(FILE *f, const char *prefix, const void *data, size_t size); - + Result GetResult() { return this->result; } - + bool WasSuccessful() { return this->result != ResultCreportIncompleteReport; } - + bool OpenProcess(u64 pid) { return R_SUCCEEDED(svcDebugActiveProcess(&debug_handle, pid)); } - + bool IsOpen() { return this->debug_handle != INVALID_HANDLE; } - + void Close() { 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; } - + bool IsUserBreak() { return this->exception_info.type == DebugExceptionType::UserBreak; } @@ -94,9 +99,10 @@ class CrashReport { void ProcessDyingMessage(); void HandleAttachProcess(DebugEventInfo &d); void HandleException(DebugEventInfo &d); - + void HandleAttachThread(DebugEventInfo &d); + void SaveToFile(FILE *f); - + void EnsureReportDirectories(); bool GetCurrentTime(u64 *out); }; diff --git a/stratosphere/creport/source/creport_debug_types.hpp b/stratosphere/creport/source/creport_debug_types.hpp index d3243d168..e39520fc4 100644 --- a/stratosphere/creport/source/creport_debug_types.hpp +++ b/stratosphere/creport/source/creport_debug_types.hpp @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + #pragma once #include diff --git a/stratosphere/creport/source/creport_main.cpp b/stratosphere/creport/source/creport_main.cpp index a2270edc2..0a9c0943d 100644 --- a/stratosphere/creport/source/creport_main.cpp +++ b/stratosphere/creport/source/creport_main.cpp @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + #include #include #include @@ -34,7 +34,7 @@ extern "C" { #define INNER_HEAP_SIZE 0x100000 size_t nx_inner_heap_size = INNER_HEAP_SIZE; char nx_inner_heap[INNER_HEAP_SIZE]; - + void __libnx_initheap(void); void __appInit(void); void __appExit(void); @@ -66,16 +66,16 @@ void __libnx_initheap(void) { void __appInit(void) { Result rc; - + SetFirmwareVersionForLibnx(); - + DoWithSmSession([&]() { rc = fsInitialize(); if (R_FAILED(rc)) { fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_FS)); } }); - + rc = fsdevMountSdmc(); if (R_FAILED(rc)) { fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_FS)); @@ -114,22 +114,22 @@ int main(int argc, char **argv) { return 0; } } - + /* Parse crashed PID. */ u64 crashed_pid = creport_parse_u64(argv[0]); - + /* Try to debug the crashed process. */ g_Creport.BuildReport(crashed_pid, argv[1][0] == '1'); if (g_Creport.WasSuccessful()) { g_Creport.SaveReport(); - + DoWithSmSession([&]() { if (R_SUCCEEDED(nsdevInitialize())) { nsdevTerminateProcess(crashed_pid); nsdevExit(); } }); - + /* Don't fatal if we have extra info. */ if ((GetRuntimeFirmwareVersion() >= FirmwareVersion_500)) { if (g_Creport.IsApplication()) { @@ -138,15 +138,15 @@ int main(int argc, char **argv) { } else if (argv[1][0] == '1') { return 0; } - + /* Also don't fatal if we're a user break. */ if (g_Creport.IsUserBreak()) { return 0; } - + FatalContext *ctx = g_Creport.GetFatalContext(); - + fatalWithContext(g_Creport.GetResult(), FatalType_ErrorScreen, ctx); } - + } \ No newline at end of file diff --git a/stratosphere/creport/source/creport_thread_info.cpp b/stratosphere/creport/source/creport_thread_info.cpp index 6bbf8a734..7f9e3f69a 100644 --- a/stratosphere/creport/source/creport_thread_info.cpp +++ b/stratosphere/creport/source/creport_thread_info.cpp @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + #include #include @@ -22,12 +22,15 @@ void ThreadInfo::SaveToFile(FILE *f_report) { fprintf(f_report, " Thread ID: %016lx\n", this->thread_id); - if (stack_top) { + if (strcmp(name, "") != 0) { + fprintf(f_report, " Thread Name: %s\n", this->name); + } + if (stack_top) { fprintf(f_report, " Stack: %016lx-%016lx\n", this->stack_bottom, this->stack_top); } fprintf(f_report, " Registers:\n"); { - for (unsigned int i = 0; i <= 28; i++) { + for (unsigned int i = 0; i <= 28; i++) { fprintf(f_report, " X[%02u]: %s\n", i, this->code_list->GetFormattedAddressString(this->context.cpu_gprs[i].x)); } fprintf(f_report, " FP: %s\n", this->code_list->GetFormattedAddressString(this->context.fp)); @@ -39,11 +42,21 @@ void ThreadInfo::SaveToFile(FILE *f_report) { for (unsigned int i = 0; i < this->stack_trace_size; i++) { fprintf(f_report, " ReturnAddress[%02u]: %s\n", i, this->code_list->GetFormattedAddressString(this->stack_trace[i])); } + if (this->tls_address != 0) { + fprintf(f_report, " TLS Address: %016lx\n", this->tls_address); + fprintf(f_report, " TLS Dump: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n"); + for (size_t i = 0; i < 0x10; i++) { + const u32 ofs = i * 0x10; + fprintf(f_report, " %012lx %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + this->tls_address + ofs, this->tls[ofs + 0], this->tls[ofs + 1], this->tls[ofs + 2], this->tls[ofs + 3], this->tls[ofs + 4], this->tls[ofs + 5], this->tls[ofs + 6], this->tls[ofs + 7], + this->tls[ofs + 8], this->tls[ofs + 9], this->tls[ofs + 10], this->tls[ofs + 11], this->tls[ofs + 12], this->tls[ofs + 13], this->tls[ofs + 14], this->tls[ofs + 15]); + } + } } -bool ThreadInfo::ReadFromProcess(Handle debug_handle, u64 thread_id, bool is_64_bit) { +bool ThreadInfo::ReadFromProcess(std::map &tls_map, Handle debug_handle, u64 thread_id, bool is_64_bit) { this->thread_id = thread_id; - + /* Verify that the thread is running or waiting. */ { u64 _; @@ -51,43 +64,63 @@ bool ThreadInfo::ReadFromProcess(Handle debug_handle, u64 thread_id, bool is_64_ if (R_FAILED(svcGetDebugThreadParam(&_, &thread_state, debug_handle, this->thread_id, DebugThreadParam_State))) { return false; } - + if (thread_state > 1) { return false; } } - + /* Get the thread context. */ if (R_FAILED(svcGetDebugThreadContext(&this->context, debug_handle, this->thread_id, 0xF))) { return false; } - + /* Don't try to parse stack frames if 32-bit. */ if (!is_64_bit) { return true; } - + + /* Parse information from TLS if present. */ + if (tls_map.find(thread_id) != tls_map.end()) { + this->tls_address = tls_map[thread_id]; + u8 thread_tls[0x200]; + if (R_SUCCEEDED(svcReadDebugProcessMemory(thread_tls, debug_handle, this->tls_address, sizeof(thread_tls)))) { + std::memcpy(this->tls, thread_tls, sizeof(this->tls)); + /* Try to detect libnx threads, and skip name parsing then. */ + if (*(reinterpret_cast(&thread_tls[0x1E0])) != 0x21545624) { + u8 thread_type[0x1D0]; + const u64 thread_type_addr = *(reinterpret_cast(&thread_tls[0x1F8])); + if (R_SUCCEEDED(svcReadDebugProcessMemory(thread_type, debug_handle, thread_type_addr, sizeof(thread_type)))) { + /* Check thread name is actually at thread name. */ + if (*(reinterpret_cast(&thread_type[0x1A8])) == thread_type_addr + 0x188) { + std::memcpy(this->name, thread_type + 0x188, 0x20); + } + } + } + } + } + /* Try to locate stack top/bottom. */ TryGetStackInfo(debug_handle); - + u64 cur_fp = this->context.fp; for (unsigned int i = 0; i < sizeof(this->stack_trace)/sizeof(u64); i++) { /* Validate the current frame. */ if (cur_fp == 0 || (cur_fp & 0xF)) { break; } - + /* Read a new frame. */ StackFrame cur_frame; if (R_FAILED(svcReadDebugProcessMemory(&cur_frame, debug_handle, cur_fp, sizeof(StackFrame)))) { break; } - + /* Advance to the next frame. */ this->stack_trace[this->stack_trace_size++] = cur_frame.lr; cur_fp = cur_frame.fp; } - + return true; } @@ -97,19 +130,19 @@ void ThreadInfo::TryGetStackInfo(Handle debug_handle) { if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, this->context.sp))) { return; } - + /* Check if sp points into the stack. */ if (mi.type == MemType_MappedMemory) { this->stack_bottom = mi.addr; this->stack_top = mi.addr + mi.size; return; } - + /* It's possible that sp is below the stack... */ if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, mi.addr + mi.size))) { return; } - + if (mi.type == MemType_MappedMemory) { this->stack_bottom = mi.addr; this->stack_top = mi.addr + mi.size; @@ -119,7 +152,11 @@ void ThreadInfo::TryGetStackInfo(Handle debug_handle) { void ThreadInfo::DumpBinary(FILE *f_bin) { fwrite(&this->thread_id, sizeof(this->thread_id), 1, f_bin); fwrite(&this->context, sizeof(this->context), 1, f_bin); - + + fwrite(&this->tls_address, sizeof(this->tls_address), 1, f_bin); + fwrite(&this->tls, sizeof(this->tls), 1, f_bin); + fwrite(&this->name, sizeof(this->name), 1, f_bin); + u64 sts = this->stack_trace_size; fwrite(&sts, sizeof(sts), 1, f_bin); fwrite(this->stack_trace, sizeof(u64), this->stack_trace_size, f_bin); @@ -128,7 +165,7 @@ void ThreadInfo::DumpBinary(FILE *f_bin) { } void ThreadList::DumpBinary(FILE *f_bin, u64 crashed_id) { - u32 magic = 0x30495444; /* 'DTI0' */ + u32 magic = 0x31495444; /* 'DTI1' */ fwrite(&magic, sizeof(magic), 1, f_bin); fwrite(&this->thread_count, sizeof(u32), 1, f_bin); fwrite(&crashed_id, sizeof(crashed_id), 1, f_bin); @@ -145,21 +182,21 @@ void ThreadList::SaveToFile(FILE *f_report) { } } -void ThreadList::ReadThreadsFromProcess(Handle debug_handle, bool is_64_bit) { +void ThreadList::ReadThreadsFromProcess(std::map &tls_map, Handle debug_handle, bool is_64_bit) { u32 thread_count; u64 thread_ids[max_thread_count]; - + if (R_FAILED(svcGetThreadList(&thread_count, thread_ids, max_thread_count, debug_handle))) { this->thread_count = 0; return; } - + if (thread_count > max_thread_count) { thread_count = max_thread_count; } - + for (unsigned int i = 0; i < thread_count; i++) { - if (this->thread_infos[this->thread_count].ReadFromProcess(debug_handle, thread_ids[this->thread_count], is_64_bit)) { + if (this->thread_infos[this->thread_count].ReadFromProcess(tls_map, debug_handle, thread_ids[this->thread_count], is_64_bit)) { this->thread_count++; } } diff --git a/stratosphere/creport/source/creport_thread_info.hpp b/stratosphere/creport/source/creport_thread_info.hpp index 7649d5c75..bec1f6a4f 100644 --- a/stratosphere/creport/source/creport_thread_info.hpp +++ b/stratosphere/creport/source/creport_thread_info.hpp @@ -13,10 +13,11 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + #pragma once #include #include +#include #include "creport_debug_types.hpp" @@ -30,15 +31,18 @@ class ThreadInfo { u64 stack_bottom = 0; u64 stack_trace[0x20]{}; u32 stack_trace_size = 0; + u64 tls_address = 0; + u8 tls[0x100]{}; + char name[0x40]{}; CodeList *code_list; - public: + public: u64 GetPC() const { return context.pc.x; } u64 GetLR() const { return context.lr; } u64 GetId() const { return thread_id; } u32 GetStackTraceSize() const { return stack_trace_size; } u64 GetStackTrace(u32 i) const { return stack_trace[i]; } - - bool ReadFromProcess(Handle debug_handle, u64 thread_id, bool is_64_bit); + + bool ReadFromProcess(std::map &tls_map, Handle debug_handle, u64 thread_id, bool is_64_bit); void SaveToFile(FILE *f_report); void DumpBinary(FILE *f_bin); void SetCodeList(CodeList *cl) { this->code_list = cl; } @@ -54,11 +58,11 @@ class ThreadList { public: u32 GetThreadCount() const { return thread_count; } const ThreadInfo *GetThreadInfo(u32 i) const { return &thread_infos[i]; } - + void SaveToFile(FILE *f_report); void DumpBinary(FILE *f_bin, u64 crashed_id); - void ReadThreadsFromProcess(Handle debug_handle, bool is_64_bit); - void SetCodeList(CodeList *cl) { + void ReadThreadsFromProcess(std::map &tls_map, Handle debug_handle, bool is_64_bit); + void SetCodeList(CodeList *cl) { for (u32 i = 0; i < thread_count; i++) { thread_infos[i].SetCodeList(cl); }