]> gitweb.factorcode.org Git - factor.git/blob - vm/mach_signal.cpp
VM: Fixup cast formatting after clang-format
[factor.git] / vm / mach_signal.cpp
1 /* Fault handler information.  MacOSX version.
2 Copyright (C) 1993-1999, 2002-2003  Bruno Haible <clisp.org at bruno>
3
4 Copyright (C) 2003  Paolo Bonzini <gnu.org at bonzini>
5
6 Used under BSD license with permission from Paolo Bonzini and Bruno Haible,
7 2005-03-10:
8
9 http://sourceforge.net/mailarchive/message.php?msg_name=200503102200.32002.bruno%40clisp.org
10
11 Modified for Factor by Slava Pestov */
12
13 #include "master.hpp"
14
15 namespace factor {
16
17 /* The exception port on which our thread listens. */
18 mach_port_t our_exception_port;
19
20 /* The following sources were used as a *reference* for this exception handling
21    code:
22
23    1. Apple's mach/xnu documentation
24    2. Timothy J. Wood's "Mach Exception Handlers 101" post to the
25       omnigroup's macosx-dev list.
26       http://www.wodeveloper.com/omniLists/macosx-dev/2000/June/msg00137.html */
27
28 /* Modify a suspended thread's thread_state so that when the thread resumes
29    executing, the call frame of the current C primitive (if any) is rewound, and
30    the appropriate Factor error is thrown from the top-most Factor frame. */
31 void factor_vm::call_fault_handler(exception_type_t exception,
32                                    exception_data_type_t code,
33                                    MACH_EXC_STATE_TYPE* exc_state,
34                                    MACH_THREAD_STATE_TYPE* thread_state,
35                                    MACH_FLOAT_STATE_TYPE* float_state) {
36   cell handler = 0;
37
38   if (exception == EXC_BAD_ACCESS) {
39     signal_fault_addr = MACH_EXC_STATE_FAULT(exc_state);
40     signal_fault_pc = (cell)MACH_PROGRAM_COUNTER(thread_state);
41     verify_memory_protection_error(signal_fault_addr);
42     handler = (cell)factor::memory_signal_handler_impl;
43   } else if (exception == EXC_ARITHMETIC && code != MACH_EXC_INTEGER_DIV) {
44     signal_fpu_status = fpu_status(mach_fpu_status(float_state));
45     mach_clear_fpu_status(float_state);
46     handler = (cell)factor::fp_signal_handler_impl;
47   } else {
48     switch (exception) {
49       case EXC_ARITHMETIC:
50         signal_number = SIGFPE;
51         break;
52       case EXC_BAD_INSTRUCTION:
53         signal_number = SIGILL;
54         break;
55       default:
56         signal_number = SIGABRT;
57         break;
58     }
59
60     handler = (cell)factor::synchronous_signal_handler_impl;
61   }
62
63   FACTOR_ASSERT(handler != 0);
64
65   dispatch_signal_handler((cell*)&MACH_STACK_POINTER(thread_state),
66                           (cell*)&MACH_PROGRAM_COUNTER(thread_state),
67                           (cell)handler);
68 }
69
70 static void call_fault_handler(mach_port_t thread, exception_type_t exception,
71                                exception_data_type_t code,
72                                MACH_EXC_STATE_TYPE* exc_state,
73                                MACH_THREAD_STATE_TYPE* thread_state,
74                                MACH_FLOAT_STATE_TYPE* float_state) {
75   /* Look up the VM instance involved */
76   THREADHANDLE thread_id = pthread_from_mach_thread_np(thread);
77   FACTOR_ASSERT(thread_id);
78   std::map<THREADHANDLE, factor_vm*>::const_iterator vm =
79       thread_vms.find(thread_id);
80
81   /* Handle the exception */
82   if (vm != thread_vms.end())
83     vm->second->call_fault_handler(exception, code, exc_state, thread_state,
84                                    float_state);
85 }
86
87 /* Handle an exception by invoking the user's fault handler and/or forwarding
88    the duty to the previously installed handlers.       */
89 extern "C" kern_return_t catch_exception_raise(
90     mach_port_t exception_port, mach_port_t thread, mach_port_t task,
91     exception_type_t exception, exception_data_t code,
92     mach_msg_type_number_t code_count) {
93   /* 10.6 likes to report exceptions from child processes too. Ignore those */
94   if (task != mach_task_self())
95     return KERN_FAILURE;
96
97   /* Get fault information and the faulting thread's register contents..
98      See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/thread_get_state.html. */
99   MACH_EXC_STATE_TYPE exc_state;
100   mach_msg_type_number_t exc_state_count = MACH_EXC_STATE_COUNT;
101   if (thread_get_state(thread, MACH_EXC_STATE_FLAVOR, (natural_t*)&exc_state,
102                        &exc_state_count) !=
103       KERN_SUCCESS) {
104     /* The thread is supposed to be suspended while the exception
105        handler is called. This shouldn't fail. */
106     return KERN_FAILURE;
107   }
108
109   MACH_THREAD_STATE_TYPE thread_state;
110   mach_msg_type_number_t thread_state_count = MACH_THREAD_STATE_COUNT;
111   if (thread_get_state(thread, MACH_THREAD_STATE_FLAVOR,
112                        (natural_t*)&thread_state, &thread_state_count) !=
113       KERN_SUCCESS) {
114     /* The thread is supposed to be suspended while the exception
115        handler is called. This shouldn't fail. */
116     return KERN_FAILURE;
117   }
118
119   MACH_FLOAT_STATE_TYPE float_state;
120   mach_msg_type_number_t float_state_count = MACH_FLOAT_STATE_COUNT;
121   if (thread_get_state(thread, MACH_FLOAT_STATE_FLAVOR,
122                        (natural_t*)&float_state, &float_state_count) !=
123       KERN_SUCCESS) {
124     /* The thread is supposed to be suspended while the exception
125        handler is called. This shouldn't fail. */
126     return KERN_FAILURE;
127   }
128
129   /* Modify registers so to have the thread resume executing the
130      fault handler */
131   call_fault_handler(thread, exception, code[0], &exc_state, &thread_state,
132                      &float_state);
133
134   /* Set the faulting thread's register contents..
135      See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/thread_set_state.html. */
136   if (thread_set_state(thread, MACH_FLOAT_STATE_FLAVOR,
137                        (natural_t*)&float_state, float_state_count) !=
138       KERN_SUCCESS) {
139     return KERN_FAILURE;
140   }
141
142   if (thread_set_state(thread, MACH_THREAD_STATE_FLAVOR,
143                        (natural_t*)&thread_state, thread_state_count) !=
144       KERN_SUCCESS) {
145     return KERN_FAILURE;
146   }
147
148   return KERN_SUCCESS;
149 }
150
151 /* The main function of the thread listening for exceptions.  */
152 static void* mach_exception_thread(void* arg) {
153   for (;;) {
154     /* These two structures contain some private kernel data. We don't need
155        to access any of it so we don't bother defining a proper struct. The
156        correct definitions are in the xnu source code. */
157     /* Buffer for a message to be received. */
158     struct {
159       mach_msg_header_t head;
160       mach_msg_body_t msgh_body;
161       char data[1024];
162     } msg;
163     /* Buffer for a reply message. */
164     struct {
165       mach_msg_header_t head;
166       char data[1024];
167     } reply;
168
169     mach_msg_return_t retval;
170
171     /* Wait for a message on the exception port. */
172     retval =
173         mach_msg(&msg.head, MACH_RCV_MSG | MACH_RCV_LARGE, 0, sizeof(msg),
174                  our_exception_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
175     if (retval != MACH_MSG_SUCCESS) {
176       abort();
177     }
178
179     /* Handle the message: Call exc_server, which will call
180        catch_exception_raise and produce a reply message. */
181     exc_server(&msg.head, &reply.head);
182
183     /* Send the reply. */
184     if (mach_msg(&reply.head, MACH_SEND_MSG, reply.head.msgh_size, 0,
185                  MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL) !=
186         MACH_MSG_SUCCESS) {
187       abort();
188     }
189   }
190   return NULL;  // quiet warning
191 }
192
193 /* Initialize the Mach exception handler thread. */
194 void mach_initialize() {
195   mach_port_t self;
196   exception_mask_t mask;
197
198   self = mach_task_self();
199
200   /* Allocate a port on which the thread shall listen for exceptions. */
201   if (mach_port_allocate(self, MACH_PORT_RIGHT_RECEIVE, &our_exception_port) !=
202       KERN_SUCCESS)
203     fatal_error("mach_port_allocate() failed", 0);
204
205   /* See
206    * http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/mach_port_insert_right.html.
207    */
208   if (mach_port_insert_right(self, our_exception_port, our_exception_port,
209                              MACH_MSG_TYPE_MAKE_SEND) !=
210       KERN_SUCCESS)
211     fatal_error("mach_port_insert_right() failed", 0);
212
213   /* The exceptions we want to catch. */
214   mask = EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC;
215
216   /* Create the thread listening on the exception port. */
217   start_thread(mach_exception_thread, NULL);
218
219   /* Replace the exception port info for these exceptions with our own.
220      Note that we replace the exception port for the entire task, not only
221      for a particular thread. This has the effect that when our exception
222      port gets the message, the thread specific exception port has already
223      been asked, and we don't need to bother about it. See
224      http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/task_set_exception_ports.html. */
225   if (task_set_exception_ports(self, mask, our_exception_port,
226                                EXCEPTION_DEFAULT, MACHINE_THREAD_STATE) !=
227       KERN_SUCCESS)
228     fatal_error("task_set_exception_ports() failed", 0);
229 }
230
231 }