// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. .syntax unified .thumb #include // generated by the build from AsmOffsets.cpp #include #define STACKSIZEOF_ExInfo ((SIZEOF__ExInfo + 7)&(~7)) #define rsp_offsetof_ExInfo 0 #define rsp_offsetof_Context STACKSIZEOF_ExInfo ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // RhpThrowHwEx // // INPUT: R0: exception code of fault // R1: faulting RIP // // OUTPUT: // ////////////////////////////////////////////////////////////////////////////////////////////////////////////// NESTED_ENTRY RhpThrowHwEx, _TEXT, NoHandler mov r2, r0 // save exception code into r2 mov r0, sp // get SP of fault site mov lr, r1 // set IP of fault site // Setup a PAL_LIMITED_CONTEXT on the stack { PROLOG_VPUSH {d8-d15} PROLOG_PUSH "{r0,lr}" // push {sp, pc} of fault site PROLOG_PUSH "{r0,r4-r11,lr}" // } end PAL_LIMITED_CONTEXT PROLOG_STACK_ALLOC STACKSIZEOF_ExInfo // r0: SP of fault site // r1: IP of fault site // r2: exception code of fault // lr: IP of fault site (as a 'return address') mov r4, r2 // save exception code of fault // r0 = GetThread() INLINE_GETTHREAD // r1 <- ExInfo* add r1, sp, #rsp_offsetof_ExInfo mov r3, #0 str r3, [r1, #OFFSETOF__ExInfo__m_exception] // pExInfo->m_exception = null mov r3, #1 strb r3, [r1, #OFFSETOF__ExInfo__m_passNumber] // pExInfo->m_passNumber = 1 mov r3, #0xFFFFFFFF str r3, [r1, #OFFSETOF__ExInfo__m_idxCurClause] // pExInfo->m_idxCurClause = MaxTryRegionIdx mov r3, #2 strb r3, [r1, #OFFSETOF__ExInfo__m_kind] // pExInfo->m_kind = ExKind.HardwareFault // link the ExInfo into the thread's ExInfo chain ldr r3, [r0, #OFFSETOF__Thread__m_pExInfoStackHead] str r3, [r1, #OFFSETOF__ExInfo__m_pPrevExInfo] // pExInfo->m_pPrevExInfo = m_pExInfoStackHead str r1, [r0, #OFFSETOF__Thread__m_pExInfoStackHead] // m_pExInfoStackHead = pExInfo mov r0, r4 // restore the exception code // r0 contains the exception code // r1 contains the address of the ExInfo bl C_FUNC(RhThrowHwEx) EXPORT_POINTER_TO_ADDRESS PointerToRhpThrowHwEx2 // no return EMIT_BREAKPOINT NESTED_END RhpThrowHwEx ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // RhpThrowEx // // INPUT: R0: exception object // // OUTPUT: // ////////////////////////////////////////////////////////////////////////////////////////////////////////////// NESTED_ENTRY RhpThrowEx, _TEXT, NoHandler // Setup a PAL_LIMITED_CONTEXT on the stack { PROLOG_VPUSH {d8-d15} PROLOG_PUSH "{r0,lr}" // Reserve space for SP and store LR PROLOG_PUSH "{r0,r4-r11,lr}" // } end PAL_LIMITED_CONTEXT PROLOG_STACK_ALLOC STACKSIZEOF_ExInfo mov r4, r0 // Save exception object // r0 = GetThread() INLINE_GETTHREAD add r2, sp, #(rsp_offsetof_Context + SIZEOF__PAL_LIMITED_CONTEXT + 0x8) // r2 <- addr of return address // There is runtime C# code that can tail call to RhpThrowEx using a binder intrinsic. So the return // address could have been hijacked when we were in that C# code and we must remove the hijack and // reflect the correct return address in our exception context record. The other throw helpers don't // need this because they cannot be tail-called from C#. // NOTE: we cannot use INLINE_THREAD_UNHIJACK because it will write into the stack at the location // where the tail-calling thread had saved LR, which may not match where we have saved LR. ldr r1, [r0, #OFFSETOF__Thread__m_pvHijackedReturnAddress] cbz r1, LOCAL_LABEL(NotHijacked) ldr r3, [r0, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] // r4: exception object // r1: hijacked return address // r0: pThread // r3: hijacked return address location add r12, sp, #(STACKSIZEOF_ExInfo + SIZEOF__PAL_LIMITED_CONTEXT) // re-compute SP at callsite cmp r3, r12 // if (m_ppvHijackedReturnAddressLocation < SP at callsite) blo LOCAL_LABEL(TailCallWasHijacked) // normal case where a valid return address location is hijacked str r1, [r3] b LOCAL_LABEL(ClearThreadState) LOCAL_LABEL(TailCallWasHijacked): // Abnormal case where the return address location is now invalid because we ended up here via a tail // call. In this case, our hijacked return address should be the correct caller of this method. // // stick the previous return address in LR as well as in the right spots in our PAL_LIMITED_CONTEXT. mov lr, r1 str lr, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__LR)] str lr, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__IP)] LOCAL_LABEL(ClearThreadState): // clear the Thread's hijack state mov r3, #0 str r3, [r0, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] str r3, [r0, #OFFSETOF__Thread__m_pvHijackedReturnAddress] LOCAL_LABEL(NotHiJacked): add r1, sp, #rsp_offsetof_ExInfo // r1 <- ExInfo* mov r3, #0 str r3, [r1, #OFFSETOF__ExInfo__m_exception] // init the exception object to null mov r3, #1 strb r3, [r1, #OFFSETOF__ExInfo__m_passNumber] // init to the first pass strb r3, [r1, #OFFSETOF__ExInfo__m_kind] mov r3, #0xFFFFFFFF str r3, [r1, #OFFSETOF__ExInfo__m_idxCurClause] // ExKind.Throw // link the ExInfo into the thread's ExInfo chain ldr r3, [r0, #OFFSETOF__Thread__m_pExInfoStackHead] str r3, [r1, #OFFSETOF__ExInfo__m_pPrevExInfo] // pExInfo->m_pPrevExInfo = m_pExInfoStackHead str r1, [r0, #OFFSETOF__Thread__m_pExInfoStackHead] // m_pExInfoStackHead = pExInfo // set the exception context field on the ExInfo add r3, sp, #rsp_offsetof_Context // r3 <- PAL_LIMITED_CONTEXT* str r3, [r1, #OFFSETOF__ExInfo__m_pExContext] // init ExInfo.m_pExContext mov r0, r4 // Restore exception object // r0 contains the exception object // r1 contains the address of the new ExInfo bl C_FUNC(RhThrowEx) EXPORT_POINTER_TO_ADDRESS PointerToRhpThrowEx2 // no return EMIT_BREAKPOINT NESTED_END RhpThrowEx, _TEXT ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // void FASTCALL RhpRethrow() // // SUMMARY: Similar to RhpThrowEx, except that it passes along the currently active ExInfo // // INPUT: // // OUTPUT: // ////////////////////////////////////////////////////////////////////////////////////////////////////////////// NESTED_ENTRY RhpRethrow, _TEXT, NoHandler // Setup a PAL_LIMITED_CONTEXT on the stack { PROLOG_VPUSH {d8-d15} PROLOG_PUSH "{r0,lr}" // Reserve space for SP and store LR PROLOG_PUSH "{r0,r4-r11,lr}" // } end PAL_LIMITED_CONTEXT PROLOG_STACK_ALLOC STACKSIZEOF_ExInfo // r0 = GetThread(); INLINE_GETTHREAD // r1 <- ExInfo* add r1, sp, #rsp_offsetof_ExInfo mov r3, #0 str r3, [r1, #OFFSETOF__ExInfo__m_exception] // init the exception object to null mov r3, #0xFFFFFFFF str r3, [r1, #OFFSETOF__ExInfo__m_idxCurClause] // link the ExInfo into the thread's ExInfo chain ldr r3, [r0, #OFFSETOF__Thread__m_pExInfoStackHead] // r3 <- currently active ExInfo str r3, [r1, #OFFSETOF__ExInfo__m_pPrevExInfo] // pExInfo->m_pPrevExInfo = m_pExInfoStackHead str r1, [r0, #OFFSETOF__Thread__m_pExInfoStackHead] // m_pExInfoStackHead = pExInfo // set the exception context field on the ExInfo add r2, sp, #rsp_offsetof_Context // r2 <- PAL_LIMITED_CONTEXT* str r2, [r1, #OFFSETOF__ExInfo__m_pExContext] // init ExInfo.m_pExContext mov r0, r3 // r0 contains the currently active ExInfo // r1 contains the address of the new ExInfo blx C_FUNC(RhRethrow) EXPORT_POINTER_TO_ADDRESS PointerToRhpRethrow2 // no return EMIT_BREAKPOINT NESTED_END RhpRethrow, _TEXT ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // void* FASTCALL RhpCallCatchFunclet(RtuObjectRef exceptionObj, // void* pHandlerIP, // REGDISPLAY* pRegDisplay, // ExInfo* pExInfo) // // INPUT: R0: exception object // R1: handler funclet address // R2: REGDISPLAY* // R3: ExInfo* // // OUTPUT: // ////////////////////////////////////////////////////////////////////////////////////////////////////////////// NESTED_ENTRY RhpCallCatchFunclet, _TEXT, NoHandler PROLOG_PUSH "{r0,r2-r11,lr}" // r0, r2 & r3 are saved so we have the exception object, // REGDISPLAY and ExInfo later PROLOG_VPUSH {d8-d15} #define rsp_offset_r2 (8 * 8) + 4 #define rsp_offset_r3 (8 * 8) + 8 mov r4, r0 // Save exception object mov r5, r1 // Save handler funclet address mov r6, r2 // Save REGDISPLAY* // Clear the DoNotTriggerGc state before calling out to our managed catch funclet, // trashes r0-r2. // r0 = GetThread() INLINE_GETTHREAD LOCAL_LABEL(ClearRetry_Catch): ldrex r1, [r0, #OFFSETOF__Thread__m_ThreadStateFlags] bics r1, #TSF_DoNotTriggerGc strex r2, r1, [r0, #OFFSETOF__Thread__m_ThreadStateFlags] cbz r2, LOCAL_LABEL(ClearSuccess_Catch) b LOCAL_LABEL(ClearRetry_Catch) LOCAL_LABEL(ClearSuccess_Catch): mov r1, r4 // Reload exception object mov r3, r5 // Reload handler funclet address mov r2, r6 // Reload REGDISPLAY pointer // // set preserved regs to the values expected by the funclet // ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR4] ldr r4, [r12] ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR5] ldr r5, [r12] ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR6] ldr r6, [r12] ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR7] ldr r7, [r12] ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR8] ldr r8, [r12] ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR9] ldr r9, [r12] ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR10] ldr r10, [r12] ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR11] ldr r11, [r12] // // load vfp preserved regs // add r12, r2, #OFFSETOF__REGDISPLAY__D vldm r12!, {d8-d15} ldr r0, [r2, #OFFSETOF__REGDISPLAY__SP] // r0 <- establisher frame // r1 <- exception object blx r3 // call handler funclet mov r4, r0 // Save the result EXPORT_POINTER_TO_ADDRESS PointerToRhpCallCatchFunclet2 INLINE_GETTHREAD // r0 <- Thread* // We must unhijack the thread at this point because the section of stack where the // hijack is applied may go dead. If it does, then the next time we try to unhijack // the thread, it will corrupt the stack. INLINE_THREAD_UNHIJACK r0, r3, r12 // Thread in r0, trashes r3 and r1 ldr r2, [sp, #rsp_offset_r2] // r2 <- REGDISPLAY* ldr r3, [sp, #rsp_offset_r3] // r3 <- current ExInfo* ldr r2, [r2, #OFFSETOF__REGDISPLAY__SP] // r2 <- resume SP value LOCAL_LABEL(PopExInfoLoop): ldr r3, [r3, #OFFSETOF__ExInfo__m_pPrevExInfo] // r3 <- next ExInfo cbz r3, LOCAL_LABEL(DonePopping) // if (pExInfo == null) { we're done } cmp r3, r2 blt LOCAL_LABEL(PopExInfoLoop) // if (pExInfo < resume SP} { keep going } LOCAL_LABEL(DonePopping): str r3, [r0, #OFFSETOF__Thread__m_pExInfoStackHead] // store the new head on the Thread // reset RSP and jump to the continuation address mov sp, r2 bx r4 NESTED_END RhpCallCatchFunclet, _TEXT ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // void FASTCALL RhpCallFinallyFunclet(void* pHandlerIP, REGDISPLAY* pRegDisplay) // // INPUT: R0: handler funclet address // R1: REGDISPLAY* // // OUTPUT: // ////////////////////////////////////////////////////////////////////////////////////////////////////////////// NESTED_ENTRY RhpCallFinallyFunclet, _TEXT, NoHandler PROLOG_PUSH "{r1,r4-r11,lr}" // r1 is saved so we have the REGDISPLAY later PROLOG_VPUSH {d8-d15} #define rsp_offset_r1 8 * 8 // // We want to suppress hijacking between invocations of subsequent finallys. We do // this because we cannot tolerate a GC after one finally has run (and possibly // side-effected the GC state of the method) and then been popped off the stack, // leaving behind no trace of its effect. // // So we clear the state before and set it after invocation of the handler. // mov r4, r0 // Save handler funclet address mov r5, r1 // Save REGDISPLAY* // // clear the DoNotTriggerGc flag, trashes r0-r2 // INLINE_GETTHREAD // r0 <- Thread* LOCAL_LABEL(ClearRetry): ldrex r1, [r0, #OFFSETOF__Thread__m_ThreadStateFlags] bics r1, #TSF_DoNotTriggerGc strex r2, r1, [r0, #OFFSETOF__Thread__m_ThreadStateFlags] cbz r2, LOCAL_LABEL(ClearSuccess) b LOCAL_LABEL(ClearRetry) LOCAL_LABEL(ClearSuccess): mov r2, r4 // reload handler funclet address mov r1, r5 // reload REGDISPLAY pointer // // set preserved regs to the values expected by the funclet // ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR4] ldr r4, [r12] ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR5] ldr r5, [r12] ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR6] ldr r6, [r12] ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR7] ldr r7, [r12] ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR8] ldr r8, [r12] ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR9] ldr r9, [r12] ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR10] ldr r10, [r12] ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR11] ldr r11, [r12] // // load vfp preserved regs // add r12, r1, #OFFSETOF__REGDISPLAY__D vldm r12!, {d8-d15} ldr r0, [r1, #OFFSETOF__REGDISPLAY__SP] // r0 <- establisher frame blx r2 // handler funclet address EXPORT_POINTER_TO_ADDRESS PointerToRhpCallFinallyFunclet2 ldr r1, [sp, #rsp_offset_r1] // reload REGDISPLAY pointer // // save new values of preserved regs into REGDISPLAY // ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR4] str r4, [r12] ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR5] str r5, [r12] ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR6] str r6, [r12] ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR7] str r7, [r12] ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR8] str r8, [r12] ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR9] str r9, [r12] ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR10] str r10, [r12] ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR11] str r11, [r12] // // store vfp preserved regs // add r12, r1, #OFFSETOF__REGDISPLAY__D vstm r12!, {d8-d15} // // set the DoNotTriggerGc flag, trashes r0-r2 // INLINE_GETTHREAD // r0 <- Thread* LOCAL_LABEL(SetRetry): ldrex r1, [r0, #OFFSETOF__Thread__m_ThreadStateFlags] orrs r1, #TSF_DoNotTriggerGc strex r2, r1, [r0, #OFFSETOF__Thread__m_ThreadStateFlags] cbz r2, LOCAL_LABEL(SetSuccess) b LOCAL_LABEL(SetRetry) LOCAL_LABEL(SetSuccess): EPILOG_VPOP {d8-d15} EPILOG_POP "{r1,r4-r11,pc}" NESTED_END RhpCallFinallyFunclet, _TEXT ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // void* FASTCALL RhpCallFilterFunclet(RtuObjectRef exceptionObj, void* pFilterIP, REGDISPLAY* pRegDisplay) // // INPUT: R0: exception object // R1: filter funclet address // R2: REGDISPLAY* // // OUTPUT: // ////////////////////////////////////////////////////////////////////////////////////////////////////////////// NESTED_ENTRY RhpCallFilterFunclet, _TEXT, NoHandler PROLOG_PUSH "{r4-r11,lr}" PROLOG_VPUSH {d8-d15} ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR7] ldr r7, [r12] mov r12, r1 // r12 <- handler funclet address mov r1, r0 // r1 <- exception object ldr r0, [r2, #OFFSETOF__REGDISPLAY__SP] // r0 <- establisher frame // // call the funclet // r0 = establisher frame // r1 = exception object blx r12 EXPORT_POINTER_TO_ADDRESS PointerToRhpCallFilterFunclet2 // R0 contains the result of the filter execution EPILOG_VPOP {d8-d15} EPILOG_POP "{r4-r11,pc}" NESTED_END RhpCallFilterFunclet, _TEXT