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