Add support for user-specified exception handlers

This commit is contained in:
TuxSH 2017-05-13 13:04:16 +02:00 committed by fincs
parent e43d368fbc
commit 8778d8c5d6
3 changed files with 73 additions and 0 deletions

View File

@ -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));

View File

@ -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;
}

View File

@ -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(;;);
}