Avoid polluting cygwin namespace.
[libsigsegv/ericb.git] / src / handler-macos.c
blob3a39e727d20c75ba1aac3e1be90be5ba47e58b9d
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)
8 any later version.
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. */
19 #include "sigsegv.h"
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <errno.h>
24 #include <signal.h>
25 #if HAVE_SYS_SIGNAL_H
26 # include <sys/signal.h>
27 #endif
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>
34 #include <pthread.h>
36 /* For MacOSX. */
37 #ifndef SS_DISABLE
38 #define SS_DISABLE SA_DISABLE
39 #endif
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
48 File Macro 10.4 10.5
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
62 #endif
64 #include "machfault.h"
66 /* The following sources were used as a *reference* for this exception handling
67 code:
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. */
89 extern boolean_t
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? */
97 kern_return_t
98 catch_exception_raise (mach_port_t exception_port,
99 mach_port_t thread,
100 mach_port_t task,
101 exception_type_t exception,
102 exception_data_t code,
103 mach_msg_type_number_t code_count);
104 kern_return_t
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);
114 kern_return_t
115 catch_exception_raise_state_identity (mach_port_t exception_port,
116 mach_port_t thread,
117 mach_port_t task,
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:
136 0: not yet called
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
156 invoked. */
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. */
160 static void
161 terminating_handler ()
163 /* Dump core. */
164 raise (SIGSEGV);
166 /* Seriously. */
167 abort ();
170 /* A handler that is called in the faulting thread, on an alternate stack.
171 It calls the user installed stack overflow handler. */
172 static void
173 altstack_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)
181 emergency++;
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. */
193 kern_return_t
194 catch_exception_raise (mach_port_t exception_port,
195 mach_port_t thread,
196 mach_port_t task,
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;
203 #endif
204 SIGSEGV_THREAD_STATE_TYPE thread_state;
205 mach_msg_type_number_t state_count;
206 unsigned long addr;
207 unsigned long sp;
209 #ifdef DEBUG_EXCEPTION_HANDLING
210 fprintf (stderr, "Exception: 0x%x Code: 0x%x 0x%x in catch....\n",
211 exception,
212 code_count > 0 ? code[0] : -1,
213 code_count > 1 ? code[1] : -1);
214 #endif
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)
221 != KERN_SUCCESS)
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");
227 #endif
228 return KERN_FAILURE;
230 #endif
232 state_count = SIGSEGV_THREAD_STATE_COUNT;
233 if (thread_get_state (thread, SIGSEGV_THREAD_STATE_FLAVOR,
234 (void *) &thread_state, &state_count)
235 != KERN_SUCCESS)
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");
241 #endif
242 return KERN_FAILURE;
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);
259 #endif
260 new_safe_esp =
261 #if STACK_DIRECTION < 0
262 stk_extra_stack + stk_extra_stack_size - 256;
263 #else
264 stk_extra_stack + 256;
265 #endif
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 */
269 #endif
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;
275 else
277 if (user_handler)
279 int done;
280 #ifdef DEBUG_EXCEPTION_HANDLING
281 fprintf (stderr, "Calling user handler, addr = 0x%lx\n", (char *) addr);
282 #endif
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");
288 #endif
289 if (done)
290 return KERN_SUCCESS;
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)
298 != KERN_SUCCESS)
300 #ifdef DEBUG_EXCEPTION_HANDLING
301 fprintf (stderr, "thread_set_state failed for altstack state\n");
302 #endif
303 return KERN_FAILURE;
305 return KERN_SUCCESS;
309 /* The main function of the thread listening for exceptions. */
310 static void *
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 ();
316 for (;;)
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. */
322 struct
324 mach_msg_header_t head;
325 mach_msg_body_t msgh_body;
326 char data[1024];
328 msg;
329 /* Buffer for a reply message. */
330 struct
332 mach_msg_header_t head;
333 char data[1024];
335 reply;
337 mach_msg_return_t retval;
339 #ifdef DEBUG_EXCEPTION_HANDLING
340 fprintf (stderr, "Exception thread going to sleep\n");
341 #endif
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");
349 #endif
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));
355 #endif
356 abort ();
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");
363 #endif
364 exc_server (&msg.head, &reply.head);
365 #ifdef DEBUG_EXCEPTION_HANDLING
366 fprintf (stderr, "Finished exc_server\n");
367 #endif
369 /* Send the reply. */
370 if (mach_msg (&reply.head, MACH_SEND_MSG, reply.head.msgh_size,
371 0, MACH_PORT_NULL,
372 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL)
373 != MACH_MSG_SUCCESS)
375 #ifdef DEBUG_EXCEPTION_HANDLING
376 fprintf (stderr, "mach_msg send failed\n");
377 #endif
378 abort ();
380 #ifdef DEBUG_EXCEPTION_HANDLING
381 fprintf (stderr, "Reply successful\n");
382 #endif
387 /* Initialize the Mach exception handler thread.
388 Return 0 if OK, -1 on error. */
389 static int
390 mach_initialize ()
392 mach_port_t self;
393 exception_mask_t mask;
394 pthread_attr_t attr;
395 pthread_t thread;
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)
401 != KERN_SUCCESS)
402 return -1;
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)
407 != KERN_SUCCESS)
408 return -1;
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)
416 return -1;
417 if (pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED) != 0)
418 return -1;
419 if (pthread_create (&thread, &attr, mach_exception_thread, NULL) != 0)
420 return -1;
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)
431 != KERN_SUCCESS)
432 return -1;
434 return 0;
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)
444 return -1;
446 user_handler = handler;
448 return 0;
451 void
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)
461 emergency--;
462 if (mach_thread_self () == our_exception_thread)
464 /* Inside user_handler invocation. */
465 mach_port_t thread;
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");
475 #endif
476 return 0;
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)
483 != KERN_SUCCESS)
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");
488 #endif
489 return 0;
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;
497 #endif
498 #if defined __x86_64__
499 /* Align stack. */
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;
519 #endif
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)
526 != KERN_SUCCESS)
528 #ifdef DEBUG_EXCEPTION_HANDLING
529 fprintf (stderr, "sigsegv_leave_handler: thread_set_state failed\n");
530 #endif
531 return 0;
534 return 1;
536 else
538 /* Inside stk_user_handler invocation. Stay in the same thread. */
539 (*continuation) (cont_arg1, cont_arg2, cont_arg3);
540 return 1;
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)
551 return -1;
553 stk_user_handler = handler;
554 stk_extra_stack = (unsigned long) extra_stack;
555 stk_extra_stack_size = extra_stack_size;
556 return 0;
559 void
560 stackoverflow_deinstall_handler (void)
562 stk_user_handler = (stackoverflow_handler_t) NULL;