From 760ed8028d22271daf69bbffc7194f45aa99b38a Mon Sep 17 00:00:00 2001 From: TuxSH Date: Sat, 13 May 2017 13:04:16 +0200 Subject: [PATCH] Add support for user-specified exception handlers --- libctru/include/3ds/errf.h | 10 ++++++++++ libctru/include/3ds/thread.h | 36 ++++++++++++++++++++++++++++++++++++ libctru/source/errf.c | 27 +++++++++++++++++++++++++++ 3 files changed, 73 insertions(+) diff --git a/libctru/include/3ds/errf.h b/libctru/include/3ds/errf.h index f699356..f1c10b5 100644 --- a/libctru/include/3ds/errf.h +++ b/libctru/include/3ds/errf.h @@ -110,3 +110,13 @@ Result ERRF_ThrowResult(Result failure); * on development units/patched ErrDisp. */ Result ERRF_ThrowResultWithMessage(Result failure, const char* message); + +/** + * @brief Handles an exception using ErrDisp. + * @param excep Exception information + * @param regs CPU registers + * + * You might want to clear ENVINFO's bit0 to be able to see any debugging information. + * @sa threadOnException + */ +void ERRF_ExceptionHandler(ERRF_ExceptionInfo* excep, CpuRegisters* regs) __attribute__((noreturn)); diff --git a/libctru/include/3ds/thread.h b/libctru/include/3ds/thread.h index 5a0f56a..0336e96 100644 --- a/libctru/include/3ds/thread.h +++ b/libctru/include/3ds/thread.h @@ -7,10 +7,23 @@ #include <3ds/result.h> #include <3ds/synchronization.h> #include <3ds/svc.h> +#include <3ds/errf.h> + +/// Makes the exception handler reuse the stack of the faulting thread as-is +#define RUN_HANDLER_ON_FAULTING_STACK ((void*)1) + +/// Makes the exception handler push the exception data on its stack +#define WRITE_DATA_TO_HANDLER_STACK NULL + +/// Makes the exception handler push the exception data on the stack of the faulting thread +#define WRITE_DATA_TO_FAULTING_STACK ((ERRF_ExceptionData*)1) /// libctru thread handle type typedef struct Thread_tag* Thread; +/// Exception handler type, necessarily an ARM function that does not return. +typedef void (*ExceptionHandler)(ERRF_ExceptionInfo* excep, CpuRegisters* regs); + /** * @brief Creates a new libctru thread. * @param entrypoint The function that will be called first upon thread creation @@ -82,3 +95,26 @@ Thread threadGetCurrent(void); * @param rc Exit code */ void threadExit(int rc) __attribute__((noreturn)); + +/** + * @brief Sets the exception handler for the current thread. Called from the main thread, this sets the default handler. + * @param handler The exception handler, necessarily an ARM function that does not return + * @param stack_top A pointer to the top of the stack that will be used by the handler. See also @ref RUN_HANDLER_ON_FAULTING_STACK + * @param exception_data A pointer to the buffer that will contain the exception data. + See also @ref WRITE_DATA_TO_HANDLER_STACK and @ref WRITE_DATA_TO_FAULTING_STACK + * + * To have CPU exceptions reported through this mechanism, it is normally necessary that UNITINFO is set to a non-zero value when Kernel11 starts, + * and this mechanism is also controlled by @ref svcKernelSetState type 6, see 3dbrew. + * + * VFP exceptions are always reported this way even if the process is being debugged using the debug SVCs. + * + * The current thread need not be a libctru thread. + */ +static inline void threadOnException(ExceptionHandler handler, void* stack_top, ERRF_ExceptionData* exception_data) +{ + u8* tls = (u8*)getThreadLocalStorage(); + + *(u32*)(tls + 0x40) = (u32)handler; + *(u32*)(tls + 0x44) = (u32)stack_top; + *(u32*)(tls + 0x48) = (u32)exception_data; +} diff --git a/libctru/source/errf.c b/libctru/source/errf.c index e9ad6b1..d5e65ad 100644 --- a/libctru/source/errf.c +++ b/libctru/source/errf.c @@ -106,3 +106,30 @@ Result ERRF_ThrowResultWithMessage(Result failure, const char* message) return ret; } + +void ERRF_ExceptionHandler(ERRF_ExceptionInfo* excep, CpuRegisters* regs) +{ + ERRF_FatalErrInfo error; + Result ret; + + if (R_FAILED(ret = errfInit())) + { + svcBreak(USERBREAK_PANIC); + for(;;); + } + + memset(&error, 0, sizeof(error)); + + error.type = ERRF_ERRTYPE_EXCEPTION; + + error.pcAddr = regs->pc; + getCommonErrorData(&error, 0); + error.data.exception_data.excep = *excep; + error.data.exception_data.regs = *regs; + + ret = ERRF_Throw(&error); + + errfExit(); + + for(;;); +}