mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-11-09 22:56:35 +00:00
creport: print symbols if they're present
This commit is contained in:
parent
d2a757c39e
commit
258a83684e
4 changed files with 218 additions and 24 deletions
|
@ -101,11 +101,9 @@ namespace ams::creport {
|
||||||
m_has_extra_info = has_extra_info;
|
m_has_extra_info = has_extra_info;
|
||||||
|
|
||||||
if (this->OpenProcess(process_id)) {
|
if (this->OpenProcess(process_id)) {
|
||||||
ON_SCOPE_EXIT { this->Close(); };
|
|
||||||
|
|
||||||
/* Parse info from the crashed process. */
|
/* Parse info from the crashed process. */
|
||||||
this->ProcessExceptions();
|
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());
|
m_thread_list->ReadFromProcess(m_debug_handle, m_thread_tls_map, this->Is64Bit());
|
||||||
|
|
||||||
/* Associate module list to threads. */
|
/* 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, */
|
/* 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. */
|
/* but there's no reason for us not to always go looking. */
|
||||||
for (size_t i = 0; i < m_thread_list->GetThreadCount(); i++) {
|
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. */
|
/* 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. */
|
/* Finalize our heap. */
|
||||||
std::destroy_at(m_module_list);
|
std::destroy_at(m_module_list);
|
||||||
std::destroy_at(m_thread_list);
|
std::destroy_at(m_thread_list);
|
||||||
|
@ -358,7 +361,7 @@ namespace ams::creport {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashReport::SaveToFile(ScopedFile &file) {
|
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());
|
file.WriteFormat("Result: 0x%X (2%03d-%04d)\n\n", m_result.GetValue(), m_result.GetModule(), m_result.GetDescription());
|
||||||
|
|
||||||
|
|
|
@ -103,13 +103,17 @@ namespace ams {
|
||||||
g_crash_report.Initialize();
|
g_crash_report.Initialize();
|
||||||
|
|
||||||
/* Try to debug the crashed process. */
|
/* Try to debug the crashed process. */
|
||||||
|
{
|
||||||
g_crash_report.BuildReport(crashed_pid, has_extra_info);
|
g_crash_report.BuildReport(crashed_pid, has_extra_info);
|
||||||
|
ON_SCOPE_EXIT { if (g_crash_report.IsOpen()) { g_crash_report.Close(); } };
|
||||||
|
|
||||||
if (!g_crash_report.IsComplete()) {
|
if (!g_crash_report.IsComplete()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Save report to file. */
|
/* Save report to file. */
|
||||||
g_crash_report.SaveReport(enable_screenshot);
|
g_crash_report.SaveReport(enable_screenshot);
|
||||||
|
}
|
||||||
|
|
||||||
/* Try to terminate the process, if we should. */
|
/* Try to terminate the process, if we should. */
|
||||||
const auto fw_ver = hos::GetVersion();
|
const auto fw_ver = hos::GetVersion();
|
||||||
|
|
|
@ -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. */
|
/* Set the debug handle, for access in other member functions. */
|
||||||
m_debug_handle = debug_handle;
|
m_debug_handle = debug_handle;
|
||||||
|
|
||||||
/* Try to add the thread's PC. */
|
/* 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. */
|
/* 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. */
|
/* Try to add all the addresses in the thread's stacktrace. */
|
||||||
for (size_t i = 0; i < thread.GetStackTraceSize(); i++) {
|
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. */
|
/* Try to locate module from guess. */
|
||||||
uintptr_t base_address = 0;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,9 +105,18 @@ namespace ams::creport {
|
||||||
module.end_address = mi.base_address + mi.size;
|
module.end_address = mi.base_address + mi.size;
|
||||||
GetModuleName(module.name, module.start_address, module.end_address);
|
GetModuleName(module.name, module.start_address, module.end_address);
|
||||||
GetModuleId(module.module_id, 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) {
|
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]);
|
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. */
|
/* Query the memory region our guess falls in. */
|
||||||
svc::MemoryInfo mi;
|
svc::MemoryInfo mi;
|
||||||
svc::PageInfo pi;
|
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<uintptr_t>(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<uintptr_t>(std::addressof(mod_offset)), m_debug_handle, module.start_address + sizeof(u32), sizeof(u32)))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(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<uintptr_t>(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<uintptr_t>(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<uintptr_t>(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<uintptr_t>(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<uintptr_t>(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<uintptr_t>(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<uintptr_t>(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<uintptr_t>(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<u32>(num_sym);
|
||||||
|
}
|
||||||
|
|
||||||
const char *ModuleList::GetFormattedAddressString(uintptr_t address) {
|
const char *ModuleList::GetFormattedAddressString(uintptr_t address) {
|
||||||
/* Print default formatted string. */
|
/* Print default formatted string. */
|
||||||
util::SNPrintf(m_address_str_buf, sizeof(m_address_str_buf), "%016lx", address);
|
util::SNPrintf(m_address_str_buf, sizeof(m_address_str_buf), "%016lx", address);
|
||||||
|
@ -255,9 +393,53 @@ namespace ams::creport {
|
||||||
for (size_t i = 0; i < m_num_modules; i++) {
|
for (size_t i = 0; i < m_num_modules; i++) {
|
||||||
const auto& module = m_modules[i];
|
const auto& module = m_modules[i];
|
||||||
if (module.start_address <= address && address < module.end_address) {
|
if (module.start_address <= address && address < module.end_address) {
|
||||||
util::SNPrintf(m_address_str_buf, sizeof(m_address_str_buf), "%016lx (%s + 0x%lx)", address, module.name, address - module.start_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<uintptr_t>(std::addressof(sym)), m_debug_handle, module.sym_tab + j * sizeof(sym), sizeof(sym)))) {
|
||||||
break;
|
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<uintptr_t>(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);
|
||||||
|
return m_address_str_buf;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_address_str_buf;
|
return m_address_str_buf;
|
||||||
|
|
|
@ -30,6 +30,10 @@ namespace ams::creport {
|
||||||
u8 module_id[ModuleIdSize];
|
u8 module_id[ModuleIdSize];
|
||||||
u64 start_address;
|
u64 start_address;
|
||||||
u64 end_address;
|
u64 end_address;
|
||||||
|
bool has_sym_table;
|
||||||
|
u64 sym_tab;
|
||||||
|
u64 str_tab;
|
||||||
|
u32 num_sym;
|
||||||
};
|
};
|
||||||
private:
|
private:
|
||||||
os::NativeHandle m_debug_handle;
|
os::NativeHandle m_debug_handle;
|
||||||
|
@ -37,7 +41,7 @@ namespace ams::creport {
|
||||||
ModuleInfo m_modules[ModuleCountMax];
|
ModuleInfo m_modules[ModuleCountMax];
|
||||||
|
|
||||||
/* For pretty-printing. */
|
/* For pretty-printing. */
|
||||||
char m_address_str_buf[0x280];
|
char m_address_str_buf[1_KB];
|
||||||
public:
|
public:
|
||||||
ModuleList() : m_debug_handle(os::InvalidNativeHandle), m_num_modules(0) {
|
ModuleList() : m_debug_handle(os::InvalidNativeHandle), m_num_modules(0) {
|
||||||
std::memset(m_modules, 0, sizeof(m_modules));
|
std::memset(m_modules, 0, sizeof(m_modules));
|
||||||
|
@ -51,14 +55,15 @@ namespace ams::creport {
|
||||||
return m_modules[i].start_address;
|
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);
|
const char *GetFormattedAddressString(uintptr_t address);
|
||||||
void SaveToFile(ScopedFile &file);
|
void SaveToFile(ScopedFile &file);
|
||||||
private:
|
private:
|
||||||
bool TryFindModule(uintptr_t *out_address, uintptr_t guess);
|
bool TryFindModule(uintptr_t *out_address, uintptr_t guess, bool is_64_bit);
|
||||||
void TryAddModule(uintptr_t guess);
|
void TryAddModule(uintptr_t guess, bool is_64_bit);
|
||||||
void GetModuleName(char *out_name, uintptr_t text_start, uintptr_t ro_start);
|
void GetModuleName(char *out_name, uintptr_t text_start, uintptr_t ro_start);
|
||||||
void GetModuleId(u8 *out, uintptr_t ro_start);
|
void GetModuleId(u8 *out, uintptr_t ro_start);
|
||||||
|
void DetectModuleSymbolTable(ModuleInfo &module);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue