1 /*
2 * sigsegv_darwin_x86.cpp - x86 Darwin SIGSEGV handler
3 *
4 * Copyright (c) 2006 Milan Jurik of ARAnyM dev team (see AUTHORS)
5 *
6 * Inspired by Bernie Meyer's UAE-JIT and Gwenole Beauchesne's Basilisk II-JIT
7 *
8 * This file is part of the ARAnyM project which builds a new and powerful
9 * TOS/FreeMiNT compatible virtual machine running on almost any hardware.
10 *
11 * ARAnyM is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * ARAnyM is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with ARAnyM; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
25 * Last modified: 2013-06-16 Jens Heitmann
26 *
27 */
28
29 #include "sysdeps.h"
30 #include "cpu_emulation.h"
31 #include "memory-uae.h"
32 #define DEBUG 0
33 #include "debug.h"
34
35 //
36 //
37 // Darwin segmentation violation handler
38 // based on the code of Basilisk II
39 //
40 #include <pthread.h>
41
42 // Address type
43 typedef char * sigsegv_address_t;
44
45 // SIGSEGV handler return state
46 enum sigsegv_return_t {
47 SIGSEGV_RETURN_SUCCESS,
48 SIGSEGV_RETURN_FAILURE,
49 // SIGSEGV_RETURN_SKIP_INSTRUCTION,
50 };
51
52 // Define an address that is bound to be invalid for a program counter
53 const sigsegv_address_t SIGSEGV_INVALID_PC = (sigsegv_address_t)(-1);
54
55 extern "C" {
56 #include <mach/mach.h>
57 #include <mach/mach_error.h>
58
59 #ifdef CPU_i386
60 # undef MACH_EXCEPTION_CODES
61 # define MACH_EXCEPTION_CODES 0
62 # define MACH_EXCEPTION_DATA_T exception_data_t
63 # define MACH_EXCEPTION_DATA_TYPE_T exception_data_type_t
64 # define MACH_EXC_SERVER exc_server
65 # define CATCH_MACH_EXCEPTION_RAISE catch_exception_raise
66 # define MACH_EXCEPTION_RAISE exception_raise
67 # define MACH_EXCEPTION_RAISE_STATE exception_raise_state
68 # define MACH_EXCEPTION_RAISE_STATE_IDENTITY exception_raise_state_identity
69
70 #else
71
72 # define MACH_EXCEPTION_DATA_T mach_exception_data_t
73 # define MACH_EXCEPTION_DATA_TYPE_T mach_exception_data_type_t
74 # define MACH_EXC_SERVER mach_exc_server
75 # define CATCH_MACH_EXCEPTION_RAISE catch_mach_exception_raise
76 # define MACH_EXCEPTION_RAISE mach_exception_raise
77 # define MACH_EXCEPTION_RAISE_STATE mach_exception_raise_state
78 # define MACH_EXCEPTION_RAISE_STATE_IDENTITY mach_exception_raise_state_identity
79
80 #endif
81
82
83 // Extern declarations of mach functions
84 // dependend on the underlying architecture this are extern declarations
85 // for "mach" or "non mach" function names. 64 Bit requieres "mach_xxx"
86 // functions
87 //
88 extern boolean_t MACH_EXC_SERVER(mach_msg_header_t *, mach_msg_header_t *);
89
90 extern kern_return_t CATCH_MACH_EXCEPTION_RAISE(mach_port_t, mach_port_t,
91 mach_port_t, exception_type_t, MACH_EXCEPTION_DATA_T, mach_msg_type_number_t);
92
93 extern kern_return_t MACH_EXCEPTION_RAISE(mach_port_t, mach_port_t, mach_port_t,
94 exception_type_t, MACH_EXCEPTION_DATA_T, mach_msg_type_number_t);
95
96 extern kern_return_t MACH_EXCEPTION_RAISE_STATE(mach_port_t, exception_type_t,
97 MACH_EXCEPTION_DATA_T, mach_msg_type_number_t, thread_state_flavor_t *,
98 thread_state_t, mach_msg_type_number_t, thread_state_t, mach_msg_type_number_t *);
99
100 extern kern_return_t MACH_EXCEPTION_RAISE_STATE_IDENTITY(mach_port_t, mach_port_t, mach_port_t,
101 exception_type_t, MACH_EXCEPTION_DATA_T, mach_msg_type_number_t, thread_state_flavor_t *,
102 thread_state_t, mach_msg_type_number_t, thread_state_t, mach_msg_type_number_t *);
103
104 extern kern_return_t catch_mach_exception_raise_state(mach_port_t,
105 exception_type_t, MACH_EXCEPTION_DATA_T, mach_msg_type_number_t,
106 int *, thread_state_t, mach_msg_type_number_t, thread_state_t, mach_msg_type_number_t *);
107
108 extern kern_return_t catch_mach_exception_raise_state_identity(mach_port_t,
109 mach_port_t, mach_port_t, exception_type_t, MACH_EXCEPTION_DATA_T, mach_msg_type_number_t,
110 int *, thread_state_t, mach_msg_type_number_t, thread_state_t, mach_msg_type_number_t *);
111
112 }
113
114 // Could make this dynamic by looking for a result of MIG_ARRAY_TOO_LARGE
115 #define HANDLER_COUNT 64
116
117 // structure to tuck away existing exception handlers
118 typedef struct _ExceptionPorts {
119 mach_msg_type_number_t maskCount;
120 exception_mask_t masks[HANDLER_COUNT];
121 exception_handler_t handlers[HANDLER_COUNT];
122 exception_behavior_t behaviors[HANDLER_COUNT];
123 thread_state_flavor_t flavors[HANDLER_COUNT];
124 } ExceptionPorts;
125
126 #ifdef CPU_i386
127 # define STATE_REGISTER_TYPE uint32
128 # ifdef i386_SAVED_STATE
129 # define SIGSEGV_THREAD_STATE_TYPE struct i386_saved_state
130 # define SIGSEGV_THREAD_STATE_FLAVOR i386_SAVED_STATE
131 # define SIGSEGV_THREAD_STATE_COUNT i386_SAVED_STATE_COUNT
132 # define SIGSEGV_REGISTER_FILE ((unsigned long *)&state->edi) /* EDI is the first GPR we consider */
133 # else
134 # ifdef x86_THREAD_STATE32
135 /* MacOS X 10.5 or newer introduces the new names and deprecates the old ones */
136 # define SIGSEGV_THREAD_STATE_TYPE x86_thread_state32_t
137 # define SIGSEGV_THREAD_STATE_FLAVOR x86_THREAD_STATE32
138 # define SIGSEGV_THREAD_STATE_COUNT x86_THREAD_STATE32_COUNT
139 # define SIGSEGV_REGISTER_FILE ((unsigned long *)&state->__eax) /* EAX is the first GPR we consider */
140 # define SIGSEGV_FAULT_INSTRUCTION state->__eip
141
142 # else
143 /* MacOS X 10.4 and below */
144 # define SIGSEGV_THREAD_STATE_TYPE struct i386_thread_state
145 # define SIGSEGV_THREAD_STATE_FLAVOR i386_THREAD_STATE
146 # define SIGSEGV_THREAD_STATE_COUNT i386_THREAD_STATE_COUNT
147 # define SIGSEGV_REGISTER_FILE ((unsigned long *)&state->eax) /* EAX is the first GPR we consider */
148 # define SIGSEGV_FAULT_INSTRUCTION state->eip
149
150 # endif
151 # endif
152 #endif
153
154 #ifdef CPU_x86_64
155 # define STATE_REGISTER_TYPE uint64
156 # ifdef x86_THREAD_STATE64
157 # define SIGSEGV_THREAD_STATE_TYPE x86_thread_state64_t
158 # define SIGSEGV_THREAD_STATE_FLAVOR x86_THREAD_STATE64
159 # define SIGSEGV_THREAD_STATE_COUNT x86_THREAD_STATE64_COUNT
160 # define SIGSEGV_REGISTER_FILE ((unsigned long *)&state->__rax) /* EAX is the first GPR we consider */
161 # define SIGSEGV_FAULT_INSTRUCTION state->__rip
162 # endif
163 #endif
164
165 #define SIGSEGV_ERROR_CODE KERN_INVALID_ADDRESS
166 #define SIGSEGV_ERROR_CODE2 KERN_PROTECTION_FAILURE
167
168 #define SIGSEGV_FAULT_ADDRESS code[1]
169
170 enum {
171 #ifdef CPU_i386
172 #ifdef i386_SAVED_STATE
173 // same as FreeBSD (in Open Darwin 8.0.1)
174 REG_RIP = 10,
175 REG_RAX = 7,
176 REG_RCX = 6,
177 REG_RDX = 5,
178 REG_RBX = 4,
179 REG_RSP = 13,
180 REG_RBP = 2,
181 REG_RSI = 1,
182 REG_RDI = 0
183 #else
184 // new layout (MacOS X 10.4.4 for x86)
185 REG_RIP = 10,
186 REG_RAX = 0,
187 REG_RCX = 2,
188 REG_RDX = 3,
189 REG_RBX = 1,
190 REG_RSP = 7,
191 REG_RBP = 6,
192 REG_RSI = 5,
193 REG_RDI = 4
194 #endif
195 #else
196 #ifdef CPU_x86_64
197 REG_R8 = 8,
198 REG_R9 = 9,
199 REG_R10 = 10,
200 REG_R11 = 11,
201 REG_R12 = 12,
202 REG_R13 = 13,
203 REG_R14 = 14,
204 REG_R15 = 15,
205 REG_RDI = 4,
206 REG_RSI = 5,
207 REG_RBP = 6,
208 REG_RBX = 1,
209 REG_RDX = 3,
210 REG_RAX = 0,
211 REG_RCX = 2,
212 REG_RSP = 7,
213 REG_RIP = 16
214 #endif
215 #endif
216 };
217
218 // Type of a SIGSEGV handler. Returns boolean expressing successful operation
219 typedef sigsegv_return_t (*sigsegv_fault_handler_t)(sigsegv_address_t fault_address, sigsegv_address_t instruction_address,
220 SIGSEGV_THREAD_STATE_TYPE *state );
221
222 // Install a SIGSEGV handler. Returns boolean expressing success
223 extern bool sigsegv_install_handler(sigsegv_fault_handler_t handler);
224
225 // exception handler thread
226 static pthread_t exc_thread;
227
228 static mach_port_t _exceptionPort = MACH_PORT_NULL;
229
230 // place where old exception handler info is stored
231 static ExceptionPorts ports;
232
233 // User's SIGSEGV handler
234 static sigsegv_fault_handler_t sigsegv_fault_handler = 0;
235
236
237 #define MACH_CHECK_ERROR(name,ret) \
238 if (ret != KERN_SUCCESS) { \
239 mach_error(#name, ret); \
240 exit (1); \
241 }
242
243 #define MSG_SIZE 512
244 static char msgbuf[MSG_SIZE];
245 static char replybuf[MSG_SIZE];
246
247 #define CONTEXT_NAME state
248 #define CONTEXT_TYPE SIGSEGV_THREAD_STATE_TYPE
249 #define CONTEXT_ATYPE CONTEXT_TYPE *
250 #define CONTEXT_REGS SIGSEGV_REGISTER_FILE
251 #define CONTEXT_AEIP CONTEXT_REGS[REG_RIP]
252 #define CONTEXT_AEAX CONTEXT_REGS[REG_RAX]
253 #define CONTEXT_AEBX CONTEXT_REGS[REG_RBX]
254 #define CONTEXT_AECX CONTEXT_REGS[REG_RCX]
255 #define CONTEXT_AEDX CONTEXT_REGS[REG_RDX]
256 #define CONTEXT_AEBP CONTEXT_REGS[REG_RBP]
257 #define CONTEXT_AESI CONTEXT_REGS[REG_RSI]
258 #define CONTEXT_AEDI CONTEXT_REGS[REG_RDI]
259
260 #ifdef CPU_x86_64
261 #define CONTEXT_AEFLAGS CONTEXT_NAME->__rflags
262 #else
263 #ifdef x86_THREAD_STATE32
264 #define CONTEXT_AEFLAGS CONTEXT_NAME->__eflags
265 #else
266 #define CONTEXT_AEFLAGS CONTEXT_NAME->eflags
267 #endif
268 #endif
269
270 #if defined(CPU_i386) || defined(CPU_x86_64)
271
272 #include "sigsegv_common_x86.h"
273
sigsegv_handler(sigsegv_address_t fault_address,sigsegv_address_t,SIGSEGV_THREAD_STATE_TYPE * state)274 static sigsegv_return_t sigsegv_handler(sigsegv_address_t fault_address,
275 sigsegv_address_t /* fault_instruction */,
276 SIGSEGV_THREAD_STATE_TYPE *state) {
277 memptr addr = (memptr)(uintptr)((char *)fault_address - fixed_memory_offset);
278 #if DEBUG
279 if (addr >= 0xff000000)
280 addr &= 0x00ffffff;
281 if (addr < 0x00f00000 || addr > 0x00ffffff) // YYY
282 bug("\nsegfault: pc=%08x, " REG_RIP_NAME " =%p, addr=%p (0x%08x)", m68k_getpc(), (void *)CONTEXT_AEIP, fault_address, addr);
283 if (fault_address < (uintptr)(fixed_memory_offset - 0x1000000UL)
284 #ifdef CPU_x86_64
285 || fault_address >= ((uintptr)fixed_memory_offset + 0x100000000UL)
286 #endif
287 )
288 {
289 #ifdef HAVE_DISASM_X86
290 if (CONTEXT_AEIP != 0)
291 {
292 char buf[256];
293
294 x86_disasm((const uint8 *)CONTEXT_AEIP, buf, 1);
295 panicbug("%s", buf);
296 }
297 #endif
298 // raise(SIGBUS);
299 }
300 #endif
301 if (fault_address == 0 || CONTEXT_AEIP == 0)
302 {
303 real_segmentationfault();
304 /* not reached (hopefully) */
305 return SIGSEGV_RETURN_FAILURE;
306 }
307 handle_access_fault((CONTEXT_ATYPE) CONTEXT_NAME, addr);
308 return SIGSEGV_RETURN_SUCCESS;
309 }
310
311 #endif /* CPU_i386 || CPU_x86_64 */
312
313
314 /*
315 * SIGSEGV global handler
316 */
317
318 // This function handles the badaccess to memory.
319 // It is called from the signal handler or the exception handler.
handle_badaccess(mach_port_t thread,MACH_EXCEPTION_DATA_T code,SIGSEGV_THREAD_STATE_TYPE * state)320 static bool handle_badaccess(mach_port_t thread, MACH_EXCEPTION_DATA_T code, SIGSEGV_THREAD_STATE_TYPE *state)
321 {
322 // We must match the initial count when writing back the CPU state registers
323 kern_return_t krc;
324 mach_msg_type_number_t count;
325
326 D2(panicbug("handle badaccess"));
327
328 count = SIGSEGV_THREAD_STATE_COUNT;
329 krc = thread_get_state(thread, SIGSEGV_THREAD_STATE_FLAVOR, (thread_state_t)state, &count);
330 MACH_CHECK_ERROR (thread_get_state, krc);
331
332 sigsegv_address_t fault_address = (sigsegv_address_t)SIGSEGV_FAULT_ADDRESS;
333 sigsegv_address_t fault_instruction = (sigsegv_address_t)SIGSEGV_FAULT_INSTRUCTION;
334
335 D2(panicbug("code:%lx %lx %lx %lx", (long)code[0], (long)code[1], (long)code[2], (long)code[3]));
336 D2(panicbug("regs eax:%lx", CONTEXT_AEAX));
337 D2(panicbug("regs ebx:%lx", CONTEXT_AEBX));
338 D2(panicbug("regs ecx:%lx", CONTEXT_AECX));
339 D2(panicbug("regs edx:%lx", CONTEXT_AEDX));
340 D2(panicbug("regs ebp:%lx", CONTEXT_AEBP));
341 D2(panicbug("regs esi:%lx", CONTEXT_AESI));
342 D2(panicbug("regs edi:%lx", CONTEXT_AEDI));
343 D2(panicbug("regs eip:%lx", CONTEXT_AEIP));
344 D2(panicbug("regs eflags:%lx", CONTEXT_AEFLAGS));
345
346 // Call user's handler
347 if (sigsegv_fault_handler(fault_address, fault_instruction, state) == SIGSEGV_RETURN_SUCCESS) {
348 D2(panicbug("esi:%lx", CONTEXT_AESI));
349 krc = thread_set_state(thread,
350 SIGSEGV_THREAD_STATE_FLAVOR, (thread_state_t)state,
351 count);
352 MACH_CHECK_ERROR (thread_set_state, krc);
353 D(panicbug("return from handle bad access with true"));
354 return true;
355 }
356
357 D(panicbug("return from handle bad access with false"));
358 return false;
359 }
360
361
362 /*
363 * We need to forward all exceptions that we do not handle.
364 * This is important, there are many exceptions that may be
365 * handled by other exception handlers. For example debuggers
366 * use exceptions and the exception handler is in another
367 * process in such a case. (Timothy J. Wood states in his
368 * message to the list that he based this code on that from
369 * gdb for Darwin.)
370 */
371 static inline kern_return_t
forward_exception(mach_port_t thread_port,mach_port_t task_port,exception_type_t exception_type,MACH_EXCEPTION_DATA_T exception_data,mach_msg_type_number_t data_count,ExceptionPorts * oldExceptionPorts)372 forward_exception(mach_port_t thread_port,
373 mach_port_t task_port,
374 exception_type_t exception_type,
375 MACH_EXCEPTION_DATA_T exception_data,
376 mach_msg_type_number_t data_count,
377 ExceptionPorts *oldExceptionPorts)
378 {
379 kern_return_t kret;
380 unsigned int portIndex;
381 mach_port_t port;
382 exception_behavior_t behavior;
383 thread_state_flavor_t flavor;
384 thread_state_data_t thread_state;
385 mach_msg_type_number_t thread_state_count;
386
387 D(panicbug("forward_exception\n"));
388
389 for (portIndex = 0; portIndex < oldExceptionPorts->maskCount; portIndex++) {
390 if (oldExceptionPorts->masks[portIndex] & (1 << exception_type)) {
391 // This handler wants the exception
392 break;
393 }
394 }
395
396 if (portIndex >= oldExceptionPorts->maskCount) {
397 panicbug("No handler for exception_type = %d. Not fowarding\n", exception_type);
398 return KERN_FAILURE;
399 }
400
401 port = oldExceptionPorts->handlers[portIndex];
402 behavior = oldExceptionPorts->behaviors[portIndex];
403 flavor = oldExceptionPorts->flavors[portIndex];
404
405 if (flavor && !VALID_THREAD_STATE_FLAVOR(flavor)) {
406 fprintf(stderr, "Invalid thread_state flavor = %d. Not forwarding\n", flavor);
407 return KERN_FAILURE;
408 }
409
410 /*
411 fprintf(stderr, "forwarding exception, port = 0x%x, behaviour = %d, flavor = %d\n", port, behavior, flavor);
412 */
413
414 if (behavior != EXCEPTION_DEFAULT) {
415 thread_state_count = THREAD_STATE_MAX;
416 kret = thread_get_state (thread_port, flavor, (natural_t *)&thread_state,
417 &thread_state_count);
418 MACH_CHECK_ERROR (thread_get_state, kret);
419 }
420
421 switch (behavior) {
422 case EXCEPTION_DEFAULT:
423 // fprintf(stderr, "forwarding to exception_raise\n");
424 kret = MACH_EXCEPTION_RAISE(port, thread_port, task_port, exception_type,
425 exception_data, data_count);
426 MACH_CHECK_ERROR (MACH_EXCEPTION_RAISE, kret);
427 break;
428 case EXCEPTION_STATE:
429 // fprintf(stderr, "forwarding to exception_raise_state\n");
430 kret = MACH_EXCEPTION_RAISE_STATE(port, exception_type, exception_data,
431 data_count, &flavor,
432 (natural_t *)&thread_state, thread_state_count,
433 (natural_t *)&thread_state, &thread_state_count);
434 MACH_CHECK_ERROR (MACH_EXCEPTION_RAISE_STATE, kret);
435 break;
436 case EXCEPTION_STATE_IDENTITY:
437 // fprintf(stderr, "forwarding to exception_raise_state_identity\n");
438 kret = MACH_EXCEPTION_RAISE_STATE_IDENTITY(port, thread_port, task_port,
439 exception_type, exception_data,
440 data_count, &flavor,
441 (natural_t *)&thread_state, thread_state_count,
442 (natural_t *)&thread_state, &thread_state_count);
443 MACH_CHECK_ERROR (MACH_EXCEPTION_RAISE_STATE_IDENTITY, kret);
444 break;
445 default:
446 panicbug("forward_exception got unknown behavior");
447 kret = KERN_FAILURE;
448 break;
449 }
450
451 if (behavior != EXCEPTION_DEFAULT) {
452 kret = thread_set_state (thread_port, flavor, (natural_t *)&thread_state,
453 thread_state_count);
454 MACH_CHECK_ERROR (thread_set_state, kret);
455 }
456
457 return kret;
458 }
459
460 /*
461 * This is the code that actually handles the exception.
462 * It is called by exc_server. For Darwin 5 Apple changed
463 * this a bit from how this family of functions worked in
464 * Mach. If you are familiar with that it is a little
465 * different. The main variation that concerns us here is
466 * that code is an array of exception specific codes and
467 * codeCount is a count of the number of codes in the code
468 * array. In typical Mach all exceptions have a code
469 * and sub-code. It happens to be the case that for a
470 * EXC_BAD_ACCESS exception the first entry is the type of
471 * bad access that occurred and the second entry is the
472 * faulting address so these entries correspond exactly to
473 * how the code and sub-code are used on Mach.
474 *
475 * This is a MIG interface. No code in Basilisk II should
476 * call this directley. This has to have external C
477 * linkage because that is what exc_server expects.
478 */
479 __attribute__ ((visibility("default")))
480 kern_return_t
CATCH_MACH_EXCEPTION_RAISE(mach_port_t exception_port,mach_port_t thread,mach_port_t task,exception_type_t exception,MACH_EXCEPTION_DATA_T code,mach_msg_type_number_t codeCount)481 CATCH_MACH_EXCEPTION_RAISE(mach_port_t exception_port,
482 mach_port_t thread,
483 mach_port_t task,
484 exception_type_t exception,
485 MACH_EXCEPTION_DATA_T code,
486 mach_msg_type_number_t codeCount)
487 {
488 SIGSEGV_THREAD_STATE_TYPE state;
489 kern_return_t krc;
490
491 D(panicbug("catch_exception_raise: %d", exception));
492
493 if (exception == EXC_BAD_ACCESS) {
494 switch (code[0]) {
495 case KERN_PROTECTION_FAILURE:
496 case KERN_INVALID_ADDRESS:
497 if (handle_badaccess(thread, code, &state))
498 return KERN_SUCCESS;
499 break;
500 }
501 }
502
503
504 // In Mach we do not need to remove the exception handler.
505 // If we forward the exception, eventually some exception handler
506 // will take care of this exception.
507 krc = forward_exception(thread, task, exception, code, codeCount, &ports);
508
509 return krc;
510 }
511
512 /* XXX: borrowed from launchd and gdb */
513 kern_return_t
catch_mach_exception_raise_state(mach_port_t exception_port,exception_type_t exception,MACH_EXCEPTION_DATA_T code,mach_msg_type_number_t code_count,int * flavor,thread_state_t old_state,mach_msg_type_number_t old_state_count,thread_state_t new_state,mach_msg_type_number_t * new_state_count)514 catch_mach_exception_raise_state(mach_port_t exception_port,
515 exception_type_t exception,
516 MACH_EXCEPTION_DATA_T code,
517 mach_msg_type_number_t code_count,
518 int *flavor,
519 thread_state_t old_state,
520 mach_msg_type_number_t old_state_count,
521 thread_state_t new_state,
522 mach_msg_type_number_t *new_state_count)
523 {
524 memcpy(new_state, old_state, old_state_count * sizeof(old_state[0]));
525 *new_state_count = old_state_count;
526 return KERN_SUCCESS;
527 }
528
529 /* XXX: borrowed from launchd and gdb */
530 kern_return_t
catch_mach_exception_raise_state_identity(mach_port_t exception_port,mach_port_t thread_port,mach_port_t task_port,exception_type_t exception,MACH_EXCEPTION_DATA_T code,mach_msg_type_number_t code_count,int * flavor,thread_state_t old_state,mach_msg_type_number_t old_state_count,thread_state_t new_state,mach_msg_type_number_t * new_state_count)531 catch_mach_exception_raise_state_identity(mach_port_t exception_port,
532 mach_port_t thread_port,
533 mach_port_t task_port,
534 exception_type_t exception,
535 MACH_EXCEPTION_DATA_T code,
536 mach_msg_type_number_t code_count,
537 int *flavor,
538 thread_state_t old_state,
539 mach_msg_type_number_t old_state_count,
540 thread_state_t new_state,
541 mach_msg_type_number_t *new_state_count)
542 {
543 kern_return_t kret;
544
545 memcpy(new_state, old_state, old_state_count * sizeof(old_state[0]));
546 *new_state_count = old_state_count;
547
548 kret = mach_port_deallocate(mach_task_self(), task_port);
549 MACH_CHECK_ERROR(mach_port_deallocate, kret);
550 kret = mach_port_deallocate(mach_task_self(), thread_port);
551 MACH_CHECK_ERROR(mach_port_deallocate, kret);
552
553 return KERN_SUCCESS;
554 }
555
556 /*
557 * This is the entry point for the exception handler thread. The job
558 * of this thread is to wait for exception messages on the exception
559 * port that was setup beforehand and to pass them on to exc_server.
560 * exc_server is a MIG generated function that is a part of Mach.
561 * Its job is to decide what to do with the exception message. In our
562 * case exc_server calls catch_exception_raise on our behalf. After
563 * exc_server returns, it is our responsibility to send the reply.
564 */
565 static void *
handleExceptions(void *)566 handleExceptions(void * /*priv*/)
567 {
568 D(panicbug("handleExceptions\n"));
569
570 mach_msg_header_t *msg, *reply;
571 kern_return_t krc;
572
573 msg = (mach_msg_header_t *)msgbuf;
574 reply = (mach_msg_header_t *)replybuf;
575
576 for (;;) {
577 krc = mach_msg(msg, MACH_RCV_MSG, MSG_SIZE, MSG_SIZE,
578 _exceptionPort, 0, MACH_PORT_NULL);
579 MACH_CHECK_ERROR(mach_msg, krc);
580
581 if (!MACH_EXC_SERVER(msg, reply)) {
582 fprintf(stderr, "exc_server hated the message\n");
583 exit(1);
584 }
585
586 krc = mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0,
587 msg->msgh_local_port, 0, MACH_PORT_NULL);
588 if (krc != KERN_SUCCESS) {
589 fprintf(stderr, "Error sending message to original reply port, krc = %d, %s",
590 krc, mach_error_string(krc));
591 exit(1);
592 }
593 }
594 }
595
sigsegv_do_install_handler(sigsegv_fault_handler_t handler)596 static bool sigsegv_do_install_handler(sigsegv_fault_handler_t handler)
597 {
598 D(panicbug("sigsegv_do_install_handler\n"));
599
600 //
601 //Except for the exception port functions, this should be
602 //pretty much stock Mach. If later you choose to support
603 //other Mach's besides Darwin, just check for __MACH__
604 //here and __APPLE__ where the actual differences are.
605 //
606 if (sigsegv_fault_handler != NULL) {
607 sigsegv_fault_handler = handler;
608 return true;
609 }
610
611 kern_return_t krc;
612
613 // create the the exception port
614 krc = mach_port_allocate(mach_task_self(),
615 MACH_PORT_RIGHT_RECEIVE, &_exceptionPort);
616 if (krc != KERN_SUCCESS) {
617 mach_error("mach_port_allocate", krc);
618 return false;
619 }
620
621 // add a port send right
622 krc = mach_port_insert_right(mach_task_self(),
623 _exceptionPort, _exceptionPort,
624 MACH_MSG_TYPE_MAKE_SEND);
625 if (krc != KERN_SUCCESS) {
626 mach_error("mach_port_insert_right", krc);
627 return false;
628 }
629
630 // get the old exception ports
631 ports.maskCount = sizeof (ports.masks) / sizeof (ports.masks[0]);
632 krc = thread_get_exception_ports(mach_thread_self(), EXC_MASK_BAD_ACCESS, ports.masks,
633 &ports.maskCount, ports.handlers, ports.behaviors, ports.flavors);
634 if (krc != KERN_SUCCESS) {
635 mach_error("thread_get_exception_ports", krc);
636 return false;
637 }
638
639 // set the new exception port
640 //
641 // We could have used EXCEPTION_STATE_IDENTITY instead of
642 // EXCEPTION_DEFAULT to get the thread state in the initial
643 // message, but it turns out that in the common case this is not
644 // neccessary. If we need it we can later ask for it from the
645 // suspended thread.
646 //
647 // Even with THREAD_STATE_NONE, Darwin provides the program
648 // counter in the thread state. The comments in the header file
649 // seem to imply that you can count on the GPR's on an exception
650 // as well but just to be safe I use MACHINE_THREAD_STATE because
651 // you have to ask for all of the GPR's anyway just to get the
652 // program counter. In any case because of update effective
653 // address from immediate and update address from effective
654 // addresses of ra and rb modes (as good an name as any for these
655 // addressing modes) used in PPC instructions, you will need the
656 // GPR state anyway.
657 krc = thread_set_exception_ports(mach_thread_self(), EXC_MASK_BAD_ACCESS, _exceptionPort,
658 EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, SIGSEGV_THREAD_STATE_FLAVOR);
659 if (krc != KERN_SUCCESS) {
660 mach_error("thread_set_exception_ports", krc);
661 return false;
662 }
663
664 // create the exception handler thread
665 if (pthread_create(&exc_thread, NULL, &handleExceptions, NULL) != 0) {
666 panicbug("creation of exception thread failed\n");
667 return false;
668 }
669
670 // do not care about the exception thread any longer, let is run standalone
671 (void)pthread_detach(exc_thread);
672
673 D(panicbug("Sigsegv installed\n"));
674 sigsegv_fault_handler = handler;
675 return true;
676 }
677
install_sigsegv()678 void install_sigsegv() {
679 sigsegv_do_install_handler(sigsegv_handler);
680 }
681
uninstall_sigsegv()682 void uninstall_sigsegv()
683 {
684 /* TODO */
685 }
686