]> gitweb.factorcode.org Git - factor.git/blob - vm/mach_signal.cpp
vm: replace block comments /**/ with line comments //
[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 }