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