/* $Id: except-vcc.h $ */
/** @file
 * IPRT - Visual C++ Compiler - Exception Management.
 */

/*
 * Copyright (C) 2022-2025 Oracle and/or its affiliates.
 *
 * This file is part of VirtualBox base platform packages, as
 * available from https://www.virtualbox.org.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation, in version 3 of the
 * License.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <https://www.gnu.org/licenses>.
 *
 * The contents of this file may alternatively be used under the terms
 * of the Common Development and Distribution License Version 1.0
 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
 * in the VirtualBox distribution, in which case the provisions of the
 * CDDL are applicable instead of those of the GPL.
 *
 * You may elect to license modified versions of this file under the
 * terms and conditions of either the GPL or the CDDL or both.
 *
 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
 */

#ifndef IPRT_INCLUDED_SRC_common_compiler_vcc_except_vcc_h
#define IPRT_INCLUDED_SRC_common_compiler_vcc_except_vcc_h
#ifndef RT_WITHOUT_PRAGMA_ONCE
# pragma once
#endif

#define __C_specific_handler their___C_specific_handler
#include <iprt/win/windows.h>
#undef __C_specific_handler

#include <iprt/types.h>
#include <iprt/assertcompile.h>


RT_C_DECLS_BEGIN

#if 0
/** This is part of the AMD64 and ARM (?) exception interface, but appear to
 * live in the runtime headers for some weird reason. */
typedef enum
{
    ExceptionContinueExecution = 0,
    ExceptionContinueSearch,
    ExceptionNestedException,
    ExceptionCollidedUnwind
} EXCEPTION_DISPOSITION;
#endif

/* The following two are borrowed from pecoff.h, as we typically want to include
   winnt.h with this header and the two header cannot co-exist. */

/**
 * An unwind code for AMD64 and ARM64.
 *
 * @note Also known as UNWIND_CODE or _UNWIND_CODE.
 */
typedef union IMAGE_UNWIND_CODE
{
    struct
    {
        /** The prolog offset where the change takes effect.
         * This means the instruction following the one being described.  */
        uint8_t CodeOffset;
        /** Unwind opcode.
         * For AMD64 see IMAGE_AMD64_UNWIND_OP_CODES. */
        RT_GCC_EXTENSION uint8_t UnwindOp : 4;
        /** Opcode specific. */
        RT_GCC_EXTENSION uint8_t OpInfo   : 4;
    } u;
    uint16_t    FrameOffset;
} IMAGE_UNWIND_CODE;
AssertCompileSize(IMAGE_UNWIND_CODE, 2);

/**
 * Unwind information for AMD64 and ARM64.
 *
 * Pointed to by IMAGE_RUNTIME_FUNCTION_ENTRY::UnwindInfoAddress,
 *
 * @note Also known as UNWIND_INFO or _UNWIND_INFO.
 */
typedef struct IMAGE_UNWIND_INFO
{
    /** Version, currently 1 or 2.  The latter if IMAGE_AMD64_UWOP_EPILOG is used. */
    RT_GCC_EXTENSION uint8_t    Version : 3;
    /** IMAGE_UNW_FLAG_XXX */
    RT_GCC_EXTENSION uint8_t    Flags : 5;
    /** Size of function prolog. */
    uint8_t                     SizeOfProlog;
    /** Number of opcodes in aOpcodes. */
    uint8_t                     CountOfCodes;
    /** Initial frame register. */
    RT_GCC_EXTENSION uint8_t    FrameRegister : 4;
    /** Scaled frame register offset. */
    RT_GCC_EXTENSION uint8_t    FrameOffset : 4;
    /** Unwind opcodes. */
    RT_FLEXIBLE_ARRAY_EXTENSION
    IMAGE_UNWIND_CODE           aOpcodes[RT_FLEXIBLE_ARRAY];
} IMAGE_UNWIND_INFO;
AssertCompileMemberOffset(IMAGE_UNWIND_INFO, aOpcodes, 4);
typedef IMAGE_UNWIND_INFO *PIMAGE_UNWIND_INFO;
typedef IMAGE_UNWIND_INFO const *PCIMAGE_UNWIND_INFO;



#ifdef RT_ARCH_AMD64
/**
 * The Visual C++ 2019 layout of the GS_HANDLER_DATA data type for AMD64.
 *
 * This is pointed to by DISPATCHER_CONTEXT::HandlerData when dispatching
 * exceptions.  The data resides after the unwind info for the function.
 */
