mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-11-22 18:26:39 +00:00
Implemented exception handler.
The exception handler is capable of logging CPU registers and a stack trace using the current log implementation. Furthermore, if borealis has been initialized, it'll also display the PC register value using a CrashFrame. Otherwise, console output is used to display the same message. Other changes include: * utils: made utilsPrintConsoleError non-static. * utils: implemented a workaround to restore console output after initializing nxlink.
This commit is contained in:
parent
4bea23b758
commit
3f9cde8185
7 changed files with 332 additions and 44 deletions
|
@ -151,6 +151,9 @@ void utilsCreateDirectoryTree(const char *path, bool create_last_element);
|
|||
/// Furthermore, if the full length for the generated path is >= FS_MAX_PATH, NULL will be returned.
|
||||
char *utilsGeneratePath(const char *prefix, const char *filename, const char *extension);
|
||||
|
||||
/// Prints an error message using the standard console output and waits for the user to press a button.
|
||||
void utilsPrintConsoleError(const char *msg);
|
||||
|
||||
/// Returns the current application updated state.
|
||||
bool utilsGetApplicationUpdatedState(void);
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
"line_07": "\uE016 The folks from NSWDB.COM and No-Intro.org, for being kind enough to put up public APIs to perform online checksum lookups.",
|
||||
"line_08": "\uE016 The folks at the nxdumptool Discord server.",
|
||||
"line_09": "\uE016 The Comfy Boyes, for always being awesome and supportive. You know who you are.",
|
||||
"line_10": "\uE016 My girlfriend, for always being by my side and motivating me to keep working on all my projects. I love you. \uE87D",
|
||||
"line_10": "\uE016 Andreína, for always being by my side and motivating me to keep working on all my projects.",
|
||||
"line_11": "\uE016 And, at last but not least, you! Thank you for using my work!"
|
||||
},
|
||||
|
||||
|
|
|
@ -9,5 +9,8 @@
|
|||
"__time_format_comment__": "Use 12 for a 12-hour clock, or 24 for a 24-hour clock",
|
||||
|
||||
"date": "{1:02d}/{2:02d}/{0} {3:02d}:{4:02d}:{5:02d} {6}",
|
||||
"__date_comment__": "{0} = Year, {1} = Month, {2} = Day, {3} = Hour, {4} = Minute, {5} = Second, {6} = AM/PM (if time_format is set to 12)"
|
||||
"__date_comment__": "{0} = Year, {1} = Month, {2} = Day, {3} = Hour, {4} = Minute, {5} = Second, {6} = AM/PM (if time_format is set to 12)",
|
||||
|
||||
"libnx_abort": "Fatal error triggered in libnx!\nError code: 0x{:08X}.",
|
||||
"exception_triggered": "Fatal exception triggered!\nReason: {} (0x{:X})."
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ static AppletHookCookie g_systemOverclockCookie = {0};
|
|||
|
||||
static bool g_longRunningProcess = false;
|
||||
|
||||
static int g_nxLinkSocketFd = -1;
|
||||
static int g_stdoutFd = -1, g_stderrFd = -1, g_nxLinkSocketFd = -1;
|
||||
|
||||
static const char *g_sizeSuffixes[] = { "B", "KiB", "MiB", "GiB" };
|
||||
static const u32 g_sizeSuffixesCount = MAX_ELEMENTS(g_sizeSuffixes);
|
||||
|
@ -106,7 +106,8 @@ static void utilsOverclockSystemAppletHook(AppletHookType hook, void *param);
|
|||
|
||||
static void utilsChangeHomeButtonBlockStatus(bool block);
|
||||
|
||||
static void utilsPrintConsoleError(void);
|
||||
NX_INLINE void utilsCloseFileDescriptor(int *fd);
|
||||
static void utilsRestoreConsoleOutput(void);
|
||||
|
||||
static size_t utilsGetUtf8CodepointCount(const char *str, size_t str_size, size_t cp_limit, size_t *last_cp_pos);
|
||||
|
||||
|
@ -120,6 +121,9 @@ bool utilsInitializeResources(const int program_argc, const char **program_argv)
|
|||
ret = g_resourcesInit;
|
||||
if (ret) break;
|
||||
|
||||
/* Lock applet exit. */
|
||||
appletLockExit();
|
||||
|
||||
/* Retrieve pointer to the application launch path. */
|
||||
_utilsGetLaunchPath(program_argc, program_argv);
|
||||
|
||||
|
@ -132,7 +136,7 @@ bool utilsInitializeResources(const int program_argc, const char **program_argv)
|
|||
|
||||
/* Create logfile. */
|
||||
logWriteStringToLogFile("________________________________________________________________\r\n");
|
||||
LOG_MSG(APP_TITLE " v%u.%u.%u starting (" GIT_REV "). Built on " __DATE__ " - " __TIME__ ".", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO);
|
||||
LOG_MSG(APP_TITLE " v" APP_VERSION " starting (" GIT_REV "). Built on " __DATE__ " - " __TIME__ ".");
|
||||
|
||||
/* Log Horizon OS version. */
|
||||
u32 hos_version = hosversionGet();
|
||||
|
@ -237,14 +241,33 @@ bool utilsInitializeResources(const int program_argc, const char **program_argv)
|
|||
if (R_SUCCEEDED(rc) && flag) appletInitializeGamePlayRecording();
|
||||
}
|
||||
|
||||
/* Redirect stdout and stderr over network to nxlink. */
|
||||
/* Duplicate the stdout and stderr file handles, then redirect stdout and stderr over network to nxlink. */
|
||||
g_stdoutFd = dup(STDOUT_FILENO);
|
||||
g_stderrFd = dup(STDERR_FILENO);
|
||||
g_nxLinkSocketFd = nxlinkConnectToHost(true, true);
|
||||
|
||||
/* Update flags. */
|
||||
ret = g_resourcesInit = true;
|
||||
}
|
||||
|
||||
if (!ret) utilsPrintConsoleError();
|
||||
if (!ret)
|
||||
{
|
||||
size_t msg_size = 0;
|
||||
char log_msg[0x100] = {0}, *msg = NULL;
|
||||
|
||||
/* Get last log message. */
|
||||
logGetLastMessage(log_msg, sizeof(log_msg));
|
||||
|
||||
/* Generate error message. */
|
||||
utilsAppendFormattedStringToBuffer(&msg, &msg_size, "An error occurred while initializing resources.");
|
||||
if (*log_msg) utilsAppendFormattedStringToBuffer(&msg, &msg_size, "\n\n%s", log_msg);
|
||||
|
||||
/* Print error message. */
|
||||
utilsPrintConsoleError(msg);
|
||||
|
||||
/* Free error message. */
|
||||
if (msg) free(msg);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -253,12 +276,8 @@ void utilsCloseResources(void)
|
|||
{
|
||||
SCOPED_LOCK(&g_resourcesMutex)
|
||||
{
|
||||
/* Close nxlink socket. */
|
||||
if (g_nxLinkSocketFd >= 0)
|
||||
{
|
||||
close(g_nxLinkSocketFd);
|
||||
g_nxLinkSocketFd = -1;
|
||||
}
|
||||
/* Restore console output. */
|
||||
utilsRestoreConsoleOutput();
|
||||
|
||||
/* Unset long running process state. */
|
||||
utilsSetLongRunningProcessState(false);
|
||||
|
@ -316,6 +335,9 @@ void utilsCloseResources(void)
|
|||
/* Close logfile. */
|
||||
logCloseLogFile();
|
||||
|
||||
/* Unlock applet exit. */
|
||||
appletUnlockExit();
|
||||
|
||||
g_resourcesInit = false;
|
||||
}
|
||||
}
|
||||
|
@ -836,6 +858,48 @@ end:
|
|||
return path;
|
||||
}
|
||||
|
||||
void utilsPrintConsoleError(const char *msg)
|
||||
{
|
||||
PadState pad = {0};
|
||||
|
||||
/* Don't consider stick movement as button inputs. */
|
||||
u64 flag = ~(HidNpadButton_StickLLeft | HidNpadButton_StickLRight | HidNpadButton_StickLUp | HidNpadButton_StickLDown | HidNpadButton_StickRLeft | HidNpadButton_StickRRight | \
|
||||
HidNpadButton_StickRUp | HidNpadButton_StickRDown);
|
||||
|
||||
/* Configure input. */
|
||||
/* Up to 8 different, full controller inputs. */
|
||||
/* Individual Joy-Cons not supported. */
|
||||
padConfigureInput(8, HidNpadStyleSet_NpadFullCtrl);
|
||||
padInitializeWithMask(&pad, 0x1000000FFUL);
|
||||
|
||||
/* Restore console output. */
|
||||
utilsRestoreConsoleOutput();
|
||||
|
||||
/* Initialize console output. */
|
||||
consoleInit(NULL);
|
||||
|
||||
/* Print message. */
|
||||
if (msg && *msg)
|
||||
{
|
||||
printf("%s", msg);
|
||||
} else {
|
||||
printf("An error occurred.");
|
||||
}
|
||||
|
||||
printf("\n\nFor more information, please check the logfile. Press any button to exit.");
|
||||
consoleUpdate(NULL);
|
||||
|
||||
/* Wait until the user presses a button. */
|
||||
while(appletMainLoop())
|
||||
{
|
||||
padUpdate(&pad);
|
||||
if (padGetButtonsDown(&pad) & flag) break;
|
||||
}
|
||||
|
||||
/* Deinitialize console output. */
|
||||
consoleExit(NULL);
|
||||
}
|
||||
|
||||
bool utilsGetApplicationUpdatedState(void)
|
||||
{
|
||||
bool ret = false;
|
||||
|
@ -1078,39 +1142,31 @@ static void utilsChangeHomeButtonBlockStatus(bool block)
|
|||
}
|
||||
}
|
||||
|
||||
static void utilsPrintConsoleError(void)
|
||||
NX_INLINE void utilsCloseFileDescriptor(int *fd)
|
||||
{
|
||||
PadState pad = {0};
|
||||
char msg[0x100] = {0};
|
||||
if (!fd || *fd < 0) return;
|
||||
close(*fd);
|
||||
*fd = -1;
|
||||
}
|
||||
|
||||
/* Don't consider stick movement as button inputs. */
|
||||
u64 flag = ~(HidNpadButton_StickLLeft | HidNpadButton_StickLRight | HidNpadButton_StickLUp | HidNpadButton_StickLDown | HidNpadButton_StickRLeft | HidNpadButton_StickRRight | \
|
||||
HidNpadButton_StickRUp | HidNpadButton_StickRDown);
|
||||
|
||||
/* Configure input. */
|
||||
/* Up to 8 different, full controller inputs. */
|
||||
/* Individual Joy-Cons not supported. */
|
||||
padConfigureInput(8, HidNpadStyleSet_NpadFullCtrl);
|
||||
padInitializeWithMask(&pad, 0x1000000FFUL);
|
||||
|
||||
/* Get last log message. */
|
||||
logGetLastMessage(msg, sizeof(msg));
|
||||
|
||||
consoleInit(NULL);
|
||||
|
||||
printf("An error occurred while initializing resources.\n\n");
|
||||
if (*msg) printf("%s\n\n", msg);
|
||||
printf("For more information, please check the logfile. Press any button to exit.");
|
||||
|
||||
consoleUpdate(NULL);
|
||||
|
||||
while(appletMainLoop())
|
||||
static void utilsRestoreConsoleOutput(void)
|
||||
{
|
||||
SCOPED_LOCK(&g_resourcesMutex)
|
||||
{
|
||||
padUpdate(&pad);
|
||||
if (padGetButtonsDown(&pad) & flag) break;
|
||||
}
|
||||
if (g_nxLinkSocketFd >= 0) utilsCloseFileDescriptor(&g_nxLinkSocketFd);
|
||||
|
||||
consoleExit(NULL);
|
||||
if (g_stdoutFd >= 0)
|
||||
{
|
||||
dup2(g_stdoutFd, STDOUT_FILENO);
|
||||
utilsCloseFileDescriptor(&g_stdoutFd);
|
||||
}
|
||||
|
||||
if (g_stderrFd >= 0)
|
||||
{
|
||||
dup2(g_stderrFd, STDERR_FILENO);
|
||||
utilsCloseFileDescriptor(&g_stderrFd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static size_t utilsGetUtf8CodepointCount(const char *str, size_t str_size, size_t cp_limit, size_t *last_cp_pos)
|
||||
|
|
|
@ -846,7 +846,7 @@ void titleFreeUserApplicationData(TitleUserApplicationData *user_app_data)
|
|||
bool titleAreOrphanTitlesAvailable(void)
|
||||
{
|
||||
bool ret = false;
|
||||
SCOPED_LOCK(&g_titleMutex) ret = (g_titleInterfaceInit && g_orphanTitleInfo && *g_orphanTitleInfo && g_orphanTitleInfoCount > 0);
|
||||
SCOPED_TRY_LOCK(&g_titleMutex) ret = (g_titleInterfaceInit && g_orphanTitleInfo && *g_orphanTitleInfo && g_orphanTitleInfoCount > 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
223
source/exception_handler.cpp
Normal file
223
source/exception_handler.cpp
Normal file
|
@ -0,0 +1,223 @@
|
|||
/*
|
||||
* exception_handler.cpp
|
||||
*
|
||||
* Copyright (c) 2020-2021, DarkMatterCore <pabloacurielz@gmail.com>.
|
||||
* Copyright (c) 2019-2020, WerWolv.
|
||||
*
|
||||
* 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>
|
||||
|
||||
#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))
|
||||
|
||||
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("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("svcQueryMemory failed! (0x%08X).", rc);
|
||||
}
|
||||
|
||||
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("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);
|
||||
}
|
||||
|
||||
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("*** libnx aborted with error code: 0x%08X ***", 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};
|
||||
u32 stack_trace_size = 0;
|
||||
u64 stack_trace[STACK_TRACE_SIZE] = {0};
|
||||
|
||||
char *exception_str = NULL;
|
||||
size_t exception_str_size = 0;
|
||||
|
||||
std::string error_desc_str, crash_str;
|
||||
|
||||
/* Get homebrew memory info. */
|
||||
nxdt::utils::GetHomebrewMemoryInfo(&info);
|
||||
|
||||
/* Log exception type. */
|
||||
LOG_MSG("*** Exception Triggered ***");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, "Type: %s (0x%X)\r\n", error_desc_str.c_str(), ctx->error_desc);
|
||||
|
||||
/* Log CPU registers. */
|
||||
utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, "Registers:");
|
||||
|
||||
for(size_t i = 0; i < MAX_ELEMENTS(ctx->cpu_gprs); i++)
|
||||
{
|
||||
u64 reg = ctx->cpu_gprs[i].x;
|
||||
utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, "\r\n X%02lu: 0x%lX", i, reg);
|
||||
if (IS_HB_ADDR(reg)) utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, " (BASE + 0x%lX)", reg - info.addr);
|
||||
}
|
||||
|
||||
utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, "\r\n FP: 0x%lX", ctx->fp.x);
|
||||
if (IS_HB_ADDR(ctx->fp.x)) utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, " (BASE + 0x%lX)", ctx->fp.x - info.addr);
|
||||
|
||||
utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, "\r\n LR: 0x%lX", ctx->lr.x);
|
||||
if (IS_HB_ADDR(ctx->lr.x)) utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, " (BASE + 0x%lX)", ctx->lr.x - info.addr);
|
||||
|
||||
utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, "\r\n SP: 0x%lX", ctx->sp.x);
|
||||
if (IS_HB_ADDR(ctx->sp.x)) utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, " (BASE + 0x%lX)", ctx->sp.x - info.addr);
|
||||
|
||||
utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, "\r\n PC: 0x%lX", ctx->pc.x);
|
||||
if (IS_HB_ADDR(ctx->pc.x)) utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, " (BASE + 0x%lX)", ctx->pc.x - info.addr);
|
||||
utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, "\r\n");
|
||||
|
||||
/* Unwind stack. */
|
||||
if (nxdt::utils::UnwindStack(stack_trace, &stack_trace_size, STACK_TRACE_SIZE, ctx->fp.x))
|
||||
{
|
||||
/* Log stack trace. */
|
||||
utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, "Stack Trace:");
|
||||
|
||||
for(u32 i = 0; i < stack_trace_size; i++)
|
||||
{
|
||||
u64 addr = stack_trace[i];
|
||||
utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, "\r\n [%02u]: 0x%lX", stack_trace_size - i - 1, addr);
|
||||
if (IS_HB_ADDR(addr)) utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, " (BASE + 0x%lX)", addr - info.addr);
|
||||
}
|
||||
|
||||
utilsAppendFormattedStringToBuffer(&exception_str, &exception_str_size, "\r\n");
|
||||
}
|
||||
|
||||
/* Write log string. */
|
||||
logWriteStringToLogFile(exception_str);
|
||||
|
||||
/* Free exception info string. */
|
||||
if (exception_str) free(exception_str);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
}
|
|
@ -25,6 +25,8 @@
|
|||
|
||||
using namespace brls::i18n::literals; /* For _i18n. */
|
||||
|
||||
bool g_borealisInitialized = false;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
/* Set scope guard to clean up resources at exit. */
|
||||
|
@ -45,6 +47,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
/* Initialize Borealis. */
|
||||
if (!brls::Application::init(APP_TITLE)) return EXIT_FAILURE;
|
||||
g_borealisInitialized = true;
|
||||
|
||||
/* Check if we're running under applet mode. */
|
||||
if (utilsAppletModeCheck())
|
||||
|
|
Loading…
Reference in a new issue