diff --git a/libraries/libstratosphere/include/stratosphere/fatal/fatal_types.hpp b/libraries/libstratosphere/include/stratosphere/fatal/fatal_types.hpp index d0740194a..a0fd2e8bf 100644 --- a/libraries/libstratosphere/include/stratosphere/fatal/fatal_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/fatal/fatal_types.hpp @@ -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)); } }; diff --git a/libraries/libvapours/include/vapours/util/util_alignment.hpp b/libraries/libvapours/include/vapours/util/util_alignment.hpp index 36f28e717..d300dce8d 100644 --- a/libraries/libvapours/include/vapours/util/util_alignment.hpp +++ b/libraries/libvapours/include/vapours/util/util_alignment.hpp @@ -20,55 +20,60 @@ namespace ams::util { /* Utilities for alignment to power of two. */ + template + constexpr ALWAYS_INLINE bool IsPowerOfTwo(T value) { + using U = typename std::make_unsigned::type; + return (static_cast(value) & static_cast(value - 1)) == 0; + } template - 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::type; const U invmask = static_cast(alignment - 1); return static_cast((value + invmask) & ~invmask); } template - 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::type; const U invmask = static_cast(alignment - 1); return static_cast(value & ~invmask); } template - 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::type; const U invmask = static_cast(alignment - 1); return (value & invmask) == 0; } template<> - constexpr inline void *AlignUp(void *value, size_t alignment) { + constexpr ALWAYS_INLINE void *AlignUp(void *value, size_t alignment) { return reinterpret_cast(AlignUp(reinterpret_cast(value), alignment)); } template<> - constexpr inline const void *AlignUp(const void *value, size_t alignment) { + constexpr ALWAYS_INLINE const void *AlignUp(const void *value, size_t alignment) { return reinterpret_cast(AlignUp(reinterpret_cast(value), alignment)); } template<> - constexpr inline void *AlignDown(void *value, size_t alignment) { + constexpr ALWAYS_INLINE void *AlignDown(void *value, size_t alignment) { return reinterpret_cast(AlignDown(reinterpret_cast(value), alignment)); } template<> - constexpr inline const void *AlignDown(const void *value, size_t alignment) { + constexpr ALWAYS_INLINE const void *AlignDown(const void *value, size_t alignment) { return reinterpret_cast(AlignDown(reinterpret_cast(value), alignment)); } template<> - constexpr inline bool IsAligned(void *value, size_t alignment) { + constexpr ALWAYS_INLINE bool IsAligned(void *value, size_t alignment) { return IsAligned(reinterpret_cast(value), alignment); } template<> - constexpr inline bool IsAligned(const void *value, size_t alignment) { + constexpr ALWAYS_INLINE bool IsAligned(const void *value, size_t alignment) { return IsAligned(reinterpret_cast(value), alignment); } diff --git a/stratosphere/fatal/source/fatal_debug.cpp b/stratosphere/fatal/source/fatal_debug.cpp index edf23d611..2f0ea6207 100644 --- a/stratosphere/fatal/source/fatal_debug.cpp +++ b/stratosphere/fatal/source/fatal_debug.cpp @@ -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())); } diff --git a/stratosphere/fatal/source/fatal_task_error_report.cpp b/stratosphere/fatal/source/fatal_task_error_report.cpp index e97e21b4b..139e31f17 100644 --- a/stratosphere/fatal/source/fatal_task_error_report.cpp +++ b/stratosphere/fatal/source/fatal_task_error_report.cpp @@ -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(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->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]); + } + } } - if (this->context->stack_dump_size) { + /* Dump data to file. */ + { snprintf(file_path, sizeof(file_path) - 1, "sdmc:/atmosphere/fatal_reports/dumps/%011lu_%016lx.bin", timestamp, static_cast(this->context->program_id)); - FILE *f_stackdump = fopen(file_path, "wb"); - if (f_stackdump == NULL) { return; } - ON_SCOPE_EXIT { fclose(f_stackdump); }; + FILE *f_dump = fopen(file_path, "wb"); + if (f_dump == NULL) { return; } + ON_SCOPE_EXIT { fclose(f_dump); }; - fwrite(this->context->stack_dump, this->context->stack_dump_size, 1, f_stackdump); - fflush(f_stackdump); + 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); } }