typedef struct _GS_HANDLER_DATA
{
    union
    {
        struct
        {
            uint32_t    fEHandler : 1;
#define GS_HANDLER_OFF_COOKIE_IS_EHANDLER   RT_BIT(0) /**< Handles exceptions. */
            uint32_t    fUHandler : 1;
#define GS_HANDLER_OFF_COOKIE_IS_UHANDLER   RT_BIT(1) /**< Handles unwind. */
            uint32_t    fHasAlignment : 1;
#define GS_HANDLER_OFF_COOKIE_HAS_ALIGNMENT RT_BIT(2) /**< Has the uAlignmentMask member. */
        } Bits;
#define GS_HANDLER_OFF_COOKIE_MASK          UINT32_C(0xfffffff8) /**< Mask to apply to offCookie to the the value. */
        uint32_t        offCookie;
    } u;
    int32_t             offAlignedBase;
    /** This field is only there when GS_HANDLER_OFF_COOKIE_HAS_ALIGNMENT is set.
     * it seems. */
    uint32_t            uAlignmentMask;
} GS_HANDLER_DATA;
typedef GS_HANDLER_DATA *PGS_HANDLER_DATA;
typedef GS_HANDLER_DATA const *PCGS_HANDLER_DATA;

/**
 * GS handler checking body.
 * Shared by __GSHandlerCheck, __GSHandlerCheck_SEH & __GSHandlerCheck_EH4.
 * @note This is not a safe macro!
 */
#define IPRT_GS_HANDLER_CHECK_BODY(pHandlerData, a_pvEstFrame) \
    /* \
     * Locate the stack cookie and call the regular stack cookie checker routine. \
     */ \
    /* Calculate the cookie address and read it. */ \
    uintptr_t uPtrFrame = (uintptr_t)(a_pvEstFrame); \
    uint32_t  offCookie = pHandlerData->u.offCookie; \
    if (offCookie & GS_HANDLER_OFF_COOKIE_HAS_ALIGNMENT) \
    { \
        uPtrFrame += pHandlerData->offAlignedBase; \
        uPtrFrame &= ~(uintptr_t)pHandlerData->uAlignmentMask; \
    } \
    uintptr_t uCookie = *(uintptr_t const *)(uPtrFrame + (int32_t)(offCookie & GS_HANDLER_OFF_COOKIE_MASK)); \
    \
    /* The stored cookie is xor'ed with the frame / registration record address \
       or with the frame pointer register if one is being used.  In the latter \
       case, we have to add the frame offset to get the correct address. */ \
    uintptr_t uXorAddr = (uintptr_t)(a_pvEstFrame); \
    PCIMAGE_UNWIND_INFO pUnwindInfo = (PCIMAGE_UNWIND_INFO)(pDispCtx->ImageBase + pDispCtx->FunctionEntry->UnwindInfoAddress); \
    if (pUnwindInfo->FrameRegister != 0) \
        uXorAddr += pUnwindInfo->FrameOffset << 4; \
    \
    /* This call will not return on failure. */ \
    __security_check_cookie(uCookie ^ uXorAddr)

/**
 * Checks the for the precense of handler matching IS_UNWINDING.
 * Shared by __GSHandlerCheck_SEH & __GSHandlerCheck_EH4.
 */
#define IPRT_GS_HANDLER_HAS_HANDLER(pHandlerData, pXcptRec) \
    (  (IS_UNWINDING((pXcptRec)->ExceptionFlags) ? GS_HANDLER_OFF_COOKIE_IS_UHANDLER : GS_HANDLER_OFF_COOKIE_IS_EHANDLER) \
     & (pHandlerData)->u.offCookie)

#endif /* RT_ARCH_AMD64 */

#ifdef RT_ARCH_X86
void __fastcall __security_check_cookie(uintptr_t uCookieToCheck);
#else
void __cdecl    __security_check_cookie(uintptr_t uCookieToCheck);
#endif


#ifdef RT_ARCH_X86

/**
 * Exception registration record for _except_handler4 users
 * (aka EH4_EXCEPTION_REGISTRATION_RECORD).
 *
 * This record is emitted immediately following the stack frame setup, i.e.
 * after doing PUSH EBP and MOV EBP, ESP.  So, EBP equals the address following
 * this structure.
 */
