Try to make the :lurking-threads test more robust.
[sbcl.git] / src / runtime / x86-64-darwin-os.c
blob3cff8701b200a955aff043e530c71c18c0d3fe6e
1 #ifdef LISP_FEATURE_SB_THREAD
2 #include <mach/mach_init.h>
3 #endif
5 #include "thread.h"
6 #include "validate.h"
7 #include "runtime.h"
8 #include "interrupt.h"
9 #include "x86-64-darwin-os.h"
10 #include "x86-64-arch.h"
11 #include "genesis/fdefn.h"
12 #include "gc-internal.h"
13 #include "arch.h"
15 #include <mach/mach.h>
16 #include <mach/mach_error.h>
17 #include <mach/mach_types.h>
18 #include <mach/sync_policy.h>
19 #include <mach/machine/thread_state.h>
20 #include <mach/machine/thread_status.h>
21 #include <sys/_types.h>
22 #include <sys/ucontext.h>
23 #include <pthread.h>
24 #include <assert.h>
25 #include <stdlib.h>
26 #include <stdio.h>
28 #if __DARWIN_UNIX03
29 #include <sys/_structs.h>
30 #endif
32 #if __DARWIN_UNIX03
34 typedef struct __darwin_ucontext darwin_ucontext;
36 #define rip __rip
37 #define rsp __rsp
38 #define rbp __rbp
39 #define rax __rax
40 #define rbx __rbx
41 #define rcx __rcx
42 #define rdx __rdx
43 #define rsi __rsi
44 #define rdi __rdi
45 #define r8 __r8
46 #define r9 __r9
47 #define faultvaddr __faultvaddr
48 #define ss __ss
49 #define es __es
50 #define fs __fs
51 #define rflags __rflags
53 #define fpu_fcw __fpu_fcw
54 #define fpu_mxcsr __fpu_mxcsr
56 #else
58 typedef struct ucontext darwin_ucontext;
60 #endif
62 #ifdef x86_AVX_STATE64_COUNT
63 typedef _STRUCT_MCONTEXT_AVX64 darwin_mcontext;
64 #else
65 typedef _STRUCT_MCONTEXT64 darwin_mcontext;
66 #endif
68 #ifdef LISP_FEATURE_MACH_EXCEPTION_HANDLER
70 void sigill_handler(int signal, siginfo_t *siginfo, os_context_t *context);
71 void sigtrap_handler(int signal, siginfo_t *siginfo, os_context_t *context);
72 void memory_fault_handler(int signal, siginfo_t *siginfo,
73 os_context_t *context);
75 void
76 undefined_alien_handler(int signal, siginfo_t *siginfo, os_context_t *context) {
77 arrange_return_to_lisp_function
78 (context, StaticSymbolFunction(UNDEFINED_ALIEN_VARIABLE_ERROR));
81 /* This executes in the faulting thread as part of the signal
82 * emulation. It is passed a context with the uc_mcontext field
83 * pointing to a valid block of memory. */
84 void build_fake_signal_context(darwin_ucontext *context,
85 x86_thread_state64_t *thread_state,
86 x86_float_state64_t *float_state) {
87 block_blockable_signals(&context->uc_sigmask);
88 context->uc_mcontext->ss = *thread_state;
89 context->uc_mcontext->fs = *float_state;
92 /* This executes in the faulting thread as part of the signal
93 * emulation. It is effectively the inverse operation from above. */
94 void update_thread_state_from_context(x86_thread_state64_t *thread_state,
95 x86_float_state64_t *float_state,
96 darwin_ucontext *context) {
97 *thread_state = context->uc_mcontext->ss;
98 *float_state = context->uc_mcontext->fs;
99 thread_sigmask(SIG_SETMASK, &context->uc_sigmask, NULL);
102 boolean will_exhaust_stack(struct thread * th, x86_thread_state64_t *context, int size) {
103 __uint64_t sp = context->rsp - size;
105 if(sp < (__uint64_t)(CONTROL_STACK_HARD_GUARD_PAGE(th) + os_vm_page_size)) {
106 lose("Control stack exhausted during signal emulation: PC: %p",
107 context->rip);
110 if(sp < (__uint64_t)(CONTROL_STACK_GUARD_PAGE(th) + os_vm_page_size) &&
111 th->control_stack_guard_page_protected != NIL) {
112 /* We hit the end of the control stack: disable guard page
113 * protection so the error handler has some headroom, protect the
114 * previous page so that we can catch returns from the guard page
115 * and restore it. */
116 lower_thread_control_stack_guard_page(th);
117 context->rsp = (__uint64_t)(CONTROL_STACK_GUARD_PAGE(th) + os_vm_page_size);
118 return 1;
120 return 0;
123 /* Modify a context to push new data on its stack. */
124 void push_context(u64 data, x86_thread_state64_t *context)
126 u64* stack_pointer = (u64*)context->rsp - 1;
128 context->rsp = (__uint64_t) stack_pointer;
129 *stack_pointer = data;
132 void align_context_stack(x86_thread_state64_t *context)
134 /* 16-byte align the stack. */
135 context->rsp &= ~15;
138 void *stack_allocate(struct thread * th, x86_thread_state64_t *context, size_t size)
141 context->rsp = context->rsp - size;
142 return (void*)context->rsp;
145 void signal_emulation_wrapper(darwin_mcontext *mcontext,
146 int signal,
147 os_vm_address_t addr,
148 void (*handler)(int, siginfo_t *, void *))
150 siginfo_t siginfo;
151 darwin_ucontext context;
153 siginfo.si_signo = signal;
154 siginfo.si_addr = addr;
156 context.uc_mcontext = (_STRUCT_MCONTEXT64 *)mcontext;
158 /* when BSD signals are fired, they mask they signals in sa_mask
159 which always seem to be the blockable_sigset, for us, so we
160 need to:
161 1) save the current sigmask
162 2) block blockable signals
163 3) call the signal handler
164 4) restore the sigmask */
166 block_blockable_signals(&context.uc_sigmask);
168 handler(signal, &siginfo, &context);
170 thread_sigmask(SIG_SETMASK, &context.uc_sigmask, NULL);
172 /* Trap to restore the signal context. */
173 asm volatile (".quad 0xffffffffffff0b0f"
174 : : "a" (mcontext));
177 void call_signal_emulator_in_context(x86_thread_state64_t *context,
178 darwin_mcontext *mcontext,
179 int signal,
180 os_vm_address_t addr,
181 void* handler)
184 align_context_stack(context);
185 push_context(context->rip, context);
186 context->rdi = (u64) mcontext;
187 context->rsi = signal;
188 context->rdx = (u64) addr;
189 context->rcx = (u64) handler;
191 context->rip = (u64) signal_emulation_wrapper;
194 /* Call CONTROL_STACK_EXHAUSTED_ERROR directly, without emulating
195 signals. It doesn't need any signal contexts and it's better to use
196 as little stack as possible. */
197 void call_stack_exhausted_in_context(x86_thread_state64_t *context)
199 align_context_stack(context);
200 push_context(context->rip, context);
201 context->rdi = (u64) StaticSymbolFunction(CONTROL_STACK_EXHAUSTED_ERROR);
202 context->rip = (u64) funcall0;
205 #if defined DUMP_CONTEXT
206 void dump_context(x86_thread_state64_t *context)
208 int i;
209 u64 *stack_pointer;
211 printf("rax: %08lx rcx: %08lx rdx: %08lx rbx: %08lx\n",
212 context->rax, context->rcx, context->rdx, context->rbx);
213 printf("rsp: %08lx rbp: %08lx rsi: %08lx rdi: %08lx\n",
214 context->rsp, context->rbp, context->rsi, context->rdi);
215 printf("rip: %08lx eflags: %08lx\n",
216 context->rip, context->rflags);
217 printf("cs: %04hx ds: %04hx es: %04hx "
218 "ss: %04hx fs: %04hx gs: %04hx\n",
219 context->cs, context->ds, context->rs,
220 context->ss, context->fs, context->gs);
222 stack_pointer = (u64 *)context->rsp;
223 for (i = 0; i < 48; i+=4) {
224 printf("%08x: %08x %08x %08x %08x\n",
225 context->rsp + (i * 4),
226 stack_pointer[i],
227 stack_pointer[i+1],
228 stack_pointer[i+2],
229 stack_pointer[i+3]);
232 #endif
234 kern_return_t
235 catch_exception_raise(mach_port_t exception_port,
236 mach_port_t thread,
237 mach_port_t task,
238 exception_type_t exception,
239 exception_data_t code_vector,
240 mach_msg_type_number_t code_count)
242 kern_return_t ret = KERN_SUCCESS, dealloc_ret;
243 int signal, rip_offset = 0;
244 void (*handler)(int, siginfo_t *, os_context_t *);
246 x86_thread_state64_t thread_state;
247 mach_msg_type_number_t thread_state_count = x86_THREAD_STATE64_COUNT;
249 #ifdef x86_AVX_STATE64_COUNT
250 mach_msg_type_number_t float_state_count = avx_supported? x86_AVX_STATE64_COUNT : x86_FLOAT_STATE64_COUNT;
251 int float_state_flavor = avx_supported? x86_AVX_STATE64 : x86_FLOAT_STATE64;
252 #else
253 mach_msg_type_number_t float_state_count = x86_FLOAT_STATE64_COUNT;
254 int float_state_flavor = x86_FLOAT_STATE64;
255 #endif
257 x86_exception_state64_t exception_state;
258 mach_msg_type_number_t exception_state_count = x86_EXCEPTION_STATE64_COUNT;
260 x86_thread_state64_t backup_thread_state;
262 os_vm_address_t addr;
264 thread_get_state(thread, x86_EXCEPTION_STATE64,
265 (thread_state_t)&exception_state, &exception_state_count);
267 if (code_count && exception == EXC_BAD_ACCESS && code_vector[0] == EXC_I386_GPFLT) {
268 /* This can happen for addresses larger than 48 bits,
269 resulting in bogus faultvaddr. */
270 addr = NULL;
271 } else {
272 addr = (void*)exception_state.faultvaddr;
275 /* Just need to unprotect the page and do some bookkeeping, no need
276 * to run it from the faulting thread. */
277 if (exception == EXC_BAD_ACCESS && gencgc_handle_wp_violation(addr)) {
278 goto do_not_handle;
281 struct thread *th;
283 FSHOW((stderr,"/entering catch_exception_raise with exception: %d\n", exception));
284 if (mach_port_get_context(mach_task_self(), exception_port, (mach_vm_address_t *)&th)
285 != KERN_SUCCESS) {
286 lose("Can't find the thread for an exception %p", exception_port);
288 thread_get_state(thread, x86_THREAD_STATE64,
289 (thread_state_t)&thread_state, &thread_state_count);
291 boolean stack_unprotected = 0;
293 switch (exception) {
295 case EXC_BAD_ACCESS:
296 signal = SIGBUS;
298 if(addr >= CONTROL_STACK_RETURN_GUARD_PAGE(th) &&
299 addr < CONTROL_STACK_RETURN_GUARD_PAGE(th) + os_vm_page_size) {
300 /* We're returning from the guard page: reprotect it, and
301 * unprotect this one. This works even if we somehow missed
302 * the return-guard-page, and hit it on our way to new
303 * exhaustion instead. */
304 reset_thread_control_stack_guard_page(th);
305 goto do_not_handle;
308 /* note the os_context hackery here. When the signal handler returns,
309 * it won't go back to what it was doing ... */
310 if(addr >= CONTROL_STACK_GUARD_PAGE(th) &&
311 addr < CONTROL_STACK_GUARD_PAGE(th) + os_vm_page_size) {
312 /* We hit the end of the control stack: disable guard page
313 * protection so the error handler has some headroom, protect the
314 * previous page so that we can catch returns from the guard page
315 * and restore it. */
316 lower_thread_control_stack_guard_page(th);
317 stack_unprotected = 1;
319 else if (addr >= undefined_alien_address &&
320 addr < undefined_alien_address + os_vm_page_size) {
321 handler = undefined_alien_handler;
322 } else {
323 handler = memory_fault_handler;
325 break;
326 case EXC_BAD_INSTRUCTION:
328 if (*((u64 *)thread_state.rip) == 0xffffffffffff0b0f) {
329 /* Fake sigreturn. See the end of signal_emulation_wrapper() */
331 /* Apply any modifications done to the context, */
333 darwin_mcontext *mcontext = (darwin_mcontext *) thread_state.rax;
335 thread_set_state(thread, x86_THREAD_STATE64,
336 (thread_state_t) &mcontext->ss, thread_state_count);
337 thread_set_state(thread, float_state_flavor,
338 (thread_state_t) &mcontext->fs, float_state_count);
339 goto do_not_handle;
340 } else if (*((unsigned short *)thread_state.rip) == 0x0b0f) {
341 signal = SIGTRAP;
342 rip_offset = 2;
343 handler = sigtrap_handler;
344 } else {
345 signal = SIGILL;
346 handler = sigill_handler;
349 break;
350 case EXC_BREAKPOINT:
351 if (single_stepping) {
352 signal = SIGTRAP;
353 /* Clear TF or the signal emulation wrapper won't proceed
354 with single stepping enabled. */
355 thread_state.rflags &= ~0x100;
356 handler = sigtrap_handler;
357 break;
359 default:
360 ret = KERN_INVALID_RIGHT;
361 goto do_not_handle;
364 backup_thread_state = thread_state;
366 /* The ABI has a 128-byte red zone. */
367 stack_allocate(th, &thread_state, 128);
369 if (will_exhaust_stack(th, &thread_state,
370 /* Won't be passing much to stack_exhausted_error */
371 stack_unprotected ? N_WORD_BYTES*4 :
372 ALIGN_UP(sizeof(darwin_mcontext) +
373 N_WORD_BYTES, /* return address */
374 N_WORD_BYTES*2))
375 || stack_unprotected) {
376 call_stack_exhausted_in_context(&thread_state);
377 } else {
379 darwin_mcontext *mcontext = stack_allocate(th, &thread_state, sizeof(darwin_mcontext));
381 backup_thread_state.rip += rip_offset;
382 mcontext->ss = backup_thread_state;
384 thread_get_state(thread, float_state_flavor, (thread_state_t) &mcontext->fs, &float_state_count);
386 call_signal_emulator_in_context(&thread_state,
387 mcontext,
388 signal,
389 addr,
390 handler);
393 thread_set_state(thread, x86_THREAD_STATE64,
394 (thread_state_t)&thread_state, thread_state_count);
395 do_not_handle:
397 dealloc_ret = mach_port_deallocate (mach_task_self(), thread);
398 if (dealloc_ret) {
399 lose("mach_port_deallocate (thread) failed with return_code %d\n", dealloc_ret);
402 dealloc_ret = mach_port_deallocate (mach_task_self(), task);
403 if (dealloc_ret) {
404 lose("mach_port_deallocate (task) failed with return_code %d\n", dealloc_ret);
407 return ret;
409 #endif
411 void
412 os_restore_fp_control(os_context_t *context)
414 /* KLUDGE: The x87 FPU control word is some nasty bitfield struct
415 * thing. Rather than deal with that, just grab it as a 16-bit
416 * integer. */
417 unsigned short fpu_control_word =
418 *((unsigned short *)&context->uc_mcontext->fs.fpu_fcw);
419 /* reset exception flags and restore control flags on SSE2 FPU */
420 unsigned int temp = (context->uc_mcontext->fs.fpu_mxcsr) & ~0x3F;
421 asm ("ldmxcsr %0" : : "m" (temp));
422 /* same for x87 FPU. */
423 asm ("fldcw %0" : : "m" (fpu_control_word));
426 os_context_register_t *
427 os_context_float_register_addr(os_context_t *context, int offset)
429 return (os_context_register_t *)((&context->uc_mcontext->__fs.__fpu_xmm0) + offset);