diff --git a/stratosphere/creport/source/creport_crash_report.cpp b/stratosphere/creport/source/creport_crash_report.cpp index bbb405adb..0cb467600 100644 --- a/stratosphere/creport/source/creport_crash_report.cpp +++ b/stratosphere/creport/source/creport_crash_report.cpp @@ -101,11 +101,9 @@ namespace ams::creport { m_has_extra_info = has_extra_info; if (this->OpenProcess(process_id)) { - ON_SCOPE_EXIT { this->Close(); }; - /* Parse info from the crashed process. */ this->ProcessExceptions(); - m_module_list->FindModulesFromThreadInfo(m_debug_handle, m_crashed_thread); + m_module_list->FindModulesFromThreadInfo(m_debug_handle, m_crashed_thread, this->Is64Bit()); m_thread_list->ReadFromProcess(m_debug_handle, m_thread_tls_map, this->Is64Bit()); /* Associate module list to threads. */ @@ -120,7 +118,7 @@ namespace ams::creport { /* Nintendo's creport finds extra modules by looking at all threads if application, */ /* but there's no reason for us not to always go looking. */ for (size_t i = 0; i < m_thread_list->GetThreadCount(); i++) { - m_module_list->FindModulesFromThreadInfo(m_debug_handle, m_thread_list->GetThreadInfo(i)); + m_module_list->FindModulesFromThreadInfo(m_debug_handle, m_thread_list->GetThreadInfo(i), this->Is64Bit()); } /* Cache the module base address to send to fatal. */ @@ -324,6 +322,11 @@ namespace ams::creport { } } + /* If we're open, we need to close here. */ + if (this->IsOpen()) { + this->Close(); + } + /* Finalize our heap. */ std::destroy_at(m_module_list); std::destroy_at(m_thread_list); @@ -358,7 +361,7 @@ namespace ams::creport { } void CrashReport::SaveToFile(ScopedFile &file) { - file.WriteFormat("Atmosphère Crash Report (v1.6):\n"); + file.WriteFormat("Atmosphère Crash Report (v1.7):\n"); file.WriteFormat("Result: 0x%X (2%03d-%04d)\n\n", m_result.GetValue(), m_result.GetModule(), m_result.GetDescription()); diff --git a/stratosphere/creport/source/creport_main.cpp b/stratosphere/creport/source/creport_main.cpp index bd24020ad..5e5d38e09 100644 --- a/stratosphere/creport/source/creport_main.cpp +++ b/stratosphere/creport/source/creport_main.cpp @@ -103,13 +103,17 @@ namespace ams { g_crash_report.Initialize(); /* Try to debug the crashed process. */ - g_crash_report.BuildReport(crashed_pid, has_extra_info); - if (!g_crash_report.IsComplete()) { - return; - } + { + g_crash_report.BuildReport(crashed_pid, has_extra_info); + ON_SCOPE_EXIT { if (g_crash_report.IsOpen()) { g_crash_report.Close(); } }; - /* Save report to file. */ - g_crash_report.SaveReport(enable_screenshot); + if (!g_crash_report.IsComplete()) { + return; + } + + /* Save report to file. */ + g_crash_report.SaveReport(enable_screenshot); + } /* Try to terminate the process, if we should. */ const auto fw_ver = hos::GetVersion(); diff --git a/stratosphere/creport/source/creport_modules.cpp b/stratosphere/creport/source/creport_modules.cpp index 6adb970fe..c7ceecdd9 100644 --- a/stratosphere/creport/source/creport_modules.cpp +++ b/stratosphere/creport/source/creport_modules.cpp @@ -58,26 +58,26 @@ namespace ams::creport { } } - void ModuleList::FindModulesFromThreadInfo(os::NativeHandle debug_handle, const ThreadInfo &thread) { + void ModuleList::FindModulesFromThreadInfo(os::NativeHandle debug_handle, const ThreadInfo &thread, bool is_64_bit) { /* Set the debug handle, for access in other member functions. */ m_debug_handle = debug_handle; /* Try to add the thread's PC. */ - this->TryAddModule(thread.GetPC()); + this->TryAddModule(thread.GetPC(), is_64_bit); /* Try to add the thread's LR. */ - this->TryAddModule(thread.GetLR()); + this->TryAddModule(thread.GetLR(), is_64_bit); /* Try to add all the addresses in the thread's stacktrace. */ for (size_t i = 0; i < thread.GetStackTraceSize(); i++) { - this->TryAddModule(thread.GetStackTrace(i)); + this->TryAddModule(thread.GetStackTrace(i), is_64_bit); } } - void ModuleList::TryAddModule(uintptr_t guess) { + void ModuleList::TryAddModule(uintptr_t guess, bool is_64_bit) { /* Try to locate module from guess. */ uintptr_t base_address = 0; - if (!this->TryFindModule(std::addressof(base_address), guess)) { + if (!this->TryFindModule(std::addressof(base_address), guess, is_64_bit)) { return; } @@ -105,9 +105,18 @@ namespace ams::creport { module.end_address = mi.base_address + mi.size; GetModuleName(module.name, module.start_address, module.end_address); GetModuleId(module.module_id, module.end_address); - /* Some homebrew won't have a name. Add a fake one for readability. */ + + /* Default to no symbol table. */ + module.has_sym_table = false; + if (std::strcmp(module.name, "") == 0) { + /* Some homebrew won't have a name. Add a fake one for readability. */ util::SNPrintf(module.name, sizeof(module.name), "[%02x%02x%02x%02x]", module.module_id[0], module.module_id[1], module.module_id[2], module.module_id[3]); + } else { + /* The module has a name, and so might have a symbol table. Try to add it, if it does. */ + if (is_64_bit) { + DetectModuleSymbolTable(module); + } } } @@ -125,7 +134,9 @@ namespace ams::creport { } } - bool ModuleList::TryFindModule(uintptr_t *out_address, uintptr_t guess) { + bool ModuleList::TryFindModule(uintptr_t *out_address, uintptr_t guess, bool is_64_bit) { + AMS_UNUSED(is_64_bit); + /* Query the memory region our guess falls in. */ svc::MemoryInfo mi; svc::PageInfo pi; @@ -247,6 +258,133 @@ namespace ams::creport { } } + void ModuleList::DetectModuleSymbolTable(ModuleInfo &module) { + /* If we already have a symbol table, no more parsing is needed. */ + if (module.has_sym_table) { + return; + } + + /* Declare temporaries. */ + u64 temp_64; + u32 temp_32; + + /* Get module state. */ + svc::MemoryInfo mi; + svc::PageInfo pi; + if (R_FAILED(svc::QueryDebugProcessMemory(std::addressof(mi), std::addressof(pi), m_debug_handle, module.start_address))) { + return; + } + + const auto module_state = mi.state; + + /* Verify .rodata is read-only with same state as .text. */ + if (R_FAILED(svc::QueryDebugProcessMemory(std::addressof(mi), std::addressof(pi), m_debug_handle, module.end_address)) || mi.permission != svc::MemoryPermission_Read || mi.state != module_state) { + return; + } + + /* Read the first instruction of .text. */ + if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast(std::addressof(temp_32)), m_debug_handle, module.start_address, sizeof(temp_32)))) { + return; + } + + /* We want to find the symbol table/.dynamic. */ + uintptr_t dyn_address = 0; + uintptr_t sym_tab = 0; + uintptr_t str_tab = 0; + size_t num_sym = 0; + + /* Detect module type. */ + if (temp_32 == 0) { + /* Module is dynamically loaded by rtld. */ + u32 mod_offset; + if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast(std::addressof(mod_offset)), m_debug_handle, module.start_address + sizeof(u32), sizeof(u32)))) { + return; + } + + if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast(std::addressof(temp_32)), m_debug_handle, module.start_address + mod_offset, sizeof(u32)))) { + return; + } + + if (temp_32 != rocrt::ModuleHeaderVersion) { /* MOD0 */ + return; + } + + if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast(std::addressof(temp_32)), m_debug_handle, module.start_address + mod_offset + sizeof(u32), sizeof(u32)))) { + return; + } + + dyn_address = module.start_address + mod_offset + temp_32; + } else if (temp_32 == 0x14000002) { + /* Module embeds rtld. */ + if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast(std::addressof(temp_32)), m_debug_handle, module.start_address + 0x5C, sizeof(u32)))) { + return; + } + + if (temp_32 != 0x94000002) { + return; + } + + if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast(std::addressof(temp_32)), m_debug_handle, module.start_address + 0x60, sizeof(u32)))) { + return; + } + + dyn_address = module.start_address + 0x60 + temp_32; + } else { + /* Module has unknown format. */ + return; + } + + + /* Locate tables inside .dyn. */ + for (size_t ofs = 0; /* ... */; ofs += 0x10) { + /* Read the DynamicTag. */ + if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast(std::addressof(temp_64)), m_debug_handle, dyn_address + ofs, sizeof(u64)))) { + return; + } + + if (temp_64 == 0) { + /* We're done parsing .dyn. */ + break; + } else if (temp_64 == 4) { + /* We found DT_HASH */ + if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast(std::addressof(temp_64)), m_debug_handle, dyn_address + ofs + sizeof(u64), sizeof(u64)))) { + return; + } + + /* Read nchain, to get the number of symbols. */ + if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast(std::addressof(temp_32)), m_debug_handle, module.start_address + temp_64 + sizeof(u32), sizeof(u32)))) { + return; + } + + num_sym = temp_32; + } else if (temp_64 == 5) { + /* We found DT_STRTAB */ + if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast(std::addressof(temp_64)), m_debug_handle, dyn_address + ofs + sizeof(u64), sizeof(u64)))) { + return; + } + + str_tab = module.start_address + temp_64; + } else if (temp_64 == 6) { + /* We found DT_SYMTAB */ + if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast(std::addressof(temp_64)), m_debug_handle, dyn_address + ofs + sizeof(u64), sizeof(u64)))) { + return; + } + + sym_tab = module.start_address + temp_64; + } + } + + /* Check that we found all the tables. */ + if (!(sym_tab != 0 && str_tab != 0 && num_sym != 0)) { + return; + } + + module.has_sym_table = true; + module.sym_tab = sym_tab; + module.str_tab = str_tab; + module.num_sym = static_cast(num_sym); + } + const char *ModuleList::GetFormattedAddressString(uintptr_t address) { /* Print default formatted string. */ util::SNPrintf(m_address_str_buf, sizeof(m_address_str_buf), "%016lx", address); @@ -255,8 +393,52 @@ namespace ams::creport { for (size_t i = 0; i < m_num_modules; i++) { const auto& module = m_modules[i]; if (module.start_address <= address && address < module.end_address) { + if (module.has_sym_table) { + /* Try to locate an appropriate symbol. */ + for (size_t j = 0; j < module.num_sym; ++j) { + /* Read symbol from the module's symbol table. */ + struct { + u32 st_name; + u8 st_info; + u8 st_other; + u16 st_shndx; + u64 st_value; + u64 st_size; + } sym; + if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast(std::addressof(sym)), m_debug_handle, module.sym_tab + j * sizeof(sym), sizeof(sym)))) { + break; + } + + /* Check the symbol is valid/STT_FUNC. */ + if (sym.st_shndx == 0 || ((sym.st_shndx & 0xFF00) == 0xFF00)) { + continue; + } + if ((sym.st_info & 0xF) != 2) { + continue; + } + + /* Check the address. */ + const uintptr_t func_start = module.start_address + sym.st_value; + if (func_start <= address && address < func_start + sym.st_size) { + /* Read the symbol name. */ + const uintptr_t sym_address = module.str_tab + sym.st_name; + char sym_name[0x80]; + if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast(sym_name), m_debug_handle, sym_address, sizeof(sym_name)))) { + break; + } + + /* Ensure null-termination. */ + sym_name[sizeof(sym_name) - 1] = '\x00'; + + /* Print the symbol. */ + util::SNPrintf(m_address_str_buf, sizeof(m_address_str_buf), "%016lx (%s + 0x%lx) (%s + 0x%lx)", address, module.name, address - module.start_address, sym_name, address - func_start); + return m_address_str_buf; + } + } + } + util::SNPrintf(m_address_str_buf, sizeof(m_address_str_buf), "%016lx (%s + 0x%lx)", address, module.name, address - module.start_address); - break; + return m_address_str_buf; } } diff --git a/stratosphere/creport/source/creport_modules.hpp b/stratosphere/creport/source/creport_modules.hpp index 695313692..f23d3aa0e 100644 --- a/stratosphere/creport/source/creport_modules.hpp +++ b/stratosphere/creport/source/creport_modules.hpp @@ -30,6 +30,10 @@ namespace ams::creport { u8 module_id[ModuleIdSize]; u64 start_address; u64 end_address; + bool has_sym_table; + u64 sym_tab; + u64 str_tab; + u32 num_sym; }; private: os::NativeHandle m_debug_handle; @@ -37,7 +41,7 @@ namespace ams::creport { ModuleInfo m_modules[ModuleCountMax]; /* For pretty-printing. */ - char m_address_str_buf[0x280]; + char m_address_str_buf[1_KB]; public: ModuleList() : m_debug_handle(os::InvalidNativeHandle), m_num_modules(0) { std::memset(m_modules, 0, sizeof(m_modules)); @@ -51,14 +55,15 @@ namespace ams::creport { return m_modules[i].start_address; } - void FindModulesFromThreadInfo(os::NativeHandle debug_handle, const ThreadInfo &thread); + void FindModulesFromThreadInfo(os::NativeHandle debug_handle, const ThreadInfo &thread, bool is_64_bit); const char *GetFormattedAddressString(uintptr_t address); void SaveToFile(ScopedFile &file); private: - bool TryFindModule(uintptr_t *out_address, uintptr_t guess); - void TryAddModule(uintptr_t guess); + bool TryFindModule(uintptr_t *out_address, uintptr_t guess, bool is_64_bit); + void TryAddModule(uintptr_t guess, bool is_64_bit); void GetModuleName(char *out_name, uintptr_t text_start, uintptr_t ro_start); void GetModuleId(u8 *out, uintptr_t ro_start); + void DetectModuleSymbolTable(ModuleInfo &module); }; }