X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fruntime%2Fx86-darwin-os.c;h=97d39996ff43a63a621c521bc0674abb1370c9d0;hb=127fd3d2fb843c6bb7ad0763e143d81877e760e8;hp=feec96271367ad03e4e0b0a8ff2c5ae0b78068ba;hpb=8e9908b6f79b7bb52a281cfcbf0712d4b914f3cf;p=sbcl.git diff --git a/src/runtime/x86-darwin-os.c b/src/runtime/x86-darwin-os.c index feec962..97d3999 100644 --- a/src/runtime/x86-darwin-os.c +++ b/src/runtime/x86-darwin-os.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -100,8 +101,8 @@ int arch_os_thread_cleanup(struct thread *thread) { #if defined(LISP_FEATURE_SB_THREAD) int n = thread->tls_cookie; - /* Set the %%fs register back to 0 and free the the ldt - * by setting it to NULL. + /* Set the %%fs register back to 0 and free the ldt by setting it + * to NULL. */ FSHOW_SIGNAL((stderr, "/ TLS: Freeing LDT %x\n", n)); @@ -129,39 +130,39 @@ extern boolean_t exc_server(); /* This executes in the faulting thread as part of the signal * emulation. It is passed a context with the uc_mcontext field * pointing to a valid block of memory. */ -void build_fake_signal_context(struct ucontext *context, +void build_fake_signal_context(os_context_t *context, x86_thread_state32_t *thread_state, x86_float_state32_t *float_state) { pthread_sigmask(0, NULL, &context->uc_sigmask); - context->uc_mcontext->ss = *thread_state; - context->uc_mcontext->fs = *float_state; + context->uc_mcontext->SS = *thread_state; + context->uc_mcontext->FS = *float_state; } /* This executes in the faulting thread as part of the signal * emulation. It is effectively the inverse operation from above. */ void update_thread_state_from_context(x86_thread_state32_t *thread_state, x86_float_state32_t *float_state, - struct ucontext *context) { - *thread_state = context->uc_mcontext->ss; - *float_state = context->uc_mcontext->fs; + os_context_t *context) { + *thread_state = context->uc_mcontext->SS; + *float_state = context->uc_mcontext->FS; pthread_sigmask(SIG_SETMASK, &context->uc_sigmask, NULL); } /* Modify a context to push new data on its stack. */ -void push_context(u32 data, x86_thread_state32_t *context) +void push_context(u32 data, x86_thread_state32_t *thread_state) { u32 *stack_pointer; - stack_pointer = (u32*) context->esp; + stack_pointer = (u32*) thread_state->ESP; *(--stack_pointer) = data; - context->esp = (unsigned int) stack_pointer; + thread_state->ESP = (unsigned int) stack_pointer; } -void align_context_stack(x86_thread_state32_t *context) +void align_context_stack(x86_thread_state32_t *thread_state) { /* 16byte align the stack (provided that the stack is, as it * should be, 4byte aligned. */ - while (context->esp & 15) push_context(0, context); + while (thread_state->ESP & 15) push_context(0, thread_state); } /* Stack allocation starts with a context that has a mod-4 ESP value @@ -171,29 +172,29 @@ void align_context_stack(x86_thread_state32_t *context) * EBP, pops EBP, and returns. */ asm("_stack_allocation_recover: movl %ebp, %esp; popl %ebp; ret;"); -void open_stack_allocation(x86_thread_state32_t *context) +void open_stack_allocation(x86_thread_state32_t *thread_state) { void stack_allocation_recover(void); - push_context(context->eip, context); - push_context(context->ebp, context); - context->ebp = context->esp; - context->eip = (unsigned int) stack_allocation_recover; + push_context(thread_state->EIP, thread_state); + push_context(thread_state->EBP, thread_state); + thread_state->EBP = thread_state->ESP; + thread_state->EIP = (unsigned int) stack_allocation_recover; - align_context_stack(context); + align_context_stack(thread_state); } /* Stack allocation of data starts with a context with a mod-16 ESP * value and reserves some space on it by manipulating the ESP * register. */ -void *stack_allocate(x86_thread_state32_t *context, size_t size) +void *stack_allocate(x86_thread_state32_t *thread_state, size_t size) { /* round up size to 16byte multiple */ size = (size + 15) & -16; - context->esp = ((u32)context->esp) - size; + thread_state->ESP = ((u32)thread_state->ESP) - size; - return (void *)context->esp; + return (void *)thread_state->ESP; } /* Arranging to invoke a C function is tricky, as we have to assume @@ -201,7 +202,7 @@ void *stack_allocate(x86_thread_state32_t *context, size_t size) * alignment requirements. The simplest way to arrange this, * actually, is to open a new stack allocation. * WARNING!!! THIS DOES NOT PRESERVE REGISTERS! */ -void call_c_function_in_context(x86_thread_state32_t *context, +void call_c_function_in_context(x86_thread_state32_t *thread_state, void *function, int nargs, ...) @@ -211,25 +212,25 @@ void call_c_function_in_context(x86_thread_state32_t *context, u32 *stack_pointer; /* Set up to restore stack on exit. */ - open_stack_allocation(context); + open_stack_allocation(thread_state); /* Have to keep stack 16byte aligned on x86/darwin. */ for (i = (3 & -nargs); i; i--) { - push_context(0, context); + push_context(0, thread_state); } - context->esp = ((u32)context->esp) - nargs * 4; - stack_pointer = (u32 *)context->esp; + thread_state->ESP = ((u32)thread_state->ESP) - nargs * 4; + stack_pointer = (u32 *)thread_state->ESP; va_start(ap, nargs); for (i = 0; i < nargs; i++) { - //push_context(va_arg(ap, u32), context); + //push_context(va_arg(ap, u32), thread_state); stack_pointer[i] = va_arg(ap, u32); } va_end(ap); - push_context(context->eip, context); - context->eip = (unsigned int) function; + push_context(thread_state->EIP, thread_state); + thread_state->EIP = (unsigned int) function; } void signal_emulation_wrapper(x86_thread_state32_t *thread_state, @@ -252,12 +253,19 @@ void signal_emulation_wrapper(x86_thread_state32_t *thread_state, * context (and regs just for symmetry). */ - struct ucontext *context; + os_context_t *context; +#if __DARWIN_UNIX03 + struct __darwin_mcontext32 *regs; +#else struct mcontext *regs; - sigset_t sigmask; +#endif - context = (struct ucontext*) os_validate(0, sizeof(struct ucontext)); + context = (os_context_t*) os_validate(0, sizeof(os_context_t)); +#if __DARWIN_UNIX03 + regs = (struct __darwin_mcontext32*) os_validate(0, sizeof(struct __darwin_mcontext32)); +#else regs = (struct mcontext*) os_validate(0, sizeof(struct mcontext)); +#endif context->uc_mcontext = regs; /* when BSD signals are fired, they mask they signals in sa_mask @@ -268,45 +276,103 @@ void signal_emulation_wrapper(x86_thread_state32_t *thread_state, 3) call the signal handler 4) restore the sigmask */ - pthread_sigmask(0, NULL, &sigmask); - block_blockable_signals(); - build_fake_signal_context(context, thread_state, float_state); + block_blockable_signals(); + handler(signal, siginfo, context); update_thread_state_from_context(thread_state, float_state, context); - pthread_sigmask(SIG_SETMASK, &sigmask, NULL); - - os_invalidate((os_vm_address_t)context, sizeof(struct ucontext)); + os_invalidate((os_vm_address_t)context, sizeof(os_context_t)); +#if __DARWIN_UNIX03 + os_invalidate((os_vm_address_t)regs, sizeof(struct __darwin_mcontext32)); +#else os_invalidate((os_vm_address_t)regs, sizeof(struct mcontext)); +#endif /* Trap to restore the signal context. */ - asm volatile ("movl %0, %%eax; .long 0xffff0b0f": : "r" (thread_state)); + asm volatile ("movl %0, %%eax; movl %1, %%ebx; .long 0xffff0b0f" + : : "r" (thread_state), "r" (float_state)); +} + +/* Convenience wrapper for the above */ +void call_handler_on_thread(mach_port_t thread, + x86_thread_state32_t *thread_state, + int signal, + siginfo_t *siginfo, + void (*handler)(int, siginfo_t *, void *)) +{ + x86_thread_state32_t new_state; + x86_thread_state32_t *save_thread_state; + x86_float_state32_t *save_float_state; + mach_msg_type_number_t state_count; + siginfo_t *save_siginfo; + kern_return_t ret; + /* Initialize the new state */ + new_state = *thread_state; + open_stack_allocation(&new_state); + /* Save old state */ + save_thread_state = (x86_thread_state32_t *)stack_allocate(&new_state, sizeof(*save_thread_state)); + *save_thread_state = *thread_state; + /* Save float state */ + save_float_state = (x86_float_state32_t *)stack_allocate(&new_state, sizeof(*save_float_state)); + state_count = x86_FLOAT_STATE32_COUNT; + if ((ret = thread_get_state(thread, + x86_FLOAT_STATE32, + (thread_state_t)save_float_state, + &state_count)) != KERN_SUCCESS) + lose("thread_get_state (x86_THREAD_STATE32) failed %d\n", ret); + /* Set up siginfo */ + save_siginfo = stack_allocate(&new_state, sizeof(*siginfo)); + if (siginfo == NULL) + save_siginfo = siginfo; + else + *save_siginfo = *siginfo; + /* Prepare to call */ + call_c_function_in_context(&new_state, + signal_emulation_wrapper, + 5, + save_thread_state, + save_float_state, + signal, + save_siginfo, + handler); + /* Update the thread state */ + state_count = x86_THREAD_STATE32_COUNT; + if ((ret = thread_set_state(thread, + x86_THREAD_STATE32, + (thread_state_t)&new_state, + state_count)) != KERN_SUCCESS) + lose("thread_set_state (x86_FLOAT_STATE32) failed %d\n", ret); + } #if defined DUMP_CONTEXT -void dump_context(x86_thread_state32_t *context) +void dump_context(x86_thread_state32_t *thread_state) { int i; u32 *stack_pointer; printf("eax: %08lx ecx: %08lx edx: %08lx ebx: %08lx\n", - context->eax, context->ecx, context->edx, context->ebx); + thread_state->EAX, thread_state->ECX, thread_state->EDX, thread_state->EAX); printf("esp: %08lx ebp: %08lx esi: %08lx edi: %08lx\n", - context->esp, context->ebp, context->esi, context->edi); + thread_state->ESP, thread_state->EBP, thread_state->ESI, thread_state->EDI); printf("eip: %08lx eflags: %08lx\n", - context->eip, context->eflags); + thread_state->EIP, thread_state->EFLAGS); printf("cs: %04hx ds: %04hx es: %04hx " "ss: %04hx fs: %04hx gs: %04hx\n", - context->cs, context->ds, context->es, - context->ss, context->fs, context->gs); - - stack_pointer = (u32 *)context->esp; + thread_state->CS, + thread_state->DS, + thread_state->ES, + thread_state->SS, + thread_state->FS, + thread_state->GS); + + stack_pointer = (u32 *)thread_state->ESP; for (i = 0; i < 48; i+=4) { printf("%08x: %08x %08x %08x %08x\n", - context->esp + (i * 4), + thread_state->ESP + (i * 4), stack_pointer[i], stack_pointer[i+1], stack_pointer[i+2], @@ -333,259 +399,145 @@ undefined_alien_handler(int signal, siginfo_t *siginfo, void *void_context) { kern_return_t catch_exception_raise(mach_port_t exception_port, - mach_port_t thread, - mach_port_t task, - exception_type_t exception, - exception_data_t code_vector, - mach_msg_type_number_t code_count) + mach_port_t thread, + mach_port_t task, + exception_type_t exception, + exception_data_t code_vector, + mach_msg_type_number_t code_count) { - kern_return_t ret; - int signal; - siginfo_t* siginfo; - - x86_thread_state32_t thread_state; - mach_msg_type_number_t thread_state_count = x86_THREAD_STATE32_COUNT; - - x86_float_state32_t float_state; - mach_msg_type_number_t float_state_count = x86_FLOAT_STATE32_COUNT; - - x86_exception_state32_t exception_state; - mach_msg_type_number_t exception_state_count = x86_EXCEPTION_STATE32_COUNT; - - x86_thread_state32_t backup_thread_state; - x86_thread_state32_t *target_thread_state; - x86_float_state32_t *target_float_state; - - os_vm_address_t addr; - struct thread *th = (struct thread*) exception_port; + x86_thread_state32_t thread_state; + mach_msg_type_number_t state_count; + vm_address_t region_addr; + vm_size_t region_size; + vm_region_basic_info_data_t region_info; + mach_msg_type_number_t info_count; + mach_port_t region_name; + void *addr = NULL; + int signal = 0; + void (*handler)(int, siginfo_t *, void *) = NULL; + siginfo_t siginfo; + kern_return_t ret; - FSHOW((stderr,"/entering catch_exception_raise with exception: %d\n", exception)); - + /* Get state and info */ + state_count = x86_THREAD_STATE32_COUNT; + if ((ret = thread_get_state(thread, + x86_THREAD_STATE32, + (thread_state_t)&thread_state, + &state_count)) != KERN_SUCCESS) + lose("thread_get_state (x86_THREAD_STATE32) failed %d\n", ret); switch (exception) { - case EXC_BAD_ACCESS: signal = SIGBUS; - ret = thread_get_state(thread, - x86_THREAD_STATE32, - (thread_state_t)&thread_state, - &thread_state_count); - ret = thread_get_state(thread, - x86_FLOAT_STATE32, - (thread_state_t)&float_state, - &float_state_count); - ret = thread_get_state(thread, - x86_EXCEPTION_STATE32, - (thread_state_t)&exception_state, - &exception_state_count); - addr = (void*)exception_state.faultvaddr; - - - /* note the os_context hackery here. When the signal handler returns, - * it won't go back to what it was doing ... */ - if(addr >= CONTROL_STACK_GUARD_PAGE(th) && - addr < CONTROL_STACK_GUARD_PAGE(th) + os_vm_page_size) { - /* We hit the end of the control stack: disable guard page - * protection so the error handler has some headroom, protect the - * previous page so that we can catch returns from the guard page - * and restore it. */ + /* Check if write protection fault */ + if ((code_vector[0] & OS_VM_PROT_ALL) == 0) { + ret = KERN_INVALID_RIGHT; + break; + } + /* Get vm_region info */ + region_addr = (vm_address_t)code_vector[1]; + info_count = VM_REGION_BASIC_INFO_COUNT; + if ((ret = vm_region(mach_task_self(), + ®ion_addr, + ®ion_size, + VM_REGION_BASIC_INFO, + (vm_region_info_t)®ion_info, + &info_count, + ®ion_name))) + lose("vm_region (VM_REGION_BASIC_INFO) failed failed %d\n", ret); + /* Check if still protected */ + if ((region_info.protection & OS_VM_PROT_ALL) == 0) { + /* KLUDGE: + * If two threads fault on the same page, the protection + * is cleared as the first thread runs memory_fault_handler. + * Grep for "not marked as write-protected" in gencgc.c + */ + ret = KERN_SUCCESS; + break; + } + addr = (void*)code_vector[1]; + /* At stack guard */ + if (os_trunc_to_page(addr) == CONTROL_STACK_GUARD_PAGE(th)) { protect_control_stack_guard_page_thread(0, th); protect_control_stack_return_guard_page_thread(1, th); - - backup_thread_state = thread_state; - open_stack_allocation(&thread_state); - - /* Save thread state */ - target_thread_state = - stack_allocate(&thread_state, sizeof(*target_thread_state)); - (*target_thread_state) = backup_thread_state; - - /* Save float state */ - target_float_state = - stack_allocate(&thread_state, sizeof(*target_float_state)); - (*target_float_state) = float_state; - - /* Set up siginfo */ - siginfo = stack_allocate(&thread_state, sizeof(*siginfo)); - /* what do we need to put in our fake siginfo? It looks like - * the x86 code only uses si_signo and si_adrr. */ - siginfo->si_signo = signal; - siginfo->si_addr = (void*)exception_state.faultvaddr; - - call_c_function_in_context(&thread_state, - signal_emulation_wrapper, - 5, - target_thread_state, - target_float_state, - signal, - siginfo, - control_stack_exhausted_handler); + handler = control_stack_exhausted_handler; + break; } - else if(addr >= CONTROL_STACK_RETURN_GUARD_PAGE(th) && - addr < CONTROL_STACK_RETURN_GUARD_PAGE(th) + os_vm_page_size) { - /* We're returning from the guard page: reprotect it, and - * unprotect this one. This works even if we somehow missed - * the return-guard-page, and hit it on our way to new - * exhaustion instead. */ + /* Return from stack guard */ + if (os_trunc_to_page(addr) == CONTROL_STACK_RETURN_GUARD_PAGE(th)) { protect_control_stack_guard_page_thread(1, th); protect_control_stack_return_guard_page_thread(0, th); - + break; } - else if (addr >= undefined_alien_address && - addr < undefined_alien_address + os_vm_page_size) { - backup_thread_state = thread_state; - open_stack_allocation(&thread_state); - - /* Save thread state */ - target_thread_state = - stack_allocate(&thread_state, sizeof(*target_thread_state)); - (*target_thread_state) = backup_thread_state; - - target_float_state = - stack_allocate(&thread_state, sizeof(*target_float_state)); - (*target_float_state) = float_state; - - /* Set up siginfo */ - siginfo = stack_allocate(&thread_state, sizeof(*siginfo)); - /* what do we need to put in our fake siginfo? It looks like - * the x86 code only uses si_signo and si_adrr. */ - siginfo->si_signo = signal; - siginfo->si_addr = (void*)exception_state.faultvaddr; - - call_c_function_in_context(&thread_state, - signal_emulation_wrapper, - 5, - target_thread_state, - target_float_state, - signal, - siginfo, - undefined_alien_handler); - } else { - - backup_thread_state = thread_state; - open_stack_allocation(&thread_state); - - /* Save thread state */ - target_thread_state = - stack_allocate(&thread_state, sizeof(*target_thread_state)); - (*target_thread_state) = backup_thread_state; - - target_float_state = - stack_allocate(&thread_state, sizeof(*target_float_state)); - (*target_float_state) = float_state; - - /* Set up siginfo */ - siginfo = stack_allocate(&thread_state, sizeof(*siginfo)); - /* what do we need to put in our fake siginfo? It looks like - * the x86 code only uses si_signo and si_adrr. */ - siginfo->si_signo = signal; - siginfo->si_addr = (void*)exception_state.faultvaddr; - - call_c_function_in_context(&thread_state, - signal_emulation_wrapper, - 5, - target_thread_state, - target_float_state, - signal, - siginfo, - memory_fault_handler); + /* Undefined alien */ + if (os_trunc_to_page(addr) == undefined_alien_address) { + handler = undefined_alien_handler; + break; } - ret = thread_set_state(thread, - x86_THREAD_STATE32, - (thread_state_t)&thread_state, - thread_state_count); - - ret = thread_set_state(thread, - x86_FLOAT_STATE32, - (thread_state_t)&float_state, - float_state_count); - return KERN_SUCCESS; - + /* Regular memory fault */ + handler = memory_fault_handler; + break; case EXC_BAD_INSTRUCTION: - - ret = thread_get_state(thread, - x86_THREAD_STATE32, - (thread_state_t)&thread_state, - &thread_state_count); - ret = thread_get_state(thread, - x86_FLOAT_STATE32, - (thread_state_t)&float_state, - &float_state_count); - ret = thread_get_state(thread, - x86_EXCEPTION_STATE32, - (thread_state_t)&exception_state, - &exception_state_count); - if (0xffff0b0f == *((u32 *)thread_state.eip)) { - /* fake sigreturn. */ - - /* When we get here, thread_state.eax is a pointer to a - * thread_state to restore. */ - /* thread_state = *((thread_state_t *)thread_state.eax); */ - - ret = thread_set_state(thread, - x86_THREAD_STATE32, - (thread_state_t) thread_state.eax, - /* &thread_state, */ - thread_state_count); - } else { - - backup_thread_state = thread_state; - open_stack_allocation(&thread_state); - - /* Save thread state */ - target_thread_state = - stack_allocate(&thread_state, sizeof(*target_thread_state)); - (*target_thread_state) = backup_thread_state; - - target_float_state = - stack_allocate(&thread_state, sizeof(*target_float_state)); - (*target_float_state) = float_state; - - /* Set up siginfo */ - siginfo = stack_allocate(&thread_state, sizeof(*siginfo)); - /* what do we need to put in our fake siginfo? It looks like - * the x86 code only uses si_signo and si_adrr. */ - if (*((unsigned short *)target_thread_state->eip) == 0x0b0f) { - signal = SIGTRAP; - siginfo->si_signo = signal; - siginfo->si_addr = (void*)exception_state.faultvaddr; - target_thread_state->eip += 2; - call_c_function_in_context(&thread_state, - signal_emulation_wrapper, - 5, - target_thread_state, - target_float_state, - signal, - siginfo, - sigtrap_handler); - } else { - signal = SIGILL; - siginfo->si_signo = signal; - siginfo->si_addr = (void*)exception_state.faultvaddr; - - call_c_function_in_context(&thread_state, - signal_emulation_wrapper, - 5, - target_thread_state, - target_float_state, - signal, - siginfo, - sigill_handler); - } - ret = thread_set_state(thread, - x86_THREAD_STATE32, - (thread_state_t)&thread_state, - thread_state_count); - ret = thread_set_state(thread, - x86_FLOAT_STATE32, - (thread_state_t)&float_state, - float_state_count); + signal = SIGTRAP; + /* Check if illegal instruction trap */ + if (code_vector[0] != EXC_I386_INVOP) { + ret = KERN_INVALID_RIGHT; + break; } - return KERN_SUCCESS; - + /* Check if UD2 instruction */ + if (*(unsigned short *)thread_state.EIP != 0x0b0f) { + /* KLUDGE: There are two ways we could get here: + * 1) We're executing data and we've hit some truly + * illegal opcode, of which there are a few, see + * Intel 64 and IA-32 Architectures + * Sofware Developer's Manual + * Volume 3A page 5-34) + * 2) The kernel started an unrelated signal handler + * before we got a chance to run. The context that + * caused the exception is saved in a stack frame + * somewhere down below. + * In either case we rely on the exception to retrigger, + * eventually bailing out if we're spinning on case 2). + */ + static mach_port_t last_thread; + static unsigned int last_eip; + if (last_thread == thread && last_eip == thread_state.EIP) + ret = KERN_INVALID_RIGHT; + else + ret = KERN_SUCCESS; + last_thread = thread; + last_eip = thread_state.EIP; + break; + } + /* Skip the trap code */ + thread_state.EIP += 2; + /* Return from handler? */ + if (*(unsigned short *)thread_state.EIP == 0xffff) { + if ((ret = thread_set_state(thread, + x86_THREAD_STATE32, + (thread_state_t)thread_state.EAX, + x86_THREAD_STATE32_COUNT)) != KERN_SUCCESS) + lose("thread_set_state (x86_THREAD_STATE32) failed %d\n", ret); + if ((ret = thread_set_state(thread, + x86_FLOAT_STATE32, + (thread_state_t)thread_state.EBX, + x86_FLOAT_STATE32_COUNT)) != KERN_SUCCESS) + lose("thread_set_state (x86_FLOAT_STATE32) failed %d\n", ret); + break; + } + /* Trap call */ + handler = sigtrap_handler; + break; default: - return KERN_INVALID_RIGHT; + ret = KERN_INVALID_RIGHT; } + /* Call handler */ + if (handler != 0) { + siginfo.si_signo = signal; + siginfo.si_addr = addr; + call_handler_on_thread(thread, &thread_state, signal, &siginfo, handler); + } + return ret; } void *