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 | } |
522 | |