summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/common/compiler
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Runtime/common/compiler
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Runtime/common/compiler')
-rw-r--r--src/VBox/Runtime/common/compiler/vcc/atexit-vcc.cpp145
-rw-r--r--src/VBox/Runtime/common/compiler/vcc/except-seh-vcc.cpp243
-rw-r--r--src/VBox/Runtime/common/compiler/vcc/except-vcc.h251
-rw-r--r--src/VBox/Runtime/common/compiler/vcc/except-x86-vcc-asm.asm358
-rw-r--r--src/VBox/Runtime/common/compiler/vcc/except-x86-vcc.cpp329
-rw-r--r--src/VBox/Runtime/common/compiler/vcc/fltused-vcc.cpp58
-rw-r--r--src/VBox/Runtime/common/compiler/vcc/ftol2-vcc.asm80
-rw-r--r--src/VBox/Runtime/common/compiler/vcc/guard-vcc.asm108
-rw-r--r--src/VBox/Runtime/common/compiler/vcc/initializers-c-cpp-vcc.cpp158
-rw-r--r--src/VBox/Runtime/common/compiler/vcc/loadcfg-vcc.c154
-rw-r--r--src/VBox/Runtime/common/compiler/vcc/purecall-vcc.cpp63
-rw-r--r--src/VBox/Runtime/common/compiler/vcc/stack-except-seh-vcc.cpp117
-rw-r--r--src/VBox/Runtime/common/compiler/vcc/stack-except-vcc.cpp106
-rw-r--r--src/VBox/Runtime/common/compiler/vcc/stack-probe-vcc.asm157
-rw-r--r--src/VBox/Runtime/common/compiler/vcc/stack-vcc.asm639
-rw-r--r--src/VBox/Runtime/common/compiler/vcc/stacksup-vcc.cpp371
-rw-r--r--src/VBox/Runtime/common/compiler/vcc/tlsdir-vcc.c114
17 files changed, 3451 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/compiler/vcc/atexit-vcc.cpp b/src/VBox/Runtime/common/compiler/vcc/atexit-vcc.cpp
new file mode 100644
index 00000000..f6fad758
--- /dev/null
+++ b/src/VBox/Runtime/common/compiler/vcc/atexit-vcc.cpp
@@ -0,0 +1,145 @@
+/* $Id: atexit-vcc.cpp $ */
+/** @file
+ * IPRT - Visual C++ Compiler - Simple atexit implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 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
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/mem.h>
+#include <iprt/nocrt/stdlib.h>
+
+#include "internal/compiler-vcc.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct RTNOCRTATEXITCHUNK
+{
+ PFNRTNOCRTATEXITCALLBACK apfnCallbacks[256];
+} RTNOCRTATEXITCHUNK;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The first atexit() registration chunk. */
+static RTNOCRTATEXITCHUNK g_aAtExitPrealloc;
+/** Array of atexit() callback chunk pointers. */
+static RTNOCRTATEXITCHUNK *g_apAtExit[8192 / 256] = { &g_aAtExitPrealloc, };
+/** Chunk and callback index in one. */
+static volatile uint32_t g_idxNextAtExit = 0;
+
+
+/* Note! not using atexit here because it'll clash with built-in prototype. */
+extern "C" int nocrt_atexit(PFNRTNOCRTATEXITCALLBACK pfnCallback) RT_NOEXCEPT
+{
+ AssertPtr(pfnCallback);
+
+ /*
+ * Allocate a table index.
+ */
+ uint32_t idx = ASMAtomicIncU32(&g_idxNextAtExit) - 1;
+ AssertReturnStmt(idx < RT_ELEMENTS(g_apAtExit) * RT_ELEMENTS(g_apAtExit[0]->apfnCallbacks),
+ ASMAtomicDecU32(&g_idxNextAtExit), -1);
+
+ /*
+ * Make sure the table chunk is there.
+ */
+ uint32_t idxChunk = idx / RT_ELEMENTS(g_apAtExit[0]->apfnCallbacks);
+ RTNOCRTATEXITCHUNK *pChunk = ASMAtomicReadPtrT(&g_apAtExit[idxChunk], RTNOCRTATEXITCHUNK *);
+ if (!pChunk)
+ {
+ pChunk = (RTNOCRTATEXITCHUNK *)RTMemAllocZ(sizeof(*pChunk)); /* ASSUMES that the allocator works w/o initialization! */
+ AssertReturn(pChunk, -1); /* don't try decrement, someone could be racing us... */
+
+ if (!ASMAtomicCmpXchgPtr(&g_apAtExit[idxChunk], pChunk, NULL))
+ {
+ RTMemFree(pChunk);
+
+ pChunk = ASMAtomicReadPtrT(&g_apAtExit[idxChunk], RTNOCRTATEXITCHUNK *);
+ Assert(pChunk);
+ }
+ }
+
+ /*
+ * Add our callback.
+ */
+ pChunk->apfnCallbacks[idxChunk % RT_ELEMENTS(pChunk->apfnCallbacks)] = pfnCallback;
+ return 0;
+}
+RT_ALIAS_AND_EXPORT_NOCRT_SYMBOL(atexit);
+
+
+void rtVccTermRunAtExit(void) RT_NOEXCEPT
+{
+ uint32_t idxAtExit = ASMAtomicReadU32(&g_idxNextAtExit);
+ if (idxAtExit-- > 0)
+ {
+ uint32_t idxChunk = idxAtExit / RT_ELEMENTS(g_apAtExit[0]->apfnCallbacks);
+ uint32_t idxCallback = idxAtExit % RT_ELEMENTS(g_apAtExit[0]->apfnCallbacks);
+ for (;;)
+ {
+ RTNOCRTATEXITCHUNK *pChunk = ASMAtomicReadPtrT(&g_apAtExit[idxChunk], RTNOCRTATEXITCHUNK *);
+ if (pChunk)
+ {
+ do
+ {
+ g_idxNextAtExit = idxAtExit--; /* Make sure we don't try problematic atexit callbacks! */
+
+ PFNRTNOCRTATEXITCALLBACK pfnCallback = pChunk->apfnCallbacks[idxCallback];
+ if (pfnCallback) /* Can be NULL see registration code */
+ {
+ pfnCallback();
+ pChunk->apfnCallbacks[idxCallback] = NULL;
+ }
+ } while (idxCallback-- > 0);
+ }
+ else
+ idxAtExit -= RT_ELEMENTS(g_apAtExit[0]->apfnCallbacks);
+ if (idxChunk == 0)
+ break;
+ idxChunk--;
+ idxCallback = RT_ELEMENTS(g_apAtExit[0]->apfnCallbacks) - 1;
+ }
+
+ g_idxNextAtExit = 0;
+ }
+}
+
diff --git a/src/VBox/Runtime/common/compiler/vcc/except-seh-vcc.cpp b/src/VBox/Runtime/common/compiler/vcc/except-seh-vcc.cpp
new file mode 100644
index 00000000..3a873673
--- /dev/null
+++ b/src/VBox/Runtime/common/compiler/vcc/except-seh-vcc.cpp
@@ -0,0 +1,243 @@
+/* $Id: except-seh-vcc.cpp $ */
+/** @file
+ * IPRT - Visual C++ Compiler - SEH exception handler (__try/__except/__finally).
+ */
+
+/*
+ * Copyright (C) 2022-2023 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
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/nocrt.h"
+
+#include "except-vcc.h"
+
+
+#if !defined(RT_ARCH_AMD64)
+# error "This file is for AMD64 (and probably ARM, but needs porting)"
+#endif
+
+
+/**
+ * Calls an exception filter w/o doing any control guard checks.
+ *
+ * Doing this within an inline function to prevent disabling CFG for any other
+ * calls that __C_specific_handler might be doing.
+ *
+ * Presumably, the presumption here is that since the target address here is
+ * taken from tables assumed to be readonly and generated by the compiler, there
+ * is no need to do any CFG checks. Besides, the target isn't a function that
+ * is safe to be called out of context and thus doesn't belong in the CFG tables
+ * in any way.
+ */
+__declspec(guard(ignore))
+DECLINLINE(LONG) CallFilterFunction(PEXCEPTION_FILTER pfnFilter, PEXCEPTION_POINTERS pXcptPtrs,
+ PEXCEPTION_REGISTRATION_RECORD pXcptRegRec)
+{
+ return pfnFilter(pXcptPtrs, pXcptRegRec);
+}
+
+
+/**
+ * Calls an exception finally block w/o doing any control guard checks.
+ *
+ * See CallFilterFunction for details.
+ */
+__declspec(guard(ignore))
+DECLINLINE(void) CallFinallyFunction(PTERMINATION_HANDLER const pfnTermHandler, BOOLEAN fAbend,
+ PEXCEPTION_REGISTRATION_RECORD pXcptRegRec)
+{
+ pfnTermHandler(fAbend, pXcptRegRec);
+}
+
+
+/**
+ * Call exception filters, handlers and unwind code.
+ *
+ * This is called for windows' structured exception handling (SEH), i.e. the
+ * __try/__except/__finally stuff in Visual C++. The compiler generate scope
+ * records for the __try/__except blocks as well as unwind records for __finally
+ * and probably C++ stack object destructors.
+ *
+ * @returns Exception disposition.
+ * @param pXcptRec The exception record.
+ * @param pXcptRegRec The exception registration record, taken to be the frame
+ * address.
+ * @param pCpuCtx The CPU context for the exception.
+ * @param pDispCtx Dispatcher context.
+ */
+EXCEPTION_DISPOSITION __C_specific_handler(PEXCEPTION_RECORD pXcptRec, PEXCEPTION_REGISTRATION_RECORD pXcptRegRec,
+ PCONTEXT pCpuCtx, PDISPATCHER_CONTEXT pDispCtx)
+{
+ /*
+ * This function works the scope table, starting at ScopeIndex
+ * from the dispatcher context.
+ */
+ SCOPE_TABLE const * const pScopeTab = (SCOPE_TABLE const *)pDispCtx->HandlerData;
+ uint32_t const cScopes = pScopeTab->Count;
+ uint32_t idxScope = pDispCtx->ScopeIndex;
+
+ /*
+ * The table addresses are RVAs, so convert the program counter (PC) to an RVA.
+ */
+ uint32_t const uRvaPc = pDispCtx->ControlPc - pDispCtx->ImageBase;
+
+ /*
+ * Before we get any further, there are two types of scope records:
+ * 1. Unwind (aka termination) handler (JumpTarget == 0).
+ * 2. Exception filter & handler (JumpTarget != 0).
+ */
+
+ if (IS_DISPATCHING(pXcptRec->ExceptionFlags))
+ {
+ /*
+ * Call exception filter functions when dispatching.
+ */
+ for (; idxScope < cScopes; idxScope++)
+ {
+ /* Skip unwind entries (exception handler set to zero). */
+ uint32_t const uXcptHandler = pScopeTab->ScopeRecord[idxScope].JumpTarget;
+ if (uXcptHandler != 0)
+ {
+ uint32_t const uBegin = pScopeTab->ScopeRecord[idxScope].BeginAddress;
+ uint32_t const uEnd = pScopeTab->ScopeRecord[idxScope].EndAddress;
+ uint32_t const cbScope = uEnd - uBegin;
+ if ( uRvaPc - uBegin < cbScope
+ && uBegin < uEnd /* paranoia */)
+ {
+ /* The special HandlerAddress value 1 translates to a
+ EXCEPTION_EXECUTE_HANDLER filter return value. */
+ LONG lRet = EXCEPTION_EXECUTE_HANDLER;
+ uint32_t const uFltTermHandler = pScopeTab->ScopeRecord[idxScope].HandlerAddress;
+ if (uFltTermHandler != 1)
+ {
+ PEXCEPTION_FILTER const pfnFilter = (PEXCEPTION_FILTER)(pDispCtx->ImageBase + uFltTermHandler);
+ EXCEPTION_POINTERS XcptPtrs = { pXcptRec, pCpuCtx };
+ lRet = CallFilterFunction(pfnFilter, &XcptPtrs, pXcptRegRec);
+
+ AssertCompile(EXCEPTION_CONTINUE_SEARCH == 0);
+ if (lRet == EXCEPTION_CONTINUE_SEARCH)
+ continue;
+ }
+
+ /* Return if we're supposed to continue execution (the convention
+ it to match negative values rather than the exact defined value): */
+ AssertCompile(EXCEPTION_CONTINUE_EXECUTION == -1);
+ AssertCompile(EXCEPTION_EXECUTE_HANDLER == 1);
+ if (lRet <= EXCEPTION_CONTINUE_EXECUTION)
+ return ExceptionContinueExecution;
+
+ /* Execute the handler (lRet >= EXCEPTION_EXECUTE_HANDLER). */
+ uintptr_t const uPtrXcptHandler = uXcptHandler + pDispCtx->ImageBase;
+ /** @todo shouldn't we do a guard check on this call? */
+
+ /// @todo _NLG_Notify(uPtrXcptHandler, pXcptRegRec, 1); - debugger notification.
+
+ RtlUnwindEx(pXcptRegRec, (void *)uPtrXcptHandler, pXcptRec,
+ (PVOID)(uintptr_t)pXcptRec->ExceptionCode, pCpuCtx, pDispCtx->HistoryTable);
+
+ /// @todo _NLG_Return2(); - debugger notification.
+ }
+ }
+ }
+ }
+ else
+ {
+ /*
+ * Do unwinding.
+ */
+
+ /* Convert the target unwind address to an RVA up front for efficiency.
+ (I think target unwinding is what the RtlUnwindEx call above does.) */
+ uint32_t const uTargetPc = pXcptRec->ExceptionFlags & EXCEPTION_TARGET_UNWIND
+ ? pDispCtx->TargetIp - pDispCtx->ImageBase
+ : UINT32_MAX;
+ //RTAssertMsg2("__C_specific_handler: unwind: idxScope=%#x cScopes=%#x uTargetPc=%#x fXcpt=%#x\n", idxScope, cScopes, uTargetPc, pXcptRec->ExceptionFlags);
+
+ for (; idxScope < cScopes; idxScope++)
+ {
+ uint32_t const uBegin = pScopeTab->ScopeRecord[idxScope].BeginAddress;
+ uint32_t const uEnd = pScopeTab->ScopeRecord[idxScope].EndAddress;
+ uint32_t const cbScope = uEnd - uBegin;
+ if ( uRvaPc - uBegin < cbScope
+ && uBegin < uEnd /* paranoia */)
+ {
+ uint32_t const uFltTermHandler = pScopeTab->ScopeRecord[idxScope].HandlerAddress;
+ uint32_t const uXcptHandler = pScopeTab->ScopeRecord[idxScope].JumpTarget;
+
+ /* Target unwind requires us to stop if the target PC is in the same
+ scope as the control PC. Happens for goto out of inner __try scope
+ or when longjmp'ing into a __try scope. */
+ if (pXcptRec->ExceptionFlags & EXCEPTION_TARGET_UNWIND)
+ {
+ /* The scope same-ness is identified by the same handler and jump target rva values. */
+ for (uint32_t idxTgtScope = 0; idxTgtScope < cScopes; idxTgtScope++)
+ if ( pScopeTab->ScopeRecord[idxTgtScope].JumpTarget == uXcptHandler
+ && pScopeTab->ScopeRecord[idxTgtScope].HandlerAddress == uFltTermHandler)
+ {
+ uint32_t const uTgtBegin = pScopeTab->ScopeRecord[idxTgtScope].BeginAddress;
+ uint32_t const uTgtEnd = pScopeTab->ScopeRecord[idxTgtScope].EndAddress;
+ uint32_t const cbTgtScope = uTgtEnd - uTgtBegin;
+ if ( uTargetPc - uTgtBegin < cbTgtScope
+ && uTgtBegin < uTgtEnd /* paranoia */)
+ {
+ //RTAssertMsg2("__C_specific_handler: ExceptionContinueSearch (#1)\n");
+ return ExceptionContinueSearch;
+ }
+ }
+ }
+
+ /* The unwind handlers are what we're here for. */
+ if (uXcptHandler == 0)
+ {
+ PTERMINATION_HANDLER const pfnTermHandler = (PTERMINATION_HANDLER)(pDispCtx->ImageBase + uFltTermHandler);
+ pDispCtx->ScopeIndex = idxScope + 1;
+ //RTAssertMsg2("__C_specific_handler: Calling __finally %p (idxScope=%#x)\n", pfnTermHandler, idxScope);
+ CallFinallyFunction(pfnTermHandler, TRUE /*fAbend*/, pXcptRegRec);
+ }
+ /* Exception filter & handler entries are skipped, unless the exception
+ handler is being targeted by the unwind, in which case we're done
+ unwinding and the caller should transfer control there. */
+ else if ( uXcptHandler == uTargetPc
+ && (pXcptRec->ExceptionFlags & EXCEPTION_TARGET_UNWIND))
+ {
+ //RTAssertMsg2("__C_specific_handler: ExceptionContinueSearch (#2)\n");
+ return ExceptionContinueSearch;
+ }
+ }
+ }
+ }
+
+ return ExceptionContinueSearch;
+}
+
diff --git a/src/VBox/Runtime/common/compiler/vcc/except-vcc.h b/src/VBox/Runtime/common/compiler/vcc/except-vcc.h
new file mode 100644
index 00000000..f611adb6
--- /dev/null
+++ b/src/VBox/Runtime/common/compiler/vcc/except-vcc.h
@@ -0,0 +1,251 @@
+/* $Id: except-vcc.h $ */
+/** @file
+ * IPRT - Visual C++ Compiler - Exception Management.
+ */
+
+/*
+ * Copyright (C) 2022-2023 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;
+#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);
+#endif
+
+RT_C_DECLS_END
+
+#endif /* !IPRT_INCLUDED_SRC_common_compiler_vcc_except_vcc_h */
+
diff --git a/src/VBox/Runtime/common/compiler/vcc/except-x86-vcc-asm.asm b/src/VBox/Runtime/common/compiler/vcc/except-x86-vcc-asm.asm
new file mode 100644
index 00000000..dfc47415
--- /dev/null
+++ b/src/VBox/Runtime/common/compiler/vcc/except-x86-vcc-asm.asm
@@ -0,0 +1,358 @@
+; $Id: except-x86-vcc-asm.asm $
+;; @file
+; IPRT - Visual C++ Compiler - x86 Exception Handler Support Code.
+;
+
+;
+; Copyright (C) 2022-2023 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
+;
+
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "iprt/asmdefs.mac"
+
+
+;*********************************************************************************************************************************
+;* Defined Constants And Macros *
+;*********************************************************************************************************************************
+
+;; @def WITH_NLG_STUFF
+; Enabled NLG debugger hooks/whatever - no idea how it's used.
+;
+; So far vsdebugeng.manimpl.dll in the VS2019 remote debugger directory is the
+; only component with any _NLG_ strings in it. Could not find any references
+; to _NLG_ in windbg.
+%define WITH_NLG_STUFF 1
+
+
+;*********************************************************************************************************************************
+;* Structures and Typedefs *
+;*********************************************************************************************************************************
+
+struc EH4_XCPT_REG_REC_T
+ .uSavedEsp resd 1 ; 0 / 0x00
+ .pXctpPtrs resd 1 ; 4 / 0x04
+ .XcptRec resd 2 ; 8 / 0x08
+ .uEncodedScopeTable resd 1 ; 16 / 0x10
+ .uTryLevel resd 1 ; 20 / 0x14
+endstruc ; 24 / 0x18
+
+
+;;
+; Scope table record describing __try / __except / __finally blocks (aka
+; EH4_SCOPETABLE_RECORD).
+;
+struc EH4_SCOPE_TAB_REC_T
+ .uEnclosingLevel resd 1
+ .pfnFilter resd 1
+ .pfnHandlerOrFinally resd 1
+endstruc
+
+;; Special EH4_SCOPE_TAB_REC_T::uEnclosingLevel used to terminate the chain.
+%define EH4_TOPMOST_TRY_LEVEL 0xfffffffe
+
+;;
+; Scope table used by _except_handler4 (aka EH4_SCOPETABLE).
+;
+struc EH4_SCOPE_TAB_T
+ .offGSCookie resd 1
+ .offGSCookieXor resd 1
+ .offEHCookie resd 1
+ .offEHCookieXor resd 1
+ .aScopeRecords resb 12
+endstruc
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+BEGINCODE
+extern IMPNAME(RtlUnwind@16)
+extern _rtVccEh4DoLocalUnwindHandler@16
+
+
+;*********************************************************************************************************************************
+;* Global Variables *
+;*********************************************************************************************************************************
+
+;; Delcare rtVccEh4DoLocalUnwindHandler() in except-x86.cpp as a save exception handler.
+; This adds the symbol table number of the exception handler to the special .sxdata section.
+safeseh _rtVccEh4DoLocalUnwindHandler@16
+
+%ifdef WITH_NLG_STUFF
+BEGINDATA
+GLOBALNAME_RAW __NLG_Destination, data, hidden
+ dd 019930520h
+ dd 0
+ dd 0
+ dd 0
+%endif
+
+
+BEGINCODE
+
+%ifdef WITH_NLG_STUFF
+
+;;
+; Some VS debugger interface, I think.
+;
+; @param EDX Notification code.
+; @uses EBP, EAX & ECX are preserved. This differs from other implementation,
+; but simplifies the calling code.
+;
+ALIGNCODE(32)
+GLOBALNAME_RAW __NLG_Notify, function, hidden
+ ;
+ ; Save registers.
+ ;
+ push eax
+ push ecx
+ push ebp
+ push ebx
+
+
+ ;
+ ; ECX = notification code.
+ ; EBX = __NLG_Destination
+ ;
+ mov ecx, edx ; Originally held in ECX, so moving it there for now.
+ mov ebx, __NLG_Destination
+
+__NLG_Go:
+ ;
+ ; Save info to __NLG_Destination and the stack (same layout).
+ ;
+ mov [ebx + 0ch], ebp
+ push ebp
+ mov [ebx + 08h], ecx
+ push ecx
+ mov [ebx + 04h], eax
+ push eax
+
+ ;
+ ; This is presumably the symbol the debugger hooks on to as the string
+ ; "__NLG_Dispatch" was found in vsdebugeng.manimpl.dll in VS2019.
+ ;
+global __NLG_Dispatch
+__NLG_Dispatch:
+
+ ; Drop the info structure from the stack.
+ add esp, 4*3
+
+ ;
+ ; Restore registers and drop the parameter as we return.
+ ;
+ ; Note! This may sabotage attempts by the debugger to modify the state...
+ ; But that can be fixed once we know this is a requirement. Just
+ ; keep things simple for the caller for now.
+ ;
+ pop ebx
+ pop ebp
+ pop ecx
+ pop eax
+ ret
+
+;;
+; NLG call + return2.
+;
+; The "__NLG_Return2" symbol was observed in several vsdebugeng.manimpl.dll
+; strings in VS2019.
+;
+ALIGNCODE(4)
+GLOBALNAME_RAW __NLG_Call, function, hidden
+ call eax
+
+global __NLG_Return2
+__NLG_Return2:
+ ret
+
+%endif
+
+
+;;
+; Calls the filter sub-function for a __finally statement.
+;
+; This sets all GRPs to zero, except for ESP, EBP and EAX.
+;
+; @param pfnFilter [ebp + 08h]
+; @param fAbend [ebp + 0ch]
+; @param pbFrame [ebp + 10h]
+;
+ALIGNCODE(64)
+BEGINPROC rtVccEh4DoFinally
+ push ebp
+ mov ebp, esp
+ push ebx
+ push edi
+ push esi
+
+ xor edi, edi
+ xor esi, esi
+%ifndef WITH_NLG_STUFF
+ xor edx, edx
+%endif
+ xor ebx, ebx
+
+ mov eax, [ebp + 08h] ; pfnFilter
+ movzx ecx, byte [ebp + 0ch] ; fAbend
+ mov ebp, [ebp + 10h] ; pbFrame
+
+%ifdef WITH_NLG_STUFF
+ mov edx, 101h
+ call __NLG_Notify
+ xor edx, edx
+
+ call __NLG_Call
+%else
+ call eax
+%endif
+
+ pop esi
+ pop edi
+ pop ebx
+ pop ebp
+ ret
+ENDPROC rtVccEh4DoFinally
+
+
+;;
+; Calls the filter sub-function for an __except statement.
+;
+; This sets all GRPs to zero, except for ESP, EBP and ECX.
+;
+; @param pfnFilter [ebp + 08h]
+; @param pbFrame [ebp + 0ch]
+;
+ALIGNCODE(32)
+BEGINPROC rtVccEh4DoFiltering
+ push ebp
+ push ebx
+ push edi
+ push esi
+
+ mov ecx, [esp + 5 * 4 + 0] ; pfnFilter
+ mov ebp, [esp + 5 * 4 + 4] ; pbFrame
+
+ xor edi, edi
+ xor esi, esi
+ xor edx, edx
+ xor ebx, ebx
+ xor eax, eax
+
+ call ecx
+
+ pop esi
+ pop edi
+ pop ebx
+ pop ebp
+ ret
+ENDPROC rtVccEh4DoFiltering
+
+
+;;
+; Resumes executing in an __except block (never returns).
+;
+; @param pfnHandler [ebp + 08h]
+; @param pbFrame [ebp + 0ch]
+;
+ALIGNCODE(32)
+BEGINPROC rtVccEh4JumpToHandler
+ ;
+ ; Since we're never returning there is no need to save anything here. So,
+ ; just start by loading parameters into registers.
+ ;
+ mov esi, [esp + 1 * 4 + 0] ; pfnFilter
+ mov ebp, [esp + 1 * 4 + 4] ; pbFrame
+
+%ifdef WITH_NLG_STUFF
+ ;
+ ; Notify VS debugger/whatever.
+ ;
+ mov eax, esi
+ mov edx, 1
+ call __NLG_Notify
+%endif
+
+ ;
+ ; Zero all GPRs except for ESP, EBP and ESI.
+ ;
+ xor edi, edi
+ xor edx, edx
+ xor ecx, ecx
+ xor ebx, ebx
+ xor eax, eax
+
+ jmp esi
+ENDPROC rtVccEh4JumpToHandler
+
+
+;;
+; This does global unwinding via RtlUnwind.
+;
+; The interface kind of requires us to do this from assembly.
+;
+; @param pXcptRec [ebp + 08h]
+; @param pXcptRegRec [ebp + 0ch]
+;
+ALIGNCODE(32)
+BEGINPROC rtVccEh4DoGlobalUnwind
+ push ebp
+ mov ebp, esp
+
+ ; Save all non-volatile registers.
+ push edi
+ push esi
+ push ebx
+
+ ;
+ ; Call unwind function.
+ ;
+ push 0 ; ReturnValue
+ push dword [ebp + 08h] ; ExceptionRecord - pXcptRec
+ push .return ; TargetIp
+ push dword [ebp + 0ch] ; TargetFrame - pXcptRegRec
+ call IMP(RtlUnwind@16)
+
+ ;
+ ; The RtlUnwind code will place us here if it doesn't return the regular way.
+ ;
+.return:
+ ; Restore non-volatile registers.
+ pop ebx
+ pop esi
+ pop edi
+
+ leave
+ ret
+ENDPROC rtVccEh4DoGlobalUnwind
+
diff --git a/src/VBox/Runtime/common/compiler/vcc/except-x86-vcc.cpp b/src/VBox/Runtime/common/compiler/vcc/except-x86-vcc.cpp
new file mode 100644
index 00000000..17dd3a0d
--- /dev/null
+++ b/src/VBox/Runtime/common/compiler/vcc/except-x86-vcc.cpp
@@ -0,0 +1,329 @@
+/* $Id: except-x86-vcc.cpp $ */
+/** @file
+ * IPRT - Visual C++ Compiler - x86 Exception Handler Filter.
+ */
+
+/*
+ * Copyright (C) 2006-2023 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
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/nt/nt-and-windows.h>
+
+#include "internal/compiler-vcc.h"
+#include "except-vcc.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Extended exception registration record used by rtVccEh4DoLocalUnwind
+ * and rtVccEh4DoLocalUnwindHandler.
+ */
+typedef struct EH4_LOCAL_UNWIND_XCPT_REG
+{
+ /** Security cookie. */
+ uintptr_t uEHCookieFront;
+ /** The actual registration record. */
+ EXCEPTION_REGISTRATION_RECORD XcptRegRec;
+ /** @name rtVccEh4DoLocalUnwind parameters
+ * @{ */
+ PEH4_XCPT_REG_REC_T pEh4XcptRegRec;
+ uint32_t uTargetTryLevel;
+ uint8_t const *pbFrame;
+ /** @} */
+ /** Security cookie. */
+ uintptr_t uEHCookieBack;
+} EH4_LOCAL_UNWIND_XCPT_REG;
+
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+extern "C" uintptr_t __security_cookie;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+DECLASM(LONG) rtVccEh4DoFiltering(PFN_EH4_XCPT_FILTER_T pfnFilter, uint8_t const *pbFrame);
+DECLASM(DECL_NO_RETURN(void)) rtVccEh4JumpToHandler(PFN_EH4_XCPT_HANDLER_T pfnHandler, uint8_t const *pbFrame);
+DECLASM(void) rtVccEh4DoGlobalUnwind(PEXCEPTION_RECORD pXcptRec, PEXCEPTION_REGISTRATION_RECORD pXcptRegRec);
+DECLASM(void) rtVccEh4DoFinally(PFN_EH4_FINALLY_T pfnFinally, bool fAbend, uint8_t const *pbFrame);
+extern "C" EXCEPTION_DISPOSITION __stdcall
+rtVccEh4DoLocalUnwindHandler(PEXCEPTION_RECORD pXcptRec, PVOID pvEstFrame, PCONTEXT pCpuCtx, PVOID pvDispCtx);
+
+
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable:4733) /* warning C4733: Inline asm assigning to 'FS:0': handler not registered as safe handler */
+#endif
+
+/**
+ * Calls the __finally blocks up to @a uTargetTryLevel is reached, starting with
+ * @a pEh4XcptRegRec->uTryLevel.
+ *
+ * @param pEh4XcptRegRec The EH4 exception registration record.
+ * @param uTargetTryLevel The target __try level to stop unwinding at.
+ * @param pbFrame The frame pointer (EBP).
+ */
+static void rtVccEh4DoLocalUnwind(PEH4_XCPT_REG_REC_T pEh4XcptRegRec, uint32_t uTargetTryLevel, uint8_t const *pbFrame)
+{
+ /*
+ * Manually set up exception handler.
+ */
+ EH4_LOCAL_UNWIND_XCPT_REG RegRec =
+ {
+ __security_cookie ^ (uintptr_t)&RegRec,
+ {
+ (EXCEPTION_REGISTRATION_RECORD *)__readfsdword(RT_UOFFSETOF(NT_TIB, ExceptionList)),
+ rtVccEh4DoLocalUnwindHandler /* safeseh (.sxdata) entry emitted by except-x86-vcc-asm.asm */
+ },
+ pEh4XcptRegRec,
+ uTargetTryLevel,
+ pbFrame,
+ __security_cookie ^ (uintptr_t)&RegRec
+ };
+ __writefsdword(RT_UOFFSETOF(NT_TIB, ExceptionList), (uintptr_t)&RegRec.XcptRegRec);
+
+ /*
+ * Do the unwinding.
+ */
+ uint32_t uCurTryLevel = pEh4XcptRegRec->uTryLevel;
+ while ( uCurTryLevel != EH4_TOPMOST_TRY_LEVEL
+ && ( uCurTryLevel > uTargetTryLevel
+ || uTargetTryLevel == EH4_TOPMOST_TRY_LEVEL /* if we knew what 0xffffffff meant, this could probably be omitted */ ))
+ {
+ PCEH4_SCOPE_TAB_T const pScopeTable = (PCEH4_SCOPE_TAB_T)(pEh4XcptRegRec->uEncodedScopeTable ^ __security_cookie);
+ PCEH4_SCOPE_TAB_REC_T const pEntry = &pScopeTable->aScopeRecords[uCurTryLevel];
+
+ pEh4XcptRegRec->uTryLevel = uCurTryLevel = pEntry->uEnclosingLevel;
+
+ /* __finally scope table entries have no filter sub-function. */
+ if (!pEntry->pfnFilter)
+ {
+ //RTAssertMsg2("rtVccEh4DoLocalUnwind: Calling %p (level %#x)\n", pEntry->pfnFinally, uCurTryLevel);
+ rtVccEh4DoFinally(pEntry->pfnFinally, true /*fAbend*/, pbFrame);
+
+ /* Read the try level again in case it changed... */
+ uCurTryLevel = pEh4XcptRegRec->uTryLevel;
+ }
+ }
+
+ /*
+ * Deregister exception handler.
+ */
+ __writefsdword(RT_UOFFSETOF(NT_TIB, ExceptionList), (uintptr_t)RegRec.XcptRegRec.Next);
+}
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
+
+/**
+ * Exception handler for rtVccEh4DoLocalUnwind.
+ */
+EXCEPTION_DISPOSITION __stdcall
+rtVccEh4DoLocalUnwindHandler(PEXCEPTION_RECORD pXcptRec, PVOID pvEstFrame, PCONTEXT pCpuCtx, PVOID pvDispCtx)
+{
+ EH4_LOCAL_UNWIND_XCPT_REG *pMyRegRec = RT_FROM_MEMBER(pvEstFrame, EH4_LOCAL_UNWIND_XCPT_REG, XcptRegRec);
+ __security_check_cookie(pMyRegRec->uEHCookieFront ^ (uintptr_t)pMyRegRec);
+ __security_check_cookie(pMyRegRec->uEHCookieBack ^ (uintptr_t)pMyRegRec);
+
+ /*
+ * This is a little sketchy as it isn't all that well documented by the OS
+ * vendor, but if invoked while unwinding, we return ExceptionCollidedUnwind
+ * and update the *ppDispCtx value to point to the colliding one.
+ */
+ if (pXcptRec->ExceptionFlags & (EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND))
+ {
+ rtVccEh4DoLocalUnwind(pMyRegRec->pEh4XcptRegRec, pMyRegRec->uTargetTryLevel, pMyRegRec->pbFrame);
+
+ PEXCEPTION_REGISTRATION_RECORD *ppDispCtx = (PEXCEPTION_REGISTRATION_RECORD *)pvDispCtx;
+ *ppDispCtx = &pMyRegRec->XcptRegRec;
+ return ExceptionCollidedUnwind;
+ }
+
+ /*
+ * In all other cases we do nothing special.
+ */
+ RT_NOREF(pCpuCtx);
+ return ExceptionContinueSearch;
+}
+
+
+/**
+ * This validates the CPU context, may terminate the application if invalid.
+ */
+DECLINLINE(void) rtVccValidateExceptionContextRecord(PCONTEXT pCpuCtx)
+{
+ if (RT_LIKELY( !rtVccIsGuardICallChecksActive()
+ || rtVccIsPointerOnTheStack(pCpuCtx->Esp)))
+ { /* likely */ }
+ else
+ rtVccCheckContextFailed(pCpuCtx);
+}
+
+
+/**
+ * Helper that validates stack cookies.
+ */
+DECLINLINE(void) rtVccEh4ValidateCookies(PCEH4_SCOPE_TAB_T pScopeTable, uint8_t const *pbFrame)
+{
+ if (pScopeTable->offGSCookie != EH4_NO_GS_COOKIE)
+ {
+ uintptr_t uGsCookie = *(uintptr_t const *)&pbFrame[pScopeTable->offGSCookie];
+ uGsCookie ^= (uintptr_t)&pbFrame[pScopeTable->offGSCookieXor];
+ __security_check_cookie(uGsCookie);
+ }
+
+ uintptr_t uEhCookie = *(uintptr_t const *)&pbFrame[pScopeTable->offEHCookie];
+ uEhCookie ^= (uintptr_t)&pbFrame[pScopeTable->offEHCookieXor];
+ __security_check_cookie(uEhCookie);
+}
+
+
+/**
+ * Call exception filters, handlers and unwind code for x86 code.
+ *
+ * This is called for windows' structured exception handling (SEH) in x86 32-bit
+ * code, i.e. the __try/__except/__finally stuff in Visual C++. The compiler
+ * generate scope records for the __try/__except blocks as well as unwind
+ * records for __finally and probably C++ stack object destructors.
+ *
+ * @returns Exception disposition.
+ * @param pXcptRec The exception record.
+ * @param pXcptRegRec The exception registration record, taken to be the frame
+ * address.
+ * @param pCpuCtx The CPU context for the exception.
+ * @param pDispCtx Dispatcher context.
+ */
+extern "C" __declspec(guard(suppress))
+DWORD _except_handler4(PEXCEPTION_RECORD pXcptRec, PEXCEPTION_REGISTRATION_RECORD pXcptRegRec, PCONTEXT pCpuCtx, PVOID pvCtx)
+{
+ /*
+ * The registration record (probably chained on FS:[0] like in the OS/2 days)
+ * points to a larger structure specific to _except_handler4. The structure
+ * is planted right after the saved caller EBP value when establishing the
+ * stack frame, so EBP = pXcptRegRec + 1;
+ */
+ PEH4_XCPT_REG_REC_T const pEh4XcptRegRec = RT_FROM_MEMBER(pXcptRegRec, EH4_XCPT_REG_REC_T, XcptRec);
+ uint8_t * const pbFrame = (uint8_t *)&pEh4XcptRegRec[1];
+ PCEH4_SCOPE_TAB_T const pScopeTable = (PCEH4_SCOPE_TAB_T)(pEh4XcptRegRec->uEncodedScopeTable ^ __security_cookie);
+
+ /*
+ * Validate the stack cookie and exception context.
+ */
+ rtVccEh4ValidateCookies(pScopeTable, pbFrame);
+ rtVccValidateExceptionContextRecord(pCpuCtx);
+
+ /*
+ * If dispatching an exception, call the exception filter functions and jump
+ * to the __except blocks if so directed.
+ */
+ if (IS_DISPATCHING(pXcptRec->ExceptionFlags))
+ {
+ uint32_t uTryLevel = pEh4XcptRegRec->uTryLevel;
+ //RTAssertMsg2("_except_handler4: dispatch: uTryLevel=%#x\n", uTryLevel);
+ while (uTryLevel != EH4_TOPMOST_TRY_LEVEL)
+ {
+ PCEH4_SCOPE_TAB_REC_T const pEntry = &pScopeTable->aScopeRecords[uTryLevel];
+ PFN_EH4_XCPT_FILTER_T const pfnFilter = pEntry->pfnFilter;
+ if (pfnFilter)
+ {
+ /* Call the __except filtering expression: */
+ //RTAssertMsg2("_except_handler4: Calling pfnFilter=%p\n", pfnFilter);
+ EXCEPTION_POINTERS XcptPtrs = { pXcptRec, pCpuCtx };
+ pEh4XcptRegRec->pXctpPtrs = &XcptPtrs;
+ LONG lRet = rtVccEh4DoFiltering(pfnFilter, pbFrame);
+ pEh4XcptRegRec->pXctpPtrs = NULL;
+ //RTAssertMsg2("_except_handler4: pfnFilter=%p -> %ld\n", pfnFilter, lRet);
+ rtVccEh4ValidateCookies(pScopeTable, pbFrame);
+
+ /* Return if we're supposed to continue execution (the convention
+ it to match negative values rather than the exact defined value): */
+ AssertCompile(EXCEPTION_CONTINUE_EXECUTION == -1);
+ if (lRet <= EXCEPTION_CONTINUE_EXECUTION)
+ return ExceptionContinueExecution;
+
+ /* Similarly, the handler is executed for any positive value. */
+ AssertCompile(EXCEPTION_CONTINUE_SEARCH == 0);
+ AssertCompile(EXCEPTION_EXECUTE_HANDLER == 1);
+ if (lRet >= EXCEPTION_EXECUTE_HANDLER)
+ {
+ /* We're about to resume execution in the __except block, so unwind
+ up to it first. */
+ //RTAssertMsg2("_except_handler4: global unwind\n");
+ rtVccEh4DoGlobalUnwind(pXcptRec, &pEh4XcptRegRec->XcptRec);
+ if (pEh4XcptRegRec->uTryLevel != EH4_TOPMOST_TRY_LEVEL)
+ {
+ //RTAssertMsg2("_except_handler4: local unwind\n");
+ rtVccEh4DoLocalUnwind(pEh4XcptRegRec, uTryLevel, pbFrame);
+ }
+ rtVccEh4ValidateCookies(pScopeTable, pbFrame);
+
+ /* Now jump to the __except block. This will _not_ return. */
+ //RTAssertMsg2("_except_handler4: jumping to __except block %p (level %#x)\n", pEntry->pfnHandler, pEntry->uEnclosingLevel);
+ pEh4XcptRegRec->uTryLevel = pEntry->uEnclosingLevel;
+ rtVccEh4ValidateCookies(pScopeTable, pbFrame); /* paranoia^2 */
+
+ rtVccEh4JumpToHandler(pEntry->pfnHandler, pbFrame);
+ /* (not reached) */
+ }
+ }
+
+ /*
+ * Next try level.
+ */
+ uTryLevel = pEntry->uEnclosingLevel;
+ }
+ }
+ /*
+ * If not dispatching we're unwinding, so we call any __finally blocks.
+ */
+ else
+ {
+ //RTAssertMsg2("_except_handler4: unwind: uTryLevel=%#x\n", pEh4XcptRegRec->uTryLevel);
+ if (pEh4XcptRegRec->uTryLevel != EH4_TOPMOST_TRY_LEVEL)
+ {
+ rtVccEh4DoLocalUnwind(pEh4XcptRegRec, EH4_TOPMOST_TRY_LEVEL, pbFrame);
+ rtVccEh4ValidateCookies(pScopeTable, pbFrame);
+ }
+ }
+
+ RT_NOREF(pvCtx);
+ return ExceptionContinueSearch;
+}
+
diff --git a/src/VBox/Runtime/common/compiler/vcc/fltused-vcc.cpp b/src/VBox/Runtime/common/compiler/vcc/fltused-vcc.cpp
new file mode 100644
index 00000000..d26f2b90
--- /dev/null
+++ b/src/VBox/Runtime/common/compiler/vcc/fltused-vcc.cpp
@@ -0,0 +1,58 @@
+/* $Id: fltused-vcc.cpp $ */
+/** @file
+ * IPRT - No-CRT - Basic allocators, Windows.
+ */
+
+/*
+ * Copyright (C) 2006-2023 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
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include "internal/compiler-vcc.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Very like some remnant from the 8086, 286 and 386 days of DOS, OS/2 and
+ * similar, where you could link with different floating point libs. My guess
+ * would be that this constant indicates to the compiled code which floating
+ * point support the library provides, probably especially as it comes to
+ * printf and scanf conversion but probably also emulation/real hw.
+ *
+ * Found some old 16-bit and 32-bit MSC C libraries (probably around v6.0)
+ * which all seems to define it as 0x9876. They also have a whole bunch of
+ * external dependencies on what seems to be mostly conversion helpers.
+ */
+unsigned _fltused = 0x9875;
diff --git a/src/VBox/Runtime/common/compiler/vcc/ftol2-vcc.asm b/src/VBox/Runtime/common/compiler/vcc/ftol2-vcc.asm
new file mode 100644
index 00000000..40bd3fa2
--- /dev/null
+++ b/src/VBox/Runtime/common/compiler/vcc/ftol2-vcc.asm
@@ -0,0 +1,80 @@
+; $Id: ftol2-vcc.asm $
+;; @file
+; IPRT - Floating Point to Integer related Visual C++ support routines.
+;
+
+;
+; Copyright (C) 2022-2023 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
+;
+
+
+%include "iprt/asmdefs.mac"
+%include "iprt/x86.mac"
+
+
+;;
+; Convert st0 to integer returning it in eax, popping st0.
+;
+; @returns value in eax
+; @param st0 The value to convet. Will be popped.
+; @uses eax, st0, FSW.TOP
+;
+GLOBALNAME_RAW __ftol2_sse_excpt, function, RT_NOTHING
+GLOBALNAME_RAW __ftol2_sse, function, RT_NOTHING ;; @todo kind of expect __ftol2_sse to take input in xmm0 and return in edx:eax.
+BEGINPROC_RAW __ftoi2
+ push ebp
+ mov ebp, esp
+ sub esp, 8h
+ fisttp dword [esp]
+ mov eax, [esp]
+ leave
+ ret
+ENDPROC_RAW __ftoi2
+
+
+;;
+; Convert st0 to integer returning it in edx:eax, popping st0.
+;
+; @returns value in edx:eax
+; @param st0 The value to convet. Will be popped.
+; @uses eax, edx, st0, FSW.TOP
+;
+BEGINPROC_RAW __ftol2
+ push ebp
+ mov ebp, esp
+ sub esp, 8h
+ and esp, 0fffffff8h ; proper alignment.
+ fisttp qword [esp]
+ mov eax, [esp]
+ mov edx, [esp + 4]
+ leave
+ ret
+ENDPROC_RAW __ftol2
+
diff --git a/src/VBox/Runtime/common/compiler/vcc/guard-vcc.asm b/src/VBox/Runtime/common/compiler/vcc/guard-vcc.asm
new file mode 100644
index 00000000..42e6d5a8
--- /dev/null
+++ b/src/VBox/Runtime/common/compiler/vcc/guard-vcc.asm
@@ -0,0 +1,108 @@
+; $Id: guard-vcc.asm $
+;; @file
+; IPRT - Control Flow Guard related Visual C++ support routines.
+;
+
+;
+; Copyright (C) 2022-2023 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
+;
+
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "iprt/asmdefs.mac"
+%include "iprt/x86.mac"
+
+
+
+;*********************************************************************************************************************************
+;* Global Variables *
+;*********************************************************************************************************************************
+
+;
+; Points to a NOP icall checker by default.
+; The loader will replace this when we start advertising it in the load config.
+;
+section .0000cfg rdata align=8
+
+GLOBALNAME __guard_check_icall_fptr
+ RTCCPTR_DEF NAME(__guard_check_icall_nop)
+
+%ifdef RT_ARCH_AMD64
+GLOBALNAME __guard_dispatch_icall_fptr
+ RTCCPTR_DEF __guard_dispatch_icall_nop
+
+; xfg stuff (haven't seen it generated or used yet).
+GLOBALNAME __guard_xfg_check_icall_fptr
+ RTCCPTR_DEF __guard_check_icall_nop
+
+GLOBALNAME __guard_xfg_dispatch_icall_fptr
+ RTCCPTR_DEF __guard_xfg_dispatch_icall_nop
+
+GLOBALNAME __guard_xfg_table_dispatch_icall_fptr
+ RTCCPTR_DEF __guard_xfg_dispatch_icall_nop
+
+%endif
+
+
+BEGINCODE
+;;
+; Check that doesn't do anything.
+;
+; This is for older windows versions which doesn't support call flow guard stuff.
+;
+BEGINPROC __guard_check_icall_nop
+ ret
+ENDPROC __guard_check_icall_nop
+
+%ifdef RT_ARCH_AMD64
+;;
+; Target function in RAX
+;
+; This is for older windows versions which doesn't support call flow guard stuff.
+;
+BEGINPROC __guard_dispatch_icall_nop
+ jmp rax
+ENDPROC __guard_dispatch_icall_nop
+%endif
+
+%ifdef RT_ARCH_AMD64
+;;
+; Target function in RAX
+;
+; This is for windows versions which doesn't support extended call flow guard stuff.
+;
+BEGINPROC __guard_xfg_dispatch_icall_nop
+ jmp [__guard_dispatch_icall_nop wrt RIP]
+ENDPROC __guard_xfg_dispatch_icall_nop
+%endif
+
diff --git a/src/VBox/Runtime/common/compiler/vcc/initializers-c-cpp-vcc.cpp b/src/VBox/Runtime/common/compiler/vcc/initializers-c-cpp-vcc.cpp
new file mode 100644
index 00000000..0fc451db
--- /dev/null
+++ b/src/VBox/Runtime/common/compiler/vcc/initializers-c-cpp-vcc.cpp
@@ -0,0 +1,158 @@
+/* $Id: initializers-c-cpp-vcc.cpp $ */
+/** @file
+ * IPRT - Visual C++ Compiler - C & C++ Initializers and Terminators.
+ */
+
+/*
+ * Copyright (C) 2022-2023 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
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define IPRT_COMPILER_VCC_WITH_C_INIT_TERM_SECTIONS
+#define IPRT_COMPILER_VCC_WITH_CPP_INIT_SECTIONS
+#include "internal/compiler-vcc.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef void (__cdecl *PFNVCINITTERM)(void);
+typedef int (__cdecl *PFNVCINITTERMRET)(void);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** @name Initializer arrays.
+ *
+ * The important thing here are the section names, the linker wi.
+ *
+ * @{ */
+/** Start of the C initializer array. */
+__declspec(allocate(".CRT$XIA")) PFNVCINITTERMRET g_apfnRTVccInitializers_C_Start = NULL;
+/** End of the C initializer array. */
+__declspec(allocate(".CRT$XIZ")) PFNVCINITTERMRET g_apfnRTVccInitializers_C_End = NULL;
+
+/** Start of the C pre-terminator array. */
+__declspec(allocate(".CRT$XPA")) PFNVCINITTERM g_apfnRTVccEarlyTerminators_C_Start = NULL;
+/** End of the C pre-terminator array. */
+__declspec(allocate(".CRT$XPZ")) PFNVCINITTERM g_apfnRTVccEarlyTerminators_C_End = NULL;
+
+/** Start of the C terminator array. */
+__declspec(allocate(".CRT$XTA")) PFNVCINITTERM g_apfnRTVccTerminators_C_Start = NULL;
+/** End of the C terminator array. */
+__declspec(allocate(".CRT$XTZ")) PFNVCINITTERM g_apfnRTVccTerminators_C_End = NULL;
+
+/** Start of the C++ initializer array. */
+__declspec(allocate(".CRT$XCA")) PFNVCINITTERM g_apfnRTVccInitializers_Cpp_Start = NULL;
+/** End of the C++ initializer array. */
+__declspec(allocate(".CRT$XCZ")) PFNVCINITTERM g_apfnRTVccInitializers_Cpp_End = NULL;
+
+
+/* Tell the linker to merge the .CRT* sections into .rdata */
+#pragma comment(linker, "/merge:.CRT=.rdata ")
+/** @} */
+
+
+/**
+ * Runs the C and C++ initializers.
+ *
+ * @returns 0 on success, non-zero return from C initalizer on failure.
+ */
+int rtVccInitializersRunInit(void)
+{
+ /*
+ * Run the C initializers first.
+ */
+ for (PFNVCINITTERMRET *ppfn = &g_apfnRTVccInitializers_C_Start;
+ (uintptr_t)ppfn < (uintptr_t)&g_apfnRTVccInitializers_C_End;
+ ppfn++)
+ {
+ PFNVCINITTERMRET const pfn = *ppfn;
+ if (pfn)
+ {
+ int const rc = pfn();
+ if (RT_LIKELY(rc == 0))
+ { /* likely */ }
+ else
+ return rc;
+ }
+ }
+
+ /*
+ * Run the C++ initializers.
+ */
+ for (PFNVCINITTERM *ppfn = &g_apfnRTVccInitializers_Cpp_Start;
+ (uintptr_t)ppfn < (uintptr_t)&g_apfnRTVccInitializers_Cpp_End;
+ ppfn++)
+ {
+ PFNVCINITTERM const pfn = *ppfn;
+ if (pfn)
+ pfn();
+ }
+
+ return 0;
+}
+
+
+/**
+ * Runs the C terminator callbacks.
+ */
+void rtVccInitializersRunTerm(void)
+{
+ /*
+ * First the early terminators.
+ */
+ for (PFNVCINITTERM *ppfn = &g_apfnRTVccEarlyTerminators_C_Start;
+ (uintptr_t)ppfn < (uintptr_t)&g_apfnRTVccEarlyTerminators_C_End;
+ ppfn++)
+ {
+ PFNVCINITTERM const pfn = *ppfn;
+ if (pfn)
+ pfn();
+ }
+
+ /*
+ * Then the real terminator list.
+ */
+ for (PFNVCINITTERM *ppfn = &g_apfnRTVccTerminators_C_Start;
+ (uintptr_t)ppfn < (uintptr_t)&g_apfnRTVccTerminators_C_End;
+ ppfn++)
+ {
+ PFNVCINITTERM const pfn = *ppfn;
+ if (pfn)
+ pfn();
+ }
+
+}
+
diff --git a/src/VBox/Runtime/common/compiler/vcc/loadcfg-vcc.c b/src/VBox/Runtime/common/compiler/vcc/loadcfg-vcc.c
new file mode 100644
index 00000000..c213db19
--- /dev/null
+++ b/src/VBox/Runtime/common/compiler/vcc/loadcfg-vcc.c
@@ -0,0 +1,154 @@
+/* $Id: loadcfg-vcc.c $ */
+/** @file
+ * IPRT - Visual C++ Compiler - PE/Windows Load Configuration.
+ *
+ * @note This is a C file and not C++ because the compiler generates fixups
+ * that upsets the linker (ADDR32 + ADDR32_4 instead of ADDR64).
+ */
+
+/*
+ * Copyright (C) 2022-2023 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
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/formats/pecoff.h>
+
+#include "internal/compiler-vcc.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+extern uintptr_t __security_cookie; /**< nocrt-stack-win.asm */
+#ifdef RT_ARCH_X86
+extern uintptr_t __safe_se_handler_table[]; /**< Linker generated. */
+extern uint8_t __safe_se_handler_count; /**< Absolute "address" defined by the linker representing the table size. */
+#endif
+#ifdef RT_ARCH_AMD64
+extern uintptr_t __guard_dispatch_icall_fptr;/**< nocrt-guard-win.asm */
+#endif
+extern uintptr_t __guard_fids_table[]; /**< Linker generated. */
+extern uint8_t __guard_fids_count; /** Another absolute "address" generated by the linker for a table size. */
+extern uint8_t __guard_flags; /** Another absolute "address" generated by the linker, flags value this time. */
+extern uintptr_t __guard_iat_table[]; /**< Linker generated. */
+extern uint8_t __guard_iat_count; /** Another absolute "address" generated by the linker for a table size. */
+extern uintptr_t __guard_longjmp_table[]; /**< Linker generated. */
+extern uint8_t __guard_longjmp_count; /** Another absolute "address" generated by the linker for a table size. */
+extern uint8_t __enclave_config; /** Another absolute "address" generated by the linker, flags value this time. */
+extern uintptr_t __guard_eh_cont_table[]; /**< Linker generated. */
+extern uint8_t __guard_eh_cont_count; /** Another absolute "address" generated by the linker for a table size. */
+#ifdef RT_ARCH_AMD64
+extern uintptr_t __guard_xfg_check_icall_fptr; /**< nocrt-guard-win.asm */
+extern uintptr_t __guard_xfg_dispatch_icall_fptr; /**< nocrt-guard-win.asm */
+extern uintptr_t __guard_xfg_table_dispatch_icall_fptr; /**< nocrt-guard-win.asm */
+#endif
+
+
+/**
+ * The load configuration for the PE image.
+ *
+ * The name of this is dictated by the linker, as it looks for a
+ * _load_config_used symbol and puts it's address and (somehow) size in the load
+ * config data dir entry.
+ *
+ * @todo use _MSC_VER to reduce this for older linkers which doesn't support all
+ * the machinactions we include here for the 2019 (14.29.30139.0) linker.
+ */
+RT_CONCAT(IMAGE_LOAD_CONFIG_DIRECTORY, ARCH_BITS) const _load_config_used =
+{
+ /* .Size = */ sizeof(_load_config_used),
+ /* .TimeDateStamp = */ 0,
+ /* .MajorVersion = */ 0,
+ /* .MinorVersion = */ 0,
+ /* .GlobalFlagsClear = */ 0,
+ /* .GlobalFlagsSet = */ 0,
+ /* .CriticalSectionDefaultTimeout = */ 0,
+ /* .DeCommitFreeBlockThreshold = */ 0,
+ /* .DeCommitTotalFreeThreshold = */ 0,
+ /* .LockPrefixTable = */ 0,
+ /* .MaximumAllocationSize = */ 0,
+ /* .VirtualMemoryThreshold = */ 0,
+ /* .ProcessHeapFlags = */ 0,
+ /* .ProcessAffinityMask = */ 0,
+ /* .CSDVersion = */ 0,
+ /* .DependentLoadFlags = */ 0,
+ /* .EditList = */ 0,
+ /* .SecurityCookie = */ (uintptr_t)&__security_cookie,
+#ifdef RT_ARCH_X86
+ /* .SEHandlerTable = */ (uintptr_t)&__safe_se_handler_table[0],
+ /* .SEHandlerCount = */ (uintptr_t)&__safe_se_handler_count, /* Absolute "address", remember. */
+#else
+ /* .SEHandlerTable = */ 0,
+ /* .SEHandlerCount = */ 0,
+#endif
+ /* .GuardCFCCheckFunctionPointer = */ (uintptr_t)&__guard_check_icall_fptr,
+#ifdef RT_ARCH_AMD64
+ /* .GuardCFDispatchFunctionPointer = */ (uintptr_t)&__guard_dispatch_icall_fptr,
+#else
+ /* .GuardCFDispatchFunctionPointer = */ 0,
+#endif
+ /* .GuardCFFunctionTable = */ (uintptr_t)&__guard_fids_table[0],
+ /* .GuardCFFunctionCount = */ (uintptr_t)&__guard_fids_count, /* Absolute "address", remember. */
+ /* .GuardFlags = */ (uint32_t)(uintptr_t)&__guard_flags, /* Ditto. */
+ /* .CodeIntegrity = */ { 0, 0, 0, 0 },
+ /* .GuardAddressTakenIatEntryTable = */ (uintptr_t)__guard_iat_table,
+ /* .GuardAddressTakenIatEntryCount = */ (uintptr_t)&__guard_iat_count, /* The same. */
+ /* .GuardLongJumpTargetTable = */ (uintptr_t)&__guard_longjmp_table[0],
+ /* .GuardLongJumpTargetCount = */ (uintptr_t)&__guard_longjmp_count, /* Another one. */
+ /* .DynamicValueRelocTable = */ 0,
+ /* .CHPEMetadataPointer = */ 0, /** @todo ARM */
+ /* .GuardRFFailureRoutine = */ 0,
+ /* .GuardRFFailureRoutineFunctionPointer= */ 0,
+ /* .DynamicValueRelocTableOffset = */ 0,
+ /* .DynamicValueRelocTableSection = */ 0,
+ /* .Reserved2 = */ 0,
+ /* .GuardRFVerifyStackPointerFunctionPointer=*/ 0,
+ /* .HotPatchTableOffset = */ 0,
+ /* .Reserved3 = */ 0,
+ /* .EnclaveConfigurationPointer = */ (uintptr_t)&__enclave_config, /* And another one. */
+ /* .VolatileMetadataPointer = */ 0,
+ /* .GuardEHContinuationTable = */ (uintptr_t)&__guard_eh_cont_table[0],
+ /* .GuardEHContinuationCount = */ (uintptr_t)&__guard_eh_cont_count, /* Yet another one. */
+#ifdef RT_ARCH_AMD64
+ /* .GuardXFGCheckFunctionPointer = */ (uintptr_t)&__guard_xfg_check_icall_fptr,
+ /* .GuardXFGDispatchFunctionPointer = */ (uintptr_t)&__guard_xfg_dispatch_icall_fptr,
+ /* .GuardXFGTableDispatchFunctionPointer= */ (uintptr_t)&__guard_xfg_table_dispatch_icall_fptr,
+#else
+ /* .GuardXFGCheckFunctionPointer = */ 0,
+ /* .GuardXFGDispatchFunctionPointer = */ 0,
+ /* .GuardXFGTableDispatchFunctionPointer= */ 0,
+#endif
+ /* .CastGuardOsDeterminedFailureMode = */ 0,
+};
+
diff --git a/src/VBox/Runtime/common/compiler/vcc/purecall-vcc.cpp b/src/VBox/Runtime/common/compiler/vcc/purecall-vcc.cpp
new file mode 100644
index 00000000..d76b3b4d
--- /dev/null
+++ b/src/VBox/Runtime/common/compiler/vcc/purecall-vcc.cpp
@@ -0,0 +1,63 @@
+/* $Id: purecall-vcc.cpp $ */
+/** @file
+ * IPRT - No-CRT - Pure virtual method vtable filler.
+ */
+
+/*
+ * Copyright (C) 2006-2023 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
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/nocrt.h"
+
+#include <iprt/asm.h>
+#ifndef IPRT_NOCRT_WITHOUT_FATAL_WRITE
+# include <iprt/assert.h>
+#endif
+
+#include "internal/compiler-vcc.h"
+
+
+extern "C" int __cdecl _purecall(void)
+{
+#ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE
+ RTAssertMsg2("\n\n!!%p called _purecall!!\n\n", ASMReturnAddress());
+#else
+ rtNoCrtFatalWriteBegin(RT_STR_TUPLE("\r\n\r\n!!_purecall called from "));
+ rtNoCrtFatalWritePtr(ASMReturnAddress());
+ rtNoCrtFatalWriteEnd(RT_STR_TUPLE("!!\r\n\r\n"));
+#endif
+ RT_BREAKPOINT();
+ return 0;
+}
+
diff --git a/src/VBox/Runtime/common/compiler/vcc/stack-except-seh-vcc.cpp b/src/VBox/Runtime/common/compiler/vcc/stack-except-seh-vcc.cpp
new file mode 100644
index 00000000..ff611e47
--- /dev/null
+++ b/src/VBox/Runtime/common/compiler/vcc/stack-except-seh-vcc.cpp
@@ -0,0 +1,117 @@
+/* $Id: stack-except-seh-vcc.cpp $ */
+/** @file
+ * IPRT - Visual C++ Compiler - Stack Checking, __GSHandlerCheck_SEH.
+ */
+
+/*
+ * Copyright (C) 2022-2023 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
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/nocrt.h"
+
+#include "except-vcc.h"
+
+
+#if !defined(RT_ARCH_AMD64)
+# error "This file is for AMD64 (and probably ARM, but needs porting)"
+#endif
+
+
+
+/**
+ * Check the stack cookie before calling the exception handler.
+ *
+ * This is to prevent attackers from bypassing stack cookie checking by
+ * triggering an exception.
+ *
+ * This is called for windows' structured exception handling (SEH), i.e. the
+ * __try/__except/__finally stuff in Visual C++, for which the compiler
+ * generates somewhat different strctures compared to the plain __GSHanderCheck
+ * scenario.
+ *
+ * @returns Exception disposition.
+ * @param pXcptRec The exception record.
+ * @param pXcptRegRec The exception registration record, taken to be the frame
+ * address.
+ * @param pCpuCtx The CPU context for the exception.
+ * @param pDispCtx Dispatcher context.
+ */
+extern "C" __declspec(guard(suppress))
+EXCEPTION_DISPOSITION __GSHandlerCheck_SEH(PEXCEPTION_RECORD pXcptRec, PEXCEPTION_REGISTRATION_RECORD pXcptRegRec,
+ PCONTEXT pCpuCtx, PDISPATCHER_CONTEXT pDispCtx)
+{
+ /*
+ * The HandlerData points to a scope table, which is then followed by GS_HANDLER_DATA.
+ *
+ * Sample offCookie values: 0521H (tst.cpp), 02caH (installNetLwf), and 0502H (installNetFlt).
+ */
+ SCOPE_TABLE const *pScopeTable = (SCOPE_TABLE const *)pDispCtx->HandlerData;
+ PCGS_HANDLER_DATA pHandlerData = (PCGS_HANDLER_DATA)&pScopeTable->ScopeRecord[pScopeTable->Count];
+
+ /*
+ * Locate the stack cookie and call the regular stack cookie checker routine.
+ * (Same code as in __GSHandlerCheck, fixes applies both places.)
+ */
+ /* Calculate the cookie address and read it. */
+ uintptr_t uPtrFrame = (uintptr_t)pXcptRegRec;
+ 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)pXcptRegRec;
+ 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);
+
+
+ /*
+ * Now call the handler if the GS handler data indicates that we ought to.
+ */
+ if ( (IS_UNWINDING(pXcptRec->ExceptionFlags) ? GS_HANDLER_OFF_COOKIE_IS_UHANDLER : GS_HANDLER_OFF_COOKIE_IS_EHANDLER)
+ & pHandlerData->u.offCookie)
+ return __C_specific_handler(pXcptRec, pXcptRegRec, pCpuCtx, pDispCtx);
+
+ return ExceptionContinueSearch;
+}
+
diff --git a/src/VBox/Runtime/common/compiler/vcc/stack-except-vcc.cpp b/src/VBox/Runtime/common/compiler/vcc/stack-except-vcc.cpp
new file mode 100644
index 00000000..13ea35b1
--- /dev/null
+++ b/src/VBox/Runtime/common/compiler/vcc/stack-except-vcc.cpp
@@ -0,0 +1,106 @@
+/* $Id: stack-except-vcc.cpp $ */
+/** @file
+ * IPRT - Visual C++ Compiler - Stack Checking, __GSHandlerCheck.
+ */
+
+/*
+ * Copyright (C) 2022-2023 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
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/nocrt.h"
+
+#include "except-vcc.h"
+
+
+#if !defined(RT_ARCH_AMD64)
+# error "This file is for AMD64 (and probably ARM, but needs porting)"
+#endif
+
+
+
+/**
+ * Check the stack cookie before calling the exception handler.
+ *
+ * This is to prevent attackers from bypassing stack cookie checking by
+ * triggering an exception.
+ *
+ * This does not call any C++ exception handlers, as it's probably (still
+ * figuring this stuff out) only used when C++ exceptions are disabled.
+ *
+ * @returns Exception disposition.
+ * @param pXcptRec The exception record.
+ * @param pXcptRegRec The exception registration record, taken to be the frame
+ * address.
+ * @param pCpuCtx The CPU context for the exception.
+ * @param pDispCtx Dispatcher context.
+ */
+extern "C" __declspec(guard(suppress))
+EXCEPTION_DISPOSITION __GSHandlerCheck(PEXCEPTION_RECORD pXcptRec, PEXCEPTION_REGISTRATION_RECORD pXcptRegRec,
+ PCONTEXT pCpuCtx, PDISPATCHER_CONTEXT pDispCtx)
+{
+ RT_NOREF(pXcptRec, pCpuCtx);
+
+ /*
+ * Only GS handler data here.
+ */
+ PCGS_HANDLER_DATA pHandlerData = (PCGS_HANDLER_DATA)pDispCtx->HandlerData;
+
+ /*
+ * Locate the stack cookie and call the regular stack cookie checker routine.
+ * (Same code as in __GSHandlerCheck_SEH, fixes applies both places.)
+ */
+ /* Calculate the cookie address and read it. */
+ uintptr_t uPtrFrame = (uintptr_t)pXcptRegRec;
+ 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)pXcptRegRec;
+ 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);
+
+ return ExceptionContinueSearch;
+}
+
diff --git a/src/VBox/Runtime/common/compiler/vcc/stack-probe-vcc.asm b/src/VBox/Runtime/common/compiler/vcc/stack-probe-vcc.asm
new file mode 100644
index 00000000..da2e2965
--- /dev/null
+++ b/src/VBox/Runtime/common/compiler/vcc/stack-probe-vcc.asm
@@ -0,0 +1,157 @@
+; $Id: stack-probe-vcc.asm $
+;; @file
+; IPRT - Stack related Visual C++ support routines.
+;
+
+;
+; Copyright (C) 2022-2023 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
+;
+
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%if 0 ; YASM's builtin SEH64 support doesn't cope well with code alignment, so use our own.
+ %define RT_ASM_WITH_SEH64
+%else
+ %define RT_ASM_WITH_SEH64_ALT
+%endif
+%include "iprt/asmdefs.mac"
+%include "iprt/x86.mac"
+
+
+;;
+; Probe stack to trigger guard faults, and for x86 to allocate stack space.
+;
+; @param xAX Frame size.
+; @uses AMD64: Probably nothing. EAX is certainly not supposed to change.
+; x86: ESP = ESP - EAX; EFLAGS, nothing else
+;
+ALIGNCODE(64)
+GLOBALNAME_RAW __alloca_probe, __alloca_probe, function
+BEGINPROC_RAW __chkstk
+ push xBP
+ SEH64_PUSH_xBP
+ mov xBP, xSP
+ SEH64_SET_FRAME_xBP 0
+ push xAX
+ SEH64_PUSH_GREG xAX
+ push xBX
+ SEH64_PUSH_GREG xBX
+ SEH64_END_PROLOGUE
+
+ ;
+ ; Adjust eax so we're relative to [xBP - xCB*2].
+ ;
+ sub xAX, xCB * 4
+ jle .touch_loop_done ; jump if rax < xCB*4, very unlikely
+
+ ;
+ ; Subtract what's left of the current page from eax and only engage
+ ; the touch loop if (int)xAX > 0.
+ ;
+ lea ebx, [ebp - xCB * 2]
+ and ebx, PAGE_SIZE - 1
+ sub xAX, xBX
+ jnl .touch_loop ; jump if pages to touch.
+
+.touch_loop_done:
+ pop xBX
+ pop xAX
+ leave
+%ifndef RT_ARCH_X86
+ ret
+%else
+ ;
+ ; Do the stack space allocation and jump to the return location.
+ ;
+ sub esp, eax
+ add esp, 4
+ jmp dword [esp + eax - 4]
+%endif
+
+ ;
+ ; The touch loop.
+ ;
+.touch_loop:
+ sub xBX, PAGE_SIZE
+%if 1
+ mov [xBP + xBX - xCB * 2], bl
+%else
+ or byte [xBP + xBX - xCB * 2], 0 ; non-destructive variant...
+%endif
+ sub xAX, PAGE_SIZE
+ jnl .touch_loop
+ jmp .touch_loop_done
+ENDPROC_RAW __chkstk
+
+
+%ifdef RT_ARCH_X86
+;;
+; 8 and 16 byte aligned alloca w/ probing.
+;
+; This routine adjusts the allocation size so __chkstk will return a
+; correctly aligned allocation.
+;
+; @param xAX Unaligned allocation size.
+;
+%macro __alloc_probe_xxx 1
+ALIGNCODE(16)
+BEGINPROC_RAW __alloca_probe_ %+ %1
+ push ecx
+
+ ;
+ ; Calc the ESP address after the allocation and adjust EAX so that it
+ ; will be aligned as desired.
+ ;
+ lea ecx, [esp + 8]
+ sub ecx, eax
+ and ecx, %1 - 1
+ add eax, ecx
+ jc .bad_alloc_size
+.continue:
+
+ pop ecx
+ jmp __alloca_probe
+
+.bad_alloc_size:
+ %ifdef RT_STRICT
+ int3
+ %endif
+ or eax, 0xfffffff0
+ jmp .continue
+ENDPROC_RAW __alloca_probe_ %+ %1
+%endmacro
+
+__alloc_probe_xxx 16
+__alloc_probe_xxx 8
+%endif ; RT_ARCH_X86
+
diff --git a/src/VBox/Runtime/common/compiler/vcc/stack-vcc.asm b/src/VBox/Runtime/common/compiler/vcc/stack-vcc.asm
new file mode 100644
index 00000000..edfd5750
--- /dev/null
+++ b/src/VBox/Runtime/common/compiler/vcc/stack-vcc.asm
@@ -0,0 +1,639 @@
+; $Id: stack-vcc.asm $
+;; @file
+; IPRT - Stack related Visual C++ support routines.
+;
+
+;
+; Copyright (C) 2022-2023 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
+;
+
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%if 0 ; YASM's builtin SEH64 support doesn't cope well with code alignment, so use our own.
+ %define RT_ASM_WITH_SEH64
+%else
+ %define RT_ASM_WITH_SEH64_ALT
+%endif
+%include "iprt/asmdefs.mac"
+%include "iprt/x86.mac"
+%ifdef RT_ARCH_AMD64
+ %include "iprt/win/context-amd64.mac"
+%else
+ %include "iprt/win/context-x86.mac"
+%endif
+
+
+;*********************************************************************************************************************************
+;* Structures and Typedefs *
+;*********************************************************************************************************************************
+
+;; Variable descriptor.
+struc RTC_VAR_DESC_T
+ .offFrame resd 1
+ .cbVar resd 1
+ alignb RTCCPTR_CB
+ .pszName RTCCPTR_RES 1
+endstruc
+
+;; Frame descriptor.
+struc RTC_FRAME_DESC_T
+ .cVars resd 1
+ alignb RTCCPTR_CB
+ .paVars RTCCPTR_RES 1 ; Array of RTC_VAR_DESC_T.
+endstruc
+
+;; An alloca allocation.
+struc RTC_ALLOCA_ENTRY_T
+ .uGuard1 resd 1
+ .pNext RTCCPTR_RES 1 ; Misaligned.
+%if ARCH_BITS == 32
+ .pNextPad resd 1
+%endif
+ .cb RTCCPTR_RES 1 ; Misaligned.
+%if ARCH_BITS == 32
+ .cbPad resd 1
+%endif
+ .auGuard2 resd 3
+endstruc
+
+%ifdef RT_ARCH_X86
+ %define FASTCALL_NAME(a_Name, a_cbArgs) $@ %+ a_Name %+ @ %+ a_cbArgs
+%else
+ %define FASTCALL_NAME(a_Name, a_cbArgs) NAME(a_Name)
+%endif
+
+
+;*********************************************************************************************************************************
+;* Defined Constants And Macros *
+;*********************************************************************************************************************************
+%define VARIABLE_MARKER_PRE 0xcccccccc
+%define VARIABLE_MARKER_POST 0xcccccccc
+
+%define ALLOCA_FILLER_BYTE 0xcc
+%define ALLOCA_FILLER_32 0xcccccccc
+
+
+;*********************************************************************************************************************************
+;* Global Variables *
+;*********************************************************************************************************************************
+BEGINDATA
+GLOBALNAME __security_cookie
+ dd 0xdeadbeef
+ dd 0x0c00ffe0
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+BEGINCODE
+extern NAME(rtVccStackVarCorrupted)
+extern NAME(rtVccSecurityCookieMismatch)
+extern NAME(rtVccRangeCheckFailed)
+%ifdef RT_ARCH_X86
+extern NAME(rtVccCheckEspFailed)
+%endif
+
+
+
+;;
+; This just initializes a global and calls _RTC_SetErrorFuncW to NULL, and
+; since we don't have either of those we have nothing to do here.
+BEGINPROC _RTC_InitBase
+ SEH64_END_PROLOGUE
+ ret
+ENDPROC _RTC_InitBase
+
+
+;;
+; Nothing to do here.
+BEGINPROC _RTC_Shutdown
+ SEH64_END_PROLOGUE
+ ret
+ENDPROC _RTC_Shutdown
+
+
+
+
+;;
+; Checks stack variable markers.
+;
+; This seems to be a regular C function in the CRT, but x86 is conveniently
+; using the fastcall convention which makes it very similar to amd64.
+;
+; We try make this as sleek as possible, leaving all the trouble for when we
+; find a corrupted stack variable and need to call a C function to complain.
+;
+; @param pStackFrame The caller RSP/ESP. [RCX/ECX]
+; @param pFrameDesc Frame descriptor. [RDX/EDX]
+;
+ALIGNCODE(64)
+BEGINPROC_RAW FASTCALL_NAME(_RTC_CheckStackVars, 8)
+ push xBP
+ SEH64_PUSH_xBP
+ SEH64_END_PROLOGUE
+
+ ;
+ ; Load the variable count into eax and check that it's not zero.
+ ;
+ mov eax, [xDX + RTC_FRAME_DESC_T.cVars]
+ test eax, eax
+ jz .return
+
+ ;
+ ; Make edx/rdx point to the current variable and xBP be the frame pointer.
+ ; The latter frees up xCX for scratch use and incidentally make stack access
+ ; go via SS instead of DS (mostly irrlevant in 64-bit and 32-bit mode).
+ ;
+ mov xDX, [xDX + RTC_FRAME_DESC_T.paVars]
+ mov xBP, xCX
+
+ ;
+ ; Loop thru the variables and check that their markers/fences haven't be
+ ; trampled over.
+ ;
+.next_var:
+ ; Marker before the variable.
+%if ARCH_BITS == 64
+ movsxd rcx, dword [xDX + RTC_VAR_DESC_T.offFrame]
+%else
+ mov xCX, dword [xDX + RTC_VAR_DESC_T.offFrame]
+%endif
+ cmp dword [xBP + xCX - 4], VARIABLE_MARKER_PRE
+ jne rtVccCheckStackVarsFailed
+
+ ; Marker after the variable.
+ add ecx, dword [xDX + RTC_VAR_DESC_T.cbVar]
+%if ARCH_BITS == 64
+ movsxd rcx, ecx
+%endif
+ cmp dword [xBP + xCX], VARIABLE_MARKER_POST
+ jne rtVccCheckStackVarsFailed
+
+ ;
+ ; Advance to the next variable.
+ ;
+.advance:
+ add xDX, RTC_VAR_DESC_T_size
+ dec eax
+ jnz .next_var
+
+ ;
+ ; Return.
+ ;
+.return:
+ pop xBP
+ ret
+ENDPROC_RAW FASTCALL_NAME(_RTC_CheckStackVars, 8)
+
+;
+; Sub-function for _RTC_CheckStackVars, for purposes of SEH64 unwinding.
+;
+; Note! While we consider this fatal and will terminate the application, the
+; compiler guys do not seem to think it is all that horrible and will
+; report failure, maybe do an int3, and then try continue execution.
+;
+BEGINPROC_RAW rtVccCheckStackVarsFailed
+ nop ;push xBP - done in parent function
+ SEH64_PUSH_xBP
+ mov xCX, xBP ; xCX = caller pStackFrame. xBP free to become frame pointer.
+ mov xBP, xSP
+ SEH64_SET_FRAME_xBP 0
+ pushf
+ push xAX
+ SEH64_PUSH_GREG xAX
+ sub xSP, CONTEXT_SIZE + 20h
+ SEH64_ALLOCATE_STACK (CONTEXT_SIZE + 20h)
+ SEH64_END_PROLOGUE
+
+ lea xAX, [xBP - CONTEXT_SIZE]
+ call NAME(rtVccCaptureContext)
+
+ ; rtVccStackVarCorrupted(uint8_t *pbFrame, RTC_VAR_DESC_T const *pVar, PCONTEXT)
+.again:
+%ifdef RT_ARCH_AMD64
+ lea r8, [xBP - CONTEXT_SIZE]
+%else
+ lea xAX, [xBP - CONTEXT_SIZE]
+ mov [xSP + 8], xAX
+ mov [xSP + 4], xDX
+ mov [xSP], xCX
+%endif
+ call NAME(rtVccStackVarCorrupted)
+ jmp .again
+ENDPROC_RAW rtVccCheckStackVarsFailed
+
+
+%ifdef RT_ARCH_X86
+;;
+; Called to follow up on a 'CMP ESP, EBP' kind of instruction,
+; expected to report failure if the compare failed.
+;
+; Note! While we consider this fatal and will terminate the application, the
+; compiler guys do not seem to think it is all that horrible and will
+; report failure, maybe do an int3, and then try continue execution.
+;
+ALIGNCODE(16)
+BEGINPROC _RTC_CheckEsp
+ jne .unexpected_esp
+ ret
+
+.unexpected_esp:
+ push xBP
+ SEH64_PUSH_xBP
+ mov xBP, xSP
+ SEH64_SET_FRAME_xBP 0
+ pushf
+ push xAX
+ SEH64_PUSH_GREG xAX
+ sub xSP, CONTEXT_SIZE + 20h
+ SEH64_ALLOCATE_STACK (CONTEXT_SIZE + 20h)
+ SEH64_END_PROLOGUE
+
+ lea xAX, [xBP - CONTEXT_SIZE]
+ call NAME(rtVccCaptureContext)
+
+ ; rtVccCheckEspFailed(PCONTEXT)
+.again:
+ lea xAX, [xBP - CONTEXT_SIZE]
+%ifdef RT_ARCH_AMD64
+ mov xCX, xAX
+%else
+ mov [xSP], xAX
+%endif
+ call NAME(rtVccCheckEspFailed)
+ jmp .again
+
+ENDPROC _RTC_CheckEsp
+%endif ; RT_ARCH_X86
+
+
+
+;;
+; Initialize an alloca allocation list entry and add it to it.
+;
+; When this is call, presumably _RTC_CheckStackVars2 is used to verify the frame.
+;
+; @param pNewEntry Pointer to the new entry. [RCX/ECX]
+; @param cbEntry The entry size, including header. [RDX/EDX]
+; @param ppHead Pointer to the list head pointer. [R8/stack]
+;
+ALIGNCODE(64)
+BEGINPROC_RAW FASTCALL_NAME(_RTC_AllocaHelper, 12)
+ SEH64_END_PROLOGUE
+
+ ;
+ ; Check that input isn't NULL or the size isn't zero.
+ ;
+ test xCX, xCX
+ jz .return
+ test xDX, xDX
+ jz .return
+%if ARCH_BITS == 64
+ test r8, r8
+%else
+ cmp dword [xSP + xCB], 0
+%endif
+ jz .return
+
+ ;
+ ; Memset the memory to ALLOCA_FILLER
+ ;
+%if ARCH_BITS == 64
+ mov r10, rdi ; save rdi
+ mov r11, rcx ; save pNewEntry
+%else
+ push xDI
+ push xCX
+ cld ; paranoia
+%endif
+
+ mov al, ALLOCA_FILLER_BYTE
+ mov xDI, xCX ; entry pointer
+ mov xCX, xDX ; entry size (in bytes)
+ rep stosb
+
+%if ARCH_BITS == 64
+ mov rdi, r10
+%else
+ pop xCX
+ pop xDI
+%endif
+
+ ;
+ ; Fill in the entry and link it as onto the head of the chain.
+ ;
+%if ARCH_BITS == 64
+ mov [r11 + RTC_ALLOCA_ENTRY_T.cb], xDX
+ mov xAX, [r8]
+ mov [r11 + RTC_ALLOCA_ENTRY_T.pNext], xAX
+ mov [r8], r11
+%else
+ mov [xCX + RTC_ALLOCA_ENTRY_T.cb], xDX
+ mov xAX, [xSP + xCB] ; ppHead
+ mov xDX, [xAX]
+ mov [xCX + RTC_ALLOCA_ENTRY_T.pNext], xDX
+ mov [xAX], xCX
+%endif
+
+.return:
+%if ARCH_BITS == 64
+ ret
+%else
+ ret 4
+%endif
+ENDPROC_RAW FASTCALL_NAME(_RTC_AllocaHelper, 12)
+
+
+;;
+; Checks if the security cookie ok, complaining and terminating if it isn't.
+;
+ALIGNCODE(16)
+BEGINPROC_RAW FASTCALL_NAME(__security_check_cookie, 4)
+ SEH64_END_PROLOGUE
+ cmp xCX, [NAME(__security_cookie) xWrtRIP]
+ jne rtVccSecurityCookieFailed
+ ;; amd64 version checks if the top 16 bits are zero, we skip that for now.
+ ret
+ENDPROC_RAW FASTCALL_NAME(__security_check_cookie, 4)
+
+; Sub-function for __security_check_cookie, for purposes of SEH64 unwinding.
+BEGINPROC_RAW rtVccSecurityCookieFailed
+ push xBP
+ SEH64_PUSH_xBP
+ mov xBP, xSP
+ SEH64_SET_FRAME_xBP 0
+ pushf
+ push xAX
+ SEH64_PUSH_GREG xAX
+ sub xSP, CONTEXT_SIZE + 20h
+ SEH64_ALLOCATE_STACK (CONTEXT_SIZE + 20h)
+ SEH64_END_PROLOGUE
+
+ lea xAX, [xBP - CONTEXT_SIZE]
+ call NAME(rtVccCaptureContext)
+
+ ; rtVccSecurityCookieMismatch(uCookie, PCONTEXT)
+.again:
+%ifdef RT_ARCH_AMD64
+ lea xDX, [xBP - CONTEXT_SIZE]
+%else
+ lea xAX, [xBP - CONTEXT_SIZE]
+ mov [xSP + 4], xAX
+ mov [xSP], xCX
+%endif
+ call NAME(rtVccSecurityCookieMismatch)
+ jmp .again
+ENDPROC_RAW rtVccSecurityCookieFailed
+
+
+;;
+; Generated when using /GS - buffer security checks - so, fatal.
+;
+; Doesn't seem to take any parameters.
+;
+BEGINPROC __report_rangecheckfailure
+ push xBP
+ SEH64_PUSH_xBP
+ mov xBP, xSP
+ SEH64_SET_FRAME_xBP 0
+ pushf
+ push xAX
+ SEH64_PUSH_GREG xAX
+ sub xSP, CONTEXT_SIZE + 20h
+ SEH64_ALLOCATE_STACK (CONTEXT_SIZE + 20h)
+ SEH64_END_PROLOGUE
+
+ lea xAX, [xBP - CONTEXT_SIZE]
+ call NAME(rtVccCaptureContext)
+
+ ; rtVccRangeCheckFailed(PCONTEXT)
+.again:
+ lea xAX, [xBP - CONTEXT_SIZE]
+%ifdef RT_ARCH_AMD64
+ mov xCX, xAX
+%else
+ mov [xSP], xAX
+%endif
+ call NAME(rtVccRangeCheckFailed)
+ jmp .again
+ENDPROC __report_rangecheckfailure
+
+
+%if 0 ; Currently not treating these as completely fatal, just like the
+ ; compiler guys do. I'm sure the compiler only generate these calls
+ ; if it thinks a variable could be used uninitialized, however I'm not
+ ; really sure if there is a runtime check in addition or if it's an
+ ; action that always will be taken in a code path deemed to be bad.
+ ; Judging from the warnings, the compile time analysis leave lots to be
+ ; desired (lots of false positives).
+;;
+; Not entirely sure how and when the compiler generates these.
+; extern "C" void __cdecl _RTC_UninitUse(const char *pszVar)
+BEGINPROC _RTC_UninitUse
+ push xBP
+ SEH64_PUSH_xBP
+ mov xBP, xSP
+ SEH64_SET_FRAME_xBP 0
+ pushf
+ push xAX
+ SEH64_PUSH_GREG xAX
+ sub xSP, CONTEXT_SIZE + 20h
+ SEH64_ALLOCATE_STACK (CONTEXT_SIZE + 20h)
+ SEH64_END_PROLOGUE
+
+ lea xAX, [xBP - CONTEXT_SIZE]
+ call NAME(rtVccCaptureContext)
+
+ extern NAME(rtVccUninitializedVariableUse)
+ ; rtVccUninitializedVariableUse(const char *pszVar, PCONTEXT)
+.again:
+%ifdef RT_ARCH_AMD64
+ lea xDX, [xBP - CONTEXT_SIZE]
+%else
+ lea xAX, [xBP - CONTEXT_SIZE]
+ mov [xSP + xCB], xAX
+ mov xAX, [xBP + xCB * 2]
+ mov [xSP], xAX
+%endif
+ call NAME(rtVccUninitializedVariableUse)
+ jmp .again
+ENDPROC _RTC_UninitUse
+%endif
+
+;;
+; Internal worker that creates a CONTEXT record for the caller.
+;
+; This expects a old-style stack frame setup, with xBP as base, such that:
+; xBP+xCB*1: Return address -> Rip/Eip
+; xBP+xCB*0: Return xBP -> Rbp/Ebp
+; xBP-xCB*1: EFLAGS -> EFlags
+; xBP-xCB*2: Saved xAX -> Rax/Eax
+;
+; @param pCtx xAX Pointer to a CONTEXT structure.
+;
+BEGINPROC rtVccCaptureContext
+ SEH64_END_PROLOGUE
+
+%ifdef RT_ARCH_AMD64
+ mov [xAX + CONTEXT.Rcx], rcx
+ mov [xAX + CONTEXT.Rdx], rdx
+ mov rcx, [xBP - xCB*2]
+ mov [xAX + CONTEXT.Rax], ecx
+ mov [xAX + CONTEXT.Rbx], rbx
+ lea rcx, [xBP + xCB*2]
+ mov [xAX + CONTEXT.Rsp], rcx
+ mov rdx, [xBP]
+ mov [xAX + CONTEXT.Rbp], rdx
+ mov [xAX + CONTEXT.Rdi], rdi
+ mov [xAX + CONTEXT.Rsi], rsi
+ mov [xAX + CONTEXT.R8], r8
+ mov [xAX + CONTEXT.R9], r9
+ mov [xAX + CONTEXT.R10], r10
+ mov [xAX + CONTEXT.R11], r11
+ mov [xAX + CONTEXT.R12], r12
+ mov [xAX + CONTEXT.R13], r13
+ mov [xAX + CONTEXT.R14], r14
+ mov [xAX + CONTEXT.R15], r15
+
+ mov rcx, [xBP + xCB*1]
+ mov [xAX + CONTEXT.Rip], rcx
+ mov edx, [xBP - xCB*1]
+ mov [xAX + CONTEXT.EFlags], edx
+
+ mov dx, ss
+ mov [xAX + CONTEXT.SegSs], dx
+ mov cx, cs
+ mov [xAX + CONTEXT.SegCs], cx
+ mov dx, ds
+ mov [xAX + CONTEXT.SegDs], dx
+ mov cx, es
+ mov [xAX + CONTEXT.SegEs], cx
+ mov dx, fs
+ mov [xAX + CONTEXT.SegFs], dx
+ mov cx, gs
+ mov [xAX + CONTEXT.SegGs], cx
+
+ mov dword [xAX + CONTEXT.ContextFlags], CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS
+
+ ; Clear stuff we didn't set.
+ xor edx, edx
+ mov [xAX + CONTEXT.P1Home], rdx
+ mov [xAX + CONTEXT.P2Home], rdx
+ mov [xAX + CONTEXT.P3Home], rdx
+ mov [xAX + CONTEXT.P4Home], rdx
+ mov [xAX + CONTEXT.P5Home], rdx
+ mov [xAX + CONTEXT.P6Home], rdx
+ mov [xAX + CONTEXT.MxCsr], edx
+ mov [xAX + CONTEXT.Dr0], rdx
+ mov [xAX + CONTEXT.Dr1], rdx
+ mov [xAX + CONTEXT.Dr2], rdx
+ mov [xAX + CONTEXT.Dr3], rdx
+ mov [xAX + CONTEXT.Dr6], rdx
+ mov [xAX + CONTEXT.Dr7], rdx
+
+ mov ecx, CONTEXT_size - CONTEXT.FltSave
+ AssertCompile(((CONTEXT_size - CONTEXT.FltSave) % 8) == 0)
+.again:
+ mov [xAX + CONTEXT.FltSave + xCX - 8], rdx
+ sub ecx, 8
+ jnz .again
+
+ ; Restore edx and ecx.
+ mov rcx, [xAX + CONTEXT.Rcx]
+ mov rdx, [xAX + CONTEXT.Rdx]
+
+%elifdef RT_ARCH_X86
+
+ mov [xAX + CONTEXT.Ecx], ecx
+ mov [xAX + CONTEXT.Edx], edx
+ mov ecx, [xBP - xCB*2]
+ mov [xAX + CONTEXT.Eax], ecx
+ mov [xAX + CONTEXT.Ebx], ebx
+ lea ecx, [xBP + xCB*2]
+ mov [xAX + CONTEXT.Esp], ecx
+ mov edx, [xBP]
+ mov [xAX + CONTEXT.Ebp], edx
+ mov [xAX + CONTEXT.Edi], edi
+ mov [xAX + CONTEXT.Esi], esi
+
+ mov ecx, [xBP + xCB]
+ mov [xAX + CONTEXT.Eip], ecx
+ mov ecx, [xBP - xCB*1]
+ mov [xAX + CONTEXT.EFlags], ecx
+
+ mov dx, ss
+ movzx edx, dx
+ mov [xAX + CONTEXT.SegSs], edx
+ mov cx, cs
+ movzx ecx, cx
+ mov [xAX + CONTEXT.SegCs], ecx
+ mov dx, ds
+ movzx edx, dx
+ mov [xAX + CONTEXT.SegDs], edx
+ mov cx, es
+ movzx ecx, cx
+ mov [xAX + CONTEXT.SegEs], ecx
+ mov dx, fs
+ movzx edx, dx
+ mov [xAX + CONTEXT.SegFs], edx
+ mov cx, gs
+ movzx ecx, cx
+ mov [xAX + CONTEXT.SegGs], ecx
+
+ mov dword [xAX + CONTEXT.ContextFlags], CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS
+
+ ; Clear stuff we didn't set.
+ xor edx, edx
+ mov [xAX + CONTEXT.Dr0], edx
+ mov [xAX + CONTEXT.Dr1], edx
+ mov [xAX + CONTEXT.Dr2], edx
+ mov [xAX + CONTEXT.Dr3], edx
+ mov [xAX + CONTEXT.Dr6], edx
+ mov [xAX + CONTEXT.Dr7], edx
+
+ mov ecx, CONTEXT_size - CONTEXT.ExtendedRegisters
+.again:
+ mov [xAX + CONTEXT.ExtendedRegisters + xCX - 4], edx
+ sub ecx, 4
+ jnz .again
+
+ ; Restore edx and ecx.
+ mov ecx, [xAX + CONTEXT.Ecx]
+ mov edx, [xAX + CONTEXT.Edx]
+
+%else
+ %error RT_ARCH
+%endif
+ ret
+ENDPROC rtVccCaptureContext
+
diff --git a/src/VBox/Runtime/common/compiler/vcc/stacksup-vcc.cpp b/src/VBox/Runtime/common/compiler/vcc/stacksup-vcc.cpp
new file mode 100644
index 00000000..8794cd07
--- /dev/null
+++ b/src/VBox/Runtime/common/compiler/vcc/stacksup-vcc.cpp
@@ -0,0 +1,371 @@
+/* $Id: stacksup-vcc.cpp $ */
+/** @file
+ * IPRT - Visual C++ Compiler - Stack Checking C/C++ Support.
+ */
+
+/*
+ * Copyright (C) 2006-2023 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
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/nocrt.h"
+
+#include <iprt/asm.h>
+#include <iprt/asm-amd64-x86.h>
+#ifndef IPRT_NOCRT_WITHOUT_FATAL_WRITE
+# include <iprt/assert.h>
+#endif
+
+#include "internal/compiler-vcc.h"
+#ifdef IN_RING3
+# include <iprt/win/windows.h>
+# include "../../../r3/win/internal-r3-win.h" /* ugly, but need some windows API function pointers */
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Gets the program counter member of Windows' CONTEXT structure. */
+#if defined(RT_ARCH_AMD64)
+# define MY_GET_PC_FROM_CONTEXT(a_pCtx) ((a_pCtx)->Rip)
+#elif defined(RT_ARCH_X86)
+# define MY_GET_PC_FROM_CONTEXT(a_pCtx) ((a_pCtx)->Eip)
+#else
+# error "Port Me!"
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Variable descriptor. */
+typedef struct RTC_VAR_DESC_T
+{
+ int32_t offFrame;
+ uint32_t cbVar;
+ const char *pszName;
+} RTC_VAR_DESC_T;
+
+/** Frame descriptor. */
+typedef struct RTC_FRAME_DESC_T
+{
+ uint32_t cVars;
+ RTC_VAR_DESC_T const *paVars;
+} RTC_FRAME_DESC_T;
+
+#define VARIABLE_MARKER_PRE 0xcccccccc
+#define VARIABLE_MARKER_POST 0xcccccccc
+
+
+/**
+ * Alloca allocation entry.
+ * @note For whatever reason the pNext and cb members are misaligned on 64-bit
+ * targets. 32-bit targets OTOH adds padding to keep the structure size
+ * and pNext + cb offsets the same.
+ */
+#pragma pack(4)
+typedef struct RTC_ALLOC_ENTRY
+{
+ uint32_t uGuard1;
+ RTC_ALLOC_ENTRY *pNext;
+#if ARCH_BITS == 32
+ uint32_t pNextPad;
+#endif
+ size_t cb;
+#if ARCH_BITS == 32
+ uint32_t cbPad;
+#endif
+ uint32_t auGuard2[3];
+} RTC_ALLOC_ENTRY;
+#pragma pack()
+
+#define ALLOCA_FILLER_BYTE 0xcc
+#define ALLOCA_FILLER_32 0xcccccccc
+
+
+/*********************************************************************************************************************************
+* External Symbols *
+*********************************************************************************************************************************/
+extern "C" void __fastcall _RTC_CheckStackVars(uint8_t *pbFrame, RTC_VAR_DESC_T const *pVar); /* nocrt-stack.asm */
+extern "C" uintptr_t __security_cookie;
+
+
+/**
+ * Initializes the security cookie value.
+ *
+ * This must be called as the first thing by the startup code. We must also no
+ * do anything fancy here.
+ */
+void rtVccInitSecurityCookie(void) RT_NOEXCEPT
+{
+ __security_cookie = (uintptr_t)ASMReadTSC() ^ (uintptr_t)&__security_cookie;
+}
+
+
+/**
+ * Reports a security error.
+ *
+ * @param uFastFailCode The fast fail code.
+ * @param pCpuCtx The CPU context at the failure location.
+ */
+static DECL_NO_RETURN(void) rtVccFatalSecurityErrorWithCtx(uint32_t uFastFailCode, PCONTEXT pCpuCtx)
+{
+#ifdef IN_RING3
+ /*
+ * Use the __fastfail() approach if available, it is more secure than the stuff below:
+ */
+ if (g_pfnIsProcessorFeaturePresent && g_pfnIsProcessorFeaturePresent(PF_FASTFAIL_AVAILABLE))
+ __fastfail(uFastFailCode);
+
+ /*
+ * Fallback for legacy systems.
+ */
+ if (g_pfnIsDebuggerPresent && g_pfnIsDebuggerPresent())
+ __debugbreak();
+
+ /* If we can, clear the unhandled exception filter and report and unhandled exception. */
+ if (g_pfnSetUnhandledExceptionFilter && g_pfnUnhandledExceptionFilter)
+ {
+ g_pfnSetUnhandledExceptionFilter(NULL);
+
+ EXCEPTION_RECORD XcptRec =
+ {
+ /* .ExceptionCode = */ STATUS_STACK_BUFFER_OVERRUN,
+ /* .ExceptionFlags = */ EXCEPTION_NONCONTINUABLE,
+ /* .ExceptionRecord = */ NULL,
+# ifdef RT_ARCH_AMD64
+ /* .ExceptionAddress = */ (void *)pCpuCtx->Rip,
+# elif defined(RT_ARCH_X86)
+ /* .ExceptionAddress = */ (void *)pCpuCtx->Eip,
+# else
+# error "Port me!"
+# endif
+ /* .NumberParameters = */ 1,
+ /* .ExceptionInformation = */ { uFastFailCode, }
+ };
+
+ EXCEPTION_POINTERS XcptPtrs = { &XcptRec, pCpuCtx };
+ g_pfnUnhandledExceptionFilter(&XcptPtrs);
+ }
+
+ for (;;)
+ TerminateProcess(GetCurrentProcess(), STATUS_STACK_BUFFER_OVERRUN);
+
+#else
+# error "Port ME!"
+#endif
+}
+
+
+DECLASM(void) rtVccStackVarCorrupted(uint8_t *pbFrame, RTC_VAR_DESC_T const *pVar, PCONTEXT pCpuCtx)
+{
+#ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE
+ RTAssertMsg2("\n\n!!Stack corruption!!\n\n"
+ "%p LB %#x - %s\n",
+ pbFrame + pVar->offFrame, pVar->cbVar, pVar->pszName);
+#else
+ rtNoCrtFatalWriteBegin(RT_STR_TUPLE("\r\n\r\n!!Stack corruption!!\r\n\r\n"));
+ rtNoCrtFatalWritePtr(pbFrame + pVar->offFrame);
+ rtNoCrtFatalWrite(RT_STR_TUPLE(" LB "));
+ rtNoCrtFatalWriteX32(pVar->cbVar);
+ rtNoCrtFatalWrite(RT_STR_TUPLE(" - "));
+ rtNoCrtFatalWriteStr(pVar->pszName);
+ rtNoCrtFatalWriteEnd(RT_STR_TUPLE("\r\n"));
+#endif
+ rtVccFatalSecurityErrorWithCtx(FAST_FAIL_INCORRECT_STACK, pCpuCtx);
+}
+
+
+DECLASM(void) rtVccSecurityCookieMismatch(uintptr_t uCookie, PCONTEXT pCpuCtx)
+{
+#ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE
+ RTAssertMsg2("\n\n!!Stack cookie corruption!!\n\n"
+ "expected %p, found %p\n",
+ __security_cookie, uCookie);
+#else
+ rtNoCrtFatalWriteBegin(RT_STR_TUPLE("\r\n\r\n!!Stack cookie corruption!!\r\n\r\n"
+ "expected"));
+ rtNoCrtFatalWritePtr((void *)__security_cookie);
+ rtNoCrtFatalWrite(RT_STR_TUPLE(", found "));
+ rtNoCrtFatalWritePtr((void *)uCookie);
+ rtNoCrtFatalWriteEnd(RT_STR_TUPLE("\r\n"));
+#endif
+ rtVccFatalSecurityErrorWithCtx(FAST_FAIL_STACK_COOKIE_CHECK_FAILURE, pCpuCtx);
+}
+
+
+#ifdef RT_ARCH_X86
+DECLASM(void) rtVccCheckEspFailed(PCONTEXT pCpuCtx)
+{
+# ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE
+ RTAssertMsg2("\n\n!!ESP check failed!!\n\n"
+ "eip=%p esp=%p ebp=%p\n",
+ pCpuCtx->Eip, pCpuCtx->Esp, pCpuCtx->Ebp);
+# else
+ rtNoCrtFatalWriteBegin(RT_STR_TUPLE("\r\n\r\n!!ESP check failed!!\r\n\r\n"
+ "eip="));
+ rtNoCrtFatalWritePtr((void *)pCpuCtx->Eip);
+ rtNoCrtFatalWrite(RT_STR_TUPLE(" esp="));
+ rtNoCrtFatalWritePtr((void *)pCpuCtx->Esp);
+ rtNoCrtFatalWrite(RT_STR_TUPLE(" ebp="));
+ rtNoCrtFatalWritePtr((void *)pCpuCtx->Ebp);
+ rtNoCrtFatalWriteEnd(RT_STR_TUPLE("\r\n"));
+# endif
+ rtVccFatalSecurityErrorWithCtx(FAST_FAIL_INCORRECT_STACK, pCpuCtx);
+}
+#endif
+
+
+/** @todo reimplement in assembly (feeling too lazy right now). */
+extern "C" void __fastcall _RTC_CheckStackVars2(uint8_t *pbFrame, RTC_VAR_DESC_T const *pVar, RTC_ALLOC_ENTRY *pHead)
+{
+ while (pHead)
+ {
+ if ( pHead->uGuard1 == ALLOCA_FILLER_32
+#if 1 && ARCH_BITS == 32
+ && pHead->pNextPad == ALLOCA_FILLER_32
+ && pHead->cbPad == ALLOCA_FILLER_32
+#endif
+ && pHead->auGuard2[0] == ALLOCA_FILLER_32
+ && pHead->auGuard2[1] == ALLOCA_FILLER_32
+ && pHead->auGuard2[2] == ALLOCA_FILLER_32
+ && *(uint32_t const *)((uint8_t const *)pHead + pHead->cb - sizeof(uint32_t)) == ALLOCA_FILLER_32)
+ { /* likely */ }
+ else
+ {
+#ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE
+ RTAssertMsg2("\n\n!!Stack corruption (alloca)!!\n\n"
+ "%p LB %#x\n",
+ pHead, pHead->cb);
+#else
+ rtNoCrtFatalWriteBegin(RT_STR_TUPLE("\r\n\r\n!!Stack corruption (alloca)!!\r\n\r\n"));
+ rtNoCrtFatalWritePtr(pHead);
+ rtNoCrtFatalWrite(RT_STR_TUPLE(" LB "));
+ rtNoCrtFatalWriteX64(pHead->cb);
+ rtNoCrtFatalWriteEnd(RT_STR_TUPLE("\r\n"));
+#endif
+#ifdef IN_RING3
+ if (g_pfnIsDebuggerPresent && g_pfnIsDebuggerPresent())
+#endif
+ RT_BREAKPOINT();
+ }
+ pHead = pHead->pNext;
+ }
+
+ _RTC_CheckStackVars(pbFrame, pVar);
+}
+
+
+DECLASM(void) rtVccRangeCheckFailed(PCONTEXT pCpuCtx)
+{
+# ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE
+ RTAssertMsg2("\n\n!!Range check failed at %p!!\n\n", MY_GET_PC_FROM_CONTEXT(pCpuCtx));
+# else
+ rtNoCrtFatalWriteBegin(RT_STR_TUPLE("\r\n\r\n!!Range check failed at "));
+ rtNoCrtFatalWritePtr((void *)MY_GET_PC_FROM_CONTEXT(pCpuCtx));
+ rtNoCrtFatalWriteEnd(RT_STR_TUPLE("!!\r\n"));
+# endif
+ rtVccFatalSecurityErrorWithCtx(FAST_FAIL_RANGE_CHECK_FAILURE, pCpuCtx);
+}
+
+
+/** Whether or not this should be a fatal issue remains to be seen. See
+ * explanation in stack-vcc.asm. */
+#if 0
+DECLASM(void) rtVccUninitializedVariableUse(const char *pszVar, PCONTEXT pCpuCtx)
+#else
+extern "C" void __cdecl _RTC_UninitUse(const char *pszVar)
+#endif
+{
+#ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE
+ RTAssertMsg2("\n\n!!Used uninitialized variable %s at %p!!\n\n",
+ pszVar ? pszVar : "", ASMReturnAddress());
+#else
+ rtNoCrtFatalWriteBegin(RT_STR_TUPLE("\r\n\r\n!!Used uninitialized variable "));
+ rtNoCrtFatalWriteStr(pszVar);
+ rtNoCrtFatalWrite(RT_STR_TUPLE(" at "));
+ rtNoCrtFatalWritePtr(ASMReturnAddress());
+ rtNoCrtFatalWriteEnd(RT_STR_TUPLE("!!\r\n\r\n"));
+#endif
+#if 0
+ rtVccFatalSecurityErrorWithCtx(FAST_FAIL_FATAL_APP_EXIT, pCpuCtx);
+#else
+# ifdef IN_RING3
+ if (g_pfnIsDebuggerPresent && g_pfnIsDebuggerPresent())
+# endif
+ RT_BREAKPOINT();
+#endif
+}
+
+
+void rtVccCheckContextFailed(PCONTEXT pCpuCtx)
+{
+#ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE
+ RTAssertMsg2("\n\n!!Context (stack) check failed!!\n\n"
+ "PC=%p SP=%p BP=%p\n",
+# ifdef RT_ARCH_AMD64
+ pCpuCtx->Rip, pCpuCtx->Rsp, pCpuCtx->Rbp
+# elif defined(RT_ARCH_X86)
+ pCpuCtx->Eip, pCpuCtx->Esp, pCpuCtx->Ebp
+# else
+# error "unsupported arch"
+# endif
+ );
+#else
+ rtNoCrtFatalWriteBegin(RT_STR_TUPLE("\r\n\r\n!!Context (stack) check failed!!\r\n\r\n"
+ "PC="));
+# ifdef RT_ARCH_AMD64
+ rtNoCrtFatalWritePtr((void *)pCpuCtx->Rip);
+# elif defined(RT_ARCH_X86)
+ rtNoCrtFatalWritePtr((void *)pCpuCtx->Eip);
+# else
+# error "unsupported arch"
+# endif
+ rtNoCrtFatalWrite(RT_STR_TUPLE(" SP="));
+# ifdef RT_ARCH_AMD64
+ rtNoCrtFatalWritePtr((void *)pCpuCtx->Rsp);
+# elif defined(RT_ARCH_X86)
+ rtNoCrtFatalWritePtr((void *)pCpuCtx->Esp);
+# endif
+ rtNoCrtFatalWrite(RT_STR_TUPLE(" BP="));
+# ifdef RT_ARCH_AMD64
+ rtNoCrtFatalWritePtr((void *)pCpuCtx->Rbp);
+# elif defined(RT_ARCH_X86)
+ rtNoCrtFatalWritePtr((void *)pCpuCtx->Ebp);
+# endif
+ rtNoCrtFatalWriteEnd(RT_STR_TUPLE("\r\n"));
+#endif
+ rtVccFatalSecurityErrorWithCtx(FAST_FAIL_INVALID_SET_OF_CONTEXT, pCpuCtx);
+}
+
diff --git a/src/VBox/Runtime/common/compiler/vcc/tlsdir-vcc.c b/src/VBox/Runtime/common/compiler/vcc/tlsdir-vcc.c
new file mode 100644
index 00000000..b151bbe5
--- /dev/null
+++ b/src/VBox/Runtime/common/compiler/vcc/tlsdir-vcc.c
@@ -0,0 +1,114 @@
+/* $Id: tlsdir-vcc.c $ */
+/** @file
+ * IPRT - Visual C++ Compiler - PE/Windows TLS Directory.
+ *
+ * @note Doing this as a C file for same reasons as loadcfg-vcc.c.
+ */
+
+/*
+ * Copyright (C) 2022-2023 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
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define IPRT_COMPILER_VCC_WITH_TLS_CALLBACK_SECTIONS
+#define IPRT_COMPILER_VCC_WITH_TLS_DATA_SECTIONS
+#include "internal/iprt.h"
+#include <iprt/win/windows.h>
+
+#include "internal/compiler-vcc.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** @name TLS callback arrays.
+ *
+ * The important thing here are the section names, the linker is told to merge
+ * and sort all the .CRT* sections into .rdata.
+ *
+ * @{ */
+/** Start of the TLS callback array. */
+__declspec(allocate(".CRT$XLA")) PIMAGE_TLS_CALLBACK g_apfnRTVccTlsCallbacks_Start[] = { NULL, };
+/** End of the TLS callback array (not actually used, but seems to be
+ * traditional). */
+__declspec(allocate(".CRT$XLZ")) PIMAGE_TLS_CALLBACK g_apfnRTVccTlsCallbacks_End[] = { NULL, };
+
+/* Tell the linker to merge the .CRT* sections into .rdata */
+#pragma comment(linker, "/merge:.CRT=.rdata ")
+/** @} */
+
+
+/** @name TLS data arrays.
+ *
+ * @{ */
+#pragma data_seg(".tls")
+
+/** Start of the TLS data.
+ * @note The linker has a reference to name '_tls_start' indicating a possible
+ * required naming convention here.
+ * @note Not sure if the byte here is ignored or not... In assembly we could
+ * optimize it out, I think... */
+extern __declspec(allocate(".tls")) char _tls_start = 0;
+
+/** End of the TLS callback array.
+ * @note The linker has a reference to name '_tls_end' indicating a possible
+ * required naming convention here. */
+extern __declspec(allocate(".tls$ZZZ")) char _tls_end = 0;
+
+#pragma data_seg ()
+/** @} */
+
+
+/** The TLS index for the module we're linked into.
+ * The linker has a reference to the name '_tls_start', so this is probably
+ * fixed in some way. */
+extern ULONG _tls_index = 0;
+
+
+/**
+ * The TLS directory for the PE image.
+ *
+ * The name of this is dictated by the linker, as it looks for a _tls_used
+ * symbol and puts it's address and (somehow) size in the TLS data dir entry.
+ */
+extern __declspec(".rdata$T") /* seems to be tranditional, doubt it is necessary */
+const RT_CONCAT(IMAGE_TLS_DIRECTORY, ARCH_BITS) _tls_used =
+{
+ /* .StartAddressOfRawData = */ (uintptr_t)&_tls_start,
+ /* .EndAddressOfRawData = */ (uintptr_t)&_tls_end,
+ /* .AddressOfIndex = */ (uintptr_t)&_tls_index,
+ /* .AddressOfCallBacks = */ (uintptr_t)&g_apfnRTVccTlsCallbacks_Start[1],
+ /* .SizeOfZeroFill = */ 0,
+ /* .Characteristics = */ 0,
+};
+