Fix unwind on ARM64 Linux (#20345)
[platform/upstream/coreclr.git] / src / pal / src / exception / seh-unwind.cpp
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4
5 /*++
6
7
8
9 Module Name:
10
11     seh-unwind.cpp
12
13 Abstract:
14
15     Implementation of exception API functions based on
16     the Unwind API.
17
18
19
20 --*/
21
22 #ifndef FEATURE_PAL_SXS
23 #error FEATURE_PAL_SXS needs to be defined for this file.
24 #endif // !FEATURE_PAL_SXS
25
26 #include "pal/context.h"
27 #include "pal.h"
28 #include <dlfcn.h>
29  
30 #define UNW_LOCAL_ONLY
31 // Sub-headers included from the libunwind.h contain an empty struct
32 // and clang issues a warning. Until the libunwind is fixed, disable
33 // the warning.
34 #pragma clang diagnostic push
35 #pragma clang diagnostic ignored "-Wextern-c-compat"
36 #include <libunwind.h>
37 #pragma clang diagnostic pop
38
39 //----------------------------------------------------------------------
40 // Virtual Unwinding
41 //----------------------------------------------------------------------
42
43 #if UNWIND_CONTEXT_IS_UCONTEXT_T
44
45 #if defined(_AMD64_)
46 #define ASSIGN_UNWIND_REGS \
47     ASSIGN_REG(Rip)        \
48     ASSIGN_REG(Rsp)        \
49     ASSIGN_REG(Rbp)        \
50     ASSIGN_REG(Rbx)        \
51     ASSIGN_REG(R12)        \
52     ASSIGN_REG(R13)        \
53     ASSIGN_REG(R14)        \
54     ASSIGN_REG(R15)
55 #elif defined(_ARM64_)
56 #define ASSIGN_UNWIND_REGS \
57     ASSIGN_REG(Pc)         \
58     ASSIGN_REG(Sp)         \
59     ASSIGN_REG(Fp)         \
60     ASSIGN_REG(Lr)         \
61     ASSIGN_REG(X19)        \
62     ASSIGN_REG(X20)        \
63     ASSIGN_REG(X21)        \
64     ASSIGN_REG(X22)        \
65     ASSIGN_REG(X23)        \
66     ASSIGN_REG(X24)        \
67     ASSIGN_REG(X25)        \
68     ASSIGN_REG(X26)        \
69     ASSIGN_REG(X27)        \
70     ASSIGN_REG(X28)
71 #elif defined(_X86_)
72 #define ASSIGN_UNWIND_REGS \
73     ASSIGN_REG(Eip)        \
74     ASSIGN_REG(Esp)        \
75     ASSIGN_REG(Ebp)        \
76     ASSIGN_REG(Ebx)        \
77     ASSIGN_REG(Esi)        \
78     ASSIGN_REG(Edi)
79 #else
80 #error unsupported architecture
81 #endif
82
83 static void WinContextToUnwindContext(CONTEXT *winContext, unw_context_t *unwContext)
84 {
85 #define ASSIGN_REG(reg) MCREG_##reg(unwContext->uc_mcontext) = winContext->reg;
86     ASSIGN_UNWIND_REGS
87 #undef ASSIGN_REG
88 }
89 #else
90 static void WinContextToUnwindContext(CONTEXT *winContext, unw_context_t *unwContext)
91 {
92 #if defined(_ARM_)    
93     // Assuming that unw_set_reg() on cursor will point the cursor to the
94     // supposed stack frame is dangerous for libunwind-arm in Linux.
95     // It is because libunwind's unw_cursor_t has other data structure
96     // initialized by unw_init_local(), which are not updated by
97     // unw_set_reg().
98     unwContext->regs[0] = 0;
99     unwContext->regs[1] = 0;
100     unwContext->regs[2] = 0;
101     unwContext->regs[3] = 0;
102     unwContext->regs[4] = winContext->R4;
103     unwContext->regs[5] = winContext->R5;
104     unwContext->regs[6] = winContext->R6;
105     unwContext->regs[7] = winContext->R7;
106     unwContext->regs[8] = winContext->R8;
107     unwContext->regs[9] = winContext->R9;
108     unwContext->regs[10] = winContext->R10;
109     unwContext->regs[11] = winContext->R11;
110     unwContext->regs[12] = 0;
111     unwContext->regs[13] = winContext->Sp;
112     unwContext->regs[14] = winContext->Lr;
113     unwContext->regs[15] = winContext->Pc;
114 #endif    
115
116
117 static void WinContextToUnwindCursor(CONTEXT *winContext, unw_cursor_t *cursor)
118 {
119 #if defined(_AMD64_)
120     unw_set_reg(cursor, UNW_REG_IP, winContext->Rip);
121     unw_set_reg(cursor, UNW_REG_SP, winContext->Rsp);
122     unw_set_reg(cursor, UNW_X86_64_RBP, winContext->Rbp);
123     unw_set_reg(cursor, UNW_X86_64_RBX, winContext->Rbx);
124     unw_set_reg(cursor, UNW_X86_64_R12, winContext->R12);
125     unw_set_reg(cursor, UNW_X86_64_R13, winContext->R13);
126     unw_set_reg(cursor, UNW_X86_64_R14, winContext->R14);
127     unw_set_reg(cursor, UNW_X86_64_R15, winContext->R15);
128 #elif defined(_X86_)
129     unw_set_reg(cursor, UNW_REG_IP, winContext->Eip);
130     unw_set_reg(cursor, UNW_REG_SP, winContext->Esp);
131     unw_set_reg(cursor, UNW_X86_EBP, winContext->Ebp);
132     unw_set_reg(cursor, UNW_X86_EBX, winContext->Ebx);
133     unw_set_reg(cursor, UNW_X86_ESI, winContext->Esi);
134     unw_set_reg(cursor, UNW_X86_EDI, winContext->Edi);
135 #endif
136 }
137 #endif
138
139 void UnwindContextToWinContext(unw_cursor_t *cursor, CONTEXT *winContext)
140 {
141 #if defined(_AMD64_)
142     unw_get_reg(cursor, UNW_REG_IP, (unw_word_t *) &winContext->Rip);
143     unw_get_reg(cursor, UNW_REG_SP, (unw_word_t *) &winContext->Rsp);
144     unw_get_reg(cursor, UNW_X86_64_RBP, (unw_word_t *) &winContext->Rbp);
145     unw_get_reg(cursor, UNW_X86_64_RBX, (unw_word_t *) &winContext->Rbx);
146     unw_get_reg(cursor, UNW_X86_64_R12, (unw_word_t *) &winContext->R12);
147     unw_get_reg(cursor, UNW_X86_64_R13, (unw_word_t *) &winContext->R13);
148     unw_get_reg(cursor, UNW_X86_64_R14, (unw_word_t *) &winContext->R14);
149     unw_get_reg(cursor, UNW_X86_64_R15, (unw_word_t *) &winContext->R15);
150 #elif defined(_X86_)
151     unw_get_reg(cursor, UNW_REG_IP, (unw_word_t *) &winContext->Eip);
152     unw_get_reg(cursor, UNW_REG_SP, (unw_word_t *) &winContext->Esp);
153     unw_get_reg(cursor, UNW_X86_EBP, (unw_word_t *) &winContext->Ebp);
154     unw_get_reg(cursor, UNW_X86_EBX, (unw_word_t *) &winContext->Ebx);
155     unw_get_reg(cursor, UNW_X86_ESI, (unw_word_t *) &winContext->Esi);
156     unw_get_reg(cursor, UNW_X86_EDI, (unw_word_t *) &winContext->Edi);
157 #elif defined(_ARM_)
158     unw_get_reg(cursor, UNW_REG_SP, (unw_word_t *) &winContext->Sp);
159     unw_get_reg(cursor, UNW_REG_IP, (unw_word_t *) &winContext->Pc);
160     unw_get_reg(cursor, UNW_ARM_R14, (unw_word_t *) &winContext->Lr);
161     unw_get_reg(cursor, UNW_ARM_R4, (unw_word_t *) &winContext->R4);
162     unw_get_reg(cursor, UNW_ARM_R5, (unw_word_t *) &winContext->R5);
163     unw_get_reg(cursor, UNW_ARM_R6, (unw_word_t *) &winContext->R6);
164     unw_get_reg(cursor, UNW_ARM_R7, (unw_word_t *) &winContext->R7);
165     unw_get_reg(cursor, UNW_ARM_R8, (unw_word_t *) &winContext->R8);
166     unw_get_reg(cursor, UNW_ARM_R9, (unw_word_t *) &winContext->R9);
167     unw_get_reg(cursor, UNW_ARM_R10, (unw_word_t *) &winContext->R10);
168     unw_get_reg(cursor, UNW_ARM_R11, (unw_word_t *) &winContext->R11);
169 #elif defined(_ARM64_)
170     unw_get_reg(cursor, UNW_REG_IP, (unw_word_t *) &winContext->Pc);
171     unw_get_reg(cursor, UNW_REG_SP, (unw_word_t *) &winContext->Sp);
172     unw_get_reg(cursor, UNW_AARCH64_X29, (unw_word_t *) &winContext->Fp);
173     unw_get_reg(cursor, UNW_AARCH64_X30, (unw_word_t *) &winContext->Lr);
174     unw_get_reg(cursor, UNW_AARCH64_X19, (unw_word_t *) &winContext->X19);
175     unw_get_reg(cursor, UNW_AARCH64_X20, (unw_word_t *) &winContext->X20);
176     unw_get_reg(cursor, UNW_AARCH64_X21, (unw_word_t *) &winContext->X21);
177     unw_get_reg(cursor, UNW_AARCH64_X22, (unw_word_t *) &winContext->X22);
178     unw_get_reg(cursor, UNW_AARCH64_X23, (unw_word_t *) &winContext->X23);
179     unw_get_reg(cursor, UNW_AARCH64_X24, (unw_word_t *) &winContext->X24);
180     unw_get_reg(cursor, UNW_AARCH64_X25, (unw_word_t *) &winContext->X25);
181     unw_get_reg(cursor, UNW_AARCH64_X26, (unw_word_t *) &winContext->X26);
182     unw_get_reg(cursor, UNW_AARCH64_X27, (unw_word_t *) &winContext->X27);
183     unw_get_reg(cursor, UNW_AARCH64_X28, (unw_word_t *) &winContext->X28);
184 #else
185 #error unsupported architecture
186 #endif
187 }
188
189 static void GetContextPointer(unw_cursor_t *cursor, unw_context_t *unwContext, int reg, SIZE_T **contextPointer)
190 {
191 #if defined(HAVE_UNW_GET_SAVE_LOC)
192     unw_save_loc_t saveLoc;
193     unw_get_save_loc(cursor, reg, &saveLoc);
194     if (saveLoc.type == UNW_SLT_MEMORY)
195     {
196         SIZE_T *pLoc = (SIZE_T *)saveLoc.u.addr;
197         // Filter out fake save locations that point to unwContext 
198         if (unwContext == NULL || (pLoc < (SIZE_T *)unwContext) || ((SIZE_T *)(unwContext + 1) <= pLoc))
199             *contextPointer = (SIZE_T *)saveLoc.u.addr;
200     }
201 #else
202     // Returning NULL indicates that we don't have context pointers available
203     *contextPointer = NULL;
204 #endif
205 }
206
207 void GetContextPointers(unw_cursor_t *cursor, unw_context_t *unwContext, KNONVOLATILE_CONTEXT_POINTERS *contextPointers)
208 {
209 #if defined(_AMD64_)
210     GetContextPointer(cursor, unwContext, UNW_X86_64_RBP, &contextPointers->Rbp);
211     GetContextPointer(cursor, unwContext, UNW_X86_64_RBX, &contextPointers->Rbx);
212     GetContextPointer(cursor, unwContext, UNW_X86_64_R12, &contextPointers->R12);
213     GetContextPointer(cursor, unwContext, UNW_X86_64_R13, &contextPointers->R13);
214     GetContextPointer(cursor, unwContext, UNW_X86_64_R14, &contextPointers->R14);
215     GetContextPointer(cursor, unwContext, UNW_X86_64_R15, &contextPointers->R15);
216 #elif defined(_X86_)
217     GetContextPointer(cursor, unwContext, UNW_X86_EBX, &contextPointers->Ebx);
218     GetContextPointer(cursor, unwContext, UNW_X86_EBP, &contextPointers->Ebp);
219     GetContextPointer(cursor, unwContext, UNW_X86_ESI, &contextPointers->Esi);
220     GetContextPointer(cursor, unwContext, UNW_X86_EDI, &contextPointers->Edi);
221 #elif defined(_ARM_)
222     GetContextPointer(cursor, unwContext, UNW_ARM_R4, &contextPointers->R4);
223     GetContextPointer(cursor, unwContext, UNW_ARM_R5, &contextPointers->R5);
224     GetContextPointer(cursor, unwContext, UNW_ARM_R6, &contextPointers->R6);
225     GetContextPointer(cursor, unwContext, UNW_ARM_R7, &contextPointers->R7);
226     GetContextPointer(cursor, unwContext, UNW_ARM_R8, &contextPointers->R8);
227     GetContextPointer(cursor, unwContext, UNW_ARM_R9, &contextPointers->R9);
228     GetContextPointer(cursor, unwContext, UNW_ARM_R10, &contextPointers->R10);
229     GetContextPointer(cursor, unwContext, UNW_ARM_R11, &contextPointers->R11);
230 #elif defined(_ARM64_)
231     GetContextPointer(cursor, unwContext, UNW_AARCH64_X19, &contextPointers->X19);
232     GetContextPointer(cursor, unwContext, UNW_AARCH64_X20, &contextPointers->X20);
233     GetContextPointer(cursor, unwContext, UNW_AARCH64_X21, &contextPointers->X21);
234     GetContextPointer(cursor, unwContext, UNW_AARCH64_X22, &contextPointers->X22);
235     GetContextPointer(cursor, unwContext, UNW_AARCH64_X23, &contextPointers->X23);
236     GetContextPointer(cursor, unwContext, UNW_AARCH64_X24, &contextPointers->X24);
237     GetContextPointer(cursor, unwContext, UNW_AARCH64_X25, &contextPointers->X25);
238     GetContextPointer(cursor, unwContext, UNW_AARCH64_X26, &contextPointers->X26);
239     GetContextPointer(cursor, unwContext, UNW_AARCH64_X27, &contextPointers->X27);
240     GetContextPointer(cursor, unwContext, UNW_AARCH64_X28, &contextPointers->X28);
241     GetContextPointer(cursor, unwContext, UNW_AARCH64_X29, &contextPointers->Fp);
242 #else
243 #error unsupported architecture
244 #endif
245 }
246
247 extern int g_common_signal_handler_context_locvar_offset;
248
249 BOOL PAL_VirtualUnwind(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextPointers)
250 {
251     int st;
252     unw_context_t unwContext;
253     unw_cursor_t cursor;
254
255     DWORD64 curPc = CONTEXTGetPC(context);
256
257 #ifndef __APPLE__
258     // Check if the PC is the return address from the SEHProcessException in the common_signal_handler. 
259     // If that's the case, extract its local variable containing the windows style context of the hardware 
260     // exception and return that. This skips the hardware signal handler trampoline that the libunwind 
261     // cannot cross on some systems.
262     if ((void*)curPc == g_SEHProcessExceptionReturnAddress)
263     {
264         CONTEXT* signalContext = (CONTEXT*)(CONTEXTGetFP(context) + g_common_signal_handler_context_locvar_offset);
265         memcpy_s(context, sizeof(CONTEXT), signalContext, sizeof(CONTEXT));
266
267         return TRUE;
268     }
269 #endif 
270
271     if ((context->ContextFlags & CONTEXT_EXCEPTION_ACTIVE) != 0)
272     {
273         // The current frame is a source of hardware exception. Due to the fact that
274         // we use the low level unwinder to unwind just one frame a time, the
275         // unwinder doesn't have the signal_frame flag set. So it doesn't
276         // know that it should not decrement the PC before looking up the unwind info.
277         // So we compensate it by incrementing the PC before passing it to the unwinder.
278         // Without it, the unwinder would not find unwind info if the hardware exception
279         // happened in the first instruction of a function.
280         CONTEXTSetPC(context, curPc + 1);
281     }
282
283 #if !UNWIND_CONTEXT_IS_UCONTEXT_T
284     st = unw_getcontext(&unwContext);
285     if (st < 0)
286     {
287         return FALSE;
288     }
289 #endif
290
291     WinContextToUnwindContext(context, &unwContext);
292
293     st = unw_init_local(&cursor, &unwContext);
294     if (st < 0)
295     {
296         return FALSE;
297     }
298
299 #if !UNWIND_CONTEXT_IS_UCONTEXT_T
300     // Set the unwind context to the specified windows context
301     WinContextToUnwindCursor(context, &cursor);
302 #endif
303
304     st = unw_step(&cursor);
305     if (st < 0)
306     {
307         return FALSE;
308     }
309
310     // Check if the frame we have unwound to is a frame that caused
311     // synchronous signal, like a hardware exception and record it
312     // in the context flags.
313     if (unw_is_signal_frame(&cursor) > 0)
314     {
315         context->ContextFlags |= CONTEXT_EXCEPTION_ACTIVE;
316 #if defined(_ARM_) || defined(_ARM64_) || defined(_X86_)
317         context->ContextFlags &= ~CONTEXT_UNWOUND_TO_CALL;
318 #endif // _ARM_ || _ARM64_
319     }
320     else
321     {
322         context->ContextFlags &= ~CONTEXT_EXCEPTION_ACTIVE;
323 #if defined(_ARM_) || defined(_ARM64_) || defined(_X86_)
324         context->ContextFlags |= CONTEXT_UNWOUND_TO_CALL;
325 #endif // _ARM_ || _ARM64_
326     }
327
328     // Update the passed in windows context to reflect the unwind
329     //
330     UnwindContextToWinContext(&cursor, context);
331
332     // On some OSes / architectures if it unwound all the way to _start
333     // (__libc_start_main on arm64 Linux with glibc older than 2.27).
334     // >= 0 is returned from the step, but $pc will stay the same.
335     // So we detect that here and set the $pc to NULL in that case.
336     // This is the default behavior of the libunwind on x64 Linux.
337     // 
338     if (st >= 0 && CONTEXTGetPC(context) == curPc)
339     {
340         CONTEXTSetPC(context, 0);
341     }
342
343     if (contextPointers != NULL)
344     {
345         GetContextPointers(&cursor, &unwContext, contextPointers);
346     }
347     return TRUE;
348 }
349
350 struct ExceptionRecords
351 {
352     CONTEXT ContextRecord;
353     EXCEPTION_RECORD ExceptionRecord;
354 };
355
356 // Max number of fallback contexts that are used when malloc fails to allocate ExceptionRecords structure
357 static const int MaxFallbackContexts = sizeof(size_t) * 8;
358 // Array of fallback contexts
359 static ExceptionRecords s_fallbackContexts[MaxFallbackContexts];
360 // Bitmap used for allocating fallback contexts - bits set to 1 represent already allocated context.
361 static volatile size_t s_allocatedContextsBitmap = 0;
362
363 /*++
364 Function:
365     AllocateExceptionRecords
366
367     Allocate EXCEPTION_RECORD and CONTEXT structures for an exception.
368 Parameters:
369     exceptionRecord - output pointer to the allocated exception record
370     contextRecord - output pointer to the allocated context record
371 --*/
372 VOID
373 AllocateExceptionRecords(EXCEPTION_RECORD** exceptionRecord, CONTEXT** contextRecord)
374 {
375     ExceptionRecords* records;
376     if (posix_memalign((void**)&records, alignof(ExceptionRecords), sizeof(ExceptionRecords)) != 0)
377     {
378         size_t bitmap;
379         size_t newBitmap;
380         int index;
381
382         do
383         {
384             bitmap = s_allocatedContextsBitmap;
385             index = __builtin_ffsl(~bitmap) - 1;
386             if (index < 0)
387             {
388                 PROCAbort();
389             }
390
391             newBitmap = bitmap | ((size_t)1 << index);
392         }
393         while (__sync_val_compare_and_swap(&s_allocatedContextsBitmap, bitmap, newBitmap) != bitmap);
394
395         records = &s_fallbackContexts[index];
396     }
397
398     *contextRecord = &records->ContextRecord;
399     *exceptionRecord = &records->ExceptionRecord;
400 }
401
402 /*++
403 Function:
404     PAL_FreeExceptionRecords
405
406     Free EXCEPTION_RECORD and CONTEXT structures of an exception that were allocated by the
407     AllocateExceptionRecords.
408 Parameters:
409     exceptionRecord - exception record
410     contextRecord - context record
411 --*/
412 VOID
413 PALAPI
414 PAL_FreeExceptionRecords(IN EXCEPTION_RECORD *exceptionRecord, IN CONTEXT *contextRecord)
415 {
416     // Both records are allocated at once and the allocated memory starts at the contextRecord
417     ExceptionRecords* records = (ExceptionRecords*)contextRecord;
418     if ((records >= &s_fallbackContexts[0]) && (records < &s_fallbackContexts[MaxFallbackContexts]))
419     {
420         int index = records - &s_fallbackContexts[0];
421         __sync_fetch_and_and(&s_allocatedContextsBitmap, ~((size_t)1 << index));
422     }
423     else
424     {
425         free(contextRecord);
426     }
427 }
428
429 /*++
430 Function:
431     RtlpRaiseException
432
433 Parameters:
434     ExceptionRecord - the Windows exception record to throw
435
436 Note:
437     The name of this function and the name of the ExceptionRecord 
438     parameter is used in the sos lldb plugin code to read the exception
439     record. See coreclr\src\ToolBox\SOS\lldbplugin\services.cpp.
440
441     This function must not be inlined or optimized so the below PAL_VirtualUnwind
442     calls end up with RaiseException caller's context and so the above debugger 
443     code finds the function and ExceptionRecord parameter.
444 --*/
445 PAL_NORETURN
446 __attribute__((noinline))
447 __attribute__((optnone))
448 static void 
449 RtlpRaiseException(EXCEPTION_RECORD *ExceptionRecord, CONTEXT *ContextRecord)
450 {
451     throw PAL_SEHException(ExceptionRecord, ContextRecord);
452 }
453
454 /*++
455 Function:
456   RaiseException
457
458 See MSDN doc.
459 --*/
460 // no PAL_NORETURN, as callers must assume this can return for continuable exceptions.
461 __attribute__((noinline))
462 VOID
463 PALAPI
464 RaiseException(IN DWORD dwExceptionCode,
465                IN DWORD dwExceptionFlags,
466                IN DWORD nNumberOfArguments,
467                IN CONST ULONG_PTR *lpArguments)
468 {
469     // PERF_ENTRY_ONLY is used here because RaiseException may or may not
470     // return. We can not get latency data without PERF_EXIT. For this reason,
471     // PERF_ENTRY_ONLY is used to profile frequency only.
472     PERF_ENTRY_ONLY(RaiseException);
473     ENTRY("RaiseException(dwCode=%#x, dwFlags=%#x, nArgs=%u, lpArguments=%p)\n",
474           dwExceptionCode, dwExceptionFlags, nNumberOfArguments, lpArguments);
475
476     /* Validate parameters */
477     if (dwExceptionCode & RESERVED_SEH_BIT)
478     {
479         WARN("Exception code %08x has bit 28 set; clearing it.\n", dwExceptionCode);
480         dwExceptionCode ^= RESERVED_SEH_BIT;
481     }
482
483     if (nNumberOfArguments > EXCEPTION_MAXIMUM_PARAMETERS)
484     {
485         WARN("Number of arguments (%d) exceeds the limit "
486             "EXCEPTION_MAXIMUM_PARAMETERS (%d); ignoring extra parameters.\n",
487             nNumberOfArguments, EXCEPTION_MAXIMUM_PARAMETERS);
488         nNumberOfArguments = EXCEPTION_MAXIMUM_PARAMETERS;
489     }
490
491     CONTEXT *contextRecord;
492     EXCEPTION_RECORD *exceptionRecord;
493     AllocateExceptionRecords(&exceptionRecord, &contextRecord);
494
495     ZeroMemory(exceptionRecord, sizeof(EXCEPTION_RECORD));
496
497     exceptionRecord->ExceptionCode = dwExceptionCode;
498     exceptionRecord->ExceptionFlags = dwExceptionFlags;
499     exceptionRecord->ExceptionRecord = NULL;
500     exceptionRecord->ExceptionAddress = NULL; // will be set by RtlpRaiseException
501     exceptionRecord->NumberParameters = nNumberOfArguments;
502     if (nNumberOfArguments)
503     {
504         CopyMemory(exceptionRecord->ExceptionInformation, lpArguments,
505                    nNumberOfArguments * sizeof(ULONG_PTR));
506     }
507
508     // Capture the context of RaiseException.
509     ZeroMemory(contextRecord, sizeof(CONTEXT));
510     contextRecord->ContextFlags = CONTEXT_FULL;
511     CONTEXT_CaptureContext(contextRecord);
512
513     // We have to unwind one level to get the actual context user code could be resumed at.
514     PAL_VirtualUnwind(contextRecord, NULL);
515
516     exceptionRecord->ExceptionAddress = (void *)CONTEXTGetPC(contextRecord);
517
518     RtlpRaiseException(exceptionRecord, contextRecord);
519
520     LOGEXIT("RaiseException returns\n");
521 }