mirror of
https://github.com/switchbrew/libnx.git
synced 2025-06-30 08:42:40 +02:00
Added support for exception-handling.
This commit is contained in:
parent
e19765445d
commit
5e69f760fb
@ -34,6 +34,17 @@ typedef enum {
|
|||||||
RegisterGroup_All = RegisterGroup_CpuAll | RegisterGroup_FpuAll, ///< All registers.
|
RegisterGroup_All = RegisterGroup_CpuAll | RegisterGroup_FpuAll, ///< All registers.
|
||||||
} RegisterGroup;
|
} RegisterGroup;
|
||||||
|
|
||||||
|
/// This is for \ref ThreadExceptionDump error_desc.
|
||||||
|
typedef enum {
|
||||||
|
ThreadExceptionDesc_InstructionAbort = 0x100, ///< Instruction abort
|
||||||
|
ThreadExceptionDesc_MisalignedPC = 0x102, ///< Misaligned PC
|
||||||
|
ThreadExceptionDesc_MisalignedSP = 0x103, ///< Misaligned SP
|
||||||
|
ThreadExceptionDesc_SError = 0x106, ///< SError [not in 1.0.0?]
|
||||||
|
ThreadExceptionDesc_BadSVC = 0x301, ///< Bad SVC
|
||||||
|
ThreadExceptionDesc_Trap = 0x104, ///< Uncategorized, CP15RTTrap, CP15RRTTrap, CP14RTTrap, CP14RRTTrap, IllegalState, SystemRegisterTrap
|
||||||
|
ThreadExceptionDesc_Other = 0x101, ///< None of the above, EC <= 0x34 and not a breakpoint
|
||||||
|
} ThreadExceptionDesc;
|
||||||
|
|
||||||
/// Thread context structure (register dump)
|
/// Thread context structure (register dump)
|
||||||
typedef struct {
|
typedef struct {
|
||||||
CpuRegister cpu_gprs[29]; ///< GPRs 0..28. Note: also contains AArch32 SPRs.
|
CpuRegister cpu_gprs[29]; ///< GPRs 0..28. Note: also contains AArch32 SPRs.
|
||||||
@ -50,6 +61,54 @@ typedef struct {
|
|||||||
u64 tpidr; ///< EL0 Read/Write Software Thread ID Register.
|
u64 tpidr; ///< EL0 Read/Write Software Thread ID Register.
|
||||||
} ThreadContext;
|
} ThreadContext;
|
||||||
|
|
||||||
|
/// Thread exception dump structure.
|
||||||
|
typedef struct {
|
||||||
|
u32 error_desc; ///< See \ref ThreadExceptionDesc.
|
||||||
|
u32 pad[3];
|
||||||
|
|
||||||
|
CpuRegister cpu_gprs[29]; ///< GPRs 0..28. Note: also contains AArch32 registers.
|
||||||
|
CpuRegister fp; ///< Frame pointer.
|
||||||
|
CpuRegister lr; ///< Link register.
|
||||||
|
CpuRegister sp; ///< Stack pointer.
|
||||||
|
CpuRegister pc; ///< Program counter (elr_el1).
|
||||||
|
|
||||||
|
u64 padding;
|
||||||
|
|
||||||
|
FpuRegister fpu_gprs[32]; ///< 32 general-purpose NEON registers.
|
||||||
|
|
||||||
|
u32 pstate; ///< pstate & 0xFF0FFE20
|
||||||
|
u32 afsr0;
|
||||||
|
u32 afsr1;
|
||||||
|
u32 esr;
|
||||||
|
|
||||||
|
CpuRegister far; ///< Fault Address Register.
|
||||||
|
} ThreadExceptionDump;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u64 cpu_gprs[9]; ///< GPRs 0..8.
|
||||||
|
u64 lr;
|
||||||
|
u64 sp;
|
||||||
|
u64 elr_el1;
|
||||||
|
u32 pstate; ///< pstate & 0xFF0FFE20
|
||||||
|
u32 afsr0;
|
||||||
|
u32 afsr1;
|
||||||
|
u32 esr;
|
||||||
|
u64 far;
|
||||||
|
} ThreadExceptionFrameA64;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u32 cpu_gprs[8]; ///< GPRs 0..7.
|
||||||
|
u32 sp;
|
||||||
|
u32 lr;
|
||||||
|
u32 elr_el1;
|
||||||
|
u32 tpidr_el0; ///< tpidr_el0 = 1
|
||||||
|
u32 cpsr; ///< cpsr & 0xFF0FFE20
|
||||||
|
u32 afsr0;
|
||||||
|
u32 afsr1;
|
||||||
|
u32 esr;
|
||||||
|
u32 far;
|
||||||
|
} ThreadExceptionFrameA32;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Determines whether a thread context belong to an AArch64 process based on the PSR.
|
* @brief Determines whether a thread context belong to an AArch64 process based on the PSR.
|
||||||
* @param[in] ctx Thread context to which PSTATE/cspr has been dumped to.
|
* @param[in] ctx Thread context to which PSTATE/cspr has been dumped to.
|
||||||
@ -59,3 +118,13 @@ static inline bool threadContextIsAArch64(const ThreadContext *ctx)
|
|||||||
{
|
{
|
||||||
return (ctx->psr & 0x10) == 0;
|
return (ctx->psr & 0x10) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Determines whether a ThreadExceptionDump belongs to an AArch64 process based on the PSTATE.
|
||||||
|
* @param[in] ctx ThreadExceptionDump.
|
||||||
|
* @return true if and only if the ThreadExceptionDump belongs to an AArch64 process.
|
||||||
|
*/
|
||||||
|
static inline bool threadExceptionIsAArch64(const ThreadExceptionDump *ctx)
|
||||||
|
{
|
||||||
|
return (ctx->pstate & 0x10) == 0;
|
||||||
|
}
|
||||||
|
@ -510,6 +510,13 @@ Result svcOutputDebugString(const char *str, u64 size);
|
|||||||
///@name Miscellaneous
|
///@name Miscellaneous
|
||||||
///@{
|
///@{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns from an exception.
|
||||||
|
* @param[in] res Result code.
|
||||||
|
* @note Syscall number 0x28.
|
||||||
|
*/
|
||||||
|
void NORETURN svcReturnFromException(Result res);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Retrieves information about the system, or a certain kernel object.
|
* @brief Retrieves information about the system, or a certain kernel object.
|
||||||
* @param[out] out Variable to which store the information.
|
* @param[out] out Variable to which store the information.
|
||||||
|
@ -235,6 +235,11 @@ SVC_BEGIN svcOutputDebugString
|
|||||||
ret
|
ret
|
||||||
SVC_END
|
SVC_END
|
||||||
|
|
||||||
|
SVC_BEGIN svcReturnFromException
|
||||||
|
svc 0x28
|
||||||
|
ret
|
||||||
|
SVC_END
|
||||||
|
|
||||||
SVC_BEGIN svcGetInfo
|
SVC_BEGIN svcGetInfo
|
||||||
str x0, [sp, #-16]!
|
str x0, [sp, #-16]!
|
||||||
svc 0x29
|
svc 0x29
|
||||||
|
182
nx/source/runtime/exception.s
Normal file
182
nx/source/runtime/exception.s
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
.macro CODE_BEGIN name
|
||||||
|
.section .text.\name, "ax", %progbits
|
||||||
|
.global \name
|
||||||
|
.type \name, %function
|
||||||
|
.align 2
|
||||||
|
.cfi_startproc
|
||||||
|
\name:
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro CODE_END
|
||||||
|
.cfi_endproc
|
||||||
|
.endm
|
||||||
|
|
||||||
|
// Called by crt0 when the args at the time of entry indicate an exception occured.
|
||||||
|
|
||||||
|
.weak __libnx_exception_entry
|
||||||
|
CODE_BEGIN __libnx_exception_entry
|
||||||
|
cmp x1, #0
|
||||||
|
beq __libnx_exception_entry_abort
|
||||||
|
|
||||||
|
// Load IsCurrentProcessBeingDebugged.
|
||||||
|
|
||||||
|
stp x9, x10, [sp, #-16]!
|
||||||
|
stp x11, x12, [sp, #-16]!
|
||||||
|
stp x13, x14, [sp, #-16]!
|
||||||
|
stp x15, x16, [sp, #-16]!
|
||||||
|
stp x17, x18, [sp, #-16]!
|
||||||
|
stp x19, x20, [sp, #-16]!
|
||||||
|
str x21, [sp, #-16]!
|
||||||
|
|
||||||
|
stp x0, x1, [sp, #-16]!
|
||||||
|
sub sp, sp, #16
|
||||||
|
|
||||||
|
mov x0, sp
|
||||||
|
mov x1, #8
|
||||||
|
mov w2, wzr
|
||||||
|
mov x3, #0
|
||||||
|
bl svcGetInfo
|
||||||
|
mov w6, w0
|
||||||
|
ldr x7, [sp], #16
|
||||||
|
|
||||||
|
ldp x0, x1, [sp], #16
|
||||||
|
|
||||||
|
ldr x21, [sp], #16
|
||||||
|
ldp x19, x20, [sp], #16
|
||||||
|
ldp x17, x18, [sp], #16
|
||||||
|
ldp x15, x16, [sp], #16
|
||||||
|
ldp x13, x14, [sp], #16
|
||||||
|
ldp x11, x12, [sp], #16
|
||||||
|
ldp x9, x10, [sp], #16
|
||||||
|
|
||||||
|
// Abort when svcGetInfo failed.
|
||||||
|
cbnz w6, __libnx_exception_entry_abort
|
||||||
|
|
||||||
|
// Abort when IsCurrentProcessBeingDebugged is set where __nx_exception_ignoredebug==0.
|
||||||
|
adrp x6, __nx_exception_ignoredebug
|
||||||
|
ldr w5, [x6, #:lo12:__nx_exception_ignoredebug]
|
||||||
|
|
||||||
|
cbnz w5, __libnx_exception_entry_start
|
||||||
|
cbnz x7, __libnx_exception_entry_abort
|
||||||
|
|
||||||
|
__libnx_exception_entry_start:
|
||||||
|
adrp x2, __nx_exceptiondump
|
||||||
|
add x2, x2, #:lo12:__nx_exceptiondump
|
||||||
|
mov x5, x2
|
||||||
|
|
||||||
|
// error_desc
|
||||||
|
str w0, [x2], #4
|
||||||
|
// pad
|
||||||
|
str wzr, [x2], #4
|
||||||
|
str wzr, [x2], #4
|
||||||
|
str wzr, [x2], #4
|
||||||
|
|
||||||
|
// GPRs 0..8
|
||||||
|
ldp x3, x4, [x1]
|
||||||
|
str x5, [x1], #16 // x0 = __nx_exceptiondump
|
||||||
|
stp x3, x4, [x2], #16
|
||||||
|
ldp x3, x4, [x1], #16
|
||||||
|
stp x3, x4, [x2], #16
|
||||||
|
ldp x3, x4, [x1], #16
|
||||||
|
stp x3, x4, [x2], #16
|
||||||
|
ldp x3, x4, [x1], #16
|
||||||
|
stp x3, x4, [x2], #16
|
||||||
|
ldr x3, [x1], #8
|
||||||
|
str x3, [x2], #8
|
||||||
|
|
||||||
|
// GPRs 9..28
|
||||||
|
str x9, [x2], #8
|
||||||
|
stp x10, x11, [x2], #16
|
||||||
|
stp x12, x13, [x2], #16
|
||||||
|
stp x14, x15, [x2], #16
|
||||||
|
stp x16, x17, [x2], #16
|
||||||
|
stp x18, x19, [x2], #16
|
||||||
|
stp x20, x21, [x2], #16
|
||||||
|
stp x22, x23, [x2], #16
|
||||||
|
stp x24, x25, [x2], #16
|
||||||
|
stp x26, x27, [x2], #16
|
||||||
|
str x28, [x2], #8
|
||||||
|
|
||||||
|
// fp
|
||||||
|
str x29, [x2], #8
|
||||||
|
|
||||||
|
// lr
|
||||||
|
ldr x3, [x1], #8
|
||||||
|
str x3, [x2], #8
|
||||||
|
|
||||||
|
// sp
|
||||||
|
adrp x4, __nx_exception_stack
|
||||||
|
add x4, x4, #:lo12:__nx_exception_stack
|
||||||
|
|
||||||
|
adrp x5, __nx_exception_stack_size
|
||||||
|
ldr x5, [x5, #:lo12:__nx_exception_stack_size]
|
||||||
|
add x4, x4, x5
|
||||||
|
|
||||||
|
ldr x3, [x1]
|
||||||
|
str x4, [x1], #8 // sp = __nx_exception_stack + __nx_exception_stack_size
|
||||||
|
str x3, [x2], #8
|
||||||
|
|
||||||
|
// elr_el1 (pc)
|
||||||
|
adrp x4, __libnx_exception_returnentry
|
||||||
|
add x4, x4, #:lo12:__libnx_exception_returnentry
|
||||||
|
|
||||||
|
ldr x3, [x1]
|
||||||
|
str x4, [x1], #8 // elr_el1 = __libnx_exception_returnentry
|
||||||
|
str x3, [x2], #8
|
||||||
|
|
||||||
|
// padding
|
||||||
|
str xzr, [x2], #8
|
||||||
|
|
||||||
|
// fpu_gprs
|
||||||
|
stp q0, q1, [x2], #32
|
||||||
|
stp q2, q3, [x2], #32
|
||||||
|
stp q4, q5, [x2], #32
|
||||||
|
stp q6, q7, [x2], #32
|
||||||
|
stp q8, q9, [x2], #32
|
||||||
|
stp q10, q11, [x2], #32
|
||||||
|
stp q12, q13, [x2], #32
|
||||||
|
stp q14, q15, [x2], #32
|
||||||
|
stp q16, q17, [x2], #32
|
||||||
|
stp q18, q19, [x2], #32
|
||||||
|
stp q20, q21, [x2], #32
|
||||||
|
stp q22, q23, [x2], #32
|
||||||
|
stp q24, q25, [x2], #32
|
||||||
|
stp q26, q27, [x2], #32
|
||||||
|
stp q28, q29, [x2], #32
|
||||||
|
stp q30, q31, [x2], #32
|
||||||
|
|
||||||
|
// 4 u32s: pstate, afsr0, afsr1, and esr.
|
||||||
|
ldr w3, [x1], #4
|
||||||
|
str w3, [x2], #4
|
||||||
|
ldr w3, [x1], #4
|
||||||
|
str w3, [x2], #4
|
||||||
|
ldr w3, [x1], #4
|
||||||
|
str w3, [x2], #4
|
||||||
|
ldr w3, [x1], #4
|
||||||
|
str w3, [x2], #4
|
||||||
|
|
||||||
|
//far
|
||||||
|
ldr x3, [x1], #8
|
||||||
|
str x3, [x2], #8
|
||||||
|
|
||||||
|
mov w0, wzr
|
||||||
|
b __libnx_exception_entry_end
|
||||||
|
|
||||||
|
__libnx_exception_entry_abort:
|
||||||
|
mov w0, #0xf801
|
||||||
|
__libnx_exception_entry_end:
|
||||||
|
bl svcReturnFromException
|
||||||
|
b .
|
||||||
|
CODE_END
|
||||||
|
|
||||||
|
// Jumped to by kernel in svcReturnFromException via the overridden elr_el1, with x0 set to __nx_exceptiondump.
|
||||||
|
CODE_BEGIN __libnx_exception_returnentry
|
||||||
|
bl __libnx_exception_handler
|
||||||
|
|
||||||
|
mov w0, wzr
|
||||||
|
mov x1, #0
|
||||||
|
mov x2, #0
|
||||||
|
bl svcBreak
|
||||||
|
b .
|
||||||
|
CODE_END
|
||||||
|
|
@ -21,6 +21,14 @@ extern u32 __nx_applet_type;
|
|||||||
// Must be a multiple of 0x200000.
|
// Must be a multiple of 0x200000.
|
||||||
__attribute__((weak)) size_t __nx_heap_size = 0;
|
__attribute__((weak)) size_t __nx_heap_size = 0;
|
||||||
|
|
||||||
|
/// Override these with your own if you're using __libnx_exception_handler. __nx_exception_stack is the stack-bottom.
|
||||||
|
__attribute__((weak)) alignas(16) u8 __nx_exception_stack[0x400];
|
||||||
|
__attribute__((weak)) u64 __nx_exception_stack_size = sizeof(__nx_exception_stack);
|
||||||
|
/// By default exception handling will be aborted when the current process is being debugged. Set this to non-zero to disable that.
|
||||||
|
__attribute__((weak)) u32 __nx_exception_ignoredebug = 0;
|
||||||
|
|
||||||
|
ThreadExceptionDump __nx_exceptiondump;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
There are three ways of allocating heap:
|
There are three ways of allocating heap:
|
||||||
|
|
||||||
@ -166,3 +174,8 @@ void __attribute__((weak)) NORETURN __libnx_exit(int rc)
|
|||||||
|
|
||||||
__nx_exit(0, envGetExitFuncPtr());
|
__nx_exit(0, envGetExitFuncPtr());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// You can override this with your own func to handle exceptions. See here: https://switchbrew.org/wiki/SVC#Exception_handling
|
||||||
|
void __attribute__((weak)) __libnx_exception_handler(ThreadExceptionDump *ctx)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
@ -9,16 +9,24 @@ _start:
|
|||||||
.org _start+0x80
|
.org _start+0x80
|
||||||
startup:
|
startup:
|
||||||
// save lr
|
// save lr
|
||||||
mov x27, x30
|
mov x7, x30
|
||||||
|
|
||||||
// get aslr base
|
// get aslr base
|
||||||
bl +4
|
bl +4
|
||||||
sub x28, x30, #0x88
|
sub x6, x30, #0x88
|
||||||
|
|
||||||
// context ptr and main thread handle
|
// context ptr and main thread handle
|
||||||
mov x25, x0
|
mov x5, x0
|
||||||
mov x26, x1
|
mov x4, x1
|
||||||
|
|
||||||
|
// Handle the exception if needed.
|
||||||
|
// if (ctx != NULL && main_thread != -1)__libnx_exception_entry(<inargs>);
|
||||||
|
cmp x5, #0
|
||||||
|
ccmn x4, #1, #4, ne // 4 = Z
|
||||||
|
beq bssclr_start
|
||||||
|
b __libnx_exception_entry
|
||||||
|
|
||||||
|
bssclr_start:
|
||||||
// clear .bss
|
// clear .bss
|
||||||
adrp x0, __bss_start__
|
adrp x0, __bss_start__
|
||||||
adrp x1, __bss_end__
|
adrp x1, __bss_end__
|
||||||
@ -39,15 +47,15 @@ bss_loop:
|
|||||||
str x1, [x0, #:lo12:__stack_top]
|
str x1, [x0, #:lo12:__stack_top]
|
||||||
|
|
||||||
// process .dynamic section
|
// process .dynamic section
|
||||||
mov x0, x28
|
mov x0, x6
|
||||||
adrp x1, _DYNAMIC
|
adrp x1, _DYNAMIC
|
||||||
add x1, x1, #:lo12:_DYNAMIC
|
add x1, x1, #:lo12:_DYNAMIC
|
||||||
bl __nx_dynamic
|
bl __nx_dynamic
|
||||||
|
|
||||||
// initialize system
|
// initialize system
|
||||||
mov x0, x25
|
mov x0, x5
|
||||||
mov x1, x26
|
mov x1, x4
|
||||||
mov x2, x27
|
mov x2, x7
|
||||||
bl __libnx_init
|
bl __libnx_init
|
||||||
|
|
||||||
// call entrypoint
|
// call entrypoint
|
||||||
|
Loading…
Reference in New Issue
Block a user