VirtualBox

Changeset 97862 in vbox for trunk/src/VBox/Runtime/common


Ignore:
Timestamp:
Dec 23, 2022 4:55:57 PM (2 years ago)
Author:
vboxsync
Message:

IPRT/nocrt: Added a simple structured exception handling (SEH) testcase and did a basic implementation of _except_handler4 for 32-bit x86. bugref:10261 ticketref:21303

Location:
trunk/src/VBox/Runtime/common/compiler/vcc
Files:
1 added
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/common/compiler/vcc/except-vcc.h

    r96559 r97862  
    120120
    121121#ifdef RT_ARCH_AMD64
    122 
    123122/**
    124123 * The Visual C++ 2019 layout of the GS_HANDLER_DATA data type for AMD64.
     
    150149typedef GS_HANDLER_DATA *PGS_HANDLER_DATA;
    151150typedef GS_HANDLER_DATA const *PCGS_HANDLER_DATA;
    152 #endif
     151#endif /* RT_ARCH_AMD64 */
    153152
    154153#ifdef RT_ARCH_X86
     
    158157#endif
    159158
     159
     160#ifdef RT_ARCH_X86
     161
     162/**
     163 * Exception registration record for _except_handler4 users
     164 * (aka EH4_EXCEPTION_REGISTRATION_RECORD).
     165 *
     166 * This record is emitted immediately following the stack frame setup, i.e.
     167 * after doing PUSH EBP and MOV EBP, ESP.  So, EBP equals the address following
     168 * this structure.
     169 */
     170typedef struct EH4_XCPT_REG_REC_T
     171{
     172    /** The saved ESP after setting up the stack frame and before the __try. */
     173    uintptr_t                       uSavedEsp;
     174    PEXCEPTION_POINTERS             pXctpPtrs;
     175    /** The SEH exception registration record (chained). */
     176    EXCEPTION_REGISTRATION_RECORD   XcptRec;
     177    uintptr_t                       uEncodedScopeTable;
     178    uint32_t                        uTryLevel;
     179    /* uintptr_t                    uSavedCallerEbp; */
     180} EH4_XCPT_REG_REC_T;
     181AssertCompileSize(EH4_XCPT_REG_REC_T, 24);
     182/** Pointer to an exception registration record for _except_handler4 (aka
     183 *  PEH4_EXCEPTION_REGISTRATION_RECORD). */
     184typedef EH4_XCPT_REG_REC_T *PEH4_XCPT_REG_REC_T;
     185
     186
     187/** Exception filter function for _except_handler4 users (aka
     188 * PEXCEPTION_FILTER_X86). */
     189typedef uint32_t (__cdecl *PFN_EH4_XCPT_FILTER_T)(void);
     190/** Exception handler block function for _except_handler4 users (aka
     191 * PEXCEPTION_HANDLER_X86). */
     192typedef void     (__cdecl *PFN_EH4_XCPT_HANDLER_T)(void);
     193/** Exception finally block function for _except_handler4 users (aka
     194 * PTERMINATION_HANDLER_X86).
     195 */
     196typedef void  (__fastcall *PFN_EH4_FINALLY_T)(BOOL fAbend);
     197
     198/**
     199 * Scope table record describing  __try / __except / __finally blocks (aka
     200 * EH4_SCOPETABLE_RECORD).
     201 */
     202typedef struct EH4_SCOPE_TAB_REC_T
     203{
     204    uint32_t                    uEnclosingLevel;
     205    /** Pointer to the filter sub-function if this is a __try/__except, NULL for
     206     *  __try/__finally. */
     207    PFN_EH4_XCPT_FILTER_T       pfnFilter;
     208    union
     209    {
     210        PFN_EH4_XCPT_HANDLER_T  pfnHandler;
     211        PFN_EH4_FINALLY_T       pfnFinally;
     212    };
     213} EH4_SCOPE_TAB_REC_T;
     214AssertCompileSize(EH4_SCOPE_TAB_REC_T, 12);
     215/** Pointer to a const _except_handler4 scope table entry. */
     216typedef EH4_SCOPE_TAB_REC_T const *PCEH4_SCOPE_TAB_REC_T;
     217
     218/** Special EH4_SCOPE_TAB_REC_T::uEnclosingLevel used to terminate the chain. */
     219#define EH4_TOPMOST_TRY_LEVEL   UINT32_C(0xfffffffe)
     220
     221/**
     222 * Scope table used by _except_handler4 (aka EH4_SCOPETABLE).
     223 */
     224typedef struct EH4_SCOPE_TAB_T
     225{
     226    uint32_t                    offGSCookie;
     227    uint32_t                    offGSCookieXor;
     228    uint32_t                    offEHCookie;
     229    uint32_t                    offEHCookieXor;
     230    EH4_SCOPE_TAB_REC_T         aScopeRecords[RT_FLEXIBLE_ARRAY];
     231} EH4_SCOPE_TAB_T;
     232AssertCompileMemberOffset(EH4_SCOPE_TAB_T, aScopeRecords, 16);
     233/** Pointer to a const _except_handler4 scope table. */
     234typedef EH4_SCOPE_TAB_T const *PCEH4_SCOPE_TAB_T;
     235
     236/** Special EH4_SCOPE_TAB_T::offGSCookie value. */
     237# define EH4_NO_GS_COOKIE       UINT32_C(0xfffffffe)
     238
     239#endif /* RT_ARCH_X86 */
     240
     241
     242
    160243#if defined(RT_ARCH_AMD64)
    161244EXCEPTION_DISPOSITION __C_specific_handler(PEXCEPTION_RECORD pXcptRec, PEXCEPTION_REGISTRATION_RECORD pXcptRegRec,
  • trunk/src/VBox/Runtime/common/compiler/vcc/except-x86-vcc.cpp

    r96407 r97862  
    4141#include <iprt/win/windows.h>
    4242
    43 
     43#include "except-vcc.h"
     44
     45
     46/*********************************************************************************************************************************
     47*   Global Variables                                                                                                             *
     48*********************************************************************************************************************************/
     49extern "C" uintptr_t __security_cookie;
     50
     51
     52/*********************************************************************************************************************************
     53*   Internal Functions                                                                                                           *
     54*********************************************************************************************************************************/
     55DECLASM(LONG)                   rtVccEh4DoFiltering(PFN_EH4_XCPT_FILTER_T pfnFilter, uint8_t const *pbFrame);
     56DECLASM(DECL_NO_RETURN(void))   rtVccEh4JumpToHandler(PFN_EH4_XCPT_HANDLER_T pfnHandler, uint8_t const *pbFrame);
     57//DECLASM(void)                   rtVccEh4DoLocalUnwind(PEXCEPTION_REGISTRATION_RECORD pXcptRegRec, uint32_t uTargetTryLevel,
     58//                                                      uint8_t const *pbFrame, uintptr_t *pSecurityCookie);
     59DECLASM(void)                   rtVccEh4DoGlobalUnwind(PEXCEPTION_RECORD pXcptRec, PEXCEPTION_REGISTRATION_RECORD pXcptRegRec);
     60DECLASM(void)                   rtVccEh4DoFinally(PFN_EH4_FINALLY_T pfnFinally, bool fAbend, uint8_t const *pbFrame);
     61
     62
     63static void rtVccEh4DoLocalUnwind(PEXCEPTION_REGISTRATION_RECORD pXcptRegRec, uint32_t uTargetTryLevel,
     64                                  uint8_t const *pbFrame, uintptr_t *pSecurityCookie)
     65{
     66    /*
     67     * Manually set up exception handler.
     68     */
     69    /** @todo    */
     70
     71    /*
     72     * Do the unwinding.
     73     */
     74    PEH4_XCPT_REG_REC_T pEh4XcptRegRec = RT_FROM_MEMBER(pXcptRegRec, EH4_XCPT_REG_REC_T, XcptRec);
     75    uint32_t            uCurTryLevel   = pEh4XcptRegRec->uTryLevel;
     76    while (   uCurTryLevel != EH4_TOPMOST_TRY_LEVEL
     77           && (   uCurTryLevel > uTargetTryLevel
     78               || uTargetTryLevel == EH4_TOPMOST_TRY_LEVEL /* if we knew what 0xffffffff meant, this could probably be omitted */ ))
     79    {
     80        PCEH4_SCOPE_TAB_T const pScopeTable = (PCEH4_SCOPE_TAB_T)(pEh4XcptRegRec->uEncodedScopeTable ^ *pSecurityCookie);
     81        PCEH4_SCOPE_TAB_REC_T const pEntry  = &pScopeTable->aScopeRecords[uCurTryLevel];
     82
     83        pEh4XcptRegRec->uTryLevel = uCurTryLevel = pEntry->uEnclosingLevel;
     84
     85        /* __finally scope table entries have no filter sub-function. */
     86        if (!pEntry->pfnFilter)
     87        {
     88            //RTAssertMsg2("rtVccEh4DoLocalUnwind: Calling %p (level %#x)\n", pEntry->pfnFinally, uCurTryLevel);
     89            rtVccEh4DoFinally(pEntry->pfnFinally, true /*fAbend*/, pbFrame);
     90
     91            /* Read the try level again in case it changed... */
     92            uCurTryLevel = pEh4XcptRegRec->uTryLevel;
     93        }
     94    }
     95
     96    /*
     97     * Deregister exception handler.
     98     */
     99    /** @todo */
     100}
     101
     102
     103DECLINLINE(void) rtVccValidateExceptionContextRecord(PCONTEXT pCpuCtx)
     104{
     105    RT_NOREF(pCpuCtx);
     106    /** @todo  Implement __exception_validate_context_record .*/
     107}
     108
     109
     110DECLINLINE(void) rtVccEh4ValidateCookies(PCEH4_SCOPE_TAB_T pScopeTable, uint8_t const *pbFrame)
     111{
     112    if (pScopeTable->offGSCookie != EH4_NO_GS_COOKIE)
     113    {
     114        uintptr_t uGsCookie = *(uintptr_t const *)&pbFrame[pScopeTable->offGSCookie];
     115        uGsCookie          ^=          (uintptr_t)&pbFrame[pScopeTable->offGSCookieXor];
     116        __security_check_cookie(uGsCookie);
     117    }
     118
     119    uintptr_t uEhCookie = *(uintptr_t const *)&pbFrame[pScopeTable->offEHCookie];
     120    uEhCookie          ^=          (uintptr_t)&pbFrame[pScopeTable->offEHCookieXor];
     121    __security_check_cookie(uEhCookie);
     122}
     123
     124
     125/**
     126 * Call exception filters, handlers and unwind code for x86 code.
     127 *
     128 * This is called for windows' structured exception handling (SEH) in x86 32-bit
     129 * code, i.e. the __try/__except/__finally stuff in Visual C++.  The compiler
     130 * generate scope records for the __try/__except blocks as well as unwind
     131 * records for __finally and probably C++ stack object destructors.
     132 *
     133 * @returns Exception disposition.
     134 * @param   pXcptRec    The exception record.
     135 * @param   pXcptRegRec The exception registration record, taken to be the frame
     136 *                      address.
     137 * @param   pCpuCtx     The CPU context for the exception.
     138 * @param   pDispCtx    Dispatcher context.
     139 */
    44140extern "C" __declspec(guard(suppress))
    45 DWORD _except_handler4(PEXCEPTION_RECORD pXcptRec, PEXCEPTION_REGISTRATION_RECORD pXcptRegRec, PCONTEXT pCtxRec, PVOID pvCtx)
    46 {
    47     RT_NOREF(pXcptRec, pXcptRegRec, pCtxRec, pvCtx);
    48     /** @todo */
    49     __debugbreak();
    50     return 0;
    51 }
    52 
     141DWORD _except_handler4(PEXCEPTION_RECORD pXcptRec, PEXCEPTION_REGISTRATION_RECORD pXcptRegRec, PCONTEXT pCpuCtx, PVOID pvCtx)
     142{
     143    /*
     144     * The registration record (probably chained on FS:[0] like in the OS/2 days)
     145     * points to a larger structure specific to _except_handler4.  The structure
     146     * is planted right after the saved caller EBP value when establishing the
     147     * stack frame, so EBP = pXcptRegRec + 1;
     148     */
     149    PEH4_XCPT_REG_REC_T const pEh4XcptRegRec = RT_FROM_MEMBER(pXcptRegRec, EH4_XCPT_REG_REC_T, XcptRec);
     150    uint8_t * const           pbFrame        = (uint8_t *)&pEh4XcptRegRec[1];
     151    PCEH4_SCOPE_TAB_T const   pScopeTable    = (PCEH4_SCOPE_TAB_T)(pEh4XcptRegRec->uEncodedScopeTable ^ __security_cookie);
     152
     153    /*
     154     * Validate the stack cookie and exception context.
     155     */
     156    rtVccEh4ValidateCookies(pScopeTable, pbFrame);
     157    rtVccValidateExceptionContextRecord(pCpuCtx);
     158
     159    /*
     160     * If dispatching an exception, call the exception filter functions and jump
     161     * to the __except blocks if so directed.
     162     */
     163    if (IS_DISPATCHING(pXcptRec->ExceptionFlags))
     164    {
     165        uint32_t uTryLevel = pEh4XcptRegRec->uTryLevel;
     166        //RTAssertMsg2("_except_handler4: dispatch: uTryLevel=%#x\n", uTryLevel);
     167        while (uTryLevel != EH4_TOPMOST_TRY_LEVEL)
     168        {
     169            PCEH4_SCOPE_TAB_REC_T const pEntry    = &pScopeTable->aScopeRecords[uTryLevel];
     170            PFN_EH4_XCPT_FILTER_T const pfnFilter = pEntry->pfnFilter;
     171            if (pfnFilter)
     172            {
     173                /* Call the __except filtering expression: */
     174                //RTAssertMsg2("_except_handler4: Calling pfnFilter=%p\n", pfnFilter);
     175                EXCEPTION_POINTERS XcptPtrs = { pXcptRec, pCpuCtx };
     176                pEh4XcptRegRec->pXctpPtrs = &XcptPtrs;
     177                LONG lRet = rtVccEh4DoFiltering(pfnFilter, pbFrame);
     178                pEh4XcptRegRec->pXctpPtrs = NULL;
     179                //RTAssertMsg2("_except_handler4: pfnFilter=%p -> %ld\n", pfnFilter, lRet);
     180                rtVccEh4ValidateCookies(pScopeTable, pbFrame);
     181
     182                /* Return if we're supposed to continue execution (the convention
     183                   it to match negative values rather than the exact defined value):  */
     184                AssertCompile(EXCEPTION_CONTINUE_EXECUTION == -1);
     185                if (lRet <= EXCEPTION_CONTINUE_EXECUTION)
     186                    return ExceptionContinueExecution;
     187
     188                /* Similarly, the handler is executed for any positive value. */
     189                AssertCompile(EXCEPTION_CONTINUE_SEARCH == 0);
     190                AssertCompile(EXCEPTION_EXECUTE_HANDLER == 1);
     191                if (lRet >= EXCEPTION_EXECUTE_HANDLER)
     192                {
     193                    /* We're about to resume execution in the __except block, so unwind
     194                       up to it first. */
     195                    //RTAssertMsg2("_except_handler4: global unwind\n");
     196                    rtVccEh4DoGlobalUnwind(pXcptRec, &pEh4XcptRegRec->XcptRec);
     197                    if (pEh4XcptRegRec->uTryLevel != EH4_TOPMOST_TRY_LEVEL)
     198                    {
     199                        //RTAssertMsg2("_except_handler4: local unwind\n");
     200                        rtVccEh4DoLocalUnwind(&pEh4XcptRegRec->XcptRec, uTryLevel, pbFrame, &__security_cookie);
     201                    }
     202                    rtVccEh4ValidateCookies(pScopeTable, pbFrame);
     203
     204                    /* Now jump to the __except block.  This will _not_ return. */
     205                    //RTAssertMsg2("_except_handler4: jumping to __except block %p (level %#x)\n", pEntry->pfnHandler, pEntry->uEnclosingLevel);
     206                    pEh4XcptRegRec->uTryLevel = pEntry->uEnclosingLevel;
     207                    rtVccEh4ValidateCookies(pScopeTable, pbFrame); /* paranoia^2 */
     208
     209                    rtVccEh4JumpToHandler(pEntry->pfnHandler, pbFrame);
     210                    /* (not reached) */
     211                }
     212            }
     213
     214            /*
     215             * Next try level.
     216             */
     217            uTryLevel = pEntry->uEnclosingLevel;
     218        }
     219    }
     220    /*
     221     * If not dispatching we're unwinding, so we call any __finally blocks.
     222     */
     223    else
     224    {
     225        //RTAssertMsg2("_except_handler4: unwind: uTryLevel=%#x\n", pEh4XcptRegRec->uTryLevel);
     226        if (pEh4XcptRegRec->uTryLevel != EH4_TOPMOST_TRY_LEVEL)
     227        {
     228            rtVccEh4DoLocalUnwind(&pEh4XcptRegRec->XcptRec, EH4_TOPMOST_TRY_LEVEL, pbFrame, &__security_cookie);
     229            rtVccEh4ValidateCookies(pScopeTable, pbFrame);
     230        }
     231    }
     232
     233    RT_NOREF(pvCtx);
     234    return ExceptionContinueSearch;
     235}
     236
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette