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.
15 Implementation of exception API functions based on
22 #ifndef FEATURE_PAL_SXS
23 #error FEATURE_PAL_SXS needs to be defined for this file.
24 #endif // !FEATURE_PAL_SXS
26 #include "pal/context.h"
32 #define UNW_LOCAL_ONLY
34 #include <libunwind.h>
35 #endif // HAVE_LIBUNWIND_H
37 //----------------------------------------------------------------------
39 //----------------------------------------------------------------------
42 #if UNWIND_CONTEXT_IS_UCONTEXT_T
45 #define ASSIGN_UNWIND_REGS \
54 #elif defined(_ARM64_)
55 #define ASSIGN_UNWIND_REGS \
71 #define ASSIGN_UNWIND_REGS \
79 #error unsupported architecture
82 static void WinContextToUnwindContext(CONTEXT *winContext, unw_context_t *unwContext)
84 #define ASSIGN_REG(reg) MCREG_##reg(unwContext->uc_mcontext) = winContext->reg;
89 static void WinContextToUnwindContext(CONTEXT *winContext, unw_context_t *unwContext)
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
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;
116 static void WinContextToUnwindCursor(CONTEXT *winContext, unw_cursor_t *cursor)
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);
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);
138 void UnwindContextToWinContext(unw_cursor_t *cursor, CONTEXT *winContext)
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);
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);
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);
184 #error unsupported architecture
188 static void GetContextPointer(unw_cursor_t *cursor, unw_context_t *unwContext, int reg, SIZE_T **contextPointer)
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)
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;
201 // Returning NULL indicates that we don't have context pointers available
202 *contextPointer = NULL;
206 void GetContextPointers(unw_cursor_t *cursor, unw_context_t *unwContext, KNONVOLATILE_CONTEXT_POINTERS *contextPointers)
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);
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);
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);
242 #error unsupported architecture
246 extern int g_common_signal_handler_context_locvar_offset;
248 BOOL PAL_VirtualUnwind(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextPointers)
251 unw_context_t unwContext;
254 DWORD64 curPc = CONTEXTGetPC(context);
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)
263 CONTEXT* signalContext = (CONTEXT*)(CONTEXTGetFP(context) + g_common_signal_handler_context_locvar_offset);
264 memcpy_s(context, sizeof(CONTEXT), signalContext, sizeof(CONTEXT));
270 if ((context->ContextFlags & CONTEXT_EXCEPTION_ACTIVE) != 0)
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);
282 #if !UNWIND_CONTEXT_IS_UCONTEXT_T
283 st = unw_getcontext(&unwContext);
290 WinContextToUnwindContext(context, &unwContext);
292 st = unw_init_local(&cursor, &unwContext);
298 #if !UNWIND_CONTEXT_IS_UCONTEXT_T
299 // Set the unwind context to the specified windows context
300 WinContextToUnwindCursor(context, &cursor);
303 st = unw_step(&cursor);
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)
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_
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_
327 // Update the passed in windows context to reflect the unwind
329 UnwindContextToWinContext(&cursor, context);
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)
340 CONTEXTSetPC(context, 0);
343 if (contextPointers != NULL)
345 GetContextPointers(&cursor, &unwContext, contextPointers);
351 #error don't know how to unwind on this platform
354 struct ExceptionRecords
356 CONTEXT ContextRecord;
357 EXCEPTION_RECORD ExceptionRecord;
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;
369 AllocateExceptionRecords
371 Allocate EXCEPTION_RECORD and CONTEXT structures for an exception.
373 exceptionRecord - output pointer to the allocated exception record
374 contextRecord - output pointer to the allocated context record
377 AllocateExceptionRecords(EXCEPTION_RECORD** exceptionRecord, CONTEXT** contextRecord)
379 ExceptionRecords* records;
380 if (posix_memalign((void**)&records, alignof(ExceptionRecords), sizeof(ExceptionRecords)) != 0)
388 bitmap = s_allocatedContextsBitmap;
389 index = __builtin_ffsl(~bitmap) - 1;
395 newBitmap = bitmap | ((size_t)1 << index);
397 while (__sync_val_compare_and_swap(&s_allocatedContextsBitmap, bitmap, newBitmap) != bitmap);
399 records = &s_fallbackContexts[index];
402 *contextRecord = &records->ContextRecord;
403 *exceptionRecord = &records->ExceptionRecord;
408 PAL_FreeExceptionRecords
410 Free EXCEPTION_RECORD and CONTEXT structures of an exception that were allocated by the
411 AllocateExceptionRecords.
413 exceptionRecord - exception record
414 contextRecord - context record
418 PAL_FreeExceptionRecords(IN EXCEPTION_RECORD *exceptionRecord, IN CONTEXT *contextRecord)
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]))
424 int index = records - &s_fallbackContexts[0];
425 __sync_fetch_and_and(&s_allocatedContextsBitmap, ~((size_t)1 << index));
438 ExceptionRecord - the Windows exception record to throw
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.
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.
450 __attribute__((noinline))
451 __attribute__((optnone))
453 RtlpRaiseException(EXCEPTION_RECORD *ExceptionRecord, CONTEXT *ContextRecord)
455 throw PAL_SEHException(ExceptionRecord, ContextRecord);
464 // no PAL_NORETURN, as callers must assume this can return for continuable exceptions.
465 __attribute__((noinline))
468 RaiseException(IN DWORD dwExceptionCode,
469 IN DWORD dwExceptionFlags,
470 IN DWORD nNumberOfArguments,
471 IN CONST ULONG_PTR *lpArguments)
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);
480 /* Validate parameters */
481 if (dwExceptionCode & RESERVED_SEH_BIT)
483 WARN("Exception code %08x has bit 28 set; clearing it.\n", dwExceptionCode);
484 dwExceptionCode ^= RESERVED_SEH_BIT;
487 if (nNumberOfArguments > EXCEPTION_MAXIMUM_PARAMETERS)
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;
495 CONTEXT *contextRecord;
496 EXCEPTION_RECORD *exceptionRecord;
497 AllocateExceptionRecords(&exceptionRecord, &contextRecord);
499 ZeroMemory(exceptionRecord, sizeof(EXCEPTION_RECORD));
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)
508 CopyMemory(exceptionRecord->ExceptionInformation, lpArguments,
509 nNumberOfArguments * sizeof(ULONG_PTR));
512 // Capture the context of RaiseException.
513 ZeroMemory(contextRecord, sizeof(CONTEXT));
514 contextRecord->ContextFlags = CONTEXT_FULL;
515 CONTEXT_CaptureContext(contextRecord);
517 // We have to unwind one level to get the actual context user code could be resumed at.
518 PAL_VirtualUnwind(contextRecord, NULL);
520 exceptionRecord->ExceptionAddress = (void *)CONTEXTGetPC(contextRecord);
522 RtlpRaiseException(exceptionRecord, contextRecord);
524 LOGEXIT("RaiseException returns\n");