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