Merge pull request #3376 from kyulee1/fixcast
[platform/upstream/coreclr.git] / src / pal / src / exception / machexception.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 Module Name:
8
9     machexception.cpp
10
11 Abstract:
12
13     Implementation of MACH exception API functions.
14
15 --*/
16
17 #include "pal/thread.hpp"
18 #include "pal/seh.hpp"
19 #include "pal/palinternal.h"
20 #if HAVE_MACH_EXCEPTIONS
21 #include "machexception.h"
22 #include "pal/dbgmsg.h"
23 #include "pal/critsect.h"
24 #include "pal/debug.h"
25 #include "pal/init.h"
26 #include "pal/utils.h"
27 #include "pal/context.h"
28 #include "pal/malloc.hpp"
29 #include "pal/process.h"
30 #include "pal/virtual.h"
31 #include "pal/map.hpp"
32
33 #include "machmessage.h"
34
35 #include <errno.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <pthread.h>
39 #include <dlfcn.h>
40 #include <mach-o/loader.h>
41
42 using namespace CorUnix;
43
44 SET_DEFAULT_DEBUG_CHANNEL(EXCEPT);
45
46 // The port we use to handle exceptions and to set the thread context
47 mach_port_t s_ExceptionPort;
48
49 static BOOL s_DebugInitialized = FALSE;
50
51 static const char * PAL_MACH_EXCEPTION_MODE = "PAL_MachExceptionMode";
52
53 // This struct is used to track the threads that need to have an exception forwarded
54 // to the next thread level port in the chain (if exists). An entry is added by the
55 // faulting sending a special message to the exception thread which saves it on an
56 // list that is searched when the restarted exception notification is received again.
57 struct ForwardedException
58 {
59     ForwardedException *m_next;
60     thread_act_t Thread;
61     exception_type_t ExceptionType;
62     CPalThread *PalThread;
63 };
64
65 // The singly linked list and enumerator for the ForwardException struct
66 struct ForwardedExceptionList
67 {
68 private:
69     ForwardedException *m_head;
70     ForwardedException *m_previous;
71
72 public:
73     ForwardedException *Current;
74
75     ForwardedExceptionList()
76     {
77         m_head = NULL;
78         MoveFirst();
79     }
80
81     void MoveFirst()
82     {
83         Current = m_head;
84         m_previous = NULL;
85     }
86
87     bool IsEOL()
88     {
89         return Current == NULL;
90     }
91
92     void MoveNext()
93     {
94         m_previous = Current;
95         Current = Current->m_next;
96     }
97
98     void Add(ForwardedException *item)
99     {
100         item->m_next = m_head;
101         m_head = item;
102     }
103
104     void Delete()
105     {
106         if (m_previous == NULL)
107         {
108             m_head = Current->m_next;
109         }
110         else
111         {
112             m_previous->m_next = Current->m_next;
113         }
114         free(Current);
115
116         Current = m_head;
117         m_previous = NULL;
118     }
119 };
120
121 enum MachExceptionMode
122 {
123     // special value to indicate we've not initialized yet
124     MachException_Uninitialized     = -1,
125
126     // These can be combined with bitwise OR to incrementally turn off
127     // functionality for diagnostics purposes.
128     //
129     // In practice, the following values are probably useful:
130     //   1: Don't turn illegal instructions into SEH exceptions.
131     //      On Intel, stack misalignment usually shows up as an
132     //      illegal instruction.  PAL client code shouldn't
133     //      expect to see any of these, so this option should
134     //      always be safe to set.
135     //   2: Don't listen for breakpoint exceptions.  This makes an
136     //      SEH-based debugger (i.e., managed debugger) unusable,
137     //      but you may need this option if you find that native
138     //      breakpoints you set in PAL-dependent code don't work
139     //      (causing hangs or crashes in the native debugger).
140     //   3: Combination of the above.
141     //      This is the typical setting for development
142     //      (unless you're working on the managed debugger).
143     //   7: In addition to the above, don't turn bad accesses and
144     //      arithmetic exceptions into SEH.
145     //      This is the typical setting for stress.
146     MachException_SuppressIllegal   = 1,
147     MachException_SuppressDebugging = 2,
148     MachException_SuppressManaged   = 4,
149
150     // Default value to use if environment variable not set.
151     MachException_Default           = 0,
152 };
153
154 /*++
155 Function :
156     GetExceptionMask()
157
158     Returns the mach exception mask for the exceptions to hook for a thread.
159
160 Return value :
161     mach exception mask
162 --*/
163 static 
164 exception_mask_t 
165 GetExceptionMask()
166 {
167     static MachExceptionMode exMode = MachException_Uninitialized;
168
169     if (exMode == MachException_Uninitialized)
170     {
171         exMode = MachException_Default;
172
173         const char * exceptionSettings = getenv(PAL_MACH_EXCEPTION_MODE);
174         if (exceptionSettings)
175         {
176             exMode = (MachExceptionMode)atoi(exceptionSettings);
177         }
178         else
179         {
180             if (PAL_IsDebuggerPresent())
181             {
182                 exMode = MachException_SuppressDebugging;
183             }
184         }
185     }
186
187     exception_mask_t machExceptionMask = 0;
188     if (!(exMode & MachException_SuppressIllegal))
189     {
190         machExceptionMask |= PAL_EXC_ILLEGAL_MASK;
191     }
192     if (!(exMode & MachException_SuppressDebugging))
193     {
194 #ifdef FEATURE_PAL_SXS
195         // Always hook exception ports for breakpoint exceptions.
196         // The reason is that we don't know when a managed debugger
197         // will attach, so we have to be prepared.  We don't want
198         // to later go through the thread list and hook exception
199         // ports for exactly those threads that currently are in
200         // this PAL.
201         machExceptionMask |= PAL_EXC_DEBUGGING_MASK;
202 #else // FEATURE_PAL_SXS
203         if (s_DebugInitialized)
204         {
205             machExceptionMask |= PAL_EXC_DEBUGGING_MASK;
206         }
207 #endif // FEATURE_PAL_SXS
208     }
209     if (!(exMode & MachException_SuppressManaged))
210     {
211         machExceptionMask |= PAL_EXC_MANAGED_MASK;
212     }
213
214     return machExceptionMask;
215 }
216
217 #ifdef FEATURE_PAL_SXS
218
219 /*++
220 Function :
221     CPalThread::EnableMachExceptions 
222
223     Hook Mach exceptions, i.e., call thread_swap_exception_ports
224     to replace the thread's current exception ports with our own.
225     The previously active exception ports are saved.  Called when
226     this thread enters a region of code that depends on this PAL.
227
228 Return value :
229     ERROR_SUCCESS, if enabling succeeded
230     an error code, otherwise
231 --*/
232 PAL_ERROR CorUnix::CPalThread::EnableMachExceptions()
233 {
234     TRACE("%08X: Enter()\n", (unsigned int)(size_t)this);
235
236     exception_mask_t machExceptionMask = GetExceptionMask();
237     if (machExceptionMask != 0)
238     {
239 #ifdef _DEBUG
240         // verify that the arrays we've allocated to hold saved exception ports
241         // are the right size.
242         exception_mask_t countBits = PAL_EXC_ALL_MASK;
243         countBits = ((countBits & 0xAAAAAAAA) >>  1) + (countBits & 0x55555555);
244         countBits = ((countBits & 0xCCCCCCCC) >>  2) + (countBits & 0x33333333);
245         countBits = ((countBits & 0xF0F0F0F0) >>  4) + (countBits & 0x0F0F0F0F);
246         countBits = ((countBits & 0xFF00FF00) >>  8) + (countBits & 0x00FF00FF);
247         countBits = ((countBits & 0xFFFF0000) >> 16) + (countBits & 0x0000FFFF);
248         if (countBits != static_cast<exception_mask_t>(CThreadMachExceptionHandlers::s_nPortsMax))
249         {
250             ASSERT("s_nPortsMax is %u, but needs to be %u\n",
251                    CThreadMachExceptionHandlers::s_nPortsMax, countBits);
252         }
253 #endif // _DEBUG
254
255         NONPAL_TRACE("Enabling handlers for thread %08x exception mask %08x exception port %08x\n", 
256             GetMachPortSelf(), machExceptionMask, s_ExceptionPort);
257
258         CThreadMachExceptionHandlers *pSavedHandlers = GetSavedMachHandlers();
259
260         // Swap current handlers into temporary storage first. That's because it's possible (even likely) that
261         // some or all of the handlers might still be ours. In those cases we don't want to overwrite the
262         // chain-back entries with these useless self-references.
263         kern_return_t machret;
264         kern_return_t machretDeallocate;
265         thread_port_t thread = mach_thread_self();
266
267         machret = thread_swap_exception_ports(
268             thread,
269             machExceptionMask,
270             s_ExceptionPort,
271             EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES,
272             THREAD_STATE_NONE,
273             pSavedHandlers->m_masks,
274             &pSavedHandlers->m_nPorts,
275             pSavedHandlers->m_handlers,
276             pSavedHandlers->m_behaviors,
277             pSavedHandlers->m_flavors);
278
279         machretDeallocate = mach_port_deallocate(mach_task_self(), thread);
280         CHECK_MACH("mach_port_deallocate", machretDeallocate);
281
282         if (machret != KERN_SUCCESS)
283         {
284             ASSERT("thread_swap_exception_ports failed: %d %s\n", machret, mach_error_string(machret));
285             return UTIL_MachErrorToPalError(machret);
286         }
287
288 #ifdef _DEBUG
289         NONPAL_TRACE("EnableMachExceptions: THREAD PORT count %d\n", pSavedHandlers->m_nPorts);
290         for (mach_msg_type_number_t i = 0; i < pSavedHandlers->m_nPorts; i++)
291         {
292             _ASSERTE(pSavedHandlers->m_handlers[i] != s_ExceptionPort);
293             NONPAL_TRACE("EnableMachExceptions: THREAD PORT mask %08x handler: %08x behavior %08x flavor %u\n",
294                 pSavedHandlers->m_masks[i],
295                 pSavedHandlers->m_handlers[i],
296                 pSavedHandlers->m_behaviors[i],
297                 pSavedHandlers->m_flavors[i]);
298         }
299 #endif // _DEBUG
300     }
301     return ERROR_SUCCESS;
302 }
303
304 /*++
305 Function :
306     CPalThread::DisableMachExceptions
307
308     Unhook Mach exceptions, i.e., call thread_set_exception_ports
309     to restore the thread's exception ports with those we saved
310     in EnableMachExceptions.  Called when this thread leaves a
311     region of code that depends on this PAL.
312
313 Return value :
314     ERROR_SUCCESS, if disabling succeeded
315     an error code, otherwise
316 --*/
317 PAL_ERROR CorUnix::CPalThread::DisableMachExceptions()
318 {
319     TRACE("%08X: Leave()\n", (unsigned int)(size_t)this);
320
321     PAL_ERROR palError = NO_ERROR;
322     
323     // We only store exceptions when we're installing exceptions.
324     if (0 == GetExceptionMask())
325         return palError;
326     
327     // Get the handlers to restore.
328     CThreadMachExceptionHandlers *savedPorts = GetSavedMachHandlers();
329
330     kern_return_t MachRet = KERN_SUCCESS;
331     for (int i = 0; i < savedPorts->m_nPorts; i++)
332     {
333         // If no handler was ever set, thread_swap_exception_ports returns
334         // MACH_PORT_NULL for the handler and zero values for behavior
335         // and flavor.  Unfortunately, the latter are invalid even for
336         // MACH_PORT_NULL when you use thread_set_exception_ports.
337         exception_behavior_t behavior = savedPorts->m_behaviors[i] ? savedPorts->m_behaviors[i] : EXCEPTION_DEFAULT;
338         thread_state_flavor_t flavor = savedPorts->m_flavors[i] ? savedPorts->m_flavors[i] : MACHINE_THREAD_STATE;
339         thread_port_t thread = mach_thread_self();
340         MachRet = thread_set_exception_ports(thread,
341                                              savedPorts->m_masks[i],
342                                              savedPorts->m_handlers[i],
343                                              behavior,
344                                              flavor);
345
346         kern_return_t MachRetDeallocate = mach_port_deallocate(mach_task_self(), thread);
347         CHECK_MACH("mach_port_deallocate", MachRetDeallocate);
348                                              
349         if (MachRet != KERN_SUCCESS)
350             break;
351     }
352     
353     if (MachRet != KERN_SUCCESS)
354     {
355         ASSERT("thread_set_exception_ports failed: %d\n", MachRet);
356         palError = UTIL_MachErrorToPalError(MachRet);
357     }
358
359     return palError;
360 }
361
362 #else // FEATURE_PAL_SXS
363
364 /*++
365 Function :
366     SEHEnableMachExceptions 
367
368     Enable SEH-related stuff related to mach exceptions
369
370     (no parameters)
371
372 Return value :
373     TRUE  if enabling succeeded
374     FALSE otherwise
375 --*/
376 BOOL SEHEnableMachExceptions()
377 {
378     exception_mask_t machExceptionMask = GetExceptionMask();
379     if (machExceptionMask != 0)
380     {
381         kern_return_t MachRet;
382         MachRet = task_set_exception_ports(mach_task_self(),
383                                            machExceptionMask,
384                                            s_ExceptionPort,
385                                            EXCEPTION_DEFAULT,
386                                            MACHINE_THREAD_STATE);
387
388         if (MachRet != KERN_SUCCESS)
389         {
390             ASSERT("task_set_exception_ports failed: %d\n", MachRet);
391             UTIL_SetLastErrorFromMach(MachRet);
392             return FALSE;
393         }
394     }
395     return TRUE;
396 }
397
398 /*++
399 Function :
400     SEHDisableMachExceptions
401
402     Disable SEH-related stuff related to mach exceptions
403
404     (no parameters)
405
406 Return value :
407     TRUE  if enabling succeeded
408     FALSE otherwise
409 --*/
410 BOOL SEHDisableMachExceptions()
411 {
412     exception_mask_t machExceptionMask = GetExceptionMask();
413     if (machExceptionMask != 0)
414     {
415         kern_return_t MachRet;
416         MachRet = task_set_exception_ports(mach_task_self(),
417                                            machExceptionMask,
418                                            MACH_PORT_NULL,
419                                            EXCEPTION_DEFAULT,
420                                            MACHINE_THREAD_STATE);
421
422         if (MachRet != KERN_SUCCESS)
423         {
424             ASSERT("task_set_exception_ports failed: %d\n", MachRet);
425             UTIL_SetLastErrorFromMach(MachRet);
426             return FALSE;
427         }
428     }
429     return TRUE;
430 }
431
432 #endif // FEATURE_PAL_SXS
433
434 #if !defined(_AMD64_)
435 extern "C"
436 void PAL_DispatchException(PCONTEXT pContext, PEXCEPTION_RECORD pExRecord, MachExceptionInfo *pMachExceptionInfo)
437 #else // defined(_AMD64_)
438
439 // Since HijackFaultingThread pushed the context, exception record and info on the stack, we need to adjust the 
440 // signature of PAL_DispatchException such that the corresponding arguments are considered to be on the stack 
441 // per GCC64 calling convention rules. Hence, the first 6 dummy arguments (corresponding to RDI, RSI, RDX,RCX, R8, R9).
442 extern "C"
443 void PAL_DispatchException(DWORD64 dwRDI, DWORD64 dwRSI, DWORD64 dwRDX, DWORD64 dwRCX, DWORD64 dwR8, DWORD64 dwR9, PCONTEXT pContext, PEXCEPTION_RECORD pExRecord, MachExceptionInfo *pMachExceptionInfo)
444 #endif // !defined(_AMD64_)
445 {
446     CPalThread *pThread = InternalGetCurrentThread();
447
448 #if FEATURE_PAL_SXS
449     if (!pThread->IsInPal())
450     {
451         // It's now possible to observe system exceptions in code running outside the PAL (as the result of a
452         // p/invoke since we no longer revert our Mach exception ports in this case). In that scenario we need
453         // to re-enter the PAL now as the exception signals the end of the p/invoke.
454         PAL_Reenter(PAL_BoundaryBottom);
455     }
456 #endif // FEATURE_PAL_SXS
457
458     EXCEPTION_POINTERS pointers;
459     pointers.ExceptionRecord = pExRecord;
460     pointers.ContextRecord = pContext;
461
462     TRACE("PAL_DispatchException(EC %08x EA %p)\n", pExRecord->ExceptionCode, pExRecord->ExceptionAddress);
463     SEHProcessException(&pointers);
464
465     // Send the forward request to the exception thread to process
466     MachMessage sSendMessage;
467     sSendMessage.SendForwardException(s_ExceptionPort, pMachExceptionInfo, pThread);
468
469     // Spin wait until this thread is hijacked by the exception thread
470     while (TRUE)
471     {
472         sched_yield();
473     }
474 }
475
476 #if defined(_X86_) || defined(_AMD64_)
477 extern "C" void PAL_DispatchExceptionWrapper();
478 extern "C" int PAL_DispatchExceptionReturnOffset;
479 #endif // _X86_ || _AMD64_
480
481 /*++
482 Function :
483     BuildExceptionRecord
484
485     Sets up up an ExceptionRecord from an exception message
486
487 Parameters :
488     exceptionInfo - exception info to build the exception record
489     pExceptionRecord - exception record to setup
490 */
491 static 
492 void 
493 BuildExceptionRecord(
494     MachExceptionInfo& exceptionInfo,               // [in] exception info
495     EXCEPTION_RECORD *pExceptionRecord)             // [out] Used to return exception parameters
496 {
497     memset(pExceptionRecord, 0, sizeof(EXCEPTION_RECORD));
498
499     DWORD exceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION;
500
501     switch(exceptionInfo.ExceptionType)
502     {
503     // Could not access memory. subcode contains the bad memory address. 
504     case EXC_BAD_ACCESS:
505         if (exceptionInfo.SubcodeCount != 2)
506         {
507             NONPAL_RETAIL_ASSERT("Got an unexpected subcode");
508             exceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION; 
509         }
510         else
511         {
512             exceptionCode = EXCEPTION_ACCESS_VIOLATION;
513
514             pExceptionRecord->NumberParameters = 2;
515             pExceptionRecord->ExceptionInformation[0] = 0;
516             pExceptionRecord->ExceptionInformation[1] = exceptionInfo.Subcodes[1];
517             NONPAL_TRACE("subcodes[1] = %llx\n", exceptionInfo.Subcodes[1]);
518         }
519         break;
520
521     // Instruction failed. Illegal or undefined instruction or operand. 
522     case EXC_BAD_INSTRUCTION :
523         // TODO: Identify privileged instruction. Need to get the thread state and read the machine code. May
524         // be better to do this in the place that calls SEHProcessException, similar to how it's done on Linux.
525         exceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION; 
526         break;
527
528     // Arithmetic exception; exact nature of exception is in subcode field. 
529     case EXC_ARITHMETIC:
530         if (exceptionInfo.SubcodeCount != 2)
531         {
532             NONPAL_RETAIL_ASSERT("Got an unexpected subcode");
533             exceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION; 
534         }
535         else
536         {
537             switch (exceptionInfo.Subcodes[0])
538             {
539 #if defined(_X86_) || defined(_AMD64_)
540                 case EXC_I386_DIV:
541                     exceptionCode = EXCEPTION_INT_DIVIDE_BY_ZERO;
542                     break;
543                 case EXC_I386_INTO:
544                     exceptionCode = EXCEPTION_INT_OVERFLOW;
545                     break;
546                 case EXC_I386_EXTOVR:
547                     exceptionCode = EXCEPTION_FLT_OVERFLOW;
548                     break;
549                 case EXC_I386_BOUND:
550                     exceptionCode = EXCEPTION_ARRAY_BOUNDS_EXCEEDED;
551                     break;
552 #else
553 #error Trap code to exception mapping not defined for this architecture
554 #endif
555                 default:
556                     exceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION;
557                     break;
558             }
559         }
560         break;
561
562     case EXC_SOFTWARE:
563 #if defined(_X86_) || defined(_AMD64_)
564         exceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION;
565         break;
566 #else
567 #error Trap code to exception mapping not defined for this architecture
568 #endif
569
570     // Trace, breakpoint, etc. Details in subcode field. 
571     case EXC_BREAKPOINT:
572 #if defined(_X86_) || defined(_AMD64_)
573         if (exceptionInfo.Subcodes[0] == EXC_I386_SGL)
574         {
575             exceptionCode = EXCEPTION_SINGLE_STEP;
576         }
577         else if (exceptionInfo.Subcodes[0] == EXC_I386_BPT)
578         {
579             exceptionCode = EXCEPTION_BREAKPOINT;
580         }
581 #else
582 #error Trap code to exception mapping not defined for this architecture
583 #endif
584         else
585         {
586             WARN("unexpected subcode %d for EXC_BREAKPOINT", exceptionInfo.Subcodes[0]);
587             exceptionCode = EXCEPTION_BREAKPOINT;
588         }
589         break;
590
591
592     // System call requested. Details in subcode field. 
593     case EXC_SYSCALL:
594         exceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION; 
595         break;
596
597     // System call with a number in the Mach call range requested. Details in subcode field. 
598     case EXC_MACH_SYSCALL:
599         exceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION; 
600         break;
601
602     default:
603         NONPAL_ASSERT("Got unknown trap code %d\n", exceptionInfo.ExceptionType);
604         break;
605     }
606
607     pExceptionRecord->ExceptionCode = exceptionCode;
608 }
609
610 #ifdef _DEBUG
611 const char *
612 GetExceptionString(
613    exception_type_t exception
614 )
615 {
616     switch(exception)
617     {
618     case EXC_BAD_ACCESS:
619         return "EXC_BAD_ACCESS";
620
621     case EXC_BAD_INSTRUCTION:
622         return "EXC_BAD_INSTRUCTION";
623
624     case EXC_ARITHMETIC:
625         return "EXC_ARITHMETIC";
626
627     case EXC_SOFTWARE:
628         return "EXC_SOFTWARE";
629
630     case EXC_BREAKPOINT:
631         return "EXC_BREAKPOINT";
632
633     case EXC_SYSCALL:
634         return "EXC_SYSCALL";
635
636     case EXC_MACH_SYSCALL:
637         return "EXC_MACH_SYSCALL";
638
639     default:
640         NONPAL_ASSERT("Got unknown trap code %d\n", exception);
641         break;
642     }
643     return "INVALID CODE";
644 }
645 #endif // _DEBUG
646
647 /*++
648 Function :
649     HijackFaultingThread
650
651     Sets the faulting thread up to return to PAL_DispatchException with an
652     ExceptionRecord and thread CONTEXT.
653
654 Parameters:
655     thread - thread the exception happened
656     task - task the exception happened
657     message - exception message
658
659 Return value :
660     None
661 --*/
662 static
663 void
664 HijackFaultingThread(
665     mach_port_t thread,             // [in] thread the exception happened on
666     mach_port_t task,               // [in] task the exception happened on
667     MachMessage& message)           // [in] exception message
668 {
669     MachExceptionInfo exceptionInfo(thread, message);
670     EXCEPTION_RECORD exceptionRecord;
671     CONTEXT threadContext;
672     kern_return_t machret;
673
674     // Fill in the exception record from the exception info
675     BuildExceptionRecord(exceptionInfo, &exceptionRecord);
676     
677 #ifdef _X86_
678     threadContext.ContextFlags = CONTEXT_FLOATING_POINT | CONTEXT_EXTENDED_REGISTERS;
679 #else
680     threadContext.ContextFlags = CONTEXT_FLOATING_POINT;
681 #endif
682     CONTEXT_GetThreadContextFromThreadState(x86_FLOAT_STATE, (thread_state_t)&exceptionInfo.FloatState, &threadContext);
683
684     threadContext.ContextFlags |= CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS;
685     CONTEXT_GetThreadContextFromThreadState(x86_THREAD_STATE, (thread_state_t)&exceptionInfo.ThreadState, &threadContext);
686
687 #if defined(CORECLR) && (defined(_X86_) || defined(_AMD64_))
688     // For CoreCLR we look more deeply at access violations to determine whether they're the result of a stack
689     // overflow. If so we'll terminate the process immediately (the current default policy of the CoreCLR EE).
690     // Otherwise we'll either A/V ourselves trying to set up the SEH exception record and context on the
691     // target thread's stack (unlike Windows there's no extra stack reservation to guarantee this can be done)
692     // or, and this the case we're trying to avoid, it's possible we'll succeed and the runtime will go ahead
693     // and process the SO like it was a simple AV. Since the runtime doesn't currently implement stack probing
694     // on non-Windows platforms, this could lead to data corruption (we have SO intolerant code in the runtime
695     // which manipulates global state under the assumption that an SO cannot occur due to a prior stack
696     // probe).
697
698     // Determining whether an AV is really an SO is not quite straightforward. We can get stack bounds
699     // information from pthreads but (a) we only have the target Mach thread port and no way to map to a
700     // pthread easily and (b) the pthread functions lie about the bounds on the main thread.
701
702     // Instead we inspect the target thread SP we just retrieved above and compare it with the AV address. If
703     // they both lie in the same page or the SP is at a higher address than the AV but in the same VM region,
704     // then we'll consider the AV to be an SO. Note that we can't assume that SP will be in the same page as
705     // the AV on an SO, even though we force GCC to generate stack probes on stack extension (-fstack-check).
706     // That's because GCC currently generates the probe *before* altering SP. Since a given stack extension can
707     // involve multiple pages and GCC generates all the required probes before updating SP in a single
708     // operation, the faulting probe can be at an address that is far removed from the thread's current value
709     // of SP.
710
711     // In the case where the AV and SP aren't in the same or adjacent pages we check if the first page
712     // following the faulting address belongs in the same VM region as the current value of SP. Since all pages
713     // in a VM region have the same attributes this check eliminates the possibility that there's another guard
714     // page in the range between the fault and the SP, effectively establishing that the AV occurred in the
715     // guard page associated with the stack associated with the SP.
716
717     // We are assuming here that thread stacks are always allocated in a single VM region. I've seen no
718     // evidence thus far that this is not the case (and the mere fact we rely on Mach apis already puts us on
719     // brittle ground anyway).
720
721     //  (a)     SP always marks the current limit of the stack (in that all valid stack accesses will be of
722     //          the form [SP + delta]). The Mac x86 ABI appears to guarantee this (or rather it does not
723     //          guarantee that stack slots below SP will not be invalidated by asynchronous events such as
724     //          interrupts, which mostly amounts to the same thing for user mode code). Note that the Mac PPC
725     //          ABI does allow some (constrained) access below SP, but we're not currently supporting this
726     //          platform.
727     //  (b)     All code will extend the stack "carefully" (by which we mean that stack extensions of more
728     //          than one page in size will touch at least one byte in each intervening page (in decreasing
729     //          address order), to guarantee that the guard page is hit before memory beyond the guard page is
730     //          corrupted). Our managed jits always generate code which does this as does MSVC. GCC, however,
731     //          does not do this by default. We have to explicitly provide the -fstack-check compiler option
732     //          to enable the behavior.
733 #if (defined(_X86_) || defined(_AMD64_)) && defined(__APPLE__)
734     if (exceptionRecord.ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
735     {
736         // Assume this AV isn't an SO to begin with.
737         bool fIsStackOverflow = false;
738
739         // Calculate the page base addresses for the fault and the faulting thread's SP.
740         int cbPage = getpagesize();
741         char *pFaultPage = (char*)(exceptionRecord.ExceptionInformation[1] & ~(cbPage - 1));
742 #ifdef _X86_
743         char *pStackTopPage = (char*)(threadContext.Esp & ~(cbPage - 1));
744 #elif defined(_AMD64_)
745         char *pStackTopPage = (char*)(threadContext.Rsp & ~(cbPage - 1));
746 #endif
747
748         if (pFaultPage == pStackTopPage || pFaultPage == (pStackTopPage - cbPage))
749         {
750             // The easy case is when the AV occurred in the same or adjacent page as the stack pointer.
751             fIsStackOverflow = true;
752         }
753         else if (pFaultPage < pStackTopPage)
754         {
755             // Calculate the address of the page immediately following the fault and check that it
756             // lies in the same VM region as the stack pointer.
757             vm_address_t vm_address;
758             vm_size_t vm_size;
759             vm_region_flavor_t vm_flavor;
760             mach_msg_type_number_t infoCnt;
761 #ifdef BIT64
762             vm_region_basic_info_data_64_t info;
763             infoCnt = VM_REGION_BASIC_INFO_COUNT_64;
764             vm_flavor = VM_REGION_BASIC_INFO_64;
765 #else
766             vm_region_basic_info_data_t info;
767             infoCnt = VM_REGION_BASIC_INFO_COUNT;
768             vm_flavor = VM_REGION_BASIC_INFO;
769 #endif
770             mach_port_t object_name;
771
772             vm_address = (vm_address_t)(pFaultPage + cbPage);
773
774 #ifdef BIT64
775             machret = vm_region_64(
776 #else
777             machret = vm_region(
778 #endif
779                 mach_task_self(),
780                 &vm_address,
781                 &vm_size,
782                 vm_flavor,
783                 (vm_region_info_t)&info,
784                 &infoCnt,
785                 &object_name);
786 #ifdef _X86_
787             CHECK_MACH("vm_region", machret);
788 #elif defined(_AMD64_)
789             CHECK_MACH("vm_region_64", machret);
790 #endif
791
792             // If vm_region updated the address we gave it then that address was not part of a region at all
793             // (and so this cannot be an SO). Otherwise check that the ESP lies in the region returned.
794             char *pRegionStart = (char*)vm_address;
795             char *pRegionEnd = (char*)vm_address + vm_size;
796             if (pRegionStart == (pFaultPage + cbPage) && pStackTopPage < pRegionEnd)
797                 fIsStackOverflow = true;
798         }
799
800 #if defined(_AMD64_)
801         if (!fIsStackOverflow)
802         {
803             // Check if we can read pointer sizeD bytes below the target thread's stack pointer.
804             // If we are unable to, then it implies we have run into SO.
805             void **targetSP = (void **)threadContext.Rsp;
806             vm_address_t targetAddr = (mach_vm_address_t)(targetSP);
807             targetAddr -= sizeof(void *);
808             vm_size_t vm_size = sizeof(void *);
809             char arr[8];
810             vm_size_t data_count = 8;
811             machret = vm_read_overwrite(mach_task_self(), targetAddr, vm_size, (pointer_t)arr, &data_count);
812             if (machret == KERN_INVALID_ADDRESS)
813             {
814                 fIsStackOverflow = true;
815             }
816         }
817 #endif // _AMD64_
818
819         if (fIsStackOverflow)
820         {
821             // We have a stack overflow. Abort the process immediately. It would be nice to let the VM do this
822             // but the Windows mechanism (where a stack overflow SEH exception is delivered on the faulting
823             // thread) will not work most of the time since non-Windows OSs don't keep a reserve stack
824             // extension allocated for this purpose.
825
826             // TODO: Once our event reporting story is further along we probably want to report something
827             // here. If our runtime policy for SO ever changes (the most likely candidate being "unload
828             // appdomain on SO) then we'll have to do something more complex here, probably involving a
829             // handshake with the runtime in order to report the SO without attempting to extend the faulting
830             // thread's stack any further. Note that we cannot call most PAL functions from the context of
831             // this thread since we're not a PAL thread.
832
833             write(STDERR_FILENO, StackOverflowMessage, sizeof(StackOverflowMessage) - 1);
834             abort();
835         }
836     }
837 #else // (_X86_ || _AMD64_) && __APPLE__
838 #error Platform not supported for correct stack overflow handling
839 #endif // (_X86_ || _AMD64_) && __APPLE__
840 #endif // CORECLR && _X86_
841
842 #if defined(_X86_)
843     NONPAL_ASSERTE(exceptionInfo.ThreadState.tsh.flavor == x86_THREAD_STATE32);
844
845     // Make a copy of the thread state because the one in exceptionInfo needs to be preserved to restore
846     // the state if the exception is forwarded.
847     x86_thread_state32_t ts32 = exceptionInfo.ThreadState.uts.ts32;
848
849     // If we're in single step mode, disable it since we're going to call PAL_DispatchException
850     if (exceptionRecord.ExceptionCode == EXCEPTION_SINGLE_STEP)
851     {
852         ts32.eflags &= ~EFL_TF;
853     }
854
855     exceptionRecord.ExceptionFlags = EXCEPTION_IS_SIGNAL; 
856     exceptionRecord.ExceptionRecord = NULL;
857     exceptionRecord.ExceptionAddress = (void *)ts32.eip;
858
859     void **FramePointer = (void **)ts32.esp;
860
861     *--FramePointer = (void *)ts32.eip;
862
863     // Construct a stack frame for a pretend activation of the function
864     // PAL_DispatchExceptionWrapper that serves only to make the stack
865     // correctly unwindable by the system exception unwinder.
866     // PAL_DispatchExceptionWrapper has an ebp frame, its local variables
867     // are the context and exception record, and it has just "called"
868     // PAL_DispatchException.
869     *--FramePointer = (void *)ts32.ebp;
870     ts32.ebp = (unsigned)FramePointer;
871
872     // Put the context on the stack
873     FramePointer = (void **)((ULONG_PTR)FramePointer - sizeof(CONTEXT));
874     // Make sure it's aligned - CONTEXT has 8-byte alignment
875     FramePointer = (void **)((ULONG_PTR)FramePointer - ((ULONG_PTR)FramePointer % 8));
876     CONTEXT *pContext = (CONTEXT *)FramePointer;
877     *pContext = threadContext;
878
879     // Put the exception record on the stack
880     FramePointer = (void **)((ULONG_PTR)FramePointer - sizeof(EXCEPTION_RECORD));
881     EXCEPTION_RECORD *pExceptionRecord = (EXCEPTION_RECORD *)FramePointer;
882     *pExceptionRecord = exceptionRecord;
883
884     FramePointer = (void **)((ULONG_PTR)FramePointer - sizeof(MachExceptionInfo));
885     MachExceptionInfo *pMachExceptionInfo = (MachExceptionInfo *)FramePointer;
886     *pMachExceptionInfo = exceptionInfo;
887
888     // Push arguments to PAL_DispatchException
889     FramePointer = (void **)((ULONG_PTR)FramePointer - 3 * sizeof(void *));
890
891     // Make sure it's aligned - ABI requires 16-byte alignment
892     FramePointer = (void **)((ULONG_PTR)FramePointer - ((ULONG_PTR)FramePointer % 16));
893     FramePointer[0] = pContext;
894     FramePointer[1] = pExceptionRecord;
895     FramePointer[2] = pMachExceptionInfo;
896
897     // Place the return address to right after the fake call in PAL_DispatchExceptionWrapper
898     FramePointer[-1] = (void *)((ULONG_PTR)PAL_DispatchExceptionWrapper + PAL_DispatchExceptionReturnOffset);
899
900     // Make the instruction register point to DispatchException
901     ts32.eip = (unsigned)PAL_DispatchException;
902     ts32.esp = (unsigned)&FramePointer[-1]; // skip return address
903
904     // Now set the thread state for the faulting thread so that PAL_DispatchException executes next
905     machret = thread_set_state(thread, x86_THREAD_STATE32, (thread_state_t)&ts32, x86_THREAD_STATE32_COUNT);
906     CHECK_MACH("thread_set_state(thread)", machret);
907 #elif defined(_AMD64_)
908     NONPAL_ASSERTE(exceptionInfo.ThreadState.tsh.flavor == x86_THREAD_STATE64);
909
910     // Make a copy of the thread state because the one in exceptionInfo needs to be preserved to restore
911     // the state if the exception is forwarded.
912     x86_thread_state64_t ts64 = exceptionInfo.ThreadState.uts.ts64;
913
914     // If we're in single step mode, disable it since we're going to call PAL_DispatchException
915     if (exceptionRecord.ExceptionCode == EXCEPTION_SINGLE_STEP)
916     {
917         ts64.__rflags &= ~EFL_TF;
918     }
919
920     exceptionRecord.ExceptionFlags = EXCEPTION_IS_SIGNAL; 
921     exceptionRecord.ExceptionRecord = NULL;
922     exceptionRecord.ExceptionAddress = (void *)ts64.__rip;
923
924     void **FramePointer = (void **)ts64.__rsp;
925
926     *--FramePointer = (void *)ts64.__rip;
927
928     // Construct a stack frame for a pretend activation of the function
929     // PAL_DispatchExceptionWrapper that serves only to make the stack
930     // correctly unwindable by the system exception unwinder.
931     // PAL_DispatchExceptionWrapper has an ebp frame, its local variables
932     // are the context and exception record, and it has just "called"
933     // PAL_DispatchException.
934     *--FramePointer = (void *)ts64.__rbp;
935     ts64.__rbp = (SIZE_T)FramePointer;
936
937     // Put the context on the stack
938     FramePointer = (void **)((ULONG_PTR)FramePointer - sizeof(CONTEXT));
939     // Make sure it's aligned - CONTEXT has 16-byte alignment
940     FramePointer = (void **)((ULONG_PTR)FramePointer - ((ULONG_PTR)FramePointer % 16));
941     CONTEXT *pContext = (CONTEXT *)FramePointer;
942     *pContext = threadContext;
943
944     // Put the exception record on the stack
945     FramePointer = (void **)((ULONG_PTR)FramePointer - sizeof(EXCEPTION_RECORD));
946     EXCEPTION_RECORD *pExceptionRecord = (EXCEPTION_RECORD *)FramePointer;
947     *pExceptionRecord = exceptionRecord;
948
949     FramePointer = (void **)((ULONG_PTR)FramePointer - sizeof(MachExceptionInfo));
950     MachExceptionInfo *pMachExceptionInfo = (MachExceptionInfo *)FramePointer;
951     *pMachExceptionInfo = exceptionInfo;
952
953     // Push arguments to PAL_DispatchException
954     FramePointer = (void **)((ULONG_PTR)FramePointer - 3 * sizeof(void *));
955
956     // Make sure it's aligned - ABI requires 16-byte alignment
957     FramePointer = (void **)((ULONG_PTR)FramePointer - ((ULONG_PTR)FramePointer % 16));
958     FramePointer[0] = pContext;
959     FramePointer[1] = pExceptionRecord;
960     FramePointer[2] = pMachExceptionInfo;
961
962     // Place the return address to right after the fake call in PAL_DispatchExceptionWrapper
963     FramePointer[-1] = (void *)((ULONG_PTR)PAL_DispatchExceptionWrapper + PAL_DispatchExceptionReturnOffset);
964
965     // Make the instruction register point to DispatchException
966     ts64.__rip = (SIZE_T)PAL_DispatchException;
967     ts64.__rsp = (SIZE_T)&FramePointer[-1]; // skip return address
968
969     // Now set the thread state for the faulting thread so that PAL_DispatchException executes next
970     machret = thread_set_state(thread, x86_THREAD_STATE64, (thread_state_t)&ts64, x86_THREAD_STATE64_COUNT);
971     CHECK_MACH("thread_set_state(thread)", machret);
972 #else
973 #error HijackFaultingThread not defined for this architecture
974 #endif
975 }
976
977 /*++
978 Function :
979     SuspendMachThread
980
981     Suspend the specified thread.
982
983 Parameters:
984     thread - mach thread port
985
986 Return value :
987     None
988 --*/
989 static
990 void
991 SuspendMachThread(thread_act_t thread)
992 {
993     kern_return_t machret;
994
995     while (true)
996     {
997         machret = thread_suspend(thread);
998         CHECK_MACH("thread_suspend", machret);
999
1000         // Ensure that if the thread was running in the kernel, the kernel operation
1001         // is safely aborted so that it can be restarted later.
1002         machret = thread_abort_safely(thread);
1003         if (machret == KERN_SUCCESS)
1004         {
1005             break;
1006         }
1007
1008         // The thread was running in the kernel executing a non-atomic operation
1009         // that cannot be restarted, so we need to resume the thread and retry
1010         machret = thread_resume(thread);
1011         CHECK_MACH("thread_resume", machret);
1012     }
1013 }
1014
1015 /*++
1016 Function :
1017     SEHExceptionThread
1018
1019     Entry point for the thread that will listen for exception in any other thread.
1020
1021 #ifdef FEATURE_PAL_SXS
1022     NOTE: This thread is not a PAL thread, and it must not be one.  If it was,
1023     exceptions on this thread would be delivered to the port this thread itself
1024     is listening on.
1025
1026     In particular, if another thread overflows its stack, the exception handling
1027     thread receives a message.  It will try to create a PAL_DispatchException
1028     frame on the faulting thread, which will likely fault.  If the exception
1029     processing thread is not a PAL thread, the process gets terminated with a
1030     bus error; if the exception processing thread was a PAL thread, we would see
1031     a hang (since no thread is listening for the exception message that gets sent).
1032     Of the two ugly behaviors, the bus error is definitely favorable.
1033
1034     This means: no printf, no TRACE, no PAL allocation, no ExitProcess,
1035     no LastError in this function and its helpers.  To report fatal failure,
1036     use NONPAL_RETAIL_ASSERT.
1037 #endif // FEATURE_PAL_SXS
1038
1039 Parameters :
1040     void *args - not used
1041
1042 Return value :
1043    Never returns
1044 --*/
1045 void *
1046 SEHExceptionThread(void *args)
1047 {
1048     ForwardedExceptionList feList;
1049     MachMessage sReplyOrForward;
1050     MachMessage sMessage;
1051     kern_return_t machret;
1052     thread_act_t thread;
1053
1054     // Loop processing incoming messages forever.
1055     while (true)
1056     {
1057         // Receive the next message.
1058         sMessage.Receive(s_ExceptionPort);
1059
1060         NONPAL_TRACE("Received message %s (%08x) from (remote) %08x to (local) %08x\n",
1061             sMessage.GetMessageTypeName(), 
1062             sMessage.GetMessageType(),
1063             sMessage.GetRemotePort(), 
1064             sMessage.GetLocalPort());
1065
1066         if (sMessage.IsSetThreadRequest())
1067         {
1068             // Handle a request to set the thread context for the specified target thread.
1069             CONTEXT sContext;
1070             thread = sMessage.GetThreadContext(&sContext);
1071
1072             // Suspend the target thread
1073             SuspendMachThread(thread);
1074             
1075             machret = CONTEXT_SetThreadContextOnPort(thread, &sContext);
1076             CHECK_MACH("CONTEXT_SetThreadContextOnPort", machret);
1077
1078             machret = thread_resume(thread);
1079             CHECK_MACH("thread_resume", machret);
1080         }
1081         else if (sMessage.IsExceptionNotification())
1082         {
1083             // This is a notification of an exception occurring on another thread.
1084             exception_type_t exceptionType = sMessage.GetException();
1085             thread = sMessage.GetThread();
1086
1087 #ifdef _DEBUG 
1088             if (NONPAL_TRACE_ENABLED)
1089             {
1090                 NONPAL_TRACE("ExceptionNotification %s (%u) thread %08x flavor %u\n",
1091                     GetExceptionString(exceptionType),
1092                     exceptionType,
1093                     thread,
1094                     sMessage.GetThreadStateFlavor());
1095
1096                 int subcode_count = sMessage.GetExceptionCodeCount();
1097                 for (int i = 0; i < subcode_count; i++)
1098                     NONPAL_TRACE("ExceptionNotification subcode[%d] = %llx\n", i, sMessage.GetExceptionCode(i));
1099
1100                 x86_thread_state64_t threadStateActual;
1101                 unsigned int count = sizeof(threadStateActual) / sizeof(unsigned);
1102                 machret = thread_get_state(thread, x86_THREAD_STATE64, (thread_state_t)&threadStateActual, &count);
1103                 CHECK_MACH("thread_get_state", machret);
1104
1105                 NONPAL_TRACE("ExceptionNotification actual  rip %016llx rsp %016llx rbp %016llx rax %016llx r15 %016llx eflags %08llx\n",
1106                     threadStateActual.__rip,
1107                     threadStateActual.__rsp,
1108                     threadStateActual.__rbp,
1109                     threadStateActual.__rax,
1110                     threadStateActual.__r15,
1111                     threadStateActual.__rflags);
1112
1113                 x86_exception_state64_t threadExceptionState;
1114                 unsigned int ehStateCount = sizeof(threadExceptionState) / sizeof(unsigned);
1115                 machret = thread_get_state(thread, x86_EXCEPTION_STATE64, (thread_state_t)&threadExceptionState, &ehStateCount);
1116                 CHECK_MACH("thread_get_state", machret);
1117
1118                 NONPAL_TRACE("ExceptionNotification trapno %04x cpu %04x err %08x faultAddr %016llx\n",
1119                     threadExceptionState.__trapno,
1120                     threadExceptionState.__cpu,
1121                     threadExceptionState.__err,
1122                     threadExceptionState.__faultvaddr);
1123             }
1124 #endif // _DEBUG
1125
1126             bool feFound = false;
1127             feList.MoveFirst();
1128
1129             while (!feList.IsEOL())
1130             {
1131                 mach_port_type_t ePortType;
1132                 if (mach_port_type(mach_task_self(), feList.Current->Thread, &ePortType) != KERN_SUCCESS || (ePortType & MACH_PORT_TYPE_DEAD_NAME))
1133                 {
1134                     NONPAL_TRACE("Forwarded exception: invalid thread port %08x\n", feList.Current->Thread);
1135
1136                     // Unlink and delete the forwarded exception instance
1137                     feList.Delete();
1138                 }
1139                 else
1140                 {
1141                     if (feList.Current->Thread == thread)
1142                     {
1143                         bool isSameException = feList.Current->ExceptionType == exceptionType;
1144                         feFound = true;
1145
1146                         // Locate the record of previously installed handlers that the target thread keeps.
1147                         CThreadMachExceptionHandlers *pHandlers = feList.Current->PalThread->GetSavedMachHandlers();
1148
1149                         // Unlink and delete the forwarded exception instance
1150                         feList.Delete();
1151
1152                         // Check if the current exception type matches the forwarded one and whether 
1153                         // there's a handler for the particular exception we've been handed.
1154                         MachExceptionHandler sHandler;
1155                         if (isSameException && pHandlers->GetHandler(exceptionType, &sHandler))
1156                         {
1157                             NONPAL_TRACE("ForwardNotification thread %08x to handler %08x\n", thread, sHandler.m_handler);
1158                             sReplyOrForward.ForwardNotification(&sHandler, sMessage);
1159                         }
1160                         else
1161                         {
1162                             NONPAL_TRACE("ReplyToNotification KERN_FAILURE thread %08x port %08x sameException %d\n", 
1163                                 thread, sMessage.GetRemotePort(), isSameException);
1164                             sReplyOrForward.ReplyToNotification(sMessage, KERN_FAILURE);
1165                         }
1166                         break;
1167                     }
1168
1169                     feList.MoveNext();
1170                 }
1171             }
1172
1173             if (!feFound)
1174             {
1175                 NONPAL_TRACE("HijackFaultingThread thread %08x\n", thread);
1176                 HijackFaultingThread(thread, mach_task_self(), sMessage);
1177
1178                 // Send the result of handling the exception back in a reply.
1179                 NONPAL_TRACE("ReplyToNotification KERN_SUCCESS thread %08x port %08x\n", thread, sMessage.GetRemotePort());
1180                 sReplyOrForward.ReplyToNotification(sMessage, KERN_SUCCESS);
1181             }
1182         }
1183         else if (sMessage.IsForwardExceptionRequest())
1184         {
1185             thread = sMessage.GetThread();
1186
1187             NONPAL_TRACE("ForwardExceptionRequest for thread %08x\n", thread);
1188
1189             // Suspend the faulting thread. 
1190             SuspendMachThread(thread);
1191
1192             // Set the context back to the original faulting state.
1193             MachExceptionInfo *pExceptionInfo = sMessage.GetExceptionInfo();
1194             pExceptionInfo->RestoreState(thread);
1195
1196             // Allocate an forwarded exception entry
1197             ForwardedException *pfe = (ForwardedException *)malloc(sizeof(ForwardedException));
1198             if (pfe == NULL)
1199             {
1200                 NONPAL_RETAIL_ASSERT("Exception thread ran out of memory to track forwarded exception notifications");
1201             }
1202
1203             // Save the forwarded exception entry away for the restarted exception message
1204             pfe->Thread = thread;
1205             pfe->ExceptionType = pExceptionInfo->ExceptionType;
1206             pfe->PalThread = sMessage.GetPalThread();
1207             feList.Add(pfe);
1208
1209             // Now let the thread run at the original exception context to restart the exception
1210             NONPAL_TRACE("ForwardExceptionRequest resuming thread %08x exception type %08x\n", thread, pfe->ExceptionType);
1211             machret = thread_resume(thread);
1212             CHECK_MACH("thread_resume", machret);
1213         }
1214         else
1215         {
1216             NONPAL_RETAIL_ASSERT("Unknown message type: %u", sMessage.GetMessageType());
1217         }
1218     }
1219 }
1220         
1221 /*++
1222 Function :
1223     MachExceptionInfo constructor
1224
1225     Saves the exception info from the exception notification message and
1226     the current thread state.
1227
1228 Parameters:
1229     thread - thread port to restore
1230     message - exception message
1231
1232 Return value :
1233     none
1234 --*/
1235 MachExceptionInfo::MachExceptionInfo(mach_port_t thread, MachMessage& message)
1236 {
1237     kern_return_t machret;
1238
1239     ExceptionType = message.GetException();
1240     SubcodeCount = message.GetExceptionCodeCount();
1241     NONPAL_RETAIL_ASSERTE(SubcodeCount >= 0 && SubcodeCount <= 2);
1242
1243     for (int i = 0; i < SubcodeCount; i++)
1244         Subcodes[i] = message.GetExceptionCode(i);
1245
1246     mach_msg_type_number_t count = x86_THREAD_STATE_COUNT;
1247     machret = thread_get_state(thread, x86_THREAD_STATE, (thread_state_t)&ThreadState, &count);
1248     CHECK_MACH("thread_get_state", machret);
1249
1250     count = x86_FLOAT_STATE_COUNT;
1251     machret = thread_get_state(thread, x86_FLOAT_STATE, (thread_state_t)&FloatState, &count);
1252     CHECK_MACH("thread_get_state(float)", machret);
1253
1254     count = x86_DEBUG_STATE_COUNT;
1255     machret = thread_get_state(thread, x86_DEBUG_STATE, (thread_state_t)&DebugState, &count);
1256     CHECK_MACH("thread_get_state(debug)", machret);
1257 }
1258
1259 /*++
1260 Function :
1261     MachExceptionInfo::RestoreState
1262
1263     Restore the thread to the saved exception info state.
1264
1265 Parameters:
1266     thread - thread port to restore
1267
1268 Return value :
1269     none
1270 --*/
1271 void MachExceptionInfo::RestoreState(mach_port_t thread)
1272 {
1273     // If we are restarting a breakpoint, we need to bump the IP back one to
1274     // point at the actual int 3 instructions.
1275     if (ExceptionType == EXC_BREAKPOINT)
1276     {
1277         if (Subcodes[0] == EXC_I386_BPT)
1278         {
1279 #ifdef _X86_
1280             ThreadState.uts.ts32.eip--;
1281 #elif defined(_AMD64_)
1282             ThreadState.uts.ts64.__rip--;
1283 #else
1284 #error Platform not supported
1285 #endif
1286         }
1287     }
1288     kern_return_t machret = thread_set_state(thread, x86_THREAD_STATE, (thread_state_t)&ThreadState, x86_THREAD_STATE_COUNT);
1289     CHECK_MACH("thread_set_state(thread)", machret);
1290
1291     machret = thread_set_state(thread, x86_FLOAT_STATE, (thread_state_t)&FloatState, x86_FLOAT_STATE_COUNT);
1292     CHECK_MACH("thread_set_state(float)", machret);
1293
1294     machret = thread_set_state(thread, x86_DEBUG_STATE, (thread_state_t)&DebugState, x86_DEBUG_STATE_COUNT);
1295     CHECK_MACH("thread_set_state(debug)", machret);
1296 }
1297
1298 /*++
1299 Function :
1300     MachSetThreadContext
1301
1302     Sets the context of the current thread by sending a notification
1303     to the exception thread.
1304
1305 Parameters:
1306     lpContext - the CONTEXT to set the current thread
1307
1308 Return value :
1309     Doesn't return
1310 --*/
1311 PAL_NORETURN 
1312 void 
1313 MachSetThreadContext(CONTEXT *lpContext)
1314 {
1315     // We need to send a message to the worker thread so that it can set our thread context.
1316     MachMessage sRequest;
1317     sRequest.SendSetThread(s_ExceptionPort, lpContext);
1318
1319     // Make sure we don't do anything
1320     while (TRUE)
1321     {
1322         sched_yield();
1323     }
1324 }
1325
1326 /*++
1327 Function :
1328     SEHInitializeMachExceptions 
1329
1330     Initialize all SEH-related stuff related to mach exceptions
1331
1332     (no parameters)
1333
1334 Return value :
1335     TRUE  if SEH support initialization succeeded
1336     FALSE otherwise
1337 --*/
1338 BOOL 
1339 SEHInitializeMachExceptions(void)
1340 {
1341     pthread_t exception_thread;
1342     kern_return_t machret;
1343
1344     // Allocate a mach port that will listen in on exceptions
1345     machret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &s_ExceptionPort);
1346     if (machret != KERN_SUCCESS)
1347     {
1348         ASSERT("mach_port_allocate failed: %d\n", machret);
1349         UTIL_SetLastErrorFromMach(machret);
1350         return FALSE;
1351     }
1352
1353     // Insert the send right into the task
1354     machret = mach_port_insert_right(mach_task_self(), s_ExceptionPort, s_ExceptionPort, MACH_MSG_TYPE_MAKE_SEND);
1355     if (machret != KERN_SUCCESS)
1356     {
1357         ASSERT("mach_port_insert_right failed: %d\n", machret);
1358         UTIL_SetLastErrorFromMach(machret);
1359         return FALSE;
1360     }
1361
1362     // Create the thread that will listen to the exception for all threads
1363     int createret = pthread_create(&exception_thread, NULL, SEHExceptionThread, NULL);
1364     if (createret != 0)
1365     {
1366         ERROR("pthread_create failed, error is %d (%s)\n", createret, strerror(createret));
1367         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1368         return FALSE;
1369     }
1370
1371 #ifdef _DEBUG
1372     if (NONPAL_TRACE_ENABLED)
1373     {
1374         CThreadMachExceptionHandlers taskHandlers;
1375         machret = task_get_exception_ports(mach_task_self(),
1376             PAL_EXC_ALL_MASK,
1377             taskHandlers.m_masks,
1378             &taskHandlers.m_nPorts,
1379             taskHandlers.m_handlers,
1380             taskHandlers.m_behaviors,
1381             taskHandlers.m_flavors);
1382
1383         if (machret == KERN_SUCCESS)
1384         {
1385             NONPAL_TRACE("SEHInitializeMachExceptions: TASK PORT count %d\n", taskHandlers.m_nPorts);
1386             for (mach_msg_type_number_t i = 0; i < taskHandlers.m_nPorts; i++)
1387             {
1388                 NONPAL_TRACE("SEHInitializeMachExceptions: TASK PORT mask %08x handler: %08x behavior %08x flavor %u\n",
1389                     taskHandlers.m_masks[i],
1390                     taskHandlers.m_handlers[i],
1391                     taskHandlers.m_behaviors[i],
1392                     taskHandlers.m_flavors[i]);
1393             }
1394         }
1395         else
1396         {
1397             NONPAL_TRACE("SEHInitializeMachExceptions: task_get_exception_ports FAILED %d %s\n", machret, mach_error_string(machret));
1398         }
1399     }
1400 #endif // _DEBUG
1401
1402 #ifndef FEATURE_PAL_SXS
1403     if (!SEHEnableMachExceptions())
1404     {
1405         return FALSE;
1406     }
1407 #endif // !FEATURE_PAL_SXS
1408
1409     // Tell the system to ignore SIGPIPE signals rather than use the default
1410     // behavior of terminating the process. Ignoring SIGPIPE will cause
1411     // calls that would otherwise raise that signal to return EPIPE instead.
1412     // The PAL expects EPIPE from those functions and won't handle a
1413     // SIGPIPE signal.
1414     signal(SIGPIPE, SIG_IGN);
1415
1416     // We're done
1417     return TRUE;
1418 }
1419
1420 /*++
1421 Function :
1422     MachExceptionInitializeDebug 
1423
1424     Initialize the mach exception handlers necessary for a managed debugger
1425     to work
1426
1427 Return value :
1428     None
1429 --*/
1430 void MachExceptionInitializeDebug(void)
1431 {
1432     if (s_DebugInitialized == FALSE)
1433     {
1434 #ifndef FEATURE_PAL_SXS
1435         kern_return_t MachRet;
1436         MachRet = task_set_exception_ports(mach_task_self(),
1437                                            PAL_EXC_DEBUGGING_MASK,
1438                                            s_ExceptionPort,
1439                                            EXCEPTION_DEFAULT,
1440                                            MACHINE_THREAD_STATE);
1441         if (MachRet != KERN_SUCCESS)
1442         {
1443             ASSERT("task_set_exception_ports failed: %d\n", MachRet);
1444             TerminateProcess(GetCurrentProcess(), (UINT)(-1));
1445         }
1446 #endif // !FEATURE_PAL_SXS
1447         s_DebugInitialized = TRUE;
1448     }
1449 }
1450
1451 /*++
1452 Function :
1453     SEHCleanupExceptionPort
1454
1455     Restore default exception port handler
1456
1457     (no parameters, no return value)
1458     
1459 Note :
1460 During PAL_Terminate, we reach a point where SEH isn't possible any more
1461 (handle manager is off, etc). Past that point, we can't avoid crashing on
1462 an exception.
1463 --*/
1464 void 
1465 SEHCleanupExceptionPort(void)
1466 {
1467     TRACE("Restoring default exception ports\n");
1468 #ifndef FEATURE_PAL_SXS
1469     SEHDisableMachExceptions();
1470 #endif // !FEATURE_PAL_SXS
1471     s_DebugInitialized = FALSE;
1472 }
1473
1474 extern "C" 
1475 void 
1476 ActivationHandler(CONTEXT* context)
1477 {
1478     if (g_activationFunction != NULL)
1479     {
1480         g_activationFunction(context);
1481     }
1482
1483     RtlRestoreContext(context, NULL);
1484     DebugBreak();
1485 }
1486
1487 extern "C" void ActivationHandlerWrapper();
1488 extern "C" int ActivationHandlerReturnOffset;
1489
1490 /*++
1491 Function :
1492     InjectActivationInternal
1493
1494     Sets up the specified thread to call the ActivationHandler.
1495
1496 Parameters:
1497     pThread - PAL thread instance
1498
1499 Return value :
1500     PAL_ERROR
1501 --*/
1502 PAL_ERROR 
1503 InjectActivationInternal(CPalThread* pThread)
1504 {
1505     PAL_ERROR palError;
1506
1507     mach_port_t threadPort = pThread->GetMachPortSelf();
1508     kern_return_t MachRet = thread_suspend(threadPort);
1509     palError = (MachRet == KERN_SUCCESS) ? NO_ERROR : ERROR_GEN_FAILURE;
1510
1511     if (palError == NO_ERROR)
1512     {
1513         mach_msg_type_number_t count;
1514
1515         x86_exception_state64_t ExceptionState;
1516         count = x86_EXCEPTION_STATE64_COUNT;
1517         MachRet = thread_get_state(threadPort,
1518                                    x86_EXCEPTION_STATE64,
1519                                    (thread_state_t)&ExceptionState,
1520                                    &count);
1521         _ASSERT_MSG(MachRet == KERN_SUCCESS, "thread_get_state for x86_EXCEPTION_STATE64\n");
1522
1523         // Inject the activation only if the thread doesn't have a pending hardware exception
1524         static const int MaxHardwareExceptionVector = 31;
1525         if (ExceptionState.__trapno > MaxHardwareExceptionVector)
1526         {
1527             x86_thread_state64_t ThreadState;
1528             count = x86_THREAD_STATE64_COUNT;
1529             MachRet = thread_get_state(threadPort,
1530                                        x86_THREAD_STATE64,
1531                                        (thread_state_t)&ThreadState,
1532                                        &count);
1533             _ASSERT_MSG(MachRet == KERN_SUCCESS, "thread_get_state for x86_THREAD_STATE64\n");
1534
1535             if ((g_safeActivationCheckFunction != NULL) && g_safeActivationCheckFunction(ThreadState.__rip))
1536             {
1537                 // TODO: it would be nice to preserve the red zone in case a jitter would want to use it
1538                 // Do we really care about unwinding through the wrapper?
1539                 size_t* sp = (size_t*)ThreadState.__rsp;
1540                 *(--sp) = ThreadState.__rip;
1541                 *(--sp) = ThreadState.__rbp;
1542                 size_t rbpAddress = (size_t)sp;
1543                 size_t contextAddress = (((size_t)sp) - sizeof(CONTEXT)) & ~15;
1544                 size_t returnAddressAddress = contextAddress - sizeof(size_t);
1545                 *(size_t*)(returnAddressAddress) =  ActivationHandlerReturnOffset + (size_t)ActivationHandlerWrapper;
1546
1547                 // Fill in the context in the helper frame with the full context of the suspended thread.
1548                 // The ActivationHandler will use the context to resume the execution of the thread
1549                 // after the activation function returns.
1550                 CONTEXT *pContext = (CONTEXT *)contextAddress;
1551                 pContext->ContextFlags = CONTEXT_FULL | CONTEXT_SEGMENTS;
1552                 MachRet = CONTEXT_GetThreadContextFromPort(threadPort, pContext);
1553                 _ASSERT_MSG(MachRet == KERN_SUCCESS, "CONTEXT_GetThreadContextFromPort\n");
1554
1555                 // Make the instruction register point to ActivationHandler
1556                 ThreadState.__rip = (size_t)ActivationHandler;
1557                 ThreadState.__rsp = returnAddressAddress;
1558                 ThreadState.__rbp = rbpAddress;
1559                 ThreadState.__rdi = contextAddress;
1560
1561                 MachRet = thread_set_state(threadPort,
1562                                            x86_THREAD_STATE64,
1563                                            (thread_state_t)&ThreadState,
1564                                            count);
1565                 _ASSERT_MSG(MachRet == KERN_SUCCESS, "thread_set_state\n");
1566             }
1567         }
1568
1569         MachRet = thread_resume(threadPort);
1570         palError = (MachRet == ERROR_SUCCESS) ? NO_ERROR : ERROR_GEN_FAILURE;
1571     }
1572     else
1573     {
1574         printf("Suspension failed with error 0x%x\n", palError);
1575     }
1576
1577     return palError;
1578 }
1579
1580 #endif // HAVE_MACH_EXCEPTIONS