1 /* Fault handler information. MacOSX version.
2 Copyright (C) 1993-1999, 2002-2003, 2007-2008 Bruno Haible <bruno@clisp.org>
3 Copyright (C) 2003 Paolo Bonzini <bonzini@gnu.org>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software Foundation,
17 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
26 # include <sys/signal.h>
29 #include <mach/mach.h>
30 #include <mach/mach_error.h>
31 #include <mach/thread_status.h>
32 #include <mach/exception.h>
33 #include <mach/task.h>
38 #define SS_DISABLE SA_DISABLE
41 /* In the header files of MacOS X >= 10.5, when compiling with flags that lead
42 to __DARWIN_UNIX03=1 (see <sys/cdefs.h>), the register names are prefixed
43 with '__'. To test for MacOS X >= 10.5 versus < 10.5, we cannot use a
44 predefined macro such as __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
45 because that does not change when a cross-compile via -isysroot is
46 activated. Instead use some macro defined inside the header files and which
47 changed in 10.5, such as
49 <mach/machine/exception.h> EXC_TYPES_COUNT 10 11
50 <mach/exception_types.h> EXC_CRASH -- 10
51 <mach/mach_vm.h> mach_vm_MSG_COUNT 18 19
52 <mach/machine.h> CPU_TYPE_ARM -- ...
53 <mach/memory_object_control.h> memory_object_control_MSG_COUNT 11 12
54 <mach/memory_object_types.h> UPL_ABORT_REFERENCE -- 0x80
55 <mach/message.h> MACH_RCV_TRAILER_AV -- 8
56 <mach/port.h> MACH_PORT_RIGHT_LABELH -- ...
57 <mach/thread_policy.h> THREAD_AFFINITY_POLICY -- 4
58 <mach/vm_region.h> VM_REGION_SUBMAP_SHORT_INFO_COUNT_64 ...
60 #if EXC_TYPES_COUNT >= 11
61 # define MacOS_X_10_5_HEADERS 1
64 #include "machfault.h"
66 /* The following sources were used as a *reference* for this exception handling
68 1. Apple's mach/xnu documentation
69 2. Timothy J. Wood's "Mach Exception Handlers 101" post to the
70 omnigroup's macosx-dev list.
71 www.omnigroup.com/mailman/archive/macosx-dev/2000-June/002030.html */
73 /* This is not defined in any header, although documented. */
75 /* http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/exc_server.html says:
76 The exc_server function is the MIG generated server handling function
77 to handle messages from the kernel relating to the occurrence of an
78 exception in a thread. Such messages are delivered to the exception port
79 set via thread_set_exception_ports or task_set_exception_ports. When an
80 exception occurs in a thread, the thread sends an exception message to its
81 exception port, blocking in the kernel waiting for the receipt of a reply.
82 The exc_server function performs all necessary argument handling for this
83 kernel message and calls catch_exception_raise, catch_exception_raise_state
84 or catch_exception_raise_state_identity, which should handle the exception.
85 If the called routine returns KERN_SUCCESS, a reply message will be sent,
86 allowing the thread to continue from the point of the exception; otherwise,
87 no reply message is sent and the called routine must have dealt with the
88 exception thread directly. */
90 exc_server (mach_msg_header_t
*request_msg
,
91 mach_msg_header_t
*reply_msg
);
94 /* http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/catch_exception_raise.html
95 These functions are defined in this file, and called by exc_server.
96 FIXME: What needs to be done when this code is put into a shared library? */
98 catch_exception_raise (mach_port_t exception_port
,
101 exception_type_t exception
,
102 exception_data_t code
,
103 mach_msg_type_number_t code_count
);
105 catch_exception_raise_state (mach_port_t exception_port
,
106 exception_type_t exception
,
107 exception_data_t code
,
108 mach_msg_type_number_t code_count
,
109 thread_state_flavor_t
*flavor
,
110 thread_state_t in_state
,
111 mach_msg_type_number_t in_state_count
,
112 thread_state_t out_state
,
113 mach_msg_type_number_t
*out_state_count
);
115 catch_exception_raise_state_identity (mach_port_t exception_port
,
118 exception_type_t exception
,
119 exception_data_t code
,
120 mach_msg_type_number_t codeCnt
,
121 thread_state_flavor_t
*flavor
,
122 thread_state_t in_state
,
123 mach_msg_type_number_t in_state_count
,
124 thread_state_t out_state
,
125 mach_msg_type_number_t
*out_state_count
);
128 /* Our exception thread. */
129 static mach_port_t our_exception_thread
;
131 /* The exception port on which our thread listens. */
132 static mach_port_t our_exception_port
;
135 /* mach_initialize() status:
137 1: called and succeeded
138 -1: called and failed */
139 static int mach_initialized
= 0;
141 /* Communication area for the exception state and thread state. */
142 static SIGSEGV_THREAD_STATE_TYPE save_thread_state
;
144 /* Check for reentrant signals. */
145 static int emergency
= -1;
147 /* User's stack overflow handler. */
148 static stackoverflow_handler_t stk_user_handler
= (stackoverflow_handler_t
)NULL
;
149 static unsigned long stk_extra_stack
;
150 static unsigned long stk_extra_stack_size
;
152 /* User's fault handler. */
153 static sigsegv_handler_t user_handler
= (sigsegv_handler_t
)NULL
;
155 /* Thread that signalled the exception. Only set while user_handler is being
157 static mach_port_t signalled_thread
= (mach_port_t
) 0;
159 /* A handler that is called in the faulting thread. It terminates the thread. */
161 terminating_handler ()
170 /* A handler that is called in the faulting thread, on an alternate stack.
171 It calls the user installed stack overflow handler. */
175 /* We arrive here when the user refused to handle a fault. */
177 /* Check if it is plausibly a stack overflow, and the user installed
178 a stack overflow handler. */
179 if (stk_user_handler
)
182 /* Call user's handler. */
183 (*stk_user_handler
) (emergency
, &save_thread_state
);
186 /* Else, terminate the thread. */
187 terminating_handler ();
191 /* Handle an exception by invoking the user's fault handler and/or forwarding
192 the duty to the previously installed handlers. */
194 catch_exception_raise (mach_port_t exception_port
,
197 exception_type_t exception
,
198 exception_data_t code
,
199 mach_msg_type_number_t code_count
)
201 #ifdef SIGSEGV_EXC_STATE_TYPE
202 SIGSEGV_EXC_STATE_TYPE exc_state
;
204 SIGSEGV_THREAD_STATE_TYPE thread_state
;
205 mach_msg_type_number_t state_count
;
209 #ifdef DEBUG_EXCEPTION_HANDLING
210 fprintf (stderr
, "Exception: 0x%x Code: 0x%x 0x%x in catch....\n",
212 code_count
> 0 ? code
[0] : -1,
213 code_count
> 1 ? code
[1] : -1);
216 /* See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/thread_get_state.html. */
217 #ifdef SIGSEGV_EXC_STATE_TYPE
218 state_count
= SIGSEGV_EXC_STATE_COUNT
;
219 if (thread_get_state (thread
, SIGSEGV_EXC_STATE_FLAVOR
,
220 (void *) &exc_state
, &state_count
)
223 /* The thread is supposed to be suspended while the exception handler
224 is called. This shouldn't fail. */
225 #ifdef DEBUG_EXCEPTION_HANDLING
226 fprintf (stderr
, "thread_get_state failed for exception state\n");
232 state_count
= SIGSEGV_THREAD_STATE_COUNT
;
233 if (thread_get_state (thread
, SIGSEGV_THREAD_STATE_FLAVOR
,
234 (void *) &thread_state
, &state_count
)
237 /* The thread is supposed to be suspended while the exception handler
238 is called. This shouldn't fail. */
239 #ifdef DEBUG_EXCEPTION_HANDLING
240 fprintf (stderr
, "thread_get_state failed for thread state\n");
245 addr
= (unsigned long) (SIGSEGV_FAULT_ADDRESS (thread_state
, exc_state
));
246 sp
= (unsigned long) (SIGSEGV_STACK_POINTER (thread_state
));
248 /* Got the thread's state. Now extract the address that caused the
249 fault and invoke the user's handler. */
250 save_thread_state
= thread_state
;
252 /* If the fault address is near the stack pointer, it's a stack overflow.
253 Otherwise, treat it like a normal SIGSEGV. */
254 if (addr
<= sp
+ 4096 && sp
<= addr
+ 4096)
256 unsigned long new_safe_esp
;
257 #ifdef DEBUG_EXCEPTION_HANDLING
258 fprintf (stderr
, "Treating as stack overflow, sp = 0x%lx\n", (char *) sp
);
261 #if STACK_DIRECTION < 0
262 stk_extra_stack
+ stk_extra_stack_size
- 256;
264 stk_extra_stack
+ 256;
266 #if defined __x86_64__ || defined __i386__
267 new_safe_esp
&= -16; /* align */
268 new_safe_esp
-= sizeof (void *); /* make room for (unused) return address slot */
270 SIGSEGV_STACK_POINTER (thread_state
) = new_safe_esp
;
271 /* Continue handling this fault in the faulting thread. (We cannot longjmp while
272 in the exception handling thread, so we need to mimic what signals do!) */
273 SIGSEGV_PROGRAM_COUNTER (thread_state
) = (unsigned long) altstack_handler
;
280 #ifdef DEBUG_EXCEPTION_HANDLING
281 fprintf (stderr
, "Calling user handler, addr = 0x%lx\n", (char *) addr
);
283 signalled_thread
= thread
;
284 done
= (*user_handler
) ((void *) addr
, 1);
285 signalled_thread
= (mach_port_t
) 0;
286 #ifdef DEBUG_EXCEPTION_HANDLING
287 fprintf (stderr
, "Back from user handler\n");
292 SIGSEGV_PROGRAM_COUNTER (thread_state
) = (unsigned long) terminating_handler
;
295 /* See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/thread_set_state.html. */
296 if (thread_set_state (thread
, SIGSEGV_THREAD_STATE_FLAVOR
,
297 (void *) &thread_state
, state_count
)
300 #ifdef DEBUG_EXCEPTION_HANDLING
301 fprintf (stderr
, "thread_set_state failed for altstack state\n");
309 /* The main function of the thread listening for exceptions. */
311 mach_exception_thread (void *arg
)
313 /* See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/mach_thread_self.html. */
314 our_exception_thread
= mach_thread_self ();
318 /* These two structures contain some private kernel data. We don't need
319 to access any of it so we don't bother defining a proper struct. The
320 correct definitions are in the xnu source code. */
321 /* Buffer for a message to be received. */
324 mach_msg_header_t head
;
325 mach_msg_body_t msgh_body
;
329 /* Buffer for a reply message. */
332 mach_msg_header_t head
;
337 mach_msg_return_t retval
;
339 #ifdef DEBUG_EXCEPTION_HANDLING
340 fprintf (stderr
, "Exception thread going to sleep\n");
343 /* Wait for a message on the exception port. */
344 retval
= mach_msg (&msg
.head
, MACH_RCV_MSG
| MACH_RCV_LARGE
, 0,
345 sizeof (msg
), our_exception_port
,
346 MACH_MSG_TIMEOUT_NONE
, MACH_PORT_NULL
);
347 #ifdef DEBUG_EXCEPTION_HANDLING
348 fprintf (stderr
, "Exception thread woke up\n");
350 if (retval
!= MACH_MSG_SUCCESS
)
352 #ifdef DEBUG_EXCEPTION_HANDLING
353 fprintf (stderr
, "mach_msg receive failed with %d %s\n",
354 (int) retval
, mach_error_string (retval
));
359 /* Handle the message: Call exc_server, which will call
360 catch_exception_raise and produce a reply message. */
361 #ifdef DEBUG_EXCEPTION_HANDLING
362 fprintf (stderr
, "Calling exc_server\n");
364 exc_server (&msg
.head
, &reply
.head
);
365 #ifdef DEBUG_EXCEPTION_HANDLING
366 fprintf (stderr
, "Finished exc_server\n");
369 /* Send the reply. */
370 if (mach_msg (&reply
.head
, MACH_SEND_MSG
, reply
.head
.msgh_size
,
372 MACH_MSG_TIMEOUT_NONE
, MACH_PORT_NULL
)
375 #ifdef DEBUG_EXCEPTION_HANDLING
376 fprintf (stderr
, "mach_msg send failed\n");
380 #ifdef DEBUG_EXCEPTION_HANDLING
381 fprintf (stderr
, "Reply successful\n");
387 /* Initialize the Mach exception handler thread.
388 Return 0 if OK, -1 on error. */
393 exception_mask_t mask
;
397 self
= mach_task_self ();
399 /* Allocate a port on which the thread shall listen for exceptions. */
400 if (mach_port_allocate (self
, MACH_PORT_RIGHT_RECEIVE
, &our_exception_port
)
404 /* See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/mach_port_insert_right.html. */
405 if (mach_port_insert_right (self
, our_exception_port
, our_exception_port
,
406 MACH_MSG_TYPE_MAKE_SEND
)
410 /* The exceptions we want to catch. Only EXC_BAD_ACCESS is interesting
411 for us (see above in function catch_exception_raise). */
412 mask
= EXC_MASK_BAD_ACCESS
;
414 /* Create the thread listening on the exception port. */
415 if (pthread_attr_init (&attr
) != 0)
417 if (pthread_attr_setdetachstate (&attr
, PTHREAD_CREATE_DETACHED
) != 0)
419 if (pthread_create (&thread
, &attr
, mach_exception_thread
, NULL
) != 0)
421 pthread_attr_destroy (&attr
);
423 /* Replace the exception port info for these exceptions with our own.
424 Note that we replace the exception port for the entire task, not only
425 for a particular thread. This has the effect that when our exception
426 port gets the message, the thread specific exception port has already
427 been asked, and we don't need to bother about it.
428 See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/task_set_exception_ports.html. */
429 if (task_set_exception_ports (self
, mask
, our_exception_port
,
430 EXCEPTION_DEFAULT
, MACHINE_THREAD_STATE
)
439 sigsegv_install_handler (sigsegv_handler_t handler
)
441 if (!mach_initialized
)
442 mach_initialized
= (mach_initialize () >= 0 ? 1 : -1);
443 if (mach_initialized
< 0)
446 user_handler
= handler
;
452 sigsegv_deinstall_handler (void)
454 user_handler
= (sigsegv_handler_t
)NULL
;
458 sigsegv_leave_handler (void (*continuation
) (void*, void*, void*),
459 void* cont_arg1
, void* cont_arg2
, void* cont_arg3
)
462 if (mach_thread_self () == our_exception_thread
)
464 /* Inside user_handler invocation. */
466 SIGSEGV_THREAD_STATE_TYPE thread_state
;
467 mach_msg_type_number_t state_count
;
469 thread
= signalled_thread
;
470 if (thread
== (mach_port_t
) 0)
472 /* The variable signalled_thread was supposed to be set! */
473 #ifdef DEBUG_EXCEPTION_HANDLING
474 fprintf (stderr
, "sigsegv_leave_handler: signalled_thread not set\n");
479 /* See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/thread_get_state.html. */
480 state_count
= SIGSEGV_THREAD_STATE_COUNT
;
481 if (thread_get_state (thread
, SIGSEGV_THREAD_STATE_FLAVOR
,
482 (void *) &thread_state
, &state_count
)
485 /* The thread was supposed to be suspended! */
486 #ifdef DEBUG_EXCEPTION_HANDLING
487 fprintf (stderr
, "sigsegv_leave_handler: thread_get_state failed for thread state\n");
492 #if defined __ppc64__ || defined __ppc__ || defined __x86_64__
493 /* Store arguments in registers. */
494 SIGSEGV_INTEGER_ARGUMENT_1 (thread_state
) = (unsigned long) cont_arg1
;
495 SIGSEGV_INTEGER_ARGUMENT_2 (thread_state
) = (unsigned long) cont_arg2
;
496 SIGSEGV_INTEGER_ARGUMENT_3 (thread_state
) = (unsigned long) cont_arg3
;
498 #if defined __x86_64__
501 unsigned long new_esp
= SIGSEGV_STACK_POINTER (thread_state
);
502 new_esp
&= -16; /* align */
503 new_esp
-= sizeof (void *); *(void **)new_esp
= SIGSEGV_FRAME_POINTER (thread_state
); /* push %rbp */
504 SIGSEGV_STACK_POINTER (thread_state
) = new_esp
;
505 SIGSEGV_FRAME_POINTER (thread_state
) = new_esp
; /* mov %rsp,%rbp */
507 #elif defined __i386__
508 /* Push arguments onto the stack. */
510 unsigned long new_esp
= SIGSEGV_STACK_POINTER (thread_state
);
511 new_esp
&= -16; /* align */
512 new_esp
-= sizeof (void *); /* unused room, alignment */
513 new_esp
-= sizeof (void *); *(void **)new_esp
= cont_arg3
;
514 new_esp
-= sizeof (void *); *(void **)new_esp
= cont_arg2
;
515 new_esp
-= sizeof (void *); *(void **)new_esp
= cont_arg1
;
516 new_esp
-= sizeof (void *); /* make room for (unused) return address slot */
517 SIGSEGV_STACK_POINTER (thread_state
) = new_esp
;
520 /* Point program counter to continuation to be executed. */
521 SIGSEGV_PROGRAM_COUNTER (thread_state
) = (unsigned long) continuation
;
523 /* See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/thread_set_state.html. */
524 if (thread_set_state (thread
, SIGSEGV_THREAD_STATE_FLAVOR
,
525 (void *) &thread_state
, state_count
)
528 #ifdef DEBUG_EXCEPTION_HANDLING
529 fprintf (stderr
, "sigsegv_leave_handler: thread_set_state failed\n");
538 /* Inside stk_user_handler invocation. Stay in the same thread. */
539 (*continuation
) (cont_arg1
, cont_arg2
, cont_arg3
);
545 stackoverflow_install_handler (stackoverflow_handler_t handler
,
546 void *extra_stack
, unsigned long extra_stack_size
)
548 if (!mach_initialized
)
549 mach_initialized
= (mach_initialize () >= 0 ? 1 : -1);
550 if (mach_initialized
< 0)
553 stk_user_handler
= handler
;
554 stk_extra_stack
= (unsigned long) extra_stack
;
555 stk_extra_stack_size
= extra_stack_size
;
560 stackoverflow_deinstall_handler (void)
562 stk_user_handler
= (stackoverflow_handler_t
) NULL
;