VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/compiler/vcc/except-x86-vcc.cpp@ 97862

Last change on this file since 97862 was 97862, checked in by vboxsync, 2 years ago

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

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.7 KB
Line 
1/* $Id: except-x86-vcc.cpp 97862 2022-12-23 16:55:57Z vboxsync $ */
2/** @file
3 * IPRT - Visual C++ Compiler - x86 Exception Handler Filter.
4 */
5
6/*
7 * Copyright (C) 2006-2022 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.215389.xyz.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include <iprt/win/windows.h>
42
43#include "except-vcc.h"
44
45
46/*********************************************************************************************************************************
47* Global Variables *
48*********************************************************************************************************************************/
49extern "C" uintptr_t __security_cookie;
50
51
52/*********************************************************************************************************************************
53* Internal Functions *
54*********************************************************************************************************************************/
55DECLASM(LONG) rtVccEh4DoFiltering(PFN_EH4_XCPT_FILTER_T pfnFilter, uint8_t const *pbFrame);
56DECLASM(DECL_NO_RETURN(void)) rtVccEh4JumpToHandler(PFN_EH4_XCPT_HANDLER_T pfnHandler, uint8_t const *pbFrame);
57//DECLASM(void) rtVccEh4DoLocalUnwind(PEXCEPTION_REGISTRATION_RECORD pXcptRegRec, uint32_t uTargetTryLevel,
58// uint8_t const *pbFrame, uintptr_t *pSecurityCookie);
59DECLASM(void) rtVccEh4DoGlobalUnwind(PEXCEPTION_RECORD pXcptRec, PEXCEPTION_REGISTRATION_RECORD pXcptRegRec);
60DECLASM(void) rtVccEh4DoFinally(PFN_EH4_FINALLY_T pfnFinally, bool fAbend, uint8_t const *pbFrame);
61
62
63static void rtVccEh4DoLocalUnwind(PEXCEPTION_REGISTRATION_RECORD pXcptRegRec, uint32_t uTargetTryLevel,
64 uint8_t const *pbFrame, uintptr_t *pSecurityCookie)
65{
66 /*
67 * Manually set up exception handler.
68 */
69 /** @todo */
70
71 /*
72 * Do the unwinding.
73 */
74 PEH4_XCPT_REG_REC_T pEh4XcptRegRec = RT_FROM_MEMBER(pXcptRegRec, EH4_XCPT_REG_REC_T, XcptRec);
75 uint32_t uCurTryLevel = pEh4XcptRegRec->uTryLevel;
76 while ( uCurTryLevel != EH4_TOPMOST_TRY_LEVEL
77 && ( uCurTryLevel > uTargetTryLevel
78 || uTargetTryLevel == EH4_TOPMOST_TRY_LEVEL /* if we knew what 0xffffffff meant, this could probably be omitted */ ))
79 {
80 PCEH4_SCOPE_TAB_T const pScopeTable = (PCEH4_SCOPE_TAB_T)(pEh4XcptRegRec->uEncodedScopeTable ^ *pSecurityCookie);
81 PCEH4_SCOPE_TAB_REC_T const pEntry = &pScopeTable->aScopeRecords[uCurTryLevel];
82
83 pEh4XcptRegRec->uTryLevel = uCurTryLevel = pEntry->uEnclosingLevel;
84
85 /* __finally scope table entries have no filter sub-function. */
86 if (!pEntry->pfnFilter)
87 {
88 //RTAssertMsg2("rtVccEh4DoLocalUnwind: Calling %p (level %#x)\n", pEntry->pfnFinally, uCurTryLevel);
89 rtVccEh4DoFinally(pEntry->pfnFinally, true /*fAbend*/, pbFrame);
90
91 /* Read the try level again in case it changed... */
92 uCurTryLevel = pEh4XcptRegRec->uTryLevel;
93 }
94 }
95
96 /*
97 * Deregister exception handler.
98 */
99 /** @todo */
100}
101
102
103DECLINLINE(void) rtVccValidateExceptionContextRecord(PCONTEXT pCpuCtx)
104{
105 RT_NOREF(pCpuCtx);
106 /** @todo Implement __exception_validate_context_record .*/
107}
108
109
110DECLINLINE(void) rtVccEh4ValidateCookies(PCEH4_SCOPE_TAB_T pScopeTable, uint8_t const *pbFrame)
111{
112 if (pScopeTable->offGSCookie != EH4_NO_GS_COOKIE)
113 {
114 uintptr_t uGsCookie = *(uintptr_t const *)&pbFrame[pScopeTable->offGSCookie];
115 uGsCookie ^= (uintptr_t)&pbFrame[pScopeTable->offGSCookieXor];
116 __security_check_cookie(uGsCookie);
117 }
118
119 uintptr_t uEhCookie = *(uintptr_t const *)&pbFrame[pScopeTable->offEHCookie];
120 uEhCookie ^= (uintptr_t)&pbFrame[pScopeTable->offEHCookieXor];
121 __security_check_cookie(uEhCookie);
122}
123
124
125/**
126 * Call exception filters, handlers and unwind code for x86 code.
127 *
128 * This is called for windows' structured exception handling (SEH) in x86 32-bit
129 * code, i.e. the __try/__except/__finally stuff in Visual C++. The compiler
130 * generate scope records for the __try/__except blocks as well as unwind
131 * records for __finally and probably C++ stack object destructors.
132 *
133 * @returns Exception disposition.
134 * @param pXcptRec The exception record.
135 * @param pXcptRegRec The exception registration record, taken to be the frame
136 * address.
137 * @param pCpuCtx The CPU context for the exception.
138 * @param pDispCtx Dispatcher context.
139 */
140extern "C" __declspec(guard(suppress))
141DWORD _except_handler4(PEXCEPTION_RECORD pXcptRec, PEXCEPTION_REGISTRATION_RECORD pXcptRegRec, PCONTEXT pCpuCtx, PVOID pvCtx)
142{
143 /*
144 * The registration record (probably chained on FS:[0] like in the OS/2 days)
145 * points to a larger structure specific to _except_handler4. The structure
146 * is planted right after the saved caller EBP value when establishing the
147 * stack frame, so EBP = pXcptRegRec + 1;
148 */
149 PEH4_XCPT_REG_REC_T const pEh4XcptRegRec = RT_FROM_MEMBER(pXcptRegRec, EH4_XCPT_REG_REC_T, XcptRec);
150 uint8_t * const pbFrame = (uint8_t *)&pEh4XcptRegRec[1];
151 PCEH4_SCOPE_TAB_T const pScopeTable = (PCEH4_SCOPE_TAB_T)(pEh4XcptRegRec->uEncodedScopeTable ^ __security_cookie);
152
153 /*
154 * Validate the stack cookie and exception context.
155 */
156 rtVccEh4ValidateCookies(pScopeTable, pbFrame);
157 rtVccValidateExceptionContextRecord(pCpuCtx);
158
159 /*
160 * If dispatching an exception, call the exception filter functions and jump
161 * to the __except blocks if so directed.
162 */
163 if (IS_DISPATCHING(pXcptRec->ExceptionFlags))
164 {
165 uint32_t uTryLevel = pEh4XcptRegRec->uTryLevel;
166 //RTAssertMsg2("_except_handler4: dispatch: uTryLevel=%#x\n", uTryLevel);
167 while (uTryLevel != EH4_TOPMOST_TRY_LEVEL)
168 {
169 PCEH4_SCOPE_TAB_REC_T const pEntry = &pScopeTable->aScopeRecords[uTryLevel];
170 PFN_EH4_XCPT_FILTER_T const pfnFilter = pEntry->pfnFilter;
171 if (pfnFilter)
172 {
173 /* Call the __except filtering expression: */
174 //RTAssertMsg2("_except_handler4: Calling pfnFilter=%p\n", pfnFilter);
175 EXCEPTION_POINTERS XcptPtrs = { pXcptRec, pCpuCtx };
176 pEh4XcptRegRec->pXctpPtrs = &XcptPtrs;
177 LONG lRet = rtVccEh4DoFiltering(pfnFilter, pbFrame);
178 pEh4XcptRegRec->pXctpPtrs = NULL;
179 //RTAssertMsg2("_except_handler4: pfnFilter=%p -> %ld\n", pfnFilter, lRet);
180 rtVccEh4ValidateCookies(pScopeTable, pbFrame);
181
182 /* Return if we're supposed to continue execution (the convention
183 it to match negative values rather than the exact defined value): */
184 AssertCompile(EXCEPTION_CONTINUE_EXECUTION == -1);
185 if (lRet <= EXCEPTION_CONTINUE_EXECUTION)
186 return ExceptionContinueExecution;
187
188 /* Similarly, the handler is executed for any positive value. */
189 AssertCompile(EXCEPTION_CONTINUE_SEARCH == 0);
190 AssertCompile(EXCEPTION_EXECUTE_HANDLER == 1);
191 if (lRet >= EXCEPTION_EXECUTE_HANDLER)
192 {
193 /* We're about to resume execution in the __except block, so unwind
194 up to it first. */
195 //RTAssertMsg2("_except_handler4: global unwind\n");
196 rtVccEh4DoGlobalUnwind(pXcptRec, &pEh4XcptRegRec->XcptRec);
197 if (pEh4XcptRegRec->uTryLevel != EH4_TOPMOST_TRY_LEVEL)
198 {
199 //RTAssertMsg2("_except_handler4: local unwind\n");
200 rtVccEh4DoLocalUnwind(&pEh4XcptRegRec->XcptRec, uTryLevel, pbFrame, &__security_cookie);
201 }
202 rtVccEh4ValidateCookies(pScopeTable, pbFrame);
203
204 /* Now jump to the __except block. This will _not_ return. */
205 //RTAssertMsg2("_except_handler4: jumping to __except block %p (level %#x)\n", pEntry->pfnHandler, pEntry->uEnclosingLevel);
206 pEh4XcptRegRec->uTryLevel = pEntry->uEnclosingLevel;
207 rtVccEh4ValidateCookies(pScopeTable, pbFrame); /* paranoia^2 */
208
209 rtVccEh4JumpToHandler(pEntry->pfnHandler, pbFrame);
210 /* (not reached) */
211 }
212 }
213
214 /*
215 * Next try level.
216 */
217 uTryLevel = pEntry->uEnclosingLevel;
218 }
219 }
220 /*
221 * If not dispatching we're unwinding, so we call any __finally blocks.
222 */
223 else
224 {
225 //RTAssertMsg2("_except_handler4: unwind: uTryLevel=%#x\n", pEh4XcptRegRec->uTryLevel);
226 if (pEh4XcptRegRec->uTryLevel != EH4_TOPMOST_TRY_LEVEL)
227 {
228 rtVccEh4DoLocalUnwind(&pEh4XcptRegRec->XcptRec, EH4_TOPMOST_TRY_LEVEL, pbFrame, &__security_cookie);
229 rtVccEh4ValidateCookies(pScopeTable, pbFrame);
230 }
231 }
232
233 RT_NOREF(pvCtx);
234 return ExceptionContinueSearch;
235}
236
Note: See TracBrowser for help on using the repository browser.

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