Merge pull request #7467 from dotnet-bot/from-tfs
[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 #ifdef __linux__
36 #ifdef HAVE_LIBUNWIND_PTRACE
37 #include <libunwind-ptrace.h>
38 #endif // HAVE_LIBUNWIND_PTRACE
39 #endif // __linux__    
40 #endif // HAVE_LIBUNWIND_H
41
42
43 //----------------------------------------------------------------------
44 // Virtual Unwinding
45 //----------------------------------------------------------------------
46
47 #if HAVE_LIBUNWIND_H
48 #if UNWIND_CONTEXT_IS_UCONTEXT_T
49
50 #if defined(_AMD64_)
51 #define ASSIGN_UNWIND_REGS \
52     ASSIGN_REG(Rip)        \
53     ASSIGN_REG(Rsp)        \
54     ASSIGN_REG(Rbp)        \
55     ASSIGN_REG(Rbx)        \
56     ASSIGN_REG(R12)        \
57     ASSIGN_REG(R13)        \
58     ASSIGN_REG(R14)        \
59     ASSIGN_REG(R15)
60 #elif defined(_ARM64_)
61 #define ASSIGN_UNWIND_REGS \
62     ASSIGN_REG(Pc)         \
63     ASSIGN_REG(Sp)         \
64     ASSIGN_REG(Fp)         \
65     ASSIGN_REG(Lr)         \
66     ASSIGN_REG(X19)        \
67     ASSIGN_REG(X20)        \
68     ASSIGN_REG(X21)        \
69     ASSIGN_REG(X22)        \
70     ASSIGN_REG(X23)        \
71     ASSIGN_REG(X24)        \
72     ASSIGN_REG(X25)        \
73     ASSIGN_REG(X26)        \
74     ASSIGN_REG(X27)        \
75     ASSIGN_REG(X28)
76 #else
77 #error unsupported architecture
78 #endif
79
80 static void WinContextToUnwindContext(CONTEXT *winContext, unw_context_t *unwContext)
81 {
82 #define ASSIGN_REG(reg) MCREG_##reg(unwContext->uc_mcontext) = winContext->reg;
83     ASSIGN_UNWIND_REGS
84 #undef ASSIGN_REG
85 }
86 #else
87 static void WinContextToUnwindContext(CONTEXT *winContext, unw_context_t *unwContext)
88 {
89 #if defined(_ARM_)    
90     // Assuming that unw_set_reg() on cursor will point the cursor to the
91     // supposed stack frame is dangerous for libunwind-arm in Linux.
92     // It is because libunwind's unw_cursor_t has other data structure
93     // initialized by unw_init_local(), which are not updated by
94     // unw_set_reg().
95     unwContext->regs[0] = 0;
96     unwContext->regs[1] = 0;
97     unwContext->regs[2] = 0;
98     unwContext->regs[3] = 0;
99     unwContext->regs[4] = winContext->R4;
100     unwContext->regs[5] = winContext->R5;
101     unwContext->regs[6] = winContext->R6;
102     unwContext->regs[7] = winContext->R7;
103     unwContext->regs[8] = winContext->R8;
104     unwContext->regs[9] = winContext->R9;
105     unwContext->regs[10] = winContext->R10;
106     unwContext->regs[11] = winContext->R11;
107     unwContext->regs[12] = 0;
108     unwContext->regs[13] = winContext->Sp;
109     unwContext->regs[14] = winContext->Lr;
110     unwContext->regs[15] = winContext->Pc;
111 #endif    
112
113
114 static void WinContextToUnwindCursor(CONTEXT *winContext, unw_cursor_t *cursor)
115 {
116 #if defined(_AMD64_)
117     unw_set_reg(cursor, UNW_REG_IP, winContext->Rip);
118     unw_set_reg(cursor, UNW_REG_SP, winContext->Rsp);
119     unw_set_reg(cursor, UNW_X86_64_RBP, winContext->Rbp);
120     unw_set_reg(cursor, UNW_X86_64_RBX, winContext->Rbx);
121     unw_set_reg(cursor, UNW_X86_64_R12, winContext->R12);
122     unw_set_reg(cursor, UNW_X86_64_R13, winContext->R13);
123     unw_set_reg(cursor, UNW_X86_64_R14, winContext->R14);
124     unw_set_reg(cursor, UNW_X86_64_R15, winContext->R15);
125 #endif
126 }
127 #endif
128
129 static void UnwindContextToWinContext(unw_cursor_t *cursor, CONTEXT *winContext)
130 {
131 #if defined(_AMD64_)
132     unw_get_reg(cursor, UNW_REG_IP, (unw_word_t *) &winContext->Rip);
133     unw_get_reg(cursor, UNW_REG_SP, (unw_word_t *) &winContext->Rsp);
134     unw_get_reg(cursor, UNW_X86_64_RBP, (unw_word_t *) &winContext->Rbp);
135     unw_get_reg(cursor, UNW_X86_64_RBX, (unw_word_t *) &winContext->Rbx);
136     unw_get_reg(cursor, UNW_X86_64_R12, (unw_word_t *) &winContext->R12);
137     unw_get_reg(cursor, UNW_X86_64_R13, (unw_word_t *) &winContext->R13);
138     unw_get_reg(cursor, UNW_X86_64_R14, (unw_word_t *) &winContext->R14);
139     unw_get_reg(cursor, UNW_X86_64_R15, (unw_word_t *) &winContext->R15);
140 #elif defined(_ARM_)
141     unw_get_reg(cursor, UNW_REG_SP, (unw_word_t *) &winContext->Sp);
142     unw_get_reg(cursor, UNW_REG_IP, (unw_word_t *) &winContext->Pc);
143     unw_get_reg(cursor, UNW_ARM_R14, (unw_word_t *) &winContext->Lr);
144     unw_get_reg(cursor, UNW_ARM_R4, (unw_word_t *) &winContext->R4);
145     unw_get_reg(cursor, UNW_ARM_R5, (unw_word_t *) &winContext->R5);
146     unw_get_reg(cursor, UNW_ARM_R6, (unw_word_t *) &winContext->R6);
147     unw_get_reg(cursor, UNW_ARM_R7, (unw_word_t *) &winContext->R7);
148     unw_get_reg(cursor, UNW_ARM_R8, (unw_word_t *) &winContext->R8);
149     unw_get_reg(cursor, UNW_ARM_R9, (unw_word_t *) &winContext->R9);
150     unw_get_reg(cursor, UNW_ARM_R10, (unw_word_t *) &winContext->R10);
151     unw_get_reg(cursor, UNW_ARM_R11, (unw_word_t *) &winContext->R11);
152 #elif defined(_ARM64_)
153     unw_get_reg(cursor, UNW_REG_IP, (unw_word_t *) &winContext->Pc);
154     unw_get_reg(cursor, UNW_REG_SP, (unw_word_t *) &winContext->Sp);
155     unw_get_reg(cursor, UNW_AARCH64_X29, (unw_word_t *) &winContext->Fp);
156     unw_get_reg(cursor, UNW_AARCH64_X30, (unw_word_t *) &winContext->Lr);
157     unw_get_reg(cursor, UNW_AARCH64_X19, (unw_word_t *) &winContext->X19);
158     unw_get_reg(cursor, UNW_AARCH64_X20, (unw_word_t *) &winContext->X20);
159     unw_get_reg(cursor, UNW_AARCH64_X21, (unw_word_t *) &winContext->X21);
160     unw_get_reg(cursor, UNW_AARCH64_X22, (unw_word_t *) &winContext->X22);
161     unw_get_reg(cursor, UNW_AARCH64_X23, (unw_word_t *) &winContext->X23);
162     unw_get_reg(cursor, UNW_AARCH64_X24, (unw_word_t *) &winContext->X24);
163     unw_get_reg(cursor, UNW_AARCH64_X25, (unw_word_t *) &winContext->X25);
164     unw_get_reg(cursor, UNW_AARCH64_X26, (unw_word_t *) &winContext->X26);
165     unw_get_reg(cursor, UNW_AARCH64_X27, (unw_word_t *) &winContext->X27);
166     unw_get_reg(cursor, UNW_AARCH64_X28, (unw_word_t *) &winContext->X28);
167 #else
168 #error unsupported architecture
169 #endif
170 }
171
172 static void GetContextPointer(unw_cursor_t *cursor, unw_context_t *unwContext, int reg, SIZE_T **contextPointer)
173 {
174 #if defined(HAVE_UNW_GET_SAVE_LOC)
175     unw_save_loc_t saveLoc;
176     unw_get_save_loc(cursor, reg, &saveLoc);
177     if (saveLoc.type == UNW_SLT_MEMORY)
178     {
179         SIZE_T *pLoc = (SIZE_T *)saveLoc.u.addr;
180         // Filter out fake save locations that point to unwContext 
181         if (unwContext == NULL || (pLoc < (SIZE_T *)unwContext) || ((SIZE_T *)(unwContext + 1) <= pLoc))
182             *contextPointer = (SIZE_T *)saveLoc.u.addr;
183     }
184 #else
185     // Returning NULL indicates that we don't have context pointers available
186     *contextPointer = NULL;
187 #endif
188 }
189
190 static void GetContextPointers(unw_cursor_t *cursor, unw_context_t *unwContext, KNONVOLATILE_CONTEXT_POINTERS *contextPointers)
191 {
192 #if defined(_AMD64_)
193     GetContextPointer(cursor, unwContext, UNW_X86_64_RBP, &contextPointers->Rbp);
194     GetContextPointer(cursor, unwContext, UNW_X86_64_RBX, &contextPointers->Rbx);
195     GetContextPointer(cursor, unwContext, UNW_X86_64_R12, &contextPointers->R12);
196     GetContextPointer(cursor, unwContext, UNW_X86_64_R13, &contextPointers->R13);
197     GetContextPointer(cursor, unwContext, UNW_X86_64_R14, &contextPointers->R14);
198     GetContextPointer(cursor, unwContext, UNW_X86_64_R15, &contextPointers->R15);
199 #elif defined(_ARM_)
200     GetContextPointer(cursor, unwContext, UNW_ARM_R4, &contextPointers->R4);
201     GetContextPointer(cursor, unwContext, UNW_ARM_R5, &contextPointers->R5);
202     GetContextPointer(cursor, unwContext, UNW_ARM_R6, &contextPointers->R6);
203     GetContextPointer(cursor, unwContext, UNW_ARM_R7, &contextPointers->R7);
204     GetContextPointer(cursor, unwContext, UNW_ARM_R8, &contextPointers->R8);
205     GetContextPointer(cursor, unwContext, UNW_ARM_R9, &contextPointers->R9);
206     GetContextPointer(cursor, unwContext, UNW_ARM_R10, &contextPointers->R10);
207     GetContextPointer(cursor, unwContext, UNW_ARM_R11, &contextPointers->R11);
208 #elif defined(_ARM64_)
209     GetContextPointer(cursor, unwContext, UNW_AARCH64_X19, &contextPointers->X19);
210     GetContextPointer(cursor, unwContext, UNW_AARCH64_X20, &contextPointers->X20);
211     GetContextPointer(cursor, unwContext, UNW_AARCH64_X21, &contextPointers->X21);
212     GetContextPointer(cursor, unwContext, UNW_AARCH64_X22, &contextPointers->X22);
213     GetContextPointer(cursor, unwContext, UNW_AARCH64_X23, &contextPointers->X23);
214     GetContextPointer(cursor, unwContext, UNW_AARCH64_X24, &contextPointers->X24);
215     GetContextPointer(cursor, unwContext, UNW_AARCH64_X25, &contextPointers->X25);
216     GetContextPointer(cursor, unwContext, UNW_AARCH64_X26, &contextPointers->X26);
217     GetContextPointer(cursor, unwContext, UNW_AARCH64_X27, &contextPointers->X27);
218     GetContextPointer(cursor, unwContext, UNW_AARCH64_X28, &contextPointers->X28);
219 #else
220 #error unsupported architecture
221 #endif
222 }
223
224 extern int g_common_signal_handler_context_locvar_offset;
225
226 BOOL PAL_VirtualUnwind(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextPointers)
227 {
228     int st;
229     unw_context_t unwContext;
230     unw_cursor_t cursor;
231
232     DWORD64 curPc = CONTEXTGetPC(context);
233
234 #ifndef __APPLE__
235     // Check if the PC is the return address from the SEHProcessException in the common_signal_handler. 
236     // If that's the case, extract its local variable containing the native_context_t of the hardware 
237     // exception and return that. This skips the hardware signal handler trampoline that the libunwind 
238     // cannot cross on some systems.
239     if ((void*)curPc == g_SEHProcessExceptionReturnAddress)
240     {
241         ULONG contextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT | CONTEXT_EXCEPTION_ACTIVE;
242
243     #if defined(_AMD64_)
244         contextFlags |= CONTEXT_XSTATE;
245     #endif
246         size_t nativeContext = *(size_t*)(CONTEXTGetFP(context) + g_common_signal_handler_context_locvar_offset);
247         CONTEXTFromNativeContext((const native_context_t *)nativeContext, context, contextFlags);
248
249         return TRUE;
250     }
251 #endif 
252
253     if ((context->ContextFlags & CONTEXT_EXCEPTION_ACTIVE) != 0)
254     {
255         // The current frame is a source of hardware exception. Due to the fact that
256         // we use the low level unwinder to unwind just one frame a time, the
257         // unwinder doesn't have the signal_frame flag set. So it doesn't
258         // know that it should not decrement the PC before looking up the unwind info.
259         // So we compensate it by incrementing the PC before passing it to the unwinder.
260         // Without it, the unwinder would not find unwind info if the hardware exception
261         // happened in the first instruction of a function.
262         CONTEXTSetPC(context, curPc + 1);
263     }
264
265 #if !UNWIND_CONTEXT_IS_UCONTEXT_T
266     st = unw_getcontext(&unwContext);
267     if (st < 0)
268     {
269         return FALSE;
270     }
271 #endif
272
273     WinContextToUnwindContext(context, &unwContext);
274
275     st = unw_init_local(&cursor, &unwContext);
276     if (st < 0)
277     {
278         return FALSE;
279     }
280
281 #if !UNWIND_CONTEXT_IS_UCONTEXT_T
282     // Set the unwind context to the specified windows context
283     WinContextToUnwindCursor(context, &cursor);
284 #endif
285
286     st = unw_step(&cursor);
287     if (st < 0)
288     {
289         return FALSE;
290     }
291
292     // Check if the frame we have unwound to is a frame that caused
293     // synchronous signal, like a hardware exception and record it
294     // in the context flags.
295     if (unw_is_signal_frame(&cursor) > 0)
296     {
297         context->ContextFlags |= CONTEXT_EXCEPTION_ACTIVE;
298 #if defined(_ARM_) || defined(_ARM64_)
299         context->ContextFlags &= ~CONTEXT_UNWOUND_TO_CALL;
300 #endif // _ARM_ || _ARM64_
301     }
302     else
303     {
304         context->ContextFlags &= ~CONTEXT_EXCEPTION_ACTIVE;
305 #if defined(_ARM_) || defined(_ARM64_)
306         context->ContextFlags |= CONTEXT_UNWOUND_TO_CALL;
307 #endif // _ARM_ || _ARM64_
308     }
309
310     // Update the passed in windows context to reflect the unwind
311     //
312     UnwindContextToWinContext(&cursor, context);
313
314     // FreeBSD, NetBSD, OSX and Alpine appear to do two different things when unwinding
315     // 1: If it reaches where it cannot unwind anymore, say a 
316     // managed frame.  It will return 0, but also update the $pc
317     // 2: If it unwinds all the way to _start it will return
318     // 0 from the step, but $pc will stay the same.
319     // So we detect that here and set the $pc to NULL in that case.
320     // This is the default behavior of the libunwind on Linux.
321     if (st == 0 && CONTEXTGetPC(context) == curPc)
322     {
323         CONTEXTSetPC(context, 0);
324     }
325
326     if (contextPointers != NULL)
327     {
328         GetContextPointers(&cursor, &unwContext, contextPointers);
329     }
330     return TRUE;
331 }
332
333 #else
334 #error don't know how to unwind on this platform
335 #endif
336
337 // These methods are only used on the AMD64 build
338 #ifdef _AMD64_
339 #ifdef HAVE_UNW_GET_ACCESSORS
340
341 static struct LibunwindCallbacksInfoType
342 {
343      CONTEXT *Context;
344      ReadMemoryWordCallback readMemCallback;
345 } LibunwindCallbacksInfo;
346
347 static int get_dyn_info_list_addr(unw_addr_space_t as, unw_word_t *dilap, void *arg)
348 {
349     return -UNW_ENOINFO;
350 }
351
352 static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp, int write, void *arg)
353 {
354     if (write)
355     {
356         ASSERT("Memory write must never be called by libunwind during stackwalk");
357         return -UNW_EINVAL;
358     }
359
360     // access_mem sometimes gets called by _UPT_find_proc_info, in such cases arg has a pointer to libunwind internal data
361     // returned by _UPT_create. It makes it impossible to use arg for passing readMemCallback. That's why we have to use global variable.
362     if (LibunwindCallbacksInfo.readMemCallback((SIZE_T)addr, (SIZE_T *)valp))
363     {
364         return UNW_ESUCCESS;
365     }
366     else 
367     {
368         return -UNW_EUNSPEC;
369     }
370 }
371
372 static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp, int write, void *arg)
373 {
374     if (write)
375     {
376         ASSERT("Register write must never be called by libunwind during stackwalk");
377         return -UNW_EREADONLYREG;
378     }
379
380     CONTEXT *winContext = LibunwindCallbacksInfo.Context;
381
382     switch (regnum) 
383     {
384 #if defined(_AMD64_)
385         case UNW_REG_IP:       *valp = (unw_word_t) winContext->Rip; break;
386         case UNW_REG_SP:       *valp = (unw_word_t) winContext->Rsp; break;
387         case UNW_X86_64_RBP:   *valp = (unw_word_t) winContext->Rbp; break;
388         case UNW_X86_64_RBX:   *valp = (unw_word_t) winContext->Rbx; break;
389         case UNW_X86_64_R12:   *valp = (unw_word_t) winContext->R12; break;
390         case UNW_X86_64_R13:   *valp = (unw_word_t) winContext->R13; break;
391         case UNW_X86_64_R14:   *valp = (unw_word_t) winContext->R14; break;
392         case UNW_X86_64_R15:   *valp = (unw_word_t) winContext->R15; break;
393 #elif defined(_ARM_)
394         case UNW_ARM_R13:      *valp = (unw_word_t) winContext->Sp; break;
395         case UNW_ARM_R14:      *valp = (unw_word_t) winContext->Lr; break;
396         case UNW_ARM_R15:      *valp = (unw_word_t) winContext->Pc; break;
397         case UNW_ARM_R4:       *valp = (unw_word_t) winContext->R4; break;
398         case UNW_ARM_R5:       *valp = (unw_word_t) winContext->R5; break;
399         case UNW_ARM_R6:       *valp = (unw_word_t) winContext->R6; break;
400         case UNW_ARM_R7:       *valp = (unw_word_t) winContext->R7; break;
401         case UNW_ARM_R8:       *valp = (unw_word_t) winContext->R8; break;
402         case UNW_ARM_R9:       *valp = (unw_word_t) winContext->R9; break;
403         case UNW_ARM_R10:      *valp = (unw_word_t) winContext->R10; break;
404         case UNW_ARM_R11:      *valp = (unw_word_t) winContext->R11; break;
405 #elif defined(_ARM64_)
406         case UNW_REG_IP:       *valp = (unw_word_t) winContext->Pc; break;
407         case UNW_REG_SP:       *valp = (unw_word_t) winContext->Sp; break;
408         case UNW_AARCH64_X29:  *valp = (unw_word_t) winContext->Fp; break;
409         case UNW_AARCH64_X30:  *valp = (unw_word_t) winContext->Lr; break;
410         case UNW_AARCH64_X19:  *valp = (unw_word_t) winContext->X19; break;
411         case UNW_AARCH64_X20:  *valp = (unw_word_t) winContext->X20; break;
412         case UNW_AARCH64_X21:  *valp = (unw_word_t) winContext->X21; break;
413         case UNW_AARCH64_X22:  *valp = (unw_word_t) winContext->X22; break;
414         case UNW_AARCH64_X23:  *valp = (unw_word_t) winContext->X23; break;
415         case UNW_AARCH64_X24:  *valp = (unw_word_t) winContext->X24; break;
416         case UNW_AARCH64_X25:  *valp = (unw_word_t) winContext->X25; break;
417         case UNW_AARCH64_X26:  *valp = (unw_word_t) winContext->X26; break;
418         case UNW_AARCH64_X27:  *valp = (unw_word_t) winContext->X27; break;
419         case UNW_AARCH64_X28:  *valp = (unw_word_t) winContext->X28; break;
420 #else
421 #error unsupported architecture
422 #endif
423         default:
424             ASSERT("Attempt to read an unknown register.");
425             return -UNW_EBADREG;
426     }
427     return UNW_ESUCCESS;
428 }
429
430 static int access_fpreg(unw_addr_space_t as, unw_regnum_t regnum, unw_fpreg_t *fpvalp, int write, void *arg)
431 {
432     ASSERT("Not supposed to be ever called");
433     return -UNW_EINVAL;
434 }
435
436 static int resume(unw_addr_space_t as, unw_cursor_t *cp, void *arg)
437 {
438     ASSERT("Not supposed to be ever called");
439     return -UNW_EINVAL;
440 }
441
442 static int get_proc_name(unw_addr_space_t as, unw_word_t addr, char *bufp, size_t buf_len, unw_word_t *offp, void *arg)
443 {
444     ASSERT("Not supposed to be ever called");
445     return -UNW_EINVAL;  
446 }
447
448 int find_proc_info(unw_addr_space_t as, 
449                    unw_word_t ip, unw_proc_info_t *pip,
450                    int need_unwind_info, void *arg)
451 {
452 #ifdef HAVE_LIBUNWIND_PTRACE
453     // UNIXTODO: libunwind RPM package on Fedora/CentOS/RedHat doesn't have libunwind-ptrace.so 
454     // and we can't use it from a shared library like libmscordaccore.so.
455     // That's why all calls to ptrace parts of libunwind ifdeffed out for now.
456     return _UPT_find_proc_info(as, ip, pip, need_unwind_info, arg);
457 #else    
458     return -UNW_EINVAL;
459 #endif    
460 }
461
462 void put_unwind_info(unw_addr_space_t as, unw_proc_info_t *pip, void *arg)
463 {
464 #ifdef HAVE_LIBUNWIND_PTRACE    
465     return _UPT_put_unwind_info(as, pip, arg);
466 #endif    
467 }
468
469 static unw_accessors_t unwind_accessors =
470 {
471     .find_proc_info = find_proc_info,
472     .put_unwind_info = put_unwind_info,
473     .get_dyn_info_list_addr = get_dyn_info_list_addr,
474     .access_mem = access_mem,
475     .access_reg = access_reg,
476     .access_fpreg = access_fpreg,
477     .resume = resume,
478     .get_proc_name = get_proc_name
479 };
480
481 BOOL PAL_VirtualUnwindOutOfProc(CONTEXT *context, 
482                                 KNONVOLATILE_CONTEXT_POINTERS *contextPointers, 
483                                 DWORD pid, 
484                                 ReadMemoryWordCallback readMemCallback)
485 {
486     // This function can be executed only by one thread at a time. 
487     // The reason for this is that we need to pass context and read mem function to libunwind callbacks
488     // but "arg" is already used by the pointer returned from _UPT_create(). 
489     // So we resort to using global variables and a lock.
490     struct Lock 
491     {
492         CRITICAL_SECTION cs;
493         Lock()
494         {        
495             // ctor of a static variable is a thread-safe way to initialize critical section exactly once (clang,gcc)
496             InitializeCriticalSection(&cs);
497         }
498     };
499     struct LockHolder
500     {
501         CRITICAL_SECTION *cs;
502         LockHolder(CRITICAL_SECTION *cs)
503         {
504             this->cs = cs;
505             EnterCriticalSection(cs);
506         }
507
508         ~LockHolder()
509         {
510             LeaveCriticalSection(cs);
511             cs = NULL;
512         }
513     };    
514     static Lock lock;
515     LockHolder lockHolder(&lock.cs);
516
517     int st;
518     unw_cursor_t cursor;
519     unw_addr_space_t addrSpace = 0;
520     void *libunwindUptPtr = NULL;
521     BOOL result = FALSE;
522
523     LibunwindCallbacksInfo.Context = context;
524     LibunwindCallbacksInfo.readMemCallback = readMemCallback;
525
526     addrSpace = unw_create_addr_space(&unwind_accessors, 0);
527 #ifdef HAVE_LIBUNWIND_PTRACE    
528     libunwindUptPtr = _UPT_create(pid);
529 #endif    
530     st = unw_init_remote(&cursor, addrSpace, libunwindUptPtr);
531     if (st < 0)
532     {
533         result = FALSE;
534         goto Exit;
535     }
536
537     st = unw_step(&cursor);
538     if (st < 0)
539     {
540         result = FALSE;
541         goto Exit;
542     }
543
544     UnwindContextToWinContext(&cursor, context);
545
546     if (contextPointers != NULL)
547     {
548         GetContextPointers(&cursor, NULL, contextPointers);
549     }
550     result = TRUE;
551
552 Exit:
553 #ifdef HAVE_LIBUNWIND_PTRACE
554     if (libunwindUptPtr != NULL) 
555     {
556         _UPT_destroy(libunwindUptPtr);
557     }
558 #endif    
559     if (addrSpace != 0) 
560     {
561         unw_destroy_addr_space(addrSpace);
562     }    
563     return result;
564 }
565 #else // HAVE_UNW_GET_ACCESSORS
566
567 BOOL PAL_VirtualUnwindOutOfProc(CONTEXT *context, 
568                                 KNONVOLATILE_CONTEXT_POINTERS *contextPointers, 
569                                 DWORD pid, 
570                                 ReadMemoryWordCallback readMemCallback)
571 {
572     //UNIXTODO: Implement for Mac flavor of libunwind
573     return FALSE;
574 }
575
576 #endif // !HAVE_UNW_GET_ACCESSORS
577 #endif // _AMD64_
578
579 struct ExceptionRecords
580 {
581     CONTEXT ContextRecord;
582     EXCEPTION_RECORD ExceptionRecord;
583 };
584
585 // Max number of fallback contexts that are used when malloc fails to allocate ExceptionRecords structure
586 static const int MaxFallbackContexts = sizeof(size_t) * 8;
587 // Array of fallback contexts
588 static ExceptionRecords s_fallbackContexts[MaxFallbackContexts];
589 // Bitmap used for allocating fallback contexts - bits set to 1 represent already allocated context.
590 static volatile size_t s_allocatedContextsBitmap = 0;
591
592 /*++
593 Function:
594     AllocateExceptionRecords
595
596     Allocate EXCEPTION_RECORD and CONTEXT structures for an exception.
597 Parameters:
598     exceptionRecord - output pointer to the allocated exception record
599     contextRecord - output pointer to the allocated context record
600 --*/
601 VOID
602 AllocateExceptionRecords(EXCEPTION_RECORD** exceptionRecord, CONTEXT** contextRecord)
603 {
604     ExceptionRecords* records;
605     if (posix_memalign((void**)&records, alignof(ExceptionRecords), sizeof(ExceptionRecords)) != 0)
606     {
607         size_t bitmap;
608         size_t newBitmap;
609         int index;
610
611         do
612         {
613             bitmap = s_allocatedContextsBitmap;
614             index = __builtin_ffsl(~bitmap) - 1;
615             if (index < 0)
616             {
617                 PROCAbort();
618             }
619
620             newBitmap = bitmap | ((size_t)1 << index);
621         }
622         while (__sync_val_compare_and_swap(&s_allocatedContextsBitmap, bitmap, newBitmap) != bitmap);
623
624         records = &s_fallbackContexts[index];
625     }
626
627     *contextRecord = &records->ContextRecord;
628     *exceptionRecord = &records->ExceptionRecord;
629 }
630
631 /*++
632 Function:
633     PAL_FreeExceptionRecords
634
635     Free EXCEPTION_RECORD and CONTEXT structures of an exception that were allocated by the
636     AllocateExceptionRecords.
637 Parameters:
638     exceptionRecord - exception record
639     contextRecord - context record
640 --*/
641 VOID
642 PALAPI
643 PAL_FreeExceptionRecords(IN EXCEPTION_RECORD *exceptionRecord, IN CONTEXT *contextRecord)
644 {
645     // Both records are allocated at once and the allocated memory starts at the contextRecord
646     ExceptionRecords* records = (ExceptionRecords*)contextRecord;
647     if ((records >= &s_fallbackContexts[0]) && (records < &s_fallbackContexts[MaxFallbackContexts]))
648     {
649         int index = records - &s_fallbackContexts[0];
650         __sync_fetch_and_and(&s_allocatedContextsBitmap, ~((size_t)1 << index));
651     }
652     else
653     {
654         free(contextRecord);
655     }
656 }
657
658 /*++
659 Function:
660     RtlpRaiseException
661
662 Parameters:
663     ExceptionRecord - the Windows exception record to throw
664
665 Note:
666     The name of this function and the name of the ExceptionRecord 
667     parameter is used in the sos lldb plugin code to read the exception
668     record. See coreclr\src\ToolBox\SOS\lldbplugin\services.cpp.
669
670     This function must not be inlined or optimized so the below PAL_VirtualUnwind
671     calls end up with RaiseException caller's context and so the above debugger 
672     code finds the function and ExceptionRecord parameter.
673 --*/
674 PAL_NORETURN
675 __attribute__((noinline))
676 __attribute__((optnone))
677 static void 
678 RtlpRaiseException(EXCEPTION_RECORD *ExceptionRecord, CONTEXT *ContextRecord)
679 {
680     throw PAL_SEHException(ExceptionRecord, ContextRecord);
681 }
682
683 /*++
684 Function:
685   RaiseException
686
687 See MSDN doc.
688 --*/
689 // no PAL_NORETURN, as callers must assume this can return for continuable exceptions.
690 __attribute__((noinline))
691 VOID
692 PALAPI
693 RaiseException(IN DWORD dwExceptionCode,
694                IN DWORD dwExceptionFlags,
695                IN DWORD nNumberOfArguments,
696                IN CONST ULONG_PTR *lpArguments)
697 {
698     // PERF_ENTRY_ONLY is used here because RaiseException may or may not
699     // return. We can not get latency data without PERF_EXIT. For this reason,
700     // PERF_ENTRY_ONLY is used to profile frequency only.
701     PERF_ENTRY_ONLY(RaiseException);
702     ENTRY("RaiseException(dwCode=%#x, dwFlags=%#x, nArgs=%u, lpArguments=%p)\n",
703           dwExceptionCode, dwExceptionFlags, nNumberOfArguments, lpArguments);
704
705     /* Validate parameters */
706     if (dwExceptionCode & RESERVED_SEH_BIT)
707     {
708         WARN("Exception code %08x has bit 28 set; clearing it.\n", dwExceptionCode);
709         dwExceptionCode ^= RESERVED_SEH_BIT;
710     }
711
712     if (nNumberOfArguments > EXCEPTION_MAXIMUM_PARAMETERS)
713     {
714         WARN("Number of arguments (%d) exceeds the limit "
715             "EXCEPTION_MAXIMUM_PARAMETERS (%d); ignoring extra parameters.\n",
716             nNumberOfArguments, EXCEPTION_MAXIMUM_PARAMETERS);
717         nNumberOfArguments = EXCEPTION_MAXIMUM_PARAMETERS;
718     }
719
720     CONTEXT *contextRecord;
721     EXCEPTION_RECORD *exceptionRecord;
722     AllocateExceptionRecords(&exceptionRecord, &contextRecord);
723
724     ZeroMemory(exceptionRecord, sizeof(EXCEPTION_RECORD));
725
726     exceptionRecord->ExceptionCode = dwExceptionCode;
727     exceptionRecord->ExceptionFlags = dwExceptionFlags;
728     exceptionRecord->ExceptionRecord = NULL;
729     exceptionRecord->ExceptionAddress = NULL; // will be set by RtlpRaiseException
730     exceptionRecord->NumberParameters = nNumberOfArguments;
731     if (nNumberOfArguments)
732     {
733         CopyMemory(exceptionRecord->ExceptionInformation, lpArguments,
734                    nNumberOfArguments * sizeof(ULONG_PTR));
735     }
736
737     // Capture the context of RaiseException.
738     ZeroMemory(contextRecord, sizeof(CONTEXT));
739     contextRecord->ContextFlags = CONTEXT_FULL;
740     CONTEXT_CaptureContext(contextRecord);
741
742     // We have to unwind one level to get the actual context user code could be resumed at.
743     PAL_VirtualUnwind(contextRecord, NULL);
744
745     exceptionRecord->ExceptionAddress = (void *)CONTEXTGetPC(contextRecord);
746
747     RtlpRaiseException(exceptionRecord, contextRecord);
748
749     LOGEXIT("RaiseException returns\n");
750 }