typedef struct EH4_XCPT_REG_REC_T
{
    /** The saved ESP after setting up the stack frame and before the __try. */
    uintptr_t                       uSavedEsp;
    PEXCEPTION_POINTERS             pXctpPtrs;
    /** The SEH exception registration record (chained). */
    EXCEPTION_REGISTRATION_RECORD   XcptRec;
    uintptr_t                       uEncodedScopeTable;
    uint32_t                        uTryLevel;
    /* uintptr_t                    uSavedCallerEbp; */
} EH4_XCPT_REG_REC_T;
AssertCompileSize(EH4_XCPT_REG_REC_T, 24);
/** Pointer to an exception registration record for _except_handler4 (aka
 *  PEH4_EXCEPTION_REGISTRATION_RECORD). */
typedef EH4_XCPT_REG_REC_T *PEH4_XCPT_REG_REC_T;


/** Exception filter function for _except_handler4 users (aka
 * PEXCEPTION_FILTER_X86). */
typedef uint32_t (__cdecl *PFN_EH4_XCPT_FILTER_T)(void);
/** Exception handler block function for _except_handler4 users (aka
 * PEXCEPTION_HANDLER_X86). */
typedef void     (__cdecl *PFN_EH4_XCPT_HANDLER_T)(void);
/** Exception finally block function for _except_handler4 users (aka
 * PTERMINATION_HANDLER_X86).
 */
typedef void  (__fastcall *PFN_EH4_FINALLY_T)(BOOL fAbend);

/**
 * Scope table record describing  __try / __except / __finally blocks (aka
 * EH4_SCOPETABLE_RECORD).
 */
typedef struct EH4_SCOPE_TAB_REC_T
{
    uint32_t                    uEnclosingLevel;
    /** Pointer to the filter sub-function if this is a __try/__except, NULL for
     *  __try/__finally. */
    PFN_EH4_XCPT_FILTER_T       pfnFilter;
    union
    {
        PFN_EH4_XCPT_HANDLER_T  pfnHandler;
        PFN_EH4_FINALLY_T       pfnFinally;
    };
} EH4_SCOPE_TAB_REC_T;
AssertCompileSize(EH4_SCOPE_TAB_REC_T, 12);
/** Pointer to a const _except_handler4 scope table entry. */
typedef EH4_SCOPE_TAB_REC_T const *PCEH4_SCOPE_TAB_REC_T;

/** Special EH4_SCOPE_TAB_REC_T::uEnclosingLevel used to terminate the chain. */
#define EH4_TOPMOST_TRY_LEVEL   UINT32_C(0xfffffffe)

/**
 * Scope table used by _except_handler4 (aka EH4_SCOPETABLE).
 */
typedef struct EH4_SCOPE_TAB_T
{
    uint32_t                    offGSCookie;
    uint32_t                    offGSCookieXor;
    uint32_t                    offEHCookie;
    uint32_t                    offEHCookieXor;
    EH4_SCOPE_TAB_REC_T         aScopeRecords[RT_FLEXIBLE_ARRAY];
} EH4_SCOPE_TAB_T;
AssertCompileMemberOffset(EH4_SCOPE_TAB_T, aScopeRecords, 16);
/** Pointer to a const _except_handler4 scope table. */
typedef EH4_SCOPE_TAB_T const *PCEH4_SCOPE_TAB_T;

/** Special EH4_SCOPE_TAB_T::offGSCookie value. */
# define EH4_NO_GS_COOKIE       UINT32_C(0xfffffffe)

#endif /* RT_ARCH_X86 */



#if defined(RT_ARCH_AMD64)
EXCEPTION_DISPOSITION __C_specific_handler(PEXCEPTION_RECORD pXcptRec, PEXCEPTION_REGISTRATION_RECORD pXcptRegRec,
                                           PCONTEXT pCpuCtx, PDISPATCHER_CONTEXT pDispCtx);
EXCEPTION_DISPOSITION __CxxFrameHandler4(PEXCEPTION_RECORD pXcptRec, PVOID pvEstFrame,
                                         PCONTEXT pCpuCtx, PDISPATCHER_CONTEXT pDispCtx);
#endif

RT_C_DECLS_END

#endif /* !IPRT_INCLUDED_SRC_common_compiler_vcc_except_vcc_h */

