fatal: include stack/tls in reports

This commit is contained in:
Michael Scire 2019-12-31 15:23:25 -08:00
parent 2e8f06ef44
commit 846f610fff
4 changed files with 64 additions and 16 deletions

View file

@ -327,7 +327,10 @@ namespace ams::fatal {
Event erpt_event;
Event battery_event;
size_t stack_dump_size;
u64 stack_dump_base;
u8 stack_dump[0x100];
u64 tls_address;
u8 tls_dump[0x100];
void ClearState() {
this->result = ResultSuccess();
@ -339,7 +342,10 @@ namespace ams::fatal {
std::memset(&this->erpt_event, 0, sizeof(this->erpt_event));
std::memset(&this->battery_event, 0, sizeof(this->battery_event));
this->stack_dump_size = 0;
this->stack_dump_base = 0;
std::memset(this->stack_dump, 0, sizeof(this->stack_dump));
this->tls_address = 0;
std::memset(this->tls_dump, 0, sizeof(this->tls_dump));
}
};

View file

@ -20,55 +20,60 @@
namespace ams::util {
/* Utilities for alignment to power of two. */
template<typename T>
constexpr ALWAYS_INLINE bool IsPowerOfTwo(T value) {
using U = typename std::make_unsigned<T>::type;
return (static_cast<U>(value) & static_cast<U>(value - 1)) == 0;
}
template<typename T>
constexpr inline T AlignUp(T value, size_t alignment) {
constexpr ALWAYS_INLINE T AlignUp(T value, size_t alignment) {
using U = typename std::make_unsigned<T>::type;
const U invmask = static_cast<U>(alignment - 1);
return static_cast<T>((value + invmask) & ~invmask);
}
template<typename T>
constexpr inline T AlignDown(T value, size_t alignment) {
constexpr ALWAYS_INLINE T AlignDown(T value, size_t alignment) {
using U = typename std::make_unsigned<T>::type;
const U invmask = static_cast<U>(alignment - 1);
return static_cast<T>(value & ~invmask);
}
template<typename T>
constexpr inline bool IsAligned(T value, size_t alignment) {
constexpr ALWAYS_INLINE bool IsAligned(T value, size_t alignment) {
using U = typename std::make_unsigned<T>::type;
const U invmask = static_cast<U>(alignment - 1);
return (value & invmask) == 0;
}
template<>
constexpr inline void *AlignUp<void *>(void *value, size_t alignment) {
constexpr ALWAYS_INLINE void *AlignUp<void *>(void *value, size_t alignment) {
return reinterpret_cast<void *>(AlignUp(reinterpret_cast<uintptr_t>(value), alignment));
}
template<>
constexpr inline const void *AlignUp<const void *>(const void *value, size_t alignment) {
constexpr ALWAYS_INLINE const void *AlignUp<const void *>(const void *value, size_t alignment) {
return reinterpret_cast<const void *>(AlignUp(reinterpret_cast<uintptr_t>(value), alignment));
}
template<>
constexpr inline void *AlignDown<void *>(void *value, size_t alignment) {
constexpr ALWAYS_INLINE void *AlignDown<void *>(void *value, size_t alignment) {
return reinterpret_cast<void *>(AlignDown(reinterpret_cast<uintptr_t>(value), alignment));
}
template<>
constexpr inline const void *AlignDown<const void *>(const void *value, size_t alignment) {
constexpr ALWAYS_INLINE const void *AlignDown<const void *>(const void *value, size_t alignment) {
return reinterpret_cast<void *>(AlignDown(reinterpret_cast<uintptr_t>(value), alignment));
}
template<>
constexpr inline bool IsAligned<void *>(void *value, size_t alignment) {
constexpr ALWAYS_INLINE bool IsAligned<void *>(void *value, size_t alignment) {
return IsAligned(reinterpret_cast<uintptr_t>(value), alignment);
}
template<>
constexpr inline bool IsAligned<const void *>(const void *value, size_t alignment) {
constexpr ALWAYS_INLINE bool IsAligned<const void *>(const void *value, size_t alignment) {
return IsAligned(reinterpret_cast<uintptr_t>(value), alignment);
}

View file

@ -209,6 +209,7 @@ namespace ams::fatal::srv {
/* Welcome to hell. Here, we try to identify which thread called into fatal. */
bool found_fatal_caller = false;
u64 thread_id = 0;
u64 thread_tls = 0;
ThreadContext thread_ctx;
{
/* We start by trying to get a list of threads. */
@ -227,6 +228,7 @@ namespace ams::fatal::srv {
if (IsThreadFatalCaller(ctx->result, debug_handle.Get(), cur_thread_id, thread_id_to_tls[cur_thread_id], &thread_ctx)) {
thread_id = cur_thread_id;
thread_tls = thread_id_to_tls[thread_id];
found_fatal_caller = true;
break;
}
@ -266,13 +268,23 @@ namespace ams::fatal::srv {
}
/* Try to read up to 0x100 of stack. */
ctx->stack_dump_base = 0;
for (size_t sz = 0x100; sz > 0; sz -= 0x10) {
if (R_SUCCEEDED(svcReadDebugProcessMemory(ctx->stack_dump, debug_handle.Get(), thread_ctx.sp, sz))) {
ctx->stack_dump_base = thread_ctx.sp;
ctx->stack_dump_size = sz;
break;
}
}
/* Try to read the first 0x100 of TLS. */
if (R_SUCCEEDED(svcReadDebugProcessMemory(ctx->tls_dump, debug_handle.Get(), thread_tls, sizeof(ctx->tls_dump)))) {
ctx->tls_address = thread_tls;
} else {
ctx->tls_address = 0;
std::memset(ctx->tls_dump, 0xCC, sizeof(ctx->tls_dump));
}
/* Parse the base address. */
ctx->cpu_ctx.aarch64_ctx.SetBaseAddress(GetBaseAddress(ctx, &thread_ctx, debug_handle.Get()));
}

View file

@ -81,7 +81,7 @@ namespace ams::fatal::srv {
if (f_report != NULL) {
ON_SCOPE_EXIT { fclose(f_report); };
fprintf(f_report, "Atmosphère Fatal Report (v1.0):\n");
fprintf(f_report, "Atmosphère Fatal Report (v1.1):\n");
fprintf(f_report, "Result: 0x%X (2%03d-%04d)\n\n", this->context->result.GetValue(), this->context->result.GetModule(), this->context->result.GetDescription());
fprintf(f_report, "Program ID: %016lx\n", static_cast<u64>(this->context->program_id));
if (strlen(this->context->proc_name)) {
@ -114,16 +114,41 @@ namespace ams::fatal::srv {
fprintf(f_report, " ReturnAddress[%02u]: %016lx\n", i, this->context->cpu_ctx.aarch64_ctx.stack_trace[i]);
}
}
if (this->context->stack_dump_size != 0) {
fprintf(f_report, "Stack 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 size_t 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->context->stack_dump_base + ofs, this->context->stack_dump[ofs + 0], this->context->stack_dump[ofs + 1], this->context->stack_dump[ofs + 2], this->context->stack_dump[ofs + 3], this->context->stack_dump[ofs + 4], this->context->stack_dump[ofs + 5], this->context->stack_dump[ofs + 6], this->context->stack_dump[ofs + 7],
this->context->stack_dump[ofs + 8], this->context->stack_dump[ofs + 9], this->context->stack_dump[ofs + 10], this->context->stack_dump[ofs + 11], this->context->stack_dump[ofs + 12], this->context->stack_dump[ofs + 13], this->context->stack_dump[ofs + 14], this->context->stack_dump[ofs + 15]);
}
}
if (this->context->stack_dump_size) {
snprintf(file_path, sizeof(file_path) - 1, "sdmc:/atmosphere/fatal_reports/dumps/%011lu_%016lx.bin", timestamp, static_cast<u64>(this->context->program_id));
FILE *f_stackdump = fopen(file_path, "wb");
if (f_stackdump == NULL) { return; }
ON_SCOPE_EXIT { fclose(f_stackdump); };
if (this->context->tls_address != 0) {
fprintf(f_report, "TLS Address: %016lx\n", this->context->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 size_t 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->context->tls_address + ofs, this->context->tls_dump[ofs + 0], this->context->tls_dump[ofs + 1], this->context->tls_dump[ofs + 2], this->context->tls_dump[ofs + 3], this->context->tls_dump[ofs + 4], this->context->tls_dump[ofs + 5], this->context->tls_dump[ofs + 6], this->context->tls_dump[ofs + 7],
this->context->tls_dump[ofs + 8], this->context->tls_dump[ofs + 9], this->context->tls_dump[ofs + 10], this->context->tls_dump[ofs + 11], this->context->tls_dump[ofs + 12], this->context->tls_dump[ofs + 13], this->context->tls_dump[ofs + 14], this->context->tls_dump[ofs + 15]);
}
}
}
fwrite(this->context->stack_dump, this->context->stack_dump_size, 1, f_stackdump);
fflush(f_stackdump);
/* Dump data to file. */
{
snprintf(file_path, sizeof(file_path) - 1, "sdmc:/atmosphere/fatal_reports/dumps/%011lu_%016lx.bin", timestamp, static_cast<u64>(this->context->program_id));
FILE *f_dump = fopen(file_path, "wb");
if (f_dump == NULL) { return; }
ON_SCOPE_EXIT { fclose(f_dump); };
fwrite(this->context->tls_dump, sizeof(this->context->tls_dump), 1, f_dump);
if (this->context->stack_dump_size) {
fwrite(this->context->stack_dump, this->context->stack_dump_size, 1, f_dump);
}
fflush(f_dump);
}
}