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.
13 Implementation of MACH exception API functions.
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"
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"
33 #include "machmessage.h"
40 #include <mach-o/loader.h>
42 using namespace CorUnix;
44 SET_DEFAULT_DEBUG_CHANNEL(EXCEPT);
46 // The port we use to handle exceptions and to set the thread context
47 mach_port_t s_ExceptionPort;
49 static BOOL s_DebugInitialized = FALSE;
51 static const char * PAL_MACH_EXCEPTION_MODE = "PAL_MachExceptionMode";
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
59 ForwardedException *m_next;
61 exception_type_t ExceptionType;
62 CPalThread *PalThread;
65 // The singly linked list and enumerator for the ForwardException struct
66 struct ForwardedExceptionList
69 ForwardedException *m_head;
70 ForwardedException *m_previous;
73 ForwardedException *Current;
75 ForwardedExceptionList()
89 return Current == NULL;
95 Current = Current->m_next;
98 void Add(ForwardedException *item)
100 item->m_next = m_head;
106 if (m_previous == NULL)
108 m_head = Current->m_next;
112 m_previous->m_next = Current->m_next;
121 enum MachExceptionMode
123 // special value to indicate we've not initialized yet
124 MachException_Uninitialized = -1,
126 // These can be combined with bitwise OR to incrementally turn off
127 // functionality for diagnostics purposes.
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,
150 // Default value to use if environment variable not set.
151 MachException_Default = 0,
158 Returns the mach exception mask for the exceptions to hook for a thread.
167 static MachExceptionMode exMode = MachException_Uninitialized;
169 if (exMode == MachException_Uninitialized)
171 exMode = MachException_Default;
173 const char * exceptionSettings = getenv(PAL_MACH_EXCEPTION_MODE);
174 if (exceptionSettings)
176 exMode = (MachExceptionMode)atoi(exceptionSettings);
180 if (PAL_IsDebuggerPresent())
182 exMode = MachException_SuppressDebugging;
187 exception_mask_t machExceptionMask = 0;
188 if (!(exMode & MachException_SuppressIllegal))
190 machExceptionMask |= PAL_EXC_ILLEGAL_MASK;
192 if (!(exMode & MachException_SuppressDebugging))
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
201 machExceptionMask |= PAL_EXC_DEBUGGING_MASK;
202 #else // FEATURE_PAL_SXS
203 if (s_DebugInitialized)
205 machExceptionMask |= PAL_EXC_DEBUGGING_MASK;
207 #endif // FEATURE_PAL_SXS
209 if (!(exMode & MachException_SuppressManaged))
211 machExceptionMask |= PAL_EXC_MANAGED_MASK;
214 return machExceptionMask;
217 #ifdef FEATURE_PAL_SXS
221 CPalThread::EnableMachExceptions
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.
229 ERROR_SUCCESS, if enabling succeeded
230 an error code, otherwise
232 PAL_ERROR CorUnix::CPalThread::EnableMachExceptions()
234 TRACE("%08X: Enter()\n", (unsigned int)(size_t)this);
236 exception_mask_t machExceptionMask = GetExceptionMask();
237 if (machExceptionMask != 0)
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))
250 ASSERT("s_nPortsMax is %u, but needs to be %u\n",
251 CThreadMachExceptionHandlers::s_nPortsMax, countBits);
255 NONPAL_TRACE("Enabling handlers for thread %08x exception mask %08x exception port %08x\n",
256 GetMachPortSelf(), machExceptionMask, s_ExceptionPort);
258 CThreadMachExceptionHandlers *pSavedHandlers = GetSavedMachHandlers();
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();
267 machret = thread_swap_exception_ports(
271 EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES,
273 pSavedHandlers->m_masks,
274 &pSavedHandlers->m_nPorts,
275 pSavedHandlers->m_handlers,
276 pSavedHandlers->m_behaviors,
277 pSavedHandlers->m_flavors);
279 machretDeallocate = mach_port_deallocate(mach_task_self(), thread);
280 CHECK_MACH("mach_port_deallocate", machretDeallocate);
282 if (machret != KERN_SUCCESS)
284 ASSERT("thread_swap_exception_ports failed: %d %s\n", machret, mach_error_string(machret));
285 return UTIL_MachErrorToPalError(machret);
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++)
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]);
301 return ERROR_SUCCESS;
306 CPalThread::DisableMachExceptions
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.
314 ERROR_SUCCESS, if disabling succeeded
315 an error code, otherwise
317 PAL_ERROR CorUnix::CPalThread::DisableMachExceptions()
319 TRACE("%08X: Leave()\n", (unsigned int)(size_t)this);
321 PAL_ERROR palError = NO_ERROR;
323 // We only store exceptions when we're installing exceptions.
324 if (0 == GetExceptionMask())
327 // Get the handlers to restore.
328 CThreadMachExceptionHandlers *savedPorts = GetSavedMachHandlers();
330 kern_return_t MachRet = KERN_SUCCESS;
331 for (int i = 0; i < savedPorts->m_nPorts; i++)
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],
346 kern_return_t MachRetDeallocate = mach_port_deallocate(mach_task_self(), thread);
347 CHECK_MACH("mach_port_deallocate", MachRetDeallocate);
349 if (MachRet != KERN_SUCCESS)
353 if (MachRet != KERN_SUCCESS)
355 ASSERT("thread_set_exception_ports failed: %d\n", MachRet);
356 palError = UTIL_MachErrorToPalError(MachRet);
362 #else // FEATURE_PAL_SXS
366 SEHEnableMachExceptions
368 Enable SEH-related stuff related to mach exceptions
373 TRUE if enabling succeeded
376 BOOL SEHEnableMachExceptions()
378 exception_mask_t machExceptionMask = GetExceptionMask();
379 if (machExceptionMask != 0)
381 kern_return_t MachRet;
382 MachRet = task_set_exception_ports(mach_task_self(),
386 MACHINE_THREAD_STATE);
388 if (MachRet != KERN_SUCCESS)
390 ASSERT("task_set_exception_ports failed: %d\n", MachRet);
391 UTIL_SetLastErrorFromMach(MachRet);
400 SEHDisableMachExceptions
402 Disable SEH-related stuff related to mach exceptions
407 TRUE if enabling succeeded
410 BOOL SEHDisableMachExceptions()
412 exception_mask_t machExceptionMask = GetExceptionMask();
413 if (machExceptionMask != 0)
415 kern_return_t MachRet;
416 MachRet = task_set_exception_ports(mach_task_self(),
420 MACHINE_THREAD_STATE);
422 if (MachRet != KERN_SUCCESS)
424 ASSERT("task_set_exception_ports failed: %d\n", MachRet);
425 UTIL_SetLastErrorFromMach(MachRet);
432 #endif // FEATURE_PAL_SXS
434 #if !defined(_AMD64_)
436 void PAL_DispatchException(PCONTEXT pContext, PEXCEPTION_RECORD pExRecord, MachExceptionInfo *pMachExceptionInfo)
437 #else // defined(_AMD64_)
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).
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_)
446 CPalThread *pThread = InternalGetCurrentThread();
449 if (!pThread->IsInPal())
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);
456 #endif // FEATURE_PAL_SXS
458 EXCEPTION_POINTERS pointers;
459 pointers.ExceptionRecord = pExRecord;
460 pointers.ContextRecord = pContext;
462 TRACE("PAL_DispatchException(EC %08x EA %p)\n", pExRecord->ExceptionCode, pExRecord->ExceptionAddress);
463 SEHProcessException(&pointers);
465 // Send the forward request to the exception thread to process
466 MachMessage sSendMessage;
467 sSendMessage.SendForwardException(s_ExceptionPort, pMachExceptionInfo, pThread);
469 // Spin wait until this thread is hijacked by the exception thread
476 #if defined(_X86_) || defined(_AMD64_)
477 extern "C" void PAL_DispatchExceptionWrapper();
478 extern "C" int PAL_DispatchExceptionReturnOffset;
479 #endif // _X86_ || _AMD64_
485 Sets up up an ExceptionRecord from an exception message
488 exceptionInfo - exception info to build the exception record
489 pExceptionRecord - exception record to setup
493 BuildExceptionRecord(
494 MachExceptionInfo& exceptionInfo, // [in] exception info
495 EXCEPTION_RECORD *pExceptionRecord) // [out] Used to return exception parameters
497 memset(pExceptionRecord, 0, sizeof(EXCEPTION_RECORD));
499 DWORD exceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION;
501 switch(exceptionInfo.ExceptionType)
503 // Could not access memory. subcode contains the bad memory address.
505 if (exceptionInfo.SubcodeCount != 2)
507 NONPAL_RETAIL_ASSERT("Got an unexpected subcode");
508 exceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION;
512 exceptionCode = EXCEPTION_ACCESS_VIOLATION;
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]);
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;
528 // Arithmetic exception; exact nature of exception is in subcode field.
530 if (exceptionInfo.SubcodeCount != 2)
532 NONPAL_RETAIL_ASSERT("Got an unexpected subcode");
533 exceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION;
537 switch (exceptionInfo.Subcodes[0])
539 #if defined(_X86_) || defined(_AMD64_)
541 exceptionCode = EXCEPTION_INT_DIVIDE_BY_ZERO;
544 exceptionCode = EXCEPTION_INT_OVERFLOW;
546 case EXC_I386_EXTOVR:
547 exceptionCode = EXCEPTION_FLT_OVERFLOW;
550 exceptionCode = EXCEPTION_ARRAY_BOUNDS_EXCEEDED;
553 #error Trap code to exception mapping not defined for this architecture
556 exceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION;
563 #if defined(_X86_) || defined(_AMD64_)
564 exceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION;
567 #error Trap code to exception mapping not defined for this architecture
570 // Trace, breakpoint, etc. Details in subcode field.
572 #if defined(_X86_) || defined(_AMD64_)
573 if (exceptionInfo.Subcodes[0] == EXC_I386_SGL)
575 exceptionCode = EXCEPTION_SINGLE_STEP;
577 else if (exceptionInfo.Subcodes[0] == EXC_I386_BPT)
579 exceptionCode = EXCEPTION_BREAKPOINT;
582 #error Trap code to exception mapping not defined for this architecture
586 WARN("unexpected subcode %d for EXC_BREAKPOINT", exceptionInfo.Subcodes[0]);
587 exceptionCode = EXCEPTION_BREAKPOINT;
592 // System call requested. Details in subcode field.
594 exceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION;
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;
603 NONPAL_ASSERT("Got unknown trap code %d\n", exceptionInfo.ExceptionType);
607 pExceptionRecord->ExceptionCode = exceptionCode;
613 exception_type_t exception
619 return "EXC_BAD_ACCESS";
621 case EXC_BAD_INSTRUCTION:
622 return "EXC_BAD_INSTRUCTION";
625 return "EXC_ARITHMETIC";
628 return "EXC_SOFTWARE";
631 return "EXC_BREAKPOINT";
634 return "EXC_SYSCALL";
636 case EXC_MACH_SYSCALL:
637 return "EXC_MACH_SYSCALL";
640 NONPAL_ASSERT("Got unknown trap code %d\n", exception);
643 return "INVALID CODE";
651 Sets the faulting thread up to return to PAL_DispatchException with an
652 ExceptionRecord and thread CONTEXT.
655 thread - thread the exception happened
656 task - task the exception happened
657 message - exception message
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
669 MachExceptionInfo exceptionInfo(thread, message);
670 EXCEPTION_RECORD exceptionRecord;
671 CONTEXT threadContext;
672 kern_return_t machret;
674 // Fill in the exception record from the exception info
675 BuildExceptionRecord(exceptionInfo, &exceptionRecord);
678 threadContext.ContextFlags = CONTEXT_FLOATING_POINT | CONTEXT_EXTENDED_REGISTERS;
680 threadContext.ContextFlags = CONTEXT_FLOATING_POINT;
682 CONTEXT_GetThreadContextFromThreadState(x86_FLOAT_STATE, (thread_state_t)&exceptionInfo.FloatState, &threadContext);
684 threadContext.ContextFlags |= CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS;
685 CONTEXT_GetThreadContextFromThreadState(x86_THREAD_STATE, (thread_state_t)&exceptionInfo.ThreadState, &threadContext);
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
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.
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
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.
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).
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
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)
736 // Assume this AV isn't an SO to begin with.
737 bool fIsStackOverflow = false;
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));
743 char *pStackTopPage = (char*)(threadContext.Esp & ~(cbPage - 1));
744 #elif defined(_AMD64_)
745 char *pStackTopPage = (char*)(threadContext.Rsp & ~(cbPage - 1));
748 if (pFaultPage == pStackTopPage || pFaultPage == (pStackTopPage - cbPage))
750 // The easy case is when the AV occurred in the same or adjacent page as the stack pointer.
751 fIsStackOverflow = true;
753 else if (pFaultPage < pStackTopPage)
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;
759 vm_region_flavor_t vm_flavor;
760 mach_msg_type_number_t infoCnt;
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;
766 vm_region_basic_info_data_t info;
767 infoCnt = VM_REGION_BASIC_INFO_COUNT;
768 vm_flavor = VM_REGION_BASIC_INFO;
770 mach_port_t object_name;
772 vm_address = (vm_address_t)(pFaultPage + cbPage);
775 machret = vm_region_64(
783 (vm_region_info_t)&info,
787 CHECK_MACH("vm_region", machret);
788 #elif defined(_AMD64_)
789 CHECK_MACH("vm_region_64", machret);
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;
801 if (!fIsStackOverflow)
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 *);
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)
814 fIsStackOverflow = true;
819 if (fIsStackOverflow)
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.
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.
833 write(STDERR_FILENO, StackOverflowMessage, sizeof(StackOverflowMessage) - 1);
837 #else // (_X86_ || _AMD64_) && __APPLE__
838 #error Platform not supported for correct stack overflow handling
839 #endif // (_X86_ || _AMD64_) && __APPLE__
840 #endif // CORECLR && _X86_
843 NONPAL_ASSERTE(exceptionInfo.ThreadState.tsh.flavor == x86_THREAD_STATE32);
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;
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)
852 ts32.eflags &= ~EFL_TF;
855 exceptionRecord.ExceptionFlags = EXCEPTION_IS_SIGNAL;
856 exceptionRecord.ExceptionRecord = NULL;
857 exceptionRecord.ExceptionAddress = (void *)ts32.eip;
859 void **FramePointer = (void **)ts32.esp;
861 *--FramePointer = (void *)ts32.eip;
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;
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;
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;
884 FramePointer = (void **)((ULONG_PTR)FramePointer - sizeof(MachExceptionInfo));
885 MachExceptionInfo *pMachExceptionInfo = (MachExceptionInfo *)FramePointer;
886 *pMachExceptionInfo = exceptionInfo;
888 // Push arguments to PAL_DispatchException
889 FramePointer = (void **)((ULONG_PTR)FramePointer - 3 * sizeof(void *));
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;
897 // Place the return address to right after the fake call in PAL_DispatchExceptionWrapper
898 FramePointer[-1] = (void *)((ULONG_PTR)PAL_DispatchExceptionWrapper + PAL_DispatchExceptionReturnOffset);
900 // Make the instruction register point to DispatchException
901 ts32.eip = (unsigned)PAL_DispatchException;
902 ts32.esp = (unsigned)&FramePointer[-1]; // skip return address
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);
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;
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)
917 ts64.__rflags &= ~EFL_TF;
920 exceptionRecord.ExceptionFlags = EXCEPTION_IS_SIGNAL;
921 exceptionRecord.ExceptionRecord = NULL;
922 exceptionRecord.ExceptionAddress = (void *)ts64.__rip;
924 void **FramePointer = (void **)ts64.__rsp;
926 *--FramePointer = (void *)ts64.__rip;
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;
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;
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;
949 FramePointer = (void **)((ULONG_PTR)FramePointer - sizeof(MachExceptionInfo));
950 MachExceptionInfo *pMachExceptionInfo = (MachExceptionInfo *)FramePointer;
951 *pMachExceptionInfo = exceptionInfo;
953 // Push arguments to PAL_DispatchException
954 FramePointer = (void **)((ULONG_PTR)FramePointer - 3 * sizeof(void *));
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;
962 // Place the return address to right after the fake call in PAL_DispatchExceptionWrapper
963 FramePointer[-1] = (void *)((ULONG_PTR)PAL_DispatchExceptionWrapper + PAL_DispatchExceptionReturnOffset);
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
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);
973 #error HijackFaultingThread not defined for this architecture
981 Suspend the specified thread.
984 thread - mach thread port
991 SuspendMachThread(thread_act_t thread)
993 kern_return_t machret;
997 machret = thread_suspend(thread);
998 CHECK_MACH("thread_suspend", machret);
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)
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);
1019 Entry point for the thread that will listen for exception in any other thread.
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
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.
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
1040 void *args - not used
1046 SEHExceptionThread(void *args)
1048 ForwardedExceptionList feList;
1049 MachMessage sReplyOrForward;
1050 MachMessage sMessage;
1051 kern_return_t machret;
1052 thread_act_t thread;
1054 // Loop processing incoming messages forever.
1057 // Receive the next message.
1058 sMessage.Receive(s_ExceptionPort);
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());
1066 if (sMessage.IsSetThreadRequest())
1068 // Handle a request to set the thread context for the specified target thread.
1070 thread = sMessage.GetThreadContext(&sContext);
1072 // Suspend the target thread
1073 SuspendMachThread(thread);
1075 machret = CONTEXT_SetThreadContextOnPort(thread, &sContext);
1076 CHECK_MACH("CONTEXT_SetThreadContextOnPort", machret);
1078 machret = thread_resume(thread);
1079 CHECK_MACH("thread_resume", machret);
1081 else if (sMessage.IsExceptionNotification())
1083 // This is a notification of an exception occurring on another thread.
1084 exception_type_t exceptionType = sMessage.GetException();
1085 thread = sMessage.GetThread();
1088 if (NONPAL_TRACE_ENABLED)
1090 NONPAL_TRACE("ExceptionNotification %s (%u) thread %08x flavor %u\n",
1091 GetExceptionString(exceptionType),
1094 sMessage.GetThreadStateFlavor());
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));
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);
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);
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);
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);
1126 bool feFound = false;
1129 while (!feList.IsEOL())
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))
1134 NONPAL_TRACE("Forwarded exception: invalid thread port %08x\n", feList.Current->Thread);
1136 // Unlink and delete the forwarded exception instance
1141 if (feList.Current->Thread == thread)
1143 bool isSameException = feList.Current->ExceptionType == exceptionType;
1146 // Locate the record of previously installed handlers that the target thread keeps.
1147 CThreadMachExceptionHandlers *pHandlers = feList.Current->PalThread->GetSavedMachHandlers();
1149 // Unlink and delete the forwarded exception instance
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))
1157 NONPAL_TRACE("ForwardNotification thread %08x to handler %08x\n", thread, sHandler.m_handler);
1158 sReplyOrForward.ForwardNotification(&sHandler, sMessage);
1162 NONPAL_TRACE("ReplyToNotification KERN_FAILURE thread %08x port %08x sameException %d\n",
1163 thread, sMessage.GetRemotePort(), isSameException);
1164 sReplyOrForward.ReplyToNotification(sMessage, KERN_FAILURE);
1175 NONPAL_TRACE("HijackFaultingThread thread %08x\n", thread);
1176 HijackFaultingThread(thread, mach_task_self(), sMessage);
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);
1183 else if (sMessage.IsForwardExceptionRequest())
1185 thread = sMessage.GetThread();
1187 NONPAL_TRACE("ForwardExceptionRequest for thread %08x\n", thread);
1189 // Suspend the faulting thread.
1190 SuspendMachThread(thread);
1192 // Set the context back to the original faulting state.
1193 MachExceptionInfo *pExceptionInfo = sMessage.GetExceptionInfo();
1194 pExceptionInfo->RestoreState(thread);
1196 // Allocate an forwarded exception entry
1197 ForwardedException *pfe = (ForwardedException *)malloc(sizeof(ForwardedException));
1200 NONPAL_RETAIL_ASSERT("Exception thread ran out of memory to track forwarded exception notifications");
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();
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);
1216 NONPAL_RETAIL_ASSERT("Unknown message type: %u", sMessage.GetMessageType());
1223 MachExceptionInfo constructor
1225 Saves the exception info from the exception notification message and
1226 the current thread state.
1229 thread - thread port to restore
1230 message - exception message
1235 MachExceptionInfo::MachExceptionInfo(mach_port_t thread, MachMessage& message)
1237 kern_return_t machret;
1239 ExceptionType = message.GetException();
1240 SubcodeCount = message.GetExceptionCodeCount();
1241 NONPAL_RETAIL_ASSERTE(SubcodeCount >= 0 && SubcodeCount <= 2);
1243 for (int i = 0; i < SubcodeCount; i++)
1244 Subcodes[i] = message.GetExceptionCode(i);
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);
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);
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);
1261 MachExceptionInfo::RestoreState
1263 Restore the thread to the saved exception info state.
1266 thread - thread port to restore
1271 void MachExceptionInfo::RestoreState(mach_port_t thread)
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)
1277 if (Subcodes[0] == EXC_I386_BPT)
1280 ThreadState.uts.ts32.eip--;
1281 #elif defined(_AMD64_)
1282 ThreadState.uts.ts64.__rip--;
1284 #error Platform not supported
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);
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);
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);
1300 MachSetThreadContext
1302 Sets the context of the current thread by sending a notification
1303 to the exception thread.
1306 lpContext - the CONTEXT to set the current thread
1313 MachSetThreadContext(CONTEXT *lpContext)
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);
1319 // Make sure we don't do anything
1328 SEHInitializeMachExceptions
1330 Initialize all SEH-related stuff related to mach exceptions
1335 TRUE if SEH support initialization succeeded
1339 SEHInitializeMachExceptions(void)
1341 pthread_t exception_thread;
1342 kern_return_t machret;
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)
1348 ASSERT("mach_port_allocate failed: %d\n", machret);
1349 UTIL_SetLastErrorFromMach(machret);
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)
1357 ASSERT("mach_port_insert_right failed: %d\n", machret);
1358 UTIL_SetLastErrorFromMach(machret);
1362 // Create the thread that will listen to the exception for all threads
1363 int createret = pthread_create(&exception_thread, NULL, SEHExceptionThread, NULL);
1366 ERROR("pthread_create failed, error is %d (%s)\n", createret, strerror(createret));
1367 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1372 if (NONPAL_TRACE_ENABLED)
1374 CThreadMachExceptionHandlers taskHandlers;
1375 machret = task_get_exception_ports(mach_task_self(),
1377 taskHandlers.m_masks,
1378 &taskHandlers.m_nPorts,
1379 taskHandlers.m_handlers,
1380 taskHandlers.m_behaviors,
1381 taskHandlers.m_flavors);
1383 if (machret == KERN_SUCCESS)
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++)
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]);
1397 NONPAL_TRACE("SEHInitializeMachExceptions: task_get_exception_ports FAILED %d %s\n", machret, mach_error_string(machret));
1402 #ifndef FEATURE_PAL_SXS
1403 if (!SEHEnableMachExceptions())
1407 #endif // !FEATURE_PAL_SXS
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
1414 signal(SIGPIPE, SIG_IGN);
1422 MachExceptionInitializeDebug
1424 Initialize the mach exception handlers necessary for a managed debugger
1430 void MachExceptionInitializeDebug(void)
1432 if (s_DebugInitialized == FALSE)
1434 #ifndef FEATURE_PAL_SXS
1435 kern_return_t MachRet;
1436 MachRet = task_set_exception_ports(mach_task_self(),
1437 PAL_EXC_DEBUGGING_MASK,
1440 MACHINE_THREAD_STATE);
1441 if (MachRet != KERN_SUCCESS)
1443 ASSERT("task_set_exception_ports failed: %d\n", MachRet);
1444 TerminateProcess(GetCurrentProcess(), (UINT)(-1));
1446 #endif // !FEATURE_PAL_SXS
1447 s_DebugInitialized = TRUE;
1453 SEHCleanupExceptionPort
1455 Restore default exception port handler
1457 (no parameters, no return value)
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
1465 SEHCleanupExceptionPort(void)
1467 TRACE("Restoring default exception ports\n");
1468 #ifndef FEATURE_PAL_SXS
1469 SEHDisableMachExceptions();
1470 #endif // !FEATURE_PAL_SXS
1471 s_DebugInitialized = FALSE;
1476 ActivationHandler(CONTEXT* context)
1478 if (g_activationFunction != NULL)
1480 g_activationFunction(context);
1483 RtlRestoreContext(context, NULL);
1487 extern "C" void ActivationHandlerWrapper();
1488 extern "C" int ActivationHandlerReturnOffset;
1492 InjectActivationInternal
1494 Sets up the specified thread to call the ActivationHandler.
1497 pThread - PAL thread instance
1503 InjectActivationInternal(CPalThread* pThread)
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;
1511 if (palError == NO_ERROR)
1513 mach_msg_type_number_t count;
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,
1521 _ASSERT_MSG(MachRet == KERN_SUCCESS, "thread_get_state for x86_EXCEPTION_STATE64\n");
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)
1527 x86_thread_state64_t ThreadState;
1528 count = x86_THREAD_STATE64_COUNT;
1529 MachRet = thread_get_state(threadPort,
1531 (thread_state_t)&ThreadState,
1533 _ASSERT_MSG(MachRet == KERN_SUCCESS, "thread_get_state for x86_THREAD_STATE64\n");
1535 if ((g_safeActivationCheckFunction != NULL) && g_safeActivationCheckFunction(ThreadState.__rip))
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;
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");
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;
1561 MachRet = thread_set_state(threadPort,
1563 (thread_state_t)&ThreadState,
1565 _ASSERT_MSG(MachRet == KERN_SUCCESS, "thread_set_state\n");
1569 MachRet = thread_resume(threadPort);
1570 palError = (MachRet == ERROR_SUCCESS) ? NO_ERROR : ERROR_GEN_FAILURE;
1574 printf("Suspension failed with error 0x%x\n", palError);
1580 #endif // HAVE_MACH_EXCEPTIONS