]> gitweb.factorcode.org Git - factor.git/blob - vm/mach_signal.cpp
Merge branch 'master' into new_gc
[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
18 /* The exception port on which our thread listens. */
19 mach_port_t our_exception_port;
20
21 /* The following sources were used as a *reference* for this exception handling
22 code:
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(
32         exception_type_t exception,
33         exception_data_type_t code,
34         MACH_EXC_STATE_TYPE *exc_state,
35         MACH_THREAD_STATE_TYPE *thread_state,
36         MACH_FLOAT_STATE_TYPE *float_state)
37 {
38         /* There is a race condition here, but in practice an exception
39         delivered during stack frame setup/teardown or while transitioning
40         from Factor to C is a sign of things seriously gone wrong, not just
41         a divide by zero or stack underflow in the listener */
42
43         /* Are we in compiled Factor code? Then use the current stack pointer */
44         if(in_code_heap_p(MACH_PROGRAM_COUNTER(thread_state)))
45                 signal_callstack_top = (stack_frame *)MACH_STACK_POINTER(thread_state);
46         /* Are we in C? Then use the saved callstack top */
47         else
48                 signal_callstack_top = NULL;
49
50         MACH_STACK_POINTER(thread_state) = align_stack_pointer(MACH_STACK_POINTER(thread_state));
51
52         /* Now we point the program counter at the right handler function. */
53         if(exception == EXC_BAD_ACCESS)
54         {
55                 signal_fault_addr = MACH_EXC_STATE_FAULT(exc_state);
56                 MACH_PROGRAM_COUNTER(thread_state) = (cell)factor::memory_signal_handler_impl;
57         }
58         else if(exception == EXC_ARITHMETIC && code != MACH_EXC_INTEGER_DIV)
59         {
60                 signal_fpu_status = fpu_status(mach_fpu_status(float_state));
61                 mach_clear_fpu_status(float_state);
62                 MACH_PROGRAM_COUNTER(thread_state) = (cell)factor::fp_signal_handler_impl;
63         }
64         else
65         {
66                 switch(exception)
67                 {
68                 case EXC_ARITHMETIC: signal_number = SIGFPE; break;
69                 case EXC_BAD_INSTRUCTION: signal_number = SIGILL; break;
70                 default: signal_number = SIGABRT; break;
71                 }
72
73                 MACH_PROGRAM_COUNTER(thread_state) = (cell)factor::misc_signal_handler_impl;
74         }
75 }
76
77 static void call_fault_handler(
78         mach_port_t thread,
79         exception_type_t exception,
80         exception_data_type_t code,
81         MACH_EXC_STATE_TYPE *exc_state,
82         MACH_THREAD_STATE_TYPE *thread_state,
83         MACH_FLOAT_STATE_TYPE *float_state)
84 {
85         THREADHANDLE thread_id = pthread_from_mach_thread_np(thread);
86         assert(thread_id);
87         std::map<THREADHANDLE, factor_vm*>::const_iterator vm = thread_vms.find(thread_id);
88         if (vm != thread_vms.end())
89             vm->second->call_fault_handler(exception,code,exc_state,thread_state,float_state);
90 }
91
92 /* Handle an exception by invoking the user's fault handler and/or forwarding
93 the duty to the previously installed handlers.  */
94 extern "C"
95 kern_return_t
96 catch_exception_raise (mach_port_t exception_port,
97         mach_port_t thread,
98         mach_port_t task,
99         exception_type_t exception,
100         exception_data_t code,
101         mach_msg_type_number_t code_count)
102 {
103         MACH_EXC_STATE_TYPE exc_state;
104         MACH_THREAD_STATE_TYPE thread_state;
105         MACH_FLOAT_STATE_TYPE float_state;
106         mach_msg_type_number_t exc_state_count, thread_state_count, float_state_count;
107
108         /* Get fault information and the faulting thread's register contents..
109         
110         See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/thread_get_state.html.  */
111         exc_state_count = MACH_EXC_STATE_COUNT;
112         if (thread_get_state (thread, MACH_EXC_STATE_FLAVOR,
113                               (natural_t *)&exc_state, &exc_state_count)
114                 != KERN_SUCCESS)
115         {
116                 /* The thread is supposed to be suspended while the exception
117                 handler is called. This shouldn't fail. */
118                 return KERN_FAILURE;
119         }
120
121         thread_state_count = MACH_THREAD_STATE_COUNT;
122         if (thread_get_state (thread, MACH_THREAD_STATE_FLAVOR,
123                               (natural_t *)&thread_state, &thread_state_count)
124                 != KERN_SUCCESS)
125         {
126                 /* The thread is supposed to be suspended while the exception
127                 handler is called. This shouldn't fail. */
128                 return KERN_FAILURE;
129         }
130
131         float_state_count = MACH_FLOAT_STATE_COUNT;
132         if (thread_get_state (thread, MACH_FLOAT_STATE_FLAVOR,
133                               (natural_t *)&float_state, &float_state_count)
134                 != KERN_SUCCESS)
135         {
136                 /* The thread is supposed to be suspended while the exception
137                 handler is called. This shouldn't fail. */
138                 return KERN_FAILURE;
139         }
140
141         /* Modify registers so to have the thread resume executing the
142         fault handler */
143         call_fault_handler(thread,exception,code[0],&exc_state,&thread_state,&float_state);
144
145         /* Set the faulting thread's register contents..
146         
147         See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/thread_set_state.html.  */
148         if (thread_set_state (thread, MACH_FLOAT_STATE_FLAVOR,
149                               (natural_t *)&float_state, float_state_count)
150                 != KERN_SUCCESS)
151         {
152                 return KERN_FAILURE;
153         }
154
155         if (thread_set_state (thread, MACH_THREAD_STATE_FLAVOR,
156                               (natural_t *)&thread_state, thread_state_count)
157                 != KERN_SUCCESS)
158         {
159                 return KERN_FAILURE;
160         }
161
162         return KERN_SUCCESS;
163 }
164
165 /* The main function of the thread listening for exceptions.  */
166 static void *
167 mach_exception_thread (void *arg)
168 {
169         for (;;)
170         {
171                 /* These two structures contain some private kernel data. We don't need
172                 to access any of it so we don't bother defining a proper struct. The
173                 correct definitions are in the xnu source code. */
174                 /* Buffer for a message to be received.  */
175                 struct
176                 {
177                         mach_msg_header_t head;
178                         mach_msg_body_t msgh_body;
179                         char data[1024];
180                 }
181                 msg;
182                 /* Buffer for a reply message.  */
183                 struct
184                 {
185                         mach_msg_header_t head;
186                         char data[1024];
187                 }
188                 reply;
189
190                 mach_msg_return_t retval;
191
192                 /* Wait for a message on the exception port.  */
193                 retval = mach_msg (&msg.head, MACH_RCV_MSG | MACH_RCV_LARGE, 0,
194                         sizeof (msg), our_exception_port,
195                         MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
196                 if (retval != MACH_MSG_SUCCESS)
197                 {
198                         abort ();
199                 }
200
201                 /* Handle the message: Call exc_server, which will call
202                 catch_exception_raise and produce a reply message.  */
203                 exc_server (&msg.head, &reply.head);
204
205                 /* Send the reply.  */
206                 if (mach_msg (&reply.head, MACH_SEND_MSG, reply.head.msgh_size,
207                         0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL)
208                         != MACH_MSG_SUCCESS)
209                 {
210                         abort ();
211                 }
212         }
213 }
214
215 /* Initialize the Mach exception handler thread. */
216 void mach_initialize ()
217 {
218         mach_port_t self;
219         exception_mask_t mask;
220
221         self = mach_task_self ();
222
223         /* Allocate a port on which the thread shall listen for exceptions.  */
224         if (mach_port_allocate (self, MACH_PORT_RIGHT_RECEIVE, &our_exception_port)
225                 != KERN_SUCCESS)
226                 fatal_error("mach_port_allocate() failed",0);
227
228         /* See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/mach_port_insert_right.html.  */
229         if (mach_port_insert_right (self, our_exception_port, our_exception_port,
230                 MACH_MSG_TYPE_MAKE_SEND)
231                 != KERN_SUCCESS)
232                 fatal_error("mach_port_insert_right() failed",0);
233
234         /* The exceptions we want to catch. */
235         mask = EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC;
236
237         /* Create the thread listening on the exception port.  */
238         start_thread(mach_exception_thread,NULL);
239
240         /* Replace the exception port info for these exceptions with our own.
241         Note that we replace the exception port for the entire task, not only
242         for a particular thread.  This has the effect that when our exception
243         port gets the message, the thread specific exception port has already
244         been asked, and we don't need to bother about it.
245         See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/task_set_exception_ports.html.  */
246         if (task_set_exception_ports (self, mask, our_exception_port,
247                 EXCEPTION_DEFAULT, MACHINE_THREAD_STATE)
248                 != KERN_SUCCESS)
249                 fatal_error("task_set_exception_ports() failed",0);
250 }
251
252 }