1 #ifdef LISP_FEATURE_SB_THREAD
2 #include <mach/mach_init.h>
9 #include "x86-64-darwin-os.h"
10 #include "x86-64-arch.h"
11 #include "genesis/fdefn.h"
12 #include "gc-internal.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>
29 #include <sys/_structs.h>
34 typedef struct __darwin_ucontext darwin_ucontext
;
47 #define faultvaddr __faultvaddr
51 #define rflags __rflags
53 #define fpu_fcw __fpu_fcw
54 #define fpu_mxcsr __fpu_mxcsr
58 typedef struct ucontext darwin_ucontext
;
62 #ifdef x86_AVX_STATE64_COUNT
63 typedef _STRUCT_MCONTEXT_AVX64 darwin_mcontext
;
65 typedef _STRUCT_MCONTEXT64 darwin_mcontext
;
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
);
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",
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
116 lower_thread_control_stack_guard_page(th
);
117 context
->rsp
= (__uint64_t
)(CONTROL_STACK_GUARD_PAGE(th
) + os_vm_page_size
);
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. */
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
,
147 os_vm_address_t addr
,
148 void (*handler
)(int, siginfo_t
*, void *))
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
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"
177 void call_signal_emulator_in_context(x86_thread_state64_t
*context
,
178 darwin_mcontext
*mcontext
,
180 os_vm_address_t addr
,
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
)
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),
235 catch_exception_raise(mach_port_t exception_port
,
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
;
253 mach_msg_type_number_t float_state_count
= x86_FLOAT_STATE64_COUNT
;
254 int float_state_flavor
= x86_FLOAT_STATE64
;
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. */
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
)) {
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
)
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;
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
);
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
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
;
323 handler
= memory_fault_handler
;
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
);
340 } else if (*((unsigned short *)thread_state
.rip
) == 0x0b0f) {
343 handler
= sigtrap_handler
;
346 handler
= sigill_handler
;
351 if (single_stepping
) {
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
;
360 ret
= KERN_INVALID_RIGHT
;
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 */
375 || stack_unprotected
) {
376 call_stack_exhausted_in_context(&thread_state
);
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
,
393 thread_set_state(thread
, x86_THREAD_STATE64
,
394 (thread_state_t
)&thread_state
, thread_state_count
);
397 dealloc_ret
= mach_port_deallocate (mach_task_self(), thread
);
399 lose("mach_port_deallocate (thread) failed with return_code %d\n", dealloc_ret
);
402 dealloc_ret
= mach_port_deallocate (mach_task_self(), task
);
404 lose("mach_port_deallocate (task) failed with return_code %d\n", dealloc_ret
);
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
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
);