1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-11-26 12:12:02 +00:00
nxdumptool/source/exception_handler.cpp
Pablo Curiel eee1b2a771 More changes.
* Update libusbhsfs.

* Update borealis.

* nsp_dumper: force free size retrieval after dumping an NSP.

* title: add titleGetGameCardApplicationMetadataEntries().

* Makefile: remove -gdwarf-4 flag.

* nxdt_utils: treat NT_MAX_FILENAME_LENGTH as bytes instead of codepoints, add "TiB" to the array of supported size suffixes.

* GameCardTab: add ProcessGameCardStatus() and PopulateList(), manage list updates in the same fashion as TitlesTab, display message about how to mitigate launch errors after exiting the application, display available applications in the inserted gamecard, display message about how to perform individual operations on the gamecard titles.

* main: add a try/catch block to intercept any possible exceptions thrown while the application is running + use brls::Application::crash() to gracefully exit afterwards. Temporarily disable Applet Mode support.

* exception_handler: use LOG_LEVEL_ERROR.

* LayeredErrorFrame: add GetListFirstFocusableChild().
2022-07-28 00:53:52 +02:00

231 lines
8.3 KiB
C++

/*
* exception_handler.cpp
*
* Copyright (c) 2019-2020, WerWolv.
* Copyright (c) 2020-2022, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
* Loosely based on debug_helpers.cpp from EdiZon-Rewrite.
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <nxdt_utils.h>
#include <borealis.hpp>
/* Helper macros. */
#define FP_MASK 0xFFFFFFFFFF000000UL
#define STACK_TRACE_SIZE 0x20
#define IS_HB_ADDR(x) (info.addr && info.size && (x) >= info.addr && (x) < (info.addr + info.size))
#define EH_ADD_FMT_STR(fmt, ...) utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, fmt, ##__VA_ARGS__)
namespace i18n = brls::i18n; /* For getStr(). */
using namespace i18n::literals; /* For _i18n. */
extern bool g_borealisInitialized;
extern "C" {
/* Overrides for libnx weak symbols. */
u32 __nx_applet_exit_mode = 0;
alignas(16) u8 __nx_exception_stack[0x8000];
u64 __nx_exception_stack_size = sizeof(__nx_exception_stack);
}
namespace nxdt::utils {
static void GetHomebrewMemoryInfo(MemoryInfo *out)
{
if (!out)
{
LOG_MSG_ERROR("Invalid parameters!");
return;
}
u32 p = 0;
Result rc = 0;
/* Find the memory region in which this function is stored. */
/* The start of it will be the base address the homebrew was mapped to. */
rc = svcQueryMemory(out, &p, static_cast<u64>(reinterpret_cast<uintptr_t>(&GetHomebrewMemoryInfo)));
if (R_FAILED(rc)) LOG_MSG_ERROR("svcQueryMemory failed! (0x%X).", rc);
}
#if LOG_LEVEL <= LOG_LEVEL_ERROR
static bool UnwindStack(u64 *out_stack_trace, u32 *out_stack_trace_size, size_t max_stack_trace_size, u64 cur_fp)
{
if (!out_stack_trace || !out_stack_trace_size || !max_stack_trace_size || !cur_fp)
{
LOG_MSG_ERROR("Invalid parameters!");
return false;
}
struct StackFrame {
u64 fp; ///< Frame Pointer (pointer to previous stack frame).
u64 lr; ///< Link Register (return address).
};
*out_stack_trace_size = 0;
u64 fp_base = (cur_fp & FP_MASK);
for(size_t i = 0; i < max_stack_trace_size; i++)
{
/* Last check fixes a crash while dealing with certain stack frame addresses. */
if (!cur_fp || (cur_fp % sizeof(u64)) || (cur_fp & FP_MASK) != fp_base) break;
auto cur_trace = reinterpret_cast<StackFrame*>(cur_fp);
out_stack_trace[(*out_stack_trace_size)++] = cur_trace->lr;
cur_fp = cur_trace->fp;
}
return (*out_stack_trace_size > 0);
}
#endif /* LOG_LEVEL <= LOG_LEVEL_ERROR */
static void NORETURN AbortProgramExecution(std::string str)
{
if (g_borealisInitialized)
{
/* Display crash frame and exit Borealis. */
brls::Application::crash(str);
while(brls::Application::mainLoop());
} else {
/* Print error message using console output. */
utilsPrintConsoleError(str.c_str());
}
/* Clean up resources. */
utilsCloseResources();
/* Exit application. */
__nx_applet_exit_mode = 1;
exit(EXIT_FAILURE);
__builtin_unreachable();
}
}
extern "C" {
/* libnx abort function override. */
void diagAbortWithResult(Result res)
{
/* Log error. */
LOG_MSG_ERROR("*** libnx aborted with error code: 0x%X ***", res);
/* Abort program execution. */
std::string crash_str = (g_borealisInitialized ? i18n::getStr("generic/libnx_abort"_i18n, res) : fmt::format("Fatal error triggered in libnx!\nError code: 0x{:08X}.", res));
nxdt::utils::AbortProgramExecution(crash_str);
}
/* libnx exception handler override. */
void __libnx_exception_handler(ThreadExceptionDump *ctx)
{
MemoryInfo info = {0};
std::string error_desc_str, crash_str;
/* Get homebrew memory info. */
nxdt::utils::GetHomebrewMemoryInfo(&info);
switch(ctx->error_desc)
{
case ThreadExceptionDesc_InstructionAbort:
error_desc_str = "Instruction Abort";
break;
case ThreadExceptionDesc_MisalignedPC:
error_desc_str = "Misaligned Program Counter";
break;
case ThreadExceptionDesc_MisalignedSP:
error_desc_str = "Misaligned Stack Pointer";
break;
case ThreadExceptionDesc_SError:
error_desc_str = "SError";
break;
case ThreadExceptionDesc_BadSVC:
error_desc_str = "Bad SVC";
break;
case ThreadExceptionDesc_Trap:
error_desc_str = "SIGTRAP";
break;
case ThreadExceptionDesc_Other:
error_desc_str = "Segmentation Fault";
break;
default:
error_desc_str = "Unknown";
break;
}
#if LOG_LEVEL <= LOG_LEVEL_ERROR
char *exception_str = NULL;
size_t exception_str_size = 0;
u32 stack_trace_size = 0;
u64 stack_trace[STACK_TRACE_SIZE] = {0};
/* Log exception type. */
LOG_MSG_ERROR("*** Exception Triggered ***");
EH_ADD_FMT_STR("Type: %s (0x%X)\r\n", error_desc_str.c_str(), ctx->error_desc);
/* Log CPU registers. */
EH_ADD_FMT_STR("Registers:");
for(size_t i = 0; i < MAX_ELEMENTS(ctx->cpu_gprs); i++)
{
u64 reg = ctx->cpu_gprs[i].x;
EH_ADD_FMT_STR("\r\n X%02lu: 0x%lX", i, reg);
if (IS_HB_ADDR(reg)) EH_ADD_FMT_STR(" (BASE + 0x%lX)", reg - info.addr);
}
EH_ADD_FMT_STR("\r\n FP: 0x%lX", ctx->fp.x);
if (IS_HB_ADDR(ctx->fp.x)) EH_ADD_FMT_STR(" (BASE + 0x%lX)", ctx->fp.x - info.addr);
EH_ADD_FMT_STR("\r\n LR: 0x%lX", ctx->lr.x);
if (IS_HB_ADDR(ctx->lr.x)) EH_ADD_FMT_STR(" (BASE + 0x%lX)", ctx->lr.x - info.addr);
EH_ADD_FMT_STR("\r\n SP: 0x%lX", ctx->sp.x);
if (IS_HB_ADDR(ctx->sp.x)) EH_ADD_FMT_STR(" (BASE + 0x%lX)", ctx->sp.x - info.addr);
EH_ADD_FMT_STR("\r\n PC: 0x%lX", ctx->pc.x);
if (IS_HB_ADDR(ctx->pc.x)) EH_ADD_FMT_STR(" (BASE + 0x%lX)", ctx->pc.x - info.addr);
EH_ADD_FMT_STR("\r\n");
/* Unwind stack. */
if (nxdt::utils::UnwindStack(stack_trace, &stack_trace_size, STACK_TRACE_SIZE, ctx->fp.x))
{
/* Log stack trace. */
EH_ADD_FMT_STR("Stack Trace:");
for(u32 i = 0; i < stack_trace_size; i++)
{
u64 addr = stack_trace[i];
EH_ADD_FMT_STR("\r\n [%02u]: 0x%lX", stack_trace_size - i - 1, addr);
if (IS_HB_ADDR(addr)) EH_ADD_FMT_STR(" (BASE + 0x%lX)", addr - info.addr);
}
EH_ADD_FMT_STR("\r\n");
}
/* Write log string. */
logWriteStringToLogFile(exception_str);
/* Free exception info string. */
if (exception_str) free(exception_str);
#endif /* LOG_LEVEL <= LOG_LEVEL_ERROR */
/* Abort program execution. */
crash_str = (g_borealisInitialized ? i18n::getStr("generic/exception_triggered"_i18n, error_desc_str, ctx->error_desc) : \
fmt::format("Fatal exception triggered!\nReason: {} (0x{:X}).", error_desc_str, ctx->error_desc));
crash_str += (fmt::format("\nPC: 0x{:X}", ctx->pc.x) + (IS_HB_ADDR(ctx->pc.x) ? fmt::format(" (BASE + 0x{:X}).", ctx->pc.x - info.addr) : "."));
nxdt::utils::AbortProgramExecution(crash_str);
}
}