Merge pull request #6281 from papaslavik/cse_sort_sz
[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 BOOL PAL_VirtualUnwind(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextPointers)
225 {
226     int st;
227     unw_context_t unwContext;
228     unw_cursor_t cursor;
229
230 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(_ARM64_) || defined(_ARM_)
231     DWORD64 curPc;
232 #endif
233
234     if ((context->ContextFlags & CONTEXT_EXCEPTION_ACTIVE) != 0)
235     {
236         // The current frame is a source of hardware exception. Due to the fact that
237         // we use the low level unwinder to unwind just one frame a time, the
238         // unwinder doesn't have the signal_frame flag set. So it doesn't
239         // know that it should not decrement the PC before looking up the unwind info.
240         // So we compensate it by incrementing the PC before passing it to the unwinder.
241         // Without it, the unwinder would not find unwind info if the hardware exception
242         // happened in the first instruction of a function.
243         CONTEXTSetPC(context, CONTEXTGetPC(context) + 1);
244     }
245
246 #if !UNWIND_CONTEXT_IS_UCONTEXT_T
247     st = unw_getcontext(&unwContext);
248     if (st < 0)
249     {
250         return FALSE;
251     }
252 #endif
253
254     WinContextToUnwindContext(context, &unwContext);
255
256     st = unw_init_local(&cursor, &unwContext);
257     if (st < 0)
258     {
259         return FALSE;
260     }
261
262 #if !UNWIND_CONTEXT_IS_UCONTEXT_T
263     // Set the unwind context to the specified windows context
264     WinContextToUnwindCursor(context, &cursor);
265 #endif
266
267 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__)  || defined(_ARM64_) || defined(_ARM_)
268     // FreeBSD, NetBSD and OSX appear to do two different things when unwinding
269     // 1: If it reaches where it cannot unwind anymore, say a 
270     // managed frame.  It wil return 0, but also update the $pc
271     // 2: If it unwinds all the way to _start it will return
272     // 0 from the step, but $pc will stay the same.
273     // The behaviour of libunwind from nongnu.org is to null the PC
274     // So we bank the original PC here, so we can compare it after
275     // the step
276     curPc = CONTEXTGetPC(context);
277 #endif
278
279     st = unw_step(&cursor);
280     if (st < 0)
281     {
282         return FALSE;
283     }
284
285     // Check if the frame we have unwound to is a frame that caused
286     // synchronous signal, like a hardware exception and record it
287     // in the context flags.
288     if (unw_is_signal_frame(&cursor) > 0)
289     {
290         context->ContextFlags |= CONTEXT_EXCEPTION_ACTIVE;
291 #if defined(_ARM_) || defined(_ARM64_)
292         context->ContextFlags &= ~CONTEXT_UNWOUND_TO_CALL;
293 #endif // _ARM_ || _ARM64_
294     }
295     else
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
303     // Update the passed in windows context to reflect the unwind
304     //
305     UnwindContextToWinContext(&cursor, context);
306 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__)  || defined(_ARM64_) || defined(_ARM_)
307     if (st == 0 && CONTEXTGetPC(context) == curPc)
308     {
309         CONTEXTSetPC(context, 0);
310     }
311 #endif
312
313     if (contextPointers != NULL)
314     {
315         GetContextPointers(&cursor, &unwContext, contextPointers);
316     }
317     return TRUE;
318 }
319
320 #else
321 #error don't know how to unwind on this platform
322 #endif
323
324 // These methods are only used on the AMD64 build
325 #ifdef _AMD64_
326 #ifdef HAVE_UNW_GET_ACCESSORS
327
328 static struct LibunwindCallbacksInfoType
329 {
330      CONTEXT *Context;
331      ReadMemoryWordCallback readMemCallback;
332 } LibunwindCallbacksInfo;
333
334 static int get_dyn_info_list_addr(unw_addr_space_t as, unw_word_t *dilap, void *arg)
335 {
336     return -UNW_ENOINFO;
337 }
338
339 static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp, int write, void *arg)
340 {
341     if (write)
342     {
343         ASSERT("Memory write must never be called by libunwind during stackwalk");
344         return -UNW_EINVAL;
345     }
346
347     // access_mem sometimes gets called by _UPT_find_proc_info, in such cases arg has a pointer to libunwind internal data
348     // returned by _UPT_create. It makes it impossible to use arg for passing readMemCallback. That's why we have to use global variable.
349     if (LibunwindCallbacksInfo.readMemCallback((SIZE_T)addr, (SIZE_T *)valp))
350     {
351         return UNW_ESUCCESS;
352     }
353     else 
354     {
355         return -UNW_EUNSPEC;
356     }
357 }
358
359 static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp, int write, void *arg)
360 {
361     if (write)
362     {
363         ASSERT("Register write must never be called by libunwind during stackwalk");
364         return -UNW_EREADONLYREG;
365     }
366
367     CONTEXT *winContext = LibunwindCallbacksInfo.Context;
368
369     switch (regnum) 
370     {
371 #if defined(_AMD64_)
372         case UNW_REG_IP:       *valp = (unw_word_t) winContext->Rip; break;
373         case UNW_REG_SP:       *valp = (unw_word_t) winContext->Rsp; break;
374         case UNW_X86_64_RBP:   *valp = (unw_word_t) winContext->Rbp; break;
375         case UNW_X86_64_RBX:   *valp = (unw_word_t) winContext->Rbx; break;
376         case UNW_X86_64_R12:   *valp = (unw_word_t) winContext->R12; break;
377         case UNW_X86_64_R13:   *valp = (unw_word_t) winContext->R13; break;
378         case UNW_X86_64_R14:   *valp = (unw_word_t) winContext->R14; break;
379         case UNW_X86_64_R15:   *valp = (unw_word_t) winContext->R15; break;
380 #elif defined(_ARM_)
381         case UNW_ARM_R13:      *valp = (unw_word_t) winContext->Sp; break;
382         case UNW_ARM_R14:      *valp = (unw_word_t) winContext->Lr; break;
383         case UNW_ARM_R15:      *valp = (unw_word_t) winContext->Pc; break;
384         case UNW_ARM_R4:       *valp = (unw_word_t) winContext->R4; break;
385         case UNW_ARM_R5:       *valp = (unw_word_t) winContext->R5; break;
386         case UNW_ARM_R6:       *valp = (unw_word_t) winContext->R6; break;
387         case UNW_ARM_R7:       *valp = (unw_word_t) winContext->R7; break;
388         case UNW_ARM_R8:       *valp = (unw_word_t) winContext->R8; break;
389         case UNW_ARM_R9:       *valp = (unw_word_t) winContext->R9; break;
390         case UNW_ARM_R10:      *valp = (unw_word_t) winContext->R10; break;
391         case UNW_ARM_R11:      *valp = (unw_word_t) winContext->R11; break;
392 #elif defined(_ARM64_)
393         case UNW_REG_IP:       *valp = (unw_word_t) winContext->Pc; break;
394         case UNW_REG_SP:       *valp = (unw_word_t) winContext->Sp; break;
395         case UNW_AARCH64_X29:  *valp = (unw_word_t) winContext->Fp; break;
396         case UNW_AARCH64_X30:  *valp = (unw_word_t) winContext->Lr; break;
397         case UNW_AARCH64_X19:  *valp = (unw_word_t) winContext->X19; break;
398         case UNW_AARCH64_X20:  *valp = (unw_word_t) winContext->X20; break;
399         case UNW_AARCH64_X21:  *valp = (unw_word_t) winContext->X21; break;
400         case UNW_AARCH64_X22:  *valp = (unw_word_t) winContext->X22; break;
401         case UNW_AARCH64_X23:  *valp = (unw_word_t) winContext->X23; break;
402         case UNW_AARCH64_X24:  *valp = (unw_word_t) winContext->X24; break;
403         case UNW_AARCH64_X25:  *valp = (unw_word_t) winContext->X25; break;
404         case UNW_AARCH64_X26:  *valp = (unw_word_t) winContext->X26; break;
405         case UNW_AARCH64_X27:  *valp = (unw_word_t) winContext->X27; break;
406         case UNW_AARCH64_X28:  *valp = (unw_word_t) winContext->X28; break;
407 #else
408 #error unsupported architecture
409 #endif
410         default:
411             ASSERT("Attempt to read an unknown register.");
412             return -UNW_EBADREG;
413     }
414     return UNW_ESUCCESS;
415 }
416
417 static int access_fpreg(unw_addr_space_t as, unw_regnum_t regnum, unw_fpreg_t *fpvalp, int write, void *arg)
418 {
419     ASSERT("Not supposed to be ever called");
420     return -UNW_EINVAL;
421 }
422
423 static int resume(unw_addr_space_t as, unw_cursor_t *cp, void *arg)
424 {
425     ASSERT("Not supposed to be ever called");
426     return -UNW_EINVAL;
427 }
428
429 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)
430 {
431     ASSERT("Not supposed to be ever called");
432     return -UNW_EINVAL;  
433 }
434
435 int find_proc_info(unw_addr_space_t as, 
436                    unw_word_t ip, unw_proc_info_t *pip,
437                    int need_unwind_info, void *arg)
438 {
439 #ifdef HAVE_LIBUNWIND_PTRACE
440     // UNIXTODO: libunwind RPM package on Fedora/CentOS/RedHat doesn't have libunwind-ptrace.so 
441     // and we can't use it from a shared library like libmscordaccore.so.
442     // That's why all calls to ptrace parts of libunwind ifdeffed out for now.
443     return _UPT_find_proc_info(as, ip, pip, need_unwind_info, arg);
444 #else    
445     return -UNW_EINVAL;
446 #endif    
447 }
448
449 void put_unwind_info(unw_addr_space_t as, unw_proc_info_t *pip, void *arg)
450 {
451 #ifdef HAVE_LIBUNWIND_PTRACE    
452     return _UPT_put_unwind_info(as, pip, arg);
453 #endif    
454 }
455
456 static unw_accessors_t unwind_accessors =
457 {
458     .find_proc_info = find_proc_info,
459     .put_unwind_info = put_unwind_info,
460     .get_dyn_info_list_addr = get_dyn_info_list_addr,
461     .access_mem = access_mem,
462     .access_reg = access_reg,
463     .access_fpreg = access_fpreg,
464     .resume = resume,
465     .get_proc_name = get_proc_name
466 };
467
468 BOOL PAL_VirtualUnwindOutOfProc(CONTEXT *context, 
469                                 KNONVOLATILE_CONTEXT_POINTERS *contextPointers, 
470                                 DWORD pid, 
471                                 ReadMemoryWordCallback readMemCallback)
472 {
473     // This function can be executed only by one thread at a time. 
474     // The reason for this is that we need to pass context and read mem function to libunwind callbacks
475     // but "arg" is already used by the pointer returned from _UPT_create(). 
476     // So we resort to using global variables and a lock.
477     struct Lock 
478     {
479         CRITICAL_SECTION cs;
480         Lock()
481         {        
482             // ctor of a static variable is a thread-safe way to initialize critical section exactly once (clang,gcc)
483             InitializeCriticalSection(&cs);
484         }
485     };
486     struct LockHolder
487     {
488         CRITICAL_SECTION *cs;
489         LockHolder(CRITICAL_SECTION *cs)
490         {
491             this->cs = cs;
492             EnterCriticalSection(cs);
493         }
494
495         ~LockHolder()
496         {
497             LeaveCriticalSection(cs);
498             cs = NULL;
499         }
500     };    
501     static Lock lock;
502     LockHolder lockHolder(&lock.cs);
503
504     int st;
505     unw_cursor_t cursor;
506     unw_addr_space_t addrSpace = 0;
507     void *libunwindUptPtr = NULL;
508     BOOL result = FALSE;
509
510     LibunwindCallbacksInfo.Context = context;
511     LibunwindCallbacksInfo.readMemCallback = readMemCallback;
512
513     addrSpace = unw_create_addr_space(&unwind_accessors, 0);
514 #ifdef HAVE_LIBUNWIND_PTRACE    
515     libunwindUptPtr = _UPT_create(pid);
516 #endif    
517     st = unw_init_remote(&cursor, addrSpace, libunwindUptPtr);
518     if (st < 0)
519     {
520         result = FALSE;
521         goto Exit;
522     }
523
524     st = unw_step(&cursor);
525     if (st < 0)
526     {
527         result = FALSE;
528         goto Exit;
529     }
530
531     UnwindContextToWinContext(&cursor, context);
532
533     if (contextPointers != NULL)
534     {
535         GetContextPointers(&cursor, NULL, contextPointers);
536     }
537     result = TRUE;
538
539 Exit:
540 #ifdef HAVE_LIBUNWIND_PTRACE
541     if (libunwindUptPtr != NULL) 
542     {
543         _UPT_destroy(libunwindUptPtr);
544     }
545 #endif    
546     if (addrSpace != 0) 
547     {
548         unw_destroy_addr_space(addrSpace);
549     }    
550     return result;
551 }
552 #else // HAVE_UNW_GET_ACCESSORS
553
554 BOOL PAL_VirtualUnwindOutOfProc(CONTEXT *context, 
555                                 KNONVOLATILE_CONTEXT_POINTERS *contextPointers, 
556                                 DWORD pid, 
557                                 ReadMemoryWordCallback readMemCallback)
558 {
559     //UNIXTODO: Implement for Mac flavor of libunwind
560     return FALSE;
561 }
562
563 #endif // !HAVE_UNW_GET_ACCESSORS
564 #endif // _AMD64_
565
566 struct ExceptionRecords
567 {
568     CONTEXT ContextRecord;
569     EXCEPTION_RECORD ExceptionRecord;
570 };
571
572 // Max number of fallback contexts that are used when malloc fails to allocate ExceptionRecords structure
573 static const int MaxFallbackContexts = sizeof(size_t) * 8;
574 // Array of fallback contexts
575 static ExceptionRecords s_fallbackContexts[MaxFallbackContexts];
576 // Bitmap used for allocating fallback contexts - bits set to 1 represent already allocated context.
577 static volatile size_t s_allocatedContextsBitmap = 0;
578
579 /*++
580 Function:
581     AllocateExceptionRecords
582
583     Allocate EXCEPTION_RECORD and CONTEXT structures for an exception.
584 Parameters:
585     exceptionRecord - output pointer to the allocated exception record
586     contextRecord - output pointer to the allocated context record
587 --*/
588 VOID
589 AllocateExceptionRecords(EXCEPTION_RECORD** exceptionRecord, CONTEXT** contextRecord)
590 {
591     ExceptionRecords* records;
592     if (posix_memalign((void**)&records, alignof(ExceptionRecords), sizeof(ExceptionRecords)) != 0)
593     {
594         size_t bitmap;
595         size_t newBitmap;
596         int index;
597
598         do
599         {
600             bitmap = s_allocatedContextsBitmap;
601             index = __builtin_ffsl(~bitmap) - 1;
602             if (index < 0)
603             {
604                 PROCAbort();
605             }
606
607             newBitmap = bitmap | ((size_t)1 << index);
608         }
609         while (__sync_val_compare_and_swap(&s_allocatedContextsBitmap, bitmap, newBitmap) != bitmap);
610
611         records = &s_fallbackContexts[index];
612     }
613
614     *contextRecord = &records->ContextRecord;
615     *exceptionRecord = &records->ExceptionRecord;
616 }
617
618 /*++
619 Function:
620     PAL_FreeExceptionRecords
621
622     Free EXCEPTION_RECORD and CONTEXT structures of an exception that were allocated by the
623     AllocateExceptionRecords.
624 Parameters:
625     exceptionRecord - exception record
626     contextRecord - context record
627 --*/
628 VOID
629 PALAPI
630 PAL_FreeExceptionRecords(IN EXCEPTION_RECORD *exceptionRecord, IN CONTEXT *contextRecord)
631 {
632     // Both records are allocated at once and the allocated memory starts at the contextRecord
633     ExceptionRecords* records = (ExceptionRecords*)contextRecord;
634     if ((records >= &s_fallbackContexts[0]) && (records < &s_fallbackContexts[MaxFallbackContexts]))
635     {
636         int index = records - &s_fallbackContexts[0];
637         __sync_fetch_and_and(&s_allocatedContextsBitmap, ~((size_t)1 << index));
638     }
639     else
640     {
641         free(contextRecord);
642     }
643 }
644
645 /*++
646 Function:
647     RtlpRaiseException
648
649 Parameters:
650     ExceptionRecord - the Windows exception record to throw
651
652 Note:
653     The name of this function and the name of the ExceptionRecord 
654     parameter is used in the sos lldb plugin code to read the exception
655     record. See coreclr\src\ToolBox\SOS\lldbplugin\services.cpp.
656
657     This function must not be inlined or optimized so the below PAL_VirtualUnwind
658     calls end up with RaiseException caller's context and so the above debugger 
659     code finds the function and ExceptionRecord parameter.
660 --*/
661 PAL_NORETURN
662 __attribute__((noinline))
663 __attribute__((optnone))
664 static void 
665 RtlpRaiseException(EXCEPTION_RECORD *ExceptionRecord, CONTEXT *ContextRecord)
666 {
667     throw PAL_SEHException(ExceptionRecord, ContextRecord);
668 }
669
670 /*++
671 Function:
672   RaiseException
673
674 See MSDN doc.
675 --*/
676 // no PAL_NORETURN, as callers must assume this can return for continuable exceptions.
677 __attribute__((noinline))
678 VOID
679 PALAPI
680 RaiseException(IN DWORD dwExceptionCode,
681                IN DWORD dwExceptionFlags,
682                IN DWORD nNumberOfArguments,
683                IN CONST ULONG_PTR *lpArguments)
684 {
685     // PERF_ENTRY_ONLY is used here because RaiseException may or may not
686     // return. We can not get latency data without PERF_EXIT. For this reason,
687     // PERF_ENTRY_ONLY is used to profile frequency only.
688     PERF_ENTRY_ONLY(RaiseException);
689     ENTRY("RaiseException(dwCode=%#x, dwFlags=%#x, nArgs=%u, lpArguments=%p)\n",
690           dwExceptionCode, dwExceptionFlags, nNumberOfArguments, lpArguments);
691
692     /* Validate parameters */
693     if (dwExceptionCode & RESERVED_SEH_BIT)
694     {
695         WARN("Exception code %08x has bit 28 set; clearing it.\n", dwExceptionCode);
696         dwExceptionCode ^= RESERVED_SEH_BIT;
697     }
698
699     if (nNumberOfArguments > EXCEPTION_MAXIMUM_PARAMETERS)
700     {
701         WARN("Number of arguments (%d) exceeds the limit "
702             "EXCEPTION_MAXIMUM_PARAMETERS (%d); ignoring extra parameters.\n",
703             nNumberOfArguments, EXCEPTION_MAXIMUM_PARAMETERS);
704         nNumberOfArguments = EXCEPTION_MAXIMUM_PARAMETERS;
705     }
706
707     CONTEXT *contextRecord;
708     EXCEPTION_RECORD *exceptionRecord;
709     AllocateExceptionRecords(&exceptionRecord, &contextRecord);
710
711     ZeroMemory(exceptionRecord, sizeof(EXCEPTION_RECORD));
712
713     exceptionRecord->ExceptionCode = dwExceptionCode;
714     exceptionRecord->ExceptionFlags = dwExceptionFlags;
715     exceptionRecord->ExceptionRecord = NULL;
716     exceptionRecord->ExceptionAddress = NULL; // will be set by RtlpRaiseException
717     exceptionRecord->NumberParameters = nNumberOfArguments;
718     if (nNumberOfArguments)
719     {
720         CopyMemory(exceptionRecord->ExceptionInformation, lpArguments,
721                    nNumberOfArguments * sizeof(ULONG_PTR));
722     }
723
724     // Capture the context of RaiseException.
725     ZeroMemory(contextRecord, sizeof(CONTEXT));
726     contextRecord->ContextFlags = CONTEXT_FULL;
727     CONTEXT_CaptureContext(contextRecord);
728
729     // We have to unwind one level to get the actual context user code could be resumed at.
730     PAL_VirtualUnwind(contextRecord, NULL);
731
732     exceptionRecord->ExceptionAddress = (void *)CONTEXTGetPC(contextRecord);
733
734     RtlpRaiseException(exceptionRecord, contextRecord);
735
736     LOGEXIT("RaiseException returns\n");
737 }