Changeset 15470


Ignore:
Timestamp:
Oct 1, 2012, 10:30:32 AM (7 years ago)
Author:
gb
Message:

Try to minimize Mach dependencies on Darwin; in particular, use POSIX
signal handling to handle exceptions (as is done on other *nix platforms.)

Use sigaltstack() on Darwin; it still seems to have problems, but at least
doesn't (usually) try to force all threads to use the same alt stack. (That
just never gets old somehow ...)

Lots of stuff removed; lots of (mostly small, mostly obvious) changes to
support the above.

Location:
trunk/source/lisp-kernel
Files:
17 edited

Legend:

Unmodified
Added
Removed
  • trunk/source/lisp-kernel/arm-exceptions.c

    r15370 r15470  
    14581458     can return to the kernel/to the Mach exception handler).
    14591459  */
    1460   if (!use_mach_exception_handling) {
    1461     exit_signal_handler(tcr, old_valence, old_last_lisp_frame);
    1462     raise_pending_interrupt(tcr);
    1463   }
     1460  exit_signal_handler(tcr, old_valence, old_last_lisp_frame);
     1461  raise_pending_interrupt(tcr);
    14641462}
    14651463
     
    19041902install_pmcl_exception_handlers()
    19051903{
    1906 #ifdef DARWIN
    1907   extern Boolean use_mach_exception_handling;
    1908 #endif
    1909 
    1910   Boolean install_signal_handlers_for_exceptions =
    1911 #ifdef DARWIN
    1912     !use_mach_exception_handling
    1913 #else
    1914     true
    1915 #endif
    1916     ;
    1917   if (install_signal_handlers_for_exceptions) {
    1918     install_signal_handler(SIGILL, (void *)sigill_handler, RESERVE_FOR_LISP);
    1919     install_signal_handler(SIGSEGV, (void *)ALTSTACK(signal_handler),
     1904  install_signal_handler(SIGILL, (void *)sigill_handler, RESERVE_FOR_LISP);
     1905  install_signal_handler(SIGSEGV, (void *)ALTSTACK(signal_handler),
     1906                         RESERVE_FOR_LISP|ON_ALTSTACK);
     1907  install_signal_handler(SIGBUS, (void *)ALTSTACK(signal_handler),
    19201908                           RESERVE_FOR_LISP|ON_ALTSTACK);
    1921     install_signal_handler(SIGBUS, (void *)ALTSTACK(signal_handler),
    1922                            RESERVE_FOR_LISP|ON_ALTSTACK);
    1923 
    1924   }
    1925  
    19261909  install_signal_handler(SIGNAL_FOR_PROCESS_INTERRUPT,
    19271910                         (void *)interrupt_handler, RESERVE_FOR_LISP);
     
    20632046
    20642047
    2065 #ifdef DARWIN
    2066 
    2067 
    2068 #define TCR_FROM_EXCEPTION_PORT(p) ((TCR *)((natural)p))
    2069 #define TCR_TO_EXCEPTION_PORT(tcr) ((mach_port_t)((natural)(tcr)))
    2070 
    2071 
    2072 
    2073 #define LISP_EXCEPTIONS_HANDLED_MASK \
    2074  (EXC_MASK_SOFTWARE | EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC)
    2075 
    2076 /* (logcount LISP_EXCEPTIONS_HANDLED_MASK) */
    2077 #define NUM_LISP_EXCEPTIONS_HANDLED 4
    2078 
    2079 typedef struct {
    2080   int foreign_exception_port_count;
    2081   exception_mask_t         masks[NUM_LISP_EXCEPTIONS_HANDLED];
    2082   mach_port_t              ports[NUM_LISP_EXCEPTIONS_HANDLED];
    2083   exception_behavior_t behaviors[NUM_LISP_EXCEPTIONS_HANDLED];
    2084   thread_state_flavor_t  flavors[NUM_LISP_EXCEPTIONS_HANDLED];
    2085 } MACH_foreign_exception_state;
    2086 
    2087 
    2088 
    2089 
    2090 /*
    2091   Mach's exception mechanism works a little better than its signal
    2092   mechanism (and, not incidentally, it gets along with GDB a lot
    2093   better.
    2094 
    2095   Initially, we install an exception handler to handle each native
    2096   thread's exceptions.  This process involves creating a distinguished
    2097   thread which listens for kernel exception messages on a set of
    2098   0 or more thread exception ports.  As threads are created, they're
    2099   added to that port set; a thread's exception port is destroyed
    2100   (and therefore removed from the port set) when the thread exits.
    2101 
    2102   A few exceptions can be handled directly in the handler thread;
    2103   others require that we resume the user thread (and that the
    2104   exception thread resumes listening for exceptions.)  The user
    2105   thread might eventually want to return to the original context
    2106   (possibly modified somewhat.)
    2107 
    2108   As it turns out, the simplest way to force the faulting user
    2109   thread to handle its own exceptions is to do pretty much what
    2110   signal() does: the exception handlng thread sets up a sigcontext
    2111   on the user thread's stack and forces the user thread to resume
    2112   execution as if a signal handler had been called with that
    2113   context as an argument.  We can use a distinguished UUO at a
    2114   distinguished address to do something like sigreturn(); that'll
    2115   have the effect of resuming the user thread's execution in
    2116   the (pseudo-) signal context.
    2117 
    2118   Since:
    2119     a) we have miles of code in C and in Lisp that knows how to
    2120     deal with Linux sigcontexts
    2121     b) Linux sigcontexts contain a little more useful information
    2122     (the DAR, DSISR, etc.) than their Darwin counterparts
    2123     c) we have to create a sigcontext ourselves when calling out
    2124     to the user thread: we aren't really generating a signal, just
    2125     leveraging existing signal-handling code.
    2126 
    2127   we create a Linux sigcontext struct.
    2128 
    2129   Simple ?  Hopefully from the outside it is ...
    2130 
    2131   We want the process of passing a thread's own context to it to
    2132   appear to be atomic: in particular, we don't want the GC to suspend
    2133   a thread that's had an exception but has not yet had its user-level
    2134   exception handler called, and we don't want the thread's exception
    2135   context to be modified by a GC while the Mach handler thread is
    2136   copying it around.  On Linux (and on Jaguar), we avoid this issue
    2137   because (a) the kernel sets up the user-level signal handler and
    2138   (b) the signal handler blocks signals (including the signal used
    2139   by the GC to suspend threads) until tcr->xframe is set up.
    2140 
    2141   The GC and the Mach server thread therefore contend for the lock
    2142   "mach_exception_lock".  The Mach server thread holds the lock
    2143   when copying exception information between the kernel and the
    2144   user thread; the GC holds this lock during most of its execution
    2145   (delaying exception processing until it can be done without
    2146   GC interference.)
    2147 
    2148 */
    2149 
    2150 
    2151 #define TRUNC_DOWN(a,b,c)  (((((natural)a)-(b))/(c)) * (c))
    2152 
    2153 void
    2154 fatal_mach_error(char *format, ...);
    2155 
    2156 #define MACH_CHECK_ERROR(context,x) if (x != KERN_SUCCESS) {fatal_mach_error("Mach error while %s : %d", context, x);}
    2157 
    2158 
    2159 void
    2160 restore_mach_thread_state(mach_port_t thread, ExceptionInformation *pseudosigcontext)
    2161 {
    2162   kern_return_t kret;
    2163   _STRUCT_MCONTEXT *mc = UC_MCONTEXT(pseudosigcontext);
    2164 
    2165   /* Set the thread's FP state from the pseudosigcontext */
    2166   kret = thread_set_state(thread,
    2167                           ARM_VFP_STATE,
    2168                           (thread_state_t)&(mc->__fs),
    2169                           ARM_VFP_STATE_COUNT);
    2170 
    2171   MACH_CHECK_ERROR("setting thread FP state", kret);
    2172 
    2173   /* The thread'll be as good as new ... */
    2174   kret = thread_set_state(thread,
    2175                           MACHINE_THREAD_STATE,
    2176                           (thread_state_t)&(mc->__ss),
    2177                           MACHINE_THREAD_STATE_COUNT);
    2178   MACH_CHECK_ERROR("setting thread state", kret);
    2179 
    2180 
    2181 /* This code runs in the exception handling thread, in response
    2182    to an attempt to execute the UU0 at "pseudo_sigreturn" (e.g.,
    2183    in response to a call to pseudo_sigreturn() from the specified
    2184    user thread.
    2185    Find that context (the user thread's R3 points to it), then
    2186    use that context to set the user thread's state.  When this
    2187    function's caller returns, the Mach kernel will resume the
    2188    user thread.
    2189 */
    2190 
    2191 kern_return_t
    2192 do_pseudo_sigreturn(mach_port_t thread, TCR *tcr)
    2193 {
    2194   ExceptionInformation *xp;
    2195 
    2196 #ifdef DEBUG_MACH_EXCEPTIONS
    2197   fprintf(dbgout, "doing pseudo_sigreturn for 0x%x\n",tcr);
    2198 #endif
    2199   tcr->last_lisp_frame = *((natural *)(tcr->last_lisp_frame));
    2200   xp = tcr->pending_exception_context;
    2201   if (xp) {
    2202     tcr->pending_exception_context = NULL;
    2203     tcr->valence = TCR_STATE_LISP;
    2204     restore_mach_thread_state(thread, xp);
    2205     raise_pending_interrupt(tcr);
    2206   } else {
    2207     Bug(NULL, "no xp here!\n");
    2208   }
    2209 #ifdef DEBUG_MACH_EXCEPTIONS
    2210   fprintf(dbgout, "did pseudo_sigreturn for 0x%x\n",tcr);
    2211 #endif
    2212   return KERN_SUCCESS;
    2213 
    2214 
    2215 ExceptionInformation *
    2216 create_thread_context_frame(mach_port_t thread,
    2217                             natural *new_stack_top)
    2218 {
    2219   arm_thread_state_t ts;
    2220   mach_msg_type_number_t thread_state_count;
    2221   kern_return_t result;
    2222   ExceptionInformation *pseudosigcontext;
    2223   _STRUCT_MCONTEXT *mc;
    2224   natural stackp, backlink;
    2225 
    2226   thread_state_count = MACHINE_THREAD_STATE_COUNT;
    2227   result = thread_get_state(thread,
    2228                             ARM_THREAD_STATE,   /* GPRs, some SPRs  */
    2229                             (thread_state_t)&ts,
    2230                             &thread_state_count);
    2231  
    2232   if (result != KERN_SUCCESS) {
    2233     get_tcr(true);
    2234     Bug(NULL, "Exception thread can't obtain thread state, Mach result = %d", result);
    2235   }
    2236   stackp = ts.__sp;
    2237   backlink = stackp;
    2238 
    2239   stackp -= sizeof(*pseudosigcontext);
    2240   pseudosigcontext = (ExceptionInformation *) ptr_from_lispobj(stackp);
    2241 
    2242   stackp -= sizeof(*mc);
    2243   mc = (_STRUCT_MCONTEXT *) ptr_from_lispobj(stackp);
    2244   memmove(&(mc->__ss),&ts,sizeof(ts));
    2245 
    2246   thread_state_count = ARM_VFP_STATE_COUNT;
    2247   thread_get_state(thread,
    2248                    ARM_VFP_STATE,
    2249                    (thread_state_t)&(mc->__fs),
    2250                    &thread_state_count);
    2251 
    2252 
    2253   thread_state_count = ARM_EXCEPTION_STATE_COUNT;
    2254   thread_get_state(thread,
    2255                    ARM_EXCEPTION_STATE,
    2256                    (thread_state_t)&(mc->__es),
    2257                    &thread_state_count);
    2258 
    2259 
    2260   UC_MCONTEXT(pseudosigcontext) = mc;
    2261   if (new_stack_top) {
    2262     *new_stack_top = stackp;
    2263   }
    2264   return pseudosigcontext;
    2265 }
    2266 
    2267 /*
    2268   This code sets up the user thread so that it executes a "pseudo-signal
    2269   handler" function when it resumes.  Create a linux sigcontext struct
    2270   on the thread's stack and pass it as an argument to the pseudo-signal
    2271   handler.
    2272 
    2273   Things are set up so that the handler "returns to" pseudo_sigreturn(),
    2274   which will restore the thread's context.
    2275 
    2276   If the handler invokes code that throws (or otherwise never sigreturn()'s
    2277   to the context), that's fine.
    2278 
    2279   Actually, check that: throw (and variants) may need to be careful and
    2280   pop the tcr's xframe list until it's younger than any frame being
    2281   entered.
    2282 */
    2283 
    2284 int
    2285 setup_signal_frame(mach_port_t thread,
    2286                    void *handler_address,
    2287                    int signum,
    2288                    int code,
    2289                    TCR *tcr)
    2290 {
    2291   arm_thread_state_t ts;
    2292   ExceptionInformation *pseudosigcontext;
    2293   int old_valence = tcr->valence;
    2294   natural stackp, *pstackp;
    2295 
    2296 #ifdef DEBUG_MACH_EXCEPTIONS
    2297   fprintf(dbgout,"Setting up exception handling for 0x%x\n", tcr);
    2298 #endif
    2299   pseudosigcontext = create_thread_context_frame(thread, &stackp);
    2300   pstackp = (natural *)stackp;
    2301   *--pstackp = tcr->last_lisp_frame;
    2302   stackp = (natural)pstackp;
    2303   tcr->last_lisp_frame = stackp;
    2304   pseudosigcontext->uc_onstack = 0;
    2305   pseudosigcontext->uc_sigmask = (sigset_t) 0;
    2306   pseudosigcontext->uc_mcsize = ARM_MCONTEXT_SIZE;
    2307   tcr->pending_exception_context = pseudosigcontext;
    2308   tcr->valence = TCR_STATE_EXCEPTION_WAIT;
    2309  
    2310 
    2311   /*
    2312      It seems like we've created a  sigcontext on the thread's
    2313      stack.  Set things up so that we call the handler (with appropriate
    2314      args) when the thread's resumed.
    2315   */
    2316 
    2317   ts.__pc = (natural) handler_address;
    2318   ts.__sp = stackp;
    2319   ts.__r[0] = signum;
    2320   ts.__r[1] = (natural)pseudosigcontext;
    2321   ts.__r[2] = (natural)tcr;
    2322   ts.__r[3] = (natural)old_valence;
    2323   ts.__lr = (natural)pseudo_sigreturn;
    2324   ts.__cpsr = xpPSR(pseudosigcontext);
    2325 
    2326 
    2327   thread_set_state(thread,
    2328                    MACHINE_THREAD_STATE,
    2329                    (thread_state_t)&ts,
    2330                    MACHINE_THREAD_STATE_COUNT);
    2331 #ifdef DEBUG_MACH_EXCEPTIONS
    2332   fprintf(dbgout,"Set up exception context for 0x%x at 0x%x\n", tcr, tcr->pending_exception_context);
    2333 #endif
    2334   return 0;
    2335 }
    2336 
    2337 
    2338 void
    2339 pseudo_signal_handler(int signum, ExceptionInformation *context, TCR *tcr, int old_valence)
    2340 {
    2341   signal_handler(signum, NULL, context, tcr, old_valence, 0);
    2342 }
    2343 
    2344 
    2345 int
    2346 thread_set_fp_exceptions_enabled(mach_port_t thread, Boolean enabled)
    2347 {
    2348   /* Likely hopeless. */
    2349   return 0;
    2350 }
    2351 
    2352 /*
    2353   This function runs in the exception handling thread.  It's
    2354   called (by this precise name) from the library function "exc_server()"
    2355   when the thread's exception ports are set up.  (exc_server() is called
    2356   via mach_msg_server(), which is a function that waits for and dispatches
    2357   on exception messages from the Mach kernel.)
    2358 
    2359   This checks to see if the exception was caused by a pseudo_sigreturn()
    2360   UUO; if so, it arranges for the thread to have its state restored
    2361   from the specified context.
    2362 
    2363   Otherwise, it tries to map the exception to a signal number and
    2364   arranges that the thread run a "pseudo signal handler" to handle
    2365   the exception.
    2366 
    2367   Some exceptions could and should be handled here directly.
    2368 */
    2369 
    2370 kern_return_t
    2371 catch_exception_raise(mach_port_t exception_port,
    2372                       mach_port_t thread,
    2373                       mach_port_t task,
    2374                       exception_type_t exception,
    2375                       exception_data_t code_vector,
    2376                       mach_msg_type_number_t code_count)
    2377 {
    2378   int signum = 0, code = *code_vector;
    2379   TCR *tcr = TCR_FROM_EXCEPTION_PORT(exception_port);
    2380   kern_return_t kret;
    2381 
    2382 #ifdef DEBUG_MACH_EXCEPTIONS
    2383   fprintf(dbgout, "obtaining Mach exception lock in exception thread\n");
    2384 #endif
    2385 
    2386   if (tcr->flags & (1<<TCR_FLAG_BIT_PENDING_EXCEPTION)) {
    2387     CLR_TCR_FLAG(tcr,TCR_FLAG_BIT_PENDING_EXCEPTION);
    2388   }
    2389   /* On the ARM, code_vector[1] contains the undefined instruction
    2390      in this case, not its address.  */
    2391   if ((exception == EXC_BAD_INSTRUCTION) &&
    2392       (code_vector[0] == EXC_ARM_UNDEFINED) &&
    2393       (code_vector[1] == PSEUDO_SIGRETURN_UUO)) {
    2394     kret = do_pseudo_sigreturn(thread, tcr);
    2395   } else if (tcr->flags & (1<<TCR_FLAG_BIT_PROPAGATE_EXCEPTION)) {
    2396     CLR_TCR_FLAG(tcr,TCR_FLAG_BIT_PROPAGATE_EXCEPTION);
    2397     kret = 17;
    2398   } else {
    2399     switch (exception) {
    2400     case EXC_BAD_ACCESS:
    2401       signum = SIGSEGV;
    2402       break;
    2403        
    2404     case EXC_BAD_INSTRUCTION:
    2405       signum = SIGILL;
    2406       break;
    2407      
    2408       break;
    2409      
    2410     case EXC_ARITHMETIC:
    2411       signum = SIGFPE;
    2412       break;
    2413 
    2414     default:
    2415       break;
    2416     }
    2417     if (signum) {
    2418       kret = setup_signal_frame(thread,
    2419                                 (void *)pseudo_signal_handler,
    2420                                 signum,
    2421                                 code,
    2422                                 tcr);
    2423 #if 0
    2424       fprintf(dbgout, "Setup pseudosignal handling in 0x%x\n",tcr);
    2425 #endif
    2426 
    2427     } else {
    2428       kret = 17;
    2429     }
    2430   }
    2431 
    2432   return kret;
    2433 }
    2434 
    2435 
    2436 
    2437 typedef struct {
    2438   mach_msg_header_t Head;
    2439   /* start of the kernel processed data */
    2440   mach_msg_body_t msgh_body;
    2441   mach_msg_port_descriptor_t thread;
    2442   mach_msg_port_descriptor_t task;
    2443   /* end of the kernel processed data */
    2444   NDR_record_t NDR;
    2445   exception_type_t exception;
    2446   mach_msg_type_number_t codeCnt;
    2447   integer_t code[2];
    2448   mach_msg_trailer_t trailer;
    2449 } exceptionRequest;
    2450 
    2451 
    2452 boolean_t
    2453 openmcl_exc_server(mach_msg_header_t *in, mach_msg_header_t *out)
    2454 {
    2455   static NDR_record_t _NDR = {0};
    2456   kern_return_t handled;
    2457   mig_reply_error_t *reply = (mig_reply_error_t *) out;
    2458   exceptionRequest *req = (exceptionRequest *) in;
    2459 
    2460   reply->NDR = _NDR;
    2461 
    2462   out->msgh_bits = in->msgh_bits & MACH_MSGH_BITS_REMOTE_MASK;
    2463   out->msgh_remote_port = in->msgh_remote_port;
    2464   out->msgh_size = sizeof(mach_msg_header_t)+(3 * sizeof(unsigned));
    2465   out->msgh_local_port = MACH_PORT_NULL;
    2466   out->msgh_id = in->msgh_id+100;
    2467 
    2468   /* Could handle other exception flavors in the range 2401-2403 */
    2469 
    2470 
    2471   if (in->msgh_id != 2401) {
    2472     reply->RetCode = MIG_BAD_ID;
    2473     return FALSE;
    2474   }
    2475   handled = catch_exception_raise(req->Head.msgh_local_port,
    2476                                   req->thread.name,
    2477                                   req->task.name,
    2478                                   req->exception,
    2479                                   req->code,
    2480                                   req->codeCnt);
    2481   reply->RetCode = handled;
    2482   return TRUE;
    2483 }
    2484 
    2485 /*
    2486   The initial function for an exception-handling thread.
    2487 */
    2488 
    2489 void *
    2490 exception_handler_proc(void *arg)
    2491 {
    2492   extern boolean_t exc_server();
    2493   mach_port_t p = TCR_TO_EXCEPTION_PORT(arg);
    2494 
    2495   mach_msg_server(openmcl_exc_server, 2048, p, 0);
    2496   /* Should never return. */
    2497   abort();
    2498 }
    2499 
    2500 
    2501 
    2502 mach_port_t
    2503 mach_exception_port_set()
    2504 {
    2505   static mach_port_t __exception_port_set = MACH_PORT_NULL;
    2506   kern_return_t kret; 
    2507   if (__exception_port_set == MACH_PORT_NULL) {
    2508     kret = mach_port_allocate(mach_task_self(),
    2509                               MACH_PORT_RIGHT_PORT_SET,
    2510                               &__exception_port_set);
    2511     MACH_CHECK_ERROR("allocating thread exception_ports",kret);
    2512     create_system_thread(0,
    2513                          NULL,
    2514                          exception_handler_proc,
    2515                          (void *)((natural)__exception_port_set));
    2516   }
    2517   return __exception_port_set;
    2518 }
    2519 
    2520 /*
    2521   Setup a new thread to handle those exceptions specified by
    2522   the mask "which".  This involves creating a special Mach
    2523   message port, telling the Mach kernel to send exception
    2524   messages for the calling thread to that port, and setting
    2525   up a handler thread which listens for and responds to
    2526   those messages.
    2527 
    2528 */
    2529 
    2530 /*
    2531   Establish the lisp thread's TCR as its exception port, and determine
    2532   whether any other ports have been established by foreign code for
    2533   exceptions that lisp cares about.
    2534 
    2535   If this happens at all, it should happen on return from foreign
    2536   code and on entry to lisp code via a callback.
    2537 
    2538   This is a lot of trouble (and overhead) to support Java, or other
    2539   embeddable systems that clobber their caller's thread exception ports.
    2540  
    2541 */
    2542 kern_return_t
    2543 tcr_establish_exception_port(TCR *tcr, mach_port_t thread)
    2544 {
    2545   kern_return_t kret;
    2546   MACH_foreign_exception_state *fxs = (MACH_foreign_exception_state *)tcr->native_thread_info;
    2547   int i;
    2548   unsigned n = NUM_LISP_EXCEPTIONS_HANDLED;
    2549   mach_port_t lisp_port = TCR_TO_EXCEPTION_PORT(tcr), foreign_port;
    2550   exception_mask_t mask = 0;
    2551 
    2552   kret = thread_swap_exception_ports(thread,
    2553                                      LISP_EXCEPTIONS_HANDLED_MASK,
    2554                                      lisp_port,
    2555                                      EXCEPTION_DEFAULT,
    2556                                      THREAD_STATE_NONE,
    2557                                      fxs->masks,
    2558                                      &n,
    2559                                      fxs->ports,
    2560                                      fxs->behaviors,
    2561                                      fxs->flavors);
    2562   if (kret == KERN_SUCCESS) {
    2563     fxs->foreign_exception_port_count = n;
    2564     for (i = 0; i < n; i ++) {
    2565       foreign_port = fxs->ports[i];
    2566 
    2567       if ((foreign_port != lisp_port) &&
    2568           (foreign_port != MACH_PORT_NULL)) {
    2569         mask |= fxs->masks[i];
    2570       }
    2571     }
    2572     tcr->foreign_exception_status = (int) mask;
    2573   }
    2574   return kret;
    2575 }
    2576 
    2577 kern_return_t
    2578 tcr_establish_lisp_exception_port(TCR *tcr)
    2579 {
    2580   return tcr_establish_exception_port(tcr, (mach_port_t)((natural)tcr->native_thread_id));
    2581 }
    2582 
    2583 /*
    2584   Do this when calling out to or returning from foreign code, if
    2585   any conflicting foreign exception ports were established when we
    2586   last entered lisp code.
    2587 */
    2588 kern_return_t
    2589 restore_foreign_exception_ports(TCR *tcr)
    2590 {
    2591   exception_mask_t m = (exception_mask_t) tcr->foreign_exception_status;
    2592  
    2593   if (m) {
    2594     MACH_foreign_exception_state *fxs  =
    2595       (MACH_foreign_exception_state *) tcr->native_thread_info;
    2596     int i, n = fxs->foreign_exception_port_count;
    2597     exception_mask_t tm;
    2598 
    2599     for (i = 0; i < n; i++) {
    2600       if ((tm = fxs->masks[i]) & m) {
    2601         thread_set_exception_ports((mach_port_t)((natural)tcr->native_thread_id),
    2602                                    tm,
    2603                                    fxs->ports[i],
    2604                                    fxs->behaviors[i],
    2605                                    fxs->flavors[i]);
    2606       }
    2607     }
    2608   }
    2609 }
    2610                                    
    2611 
    2612 /*
    2613   This assumes that a Mach port (to be used as the thread's exception port) whose
    2614   "name" matches the TCR's 32-bit address has already been allocated.
    2615 */
    2616 
    2617 kern_return_t
    2618 setup_mach_exception_handling(TCR *tcr)
    2619 {
    2620   mach_port_t
    2621     thread_exception_port = TCR_TO_EXCEPTION_PORT(tcr),
    2622     task_self = mach_task_self();
    2623   kern_return_t kret;
    2624 
    2625   kret = mach_port_insert_right(task_self,
    2626                                 thread_exception_port,
    2627                                 thread_exception_port,
    2628                                 MACH_MSG_TYPE_MAKE_SEND);
    2629   MACH_CHECK_ERROR("adding send right to exception_port",kret);
    2630 
    2631   kret = tcr_establish_exception_port(tcr, (mach_port_t)((natural) tcr->native_thread_id));
    2632   if (kret == KERN_SUCCESS) {
    2633     mach_port_t exception_port_set = mach_exception_port_set();
    2634 
    2635     kret = mach_port_move_member(task_self,
    2636                                  thread_exception_port,
    2637                                  exception_port_set);
    2638   }
    2639   return kret;
    2640 }
    2641 
    2642 void
    2643 darwin_exception_init(TCR *tcr)
    2644 {
    2645   void tcr_monitor_exception_handling(TCR*, Boolean);
    2646   kern_return_t kret;
    2647   MACH_foreign_exception_state *fxs =
    2648     calloc(1, sizeof(MACH_foreign_exception_state));
    2649  
    2650   tcr->native_thread_info = (void *) fxs;
    2651 
    2652   if ((kret = setup_mach_exception_handling(tcr))
    2653       != KERN_SUCCESS) {
    2654     fprintf(dbgout, "Couldn't setup exception handler - error = %d\n", kret);
    2655     terminate_lisp();
    2656   }
    2657 }
    2658 
    2659 /*
    2660   The tcr is the "name" of the corresponding thread's exception port.
    2661   Destroying the port should remove it from all port sets of which it's
    2662   a member (notably, the exception port set.)
    2663 */
    2664 void
    2665 darwin_exception_cleanup(TCR *tcr)
    2666 {
    2667   void *fxs = tcr->native_thread_info;
    2668   extern Boolean use_mach_exception_handling;
    2669 
    2670   if (fxs) {
    2671     tcr->native_thread_info = NULL;
    2672     free(fxs);
    2673   }
    2674   if (use_mach_exception_handling) {
    2675     mach_port_deallocate(mach_task_self(),TCR_TO_EXCEPTION_PORT(tcr));
    2676     mach_port_destroy(mach_task_self(),TCR_TO_EXCEPTION_PORT(tcr));
    2677   }
    2678 }
    2679 
    2680 
    2681 Boolean
    2682 suspend_mach_thread(mach_port_t mach_thread)
    2683 {
    2684   kern_return_t status;
    2685   Boolean aborted = false;
    2686  
    2687   do {
    2688     aborted = false;
    2689     status = thread_suspend(mach_thread);
    2690     if (status == KERN_SUCCESS) {
    2691       status = thread_abort_safely(mach_thread);
    2692       if (status == KERN_SUCCESS) {
    2693         aborted = true;
    2694       } else {
    2695         fprintf(dbgout, "abort failed on thread = 0x%x\n",mach_thread);
    2696         thread_resume(mach_thread);
    2697       }
    2698     } else {
    2699       return false;
    2700     }
    2701   } while (! aborted);
    2702   return true;
    2703 }
    2704 
    2705 /*
    2706   Only do this if pthread_kill indicated that the pthread isn't
    2707   listening to signals anymore, as can happen as soon as pthread_exit()
    2708   is called on Darwin.  The thread could still call out to lisp as it
    2709   is exiting, so we need another way to suspend it in this case.
    2710 */
    2711 Boolean
    2712 mach_suspend_tcr(TCR *tcr)
    2713 {
    2714   mach_port_t mach_thread = (mach_port_t)((natural)( tcr->native_thread_id));
    2715   ExceptionInformation *pseudosigcontext;
    2716   Boolean result = false;
    2717  
    2718   result = suspend_mach_thread(mach_thread);
    2719   if (result) {
    2720     pseudosigcontext = create_thread_context_frame(mach_thread, NULL);
    2721     pseudosigcontext->uc_onstack = 0;
    2722     pseudosigcontext->uc_sigmask = (sigset_t) 0;
    2723     tcr->suspend_context = pseudosigcontext;
    2724   }
    2725   return result;
    2726 }
    2727 
    2728 void
    2729 mach_resume_tcr(TCR *tcr)
    2730 {
    2731   ExceptionInformation *xp;
    2732   mach_port_t mach_thread = (mach_port_t)((natural)(tcr->native_thread_id));
    2733  
    2734   xp = tcr->suspend_context;
    2735 #ifdef DEBUG_MACH_EXCEPTIONS
    2736   fprintf(dbgout, "resuming TCR 0x%x, pending_exception_context = 0x%x\n",
    2737           tcr, tcr->pending_exception_context);
    2738 #endif
    2739   tcr->suspend_context = NULL;
    2740   restore_mach_thread_state(mach_thread, xp);
    2741 #ifdef DEBUG_MACH_EXCEPTIONS
    2742   fprintf(dbgout, "restored state in TCR 0x%x, pending_exception_context = 0x%x\n",
    2743           tcr, tcr->pending_exception_context);
    2744 #endif
    2745   thread_resume(mach_thread);
    2746 }
    2747 
    2748 void
    2749 fatal_mach_error(char *format, ...)
    2750 {
    2751   va_list args;
    2752   char s[512];
    2753  
    2754 
    2755   va_start(args, format);
    2756   vsnprintf(s, sizeof(s),format, args);
    2757   va_end(args);
    2758 
    2759   Fatal("Mach error", s);
    2760 }
    2761 
    2762 void
    2763 pseudo_interrupt_handler(int signum, ExceptionInformation *context)
    2764 {
    2765   interrupt_handler(signum, NULL, context);
    2766 }
    2767 
    2768 int
    2769 mach_raise_thread_interrupt(TCR *target)
    2770 {
    2771   mach_port_t mach_thread = (mach_port_t)((natural)(target->native_thread_id));
    2772   kern_return_t kret;
    2773   Boolean result = false;
    2774   TCR *current = get_tcr(false);
    2775   thread_basic_info_data_t info;
    2776   mach_msg_type_number_t info_count = THREAD_BASIC_INFO_COUNT;
    2777 
    2778   LOCK(lisp_global(TCR_AREA_LOCK), current);
    2779 
    2780   if (suspend_mach_thread(mach_thread)) {
    2781     if (thread_info(mach_thread,
    2782                     THREAD_BASIC_INFO,
    2783                     (thread_info_t)&info,
    2784                     &info_count) == KERN_SUCCESS) {
    2785       if (info.suspend_count == 1) {
    2786         if ((target->valence == TCR_STATE_LISP) &&
    2787             (!target->unwinding) &&
    2788             (TCR_INTERRUPT_LEVEL(target) >= 0)) {
    2789           kret = setup_signal_frame(mach_thread,
    2790                                     (void *)pseudo_interrupt_handler,
    2791                                     SIGNAL_FOR_PROCESS_INTERRUPT,
    2792                                     0,
    2793                                     target);
    2794           if (kret == KERN_SUCCESS) {
    2795             result = true;
    2796           }
    2797         }
    2798       }
    2799     }
    2800     if (! result) {
    2801       target->interrupt_pending = 1 << fixnumshift;
    2802     }
    2803     thread_resume(mach_thread);
    2804    
    2805   }
    2806   UNLOCK(lisp_global(TCR_AREA_LOCK), current);
    2807   return 0;
    2808 }
    2809 
    2810 #endif
  • trunk/source/lisp-kernel/arm-exceptions.h

    r15158 r15470  
    108108int altivec_available;
    109109
    110 #ifdef DARWIN
    111 #include <mach/mach.h>
    112 #include <mach/mach_error.h>
    113 #include <mach/machine/thread_state.h>
    114 #include <mach/machine/thread_status.h>
    115 
    116 #endif
    117110
    118111
  • trunk/source/lisp-kernel/lisp-exceptions.h

    r14549 r15470  
    147147#ifdef DARWIN
    148148void darwin_exception_init(TCR *tcr);
    149 void darwin_exception_cleanup(TCR *tcr);
    150149#endif
    151150
  • trunk/source/lisp-kernel/lisp.h

    r15429 r15470  
    2828#include "macros.h"
    2929
    30 extern Boolean use_mach_exception_handling;
    3130
    3231extern int page_size, log2_page_size;
  • trunk/source/lisp-kernel/mach-o-image.c

    r15429 r15470  
    2020#include <string.h>
    2121#include <stdlib.h>
     22#include <stdarg.h>
    2223#include <limits.h>
    2324#include <unistd.h>
  • trunk/source/lisp-kernel/memory.c

    r15463 r15470  
    6262}
    6363
    64 #ifdef DARWIN
    65 #if WORD_SIZE == 64
    66 #define vm_region vm_region_64
    67 #endif
    68 
    69 /*
    70   Check to see if the specified address is unmapped by trying to get
    71   information about the mapped address at or beyond the target.  If
    72   the difference between the target address and the next mapped address
    73   is >= len, we can safely mmap len bytes at addr.
    74 */
    75 Boolean
    76 address_unmapped_p(char *addr, natural len)
    77 {
    78   vm_address_t vm_addr = (vm_address_t)addr;
    79   vm_size_t vm_size;
    80 #if WORD_SIZE == 64
    81   vm_region_basic_info_data_64_t vm_info;
    82 #else
    83   vm_region_basic_info_data_t vm_info;
    84 #endif
    85 #if WORD_SIZE == 64
    86   mach_msg_type_number_t vm_info_size = VM_REGION_BASIC_INFO_COUNT_64;
    87 #else
    88   mach_msg_type_number_t vm_info_size = VM_REGION_BASIC_INFO_COUNT;
    89 #endif
    90   mach_port_t vm_object_name = (mach_port_t) 0;
    91   kern_return_t kret;
    92 
    93   kret = vm_region(mach_task_self(),
    94                    &vm_addr,
    95                    &vm_size,
    96 #if WORD_SIZE == 64
    97                    VM_REGION_BASIC_INFO_64,
    98 #else
    99                    VM_REGION_BASIC_INFO,
    100 #endif
    101                    (vm_region_info_t)&vm_info,
    102                    &vm_info_size,
    103                    &vm_object_name);
    104   if (kret != KERN_SUCCESS) {
    105     return false;
    106   }
    107 
    108   return vm_addr >= (vm_address_t)(addr+len);
    109 }
    110 #endif
    111 
    112 
    113 
    114   /*
    115     Through trial and error, we've found that IMAGE_BASE_ADDRESS is
    116     likely to reside near the beginning of an unmapped block of memory
    117     that's at least 1GB in size.  We'd like to load the heap image's
    118     sections relative to IMAGE_BASE_ADDRESS; if we're able to do so,
    119     that'd allow us to file-map those sections (and would enable us to
    120     avoid having to relocate references in the data sections.)
    121 
    122     In short, we'd like to reserve 1GB starting at IMAGE_BASE_ADDRESS
    123     by creating an anonymous mapping with mmap().
    124 
    125     If we try to insist that mmap() map a 1GB block at
    126     IMAGE_BASE_ADDRESS exactly (by specifying the MAP_FIXED flag),
    127     mmap() will gleefully clobber any mapped memory that's already
    128     there.  (That region's empty at this writing, but some future
    129     version of the OS might decide to put something there.)
    130 
    131     If we don't specify MAP_FIXED, mmap() is free to treat the address
    132     we give it as a hint; Linux seems to accept the hint if doing so
    133     wouldn't cause a problem.  Naturally, that behavior's too useful
    134     for Darwin (or perhaps too inconvenient for it): it'll often
    135     return another address, even if the hint would have worked fine.
    136 
    137     We call address_unmapped_p() to ask Mach whether using MAP_FIXED
    138     would conflict with anything.  Until we discover a need to do
    139     otherwise, we'll assume that if Linux's mmap() fails to take the
    140     hint, it's because of a legitimate conflict.
    141 
    142     If Linux starts ignoring hints, we can parse /proc/<pid>/maps
    143     to implement an address_unmapped_p() for Linux.
    144   */
    14564
    14665LogicalAddress
     
    14968  void raise_limit(void);
    15069  LogicalAddress start;
    151   Boolean fixed_map_ok = false;
    152 #ifdef DARWIN
    153   fixed_map_ok = address_unmapped_p(want,totalsize);
    154 #endif
    155 #ifdef SOLARIS
    156   fixed_map_ok = true;
    157 #endif
    15870  raise_limit();
    15971#ifdef WINDOWS
     
    17890               totalsize + heap_segment_size,
    17991               PROT_NONE,
    180                MAP_PRIVATE | MAP_ANON | (fixed_map_ok ? MAP_FIXED : 0) | MAP_NORESERVE,
     92               MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
    18193               -1,
    18294               0);
  • trunk/source/lisp-kernel/platform-darwinx8632.h

    r15147 r15470  
    5252
    5353/* xp accessors, sigreturn stuff */
    54 #define DARWIN_USE_PSEUDO_SIGRETURN 1
    55 extern int darwin_sigreturn(ExceptionInformation *context);
    5654#define DarwinSigReturn(context) do {\
    57     darwin_sigreturn(context);\
     55    darwin_sigreturn(context, 0x1e);                 \
    5856    Bug(context,"sigreturn returned");\
    5957  } while (0)
     
    7674#define SIGRETURN(context) DarwinSigReturn(context)
    7775
    78 #include <mach/mach.h>
    79 #include <mach/mach_error.h>
    80 #include <mach/machine/thread_state.h>
    81 #include <mach/machine/thread_status.h>
    82 
    83 pthread_mutex_t *mach_exception_lock;
    8476
    8577#include "os-darwin.h"
     78#define SEPARATE_ALTSTACK 1
  • trunk/source/lisp-kernel/platform-darwinx8664.h

    r15461 r15470  
    5858
    5959/* xp accessors, sigreturn stuff */
    60 #define DARWIN_USE_PSEUDO_SIGRETURN 1
    61 extern int darwin_sigreturn(ExceptionInformation *context);
    6260#define DarwinSigReturn(context) do {\
    63     darwin_sigreturn(context);\
     61    darwin_sigreturn(context, 0x1e);                 \
    6462    Bug(context,"sigreturn returned");\
    6563  } while (0)
     
    8280#define SIGRETURN(context) DarwinSigReturn(context)
    8381
    84 #include <mach/mach.h>
    85 #include <mach/mach_error.h>
    86 #include <mach/machine/thread_state.h>
    87 #include <mach/machine/thread_status.h>
    88 
    89 pthread_mutex_t *mach_exception_lock;
    9082
    9183#include "os-darwin.h"
    9284
    9385
     86#define SEPARATE_ALTSTACK 1
  • trunk/source/lisp-kernel/platform-linuxarm.h

    r14354 r15470  
    4949
    5050#define PROTECT_CSTACK 1
     51#define SEPARATE_ALTSTACK 1
  • trunk/source/lisp-kernel/pmcl-kernel.c

    r15452 r15470  
    7777#endif
    7878
    79 Boolean use_mach_exception_handling =
    80 #ifdef DARWIN
    81   true
    82 #else
    83   false
    84 #endif
    85 ;
    8679
    8780#ifdef DARWIN
     
    9083#include <sys/mman.h>
    9184#include <sys/resource.h>
    92 #include <mach/mach_types.h>
    93 #include <mach/message.h>
    94 #include <mach/vm_region.h>
    95 #include <mach/port.h>
    9685#include <sys/sysctl.h>
    9786#undef undefined
     
    23592348  int result = -1;
    23602349  TCR *tcr = get_tcr(1);
    2361 #ifdef DARWIN
    2362   extern kern_return_t tcr_establish_lisp_exception_port(TCR *);
    2363 #endif
    23642350 
    23652351  result = f(arg0,arg1,arg2);
    2366 #ifdef DARWIN
    2367   tcr_establish_lisp_exception_port(tcr);
    2368 #endif
    23692352  return result;
    23702353}
  • trunk/source/lisp-kernel/ppc-exceptions.c

    r15370 r15470  
    18551855
    18561856void
    1857 signal_handler(int signum, siginfo_t *info, ExceptionInformation  *context, TCR *tcr, int old_valence)
    1858 {
     1857signal_handler(int signum, siginfo_t *info, ExceptionInformation  *context)
     1858{
     1859  TCR *tcr;
     1860  int old_valence;
    18591861  xframe_list xframe_link;
    18601862
    1861   if (!use_mach_exception_handling) {
    1862    
    1863     tcr = (TCR *) get_interrupt_tcr(false);
     1863  tcr = (TCR *) get_interrupt_tcr(false);
    18641864 
    1865     /* The signal handler's entered with all signals (notably the
    1866        thread_suspend signal) blocked.  Don't allow any other signals
    1867        (notably the thread_suspend signal) to preempt us until we've
    1868        set the TCR's xframe slot to include the current exception
    1869        context.
    1870     */
     1865  /* The signal handler's entered with all signals (notably the
     1866     thread_suspend signal) blocked.  Don't allow any other signals
     1867     (notably the thread_suspend signal) to preempt us until we've
     1868     set the TCR's xframe slot to include the current exception
     1869     context.
     1870  */
    18711871   
    18721872    old_valence = prepare_to_wait_for_exception_lock(tcr, context);
    1873   }
    18741873
    18751874  if (tcr->flags & (1<<TCR_FLAG_BIT_PENDING_SUSPEND)) {
     
    18961895     can return to the kernel/to the Mach exception handler).
    18971896  */
    1898   if (!use_mach_exception_handling) {
    1899     exit_signal_handler(tcr, old_valence);
    1900     raise_pending_interrupt(tcr);
    1901   }
     1897  exit_signal_handler(tcr, old_valence);
     1898  raise_pending_interrupt(tcr);
    19021899}
    19031900
     
    22382235install_pmcl_exception_handlers()
    22392236{
    2240 #ifdef DARWIN
    2241   extern Boolean use_mach_exception_handling;
    2242 #endif
    2243 
    2244   Boolean install_signal_handlers_for_exceptions =
    2245 #ifdef DARWIN
    2246     !use_mach_exception_handling
    2247 #else
    2248     true
    2249 #endif
    2250     ;
    2251   if (install_signal_handlers_for_exceptions) {
    2252     extern int no_sigtrap;
    2253     install_signal_handler(SIGILL, (void *)signal_handler, RESERVE_FOR_LISP);
    2254     if (no_sigtrap != 1) {
    2255       install_signal_handler(SIGTRAP, (void *)signal_handler, RESERVE_FOR_LISP);
    2256     }
    2257     install_signal_handler(SIGBUS,  (void *)signal_handler, RESERVE_FOR_LISP);
    2258     install_signal_handler(SIGSEGV, (void *)signal_handler, RESERVE_FOR_LISP);
    2259     install_signal_handler(SIGFPE, (void *)signal_handler, RESERVE_FOR_LISP);
    2260   }
     2237
     2238  extern int no_sigtrap;
     2239  install_signal_handler(SIGILL, (void *)signal_handler, RESERVE_FOR_LISP);
     2240  if (no_sigtrap != 1) {
     2241    install_signal_handler(SIGTRAP, (void *)signal_handler, RESERVE_FOR_LISP);
     2242  }
     2243  install_signal_handler(SIGBUS,  (void *)signal_handler, RESERVE_FOR_LISP);
     2244  install_signal_handler(SIGSEGV, (void *)signal_handler, RESERVE_FOR_LISP);
     2245  install_signal_handler(SIGFPE, (void *)signal_handler, RESERVE_FOR_LISP);
     2246
    22612247 
    22622248  install_signal_handler(SIGNAL_FOR_PROCESS_INTERRUPT,
     
    23702356
    23712357
    2372 
    2373 
    2374 
    2375 #ifdef DARWIN
    2376 
    2377 
    2378 #define TCR_FROM_EXCEPTION_PORT(p) ((TCR *)((natural)p))
    2379 #define TCR_TO_EXCEPTION_PORT(tcr) ((mach_port_t)((natural)(tcr)))
    2380 
    2381 
    2382 
    2383 #define LISP_EXCEPTIONS_HANDLED_MASK \
    2384  (EXC_MASK_SOFTWARE | EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC)
    2385 
    2386 /* (logcount LISP_EXCEPTIONS_HANDLED_MASK) */
    2387 #define NUM_LISP_EXCEPTIONS_HANDLED 4
    2388 
    2389 typedef struct {
    2390   int foreign_exception_port_count;
    2391   exception_mask_t         masks[NUM_LISP_EXCEPTIONS_HANDLED];
    2392   mach_port_t              ports[NUM_LISP_EXCEPTIONS_HANDLED];
    2393   exception_behavior_t behaviors[NUM_LISP_EXCEPTIONS_HANDLED];
    2394   thread_state_flavor_t  flavors[NUM_LISP_EXCEPTIONS_HANDLED];
    2395 } MACH_foreign_exception_state;
    2396 
    2397 
    2398 
    2399 
    2400 /*
    2401   Mach's exception mechanism works a little better than its signal
    2402   mechanism (and, not incidentally, it gets along with GDB a lot
    2403   better.
    2404 
    2405   Initially, we install an exception handler to handle each native
    2406   thread's exceptions.  This process involves creating a distinguished
    2407   thread which listens for kernel exception messages on a set of
    2408   0 or more thread exception ports.  As threads are created, they're
    2409   added to that port set; a thread's exception port is destroyed
    2410   (and therefore removed from the port set) when the thread exits.
    2411 
    2412   A few exceptions can be handled directly in the handler thread;
    2413   others require that we resume the user thread (and that the
    2414   exception thread resumes listening for exceptions.)  The user
    2415   thread might eventually want to return to the original context
    2416   (possibly modified somewhat.)
    2417 
    2418   As it turns out, the simplest way to force the faulting user
    2419   thread to handle its own exceptions is to do pretty much what
    2420   signal() does: the exception handlng thread sets up a sigcontext
    2421   on the user thread's stack and forces the user thread to resume
    2422   execution as if a signal handler had been called with that
    2423   context as an argument.  We can use a distinguished UUO at a
    2424   distinguished address to do something like sigreturn(); that'll
    2425   have the effect of resuming the user thread's execution in
    2426   the (pseudo-) signal context.
    2427 
    2428   Since:
    2429     a) we have miles of code in C and in Lisp that knows how to
    2430     deal with Linux sigcontexts
    2431     b) Linux sigcontexts contain a little more useful information
    2432     (the DAR, DSISR, etc.) than their Darwin counterparts
    2433     c) we have to create a sigcontext ourselves when calling out
    2434     to the user thread: we aren't really generating a signal, just
    2435     leveraging existing signal-handling code.
    2436 
    2437   we create a Linux sigcontext struct.
    2438 
    2439   Simple ?  Hopefully from the outside it is ...
    2440 
    2441   We want the process of passing a thread's own context to it to
    2442   appear to be atomic: in particular, we don't want the GC to suspend
    2443   a thread that's had an exception but has not yet had its user-level
    2444   exception handler called, and we don't want the thread's exception
    2445   context to be modified by a GC while the Mach handler thread is
    2446   copying it around.  On Linux (and on Jaguar), we avoid this issue
    2447   because (a) the kernel sets up the user-level signal handler and
    2448   (b) the signal handler blocks signals (including the signal used
    2449   by the GC to suspend threads) until tcr->xframe is set up.
    2450 
    2451   The GC and the Mach server thread therefore contend for the lock
    2452   "mach_exception_lock".  The Mach server thread holds the lock
    2453   when copying exception information between the kernel and the
    2454   user thread; the GC holds this lock during most of its execution
    2455   (delaying exception processing until it can be done without
    2456   GC interference.)
    2457 
    2458 */
    2459 
    2460 #ifdef PPC64
    2461 #define C_REDZONE_LEN           320
    2462 #define C_STK_ALIGN             32
    2463 #else
    2464 #define C_REDZONE_LEN           224
    2465 #define C_STK_ALIGN             16
    2466 #endif
    2467 #define C_PARAMSAVE_LEN         64
    2468 #define C_LINKAGE_LEN           48
    2469 
    2470 #define TRUNC_DOWN(a,b,c)  (((((natural)a)-(b))/(c)) * (c))
    2471 
    2472 void
    2473 fatal_mach_error(char *format, ...);
    2474 
    2475 #define MACH_CHECK_ERROR(context,x) if (x != KERN_SUCCESS) {fatal_mach_error("Mach error while %s : %d", context, x);}
    2476 
    2477 
    2478 void
    2479 restore_mach_thread_state(mach_port_t thread, ExceptionInformation *pseudosigcontext)
    2480 {
    2481   kern_return_t kret;
    2482   MCONTEXT_T mc = UC_MCONTEXT(pseudosigcontext);
    2483 
    2484   /* Set the thread's FP state from the pseudosigcontext */
    2485   kret = thread_set_state(thread,
    2486                           PPC_FLOAT_STATE,
    2487                           (thread_state_t)&(mc->__fs),
    2488                           PPC_FLOAT_STATE_COUNT);
    2489 
    2490   MACH_CHECK_ERROR("setting thread FP state", kret);
    2491 
    2492   /* The thread'll be as good as new ... */
    2493 #ifdef PPC64
    2494   kret = thread_set_state(thread,
    2495                           PPC_THREAD_STATE64,
    2496                           (thread_state_t)&(mc->__ss),
    2497                           PPC_THREAD_STATE64_COUNT);
    2498 #else
    2499   kret = thread_set_state(thread,
    2500                           MACHINE_THREAD_STATE,
    2501                           (thread_state_t)&(mc->__ss),
    2502                           MACHINE_THREAD_STATE_COUNT);
    2503 #endif
    2504   MACH_CHECK_ERROR("setting thread state", kret);
    2505 
    2506 
    2507 /* This code runs in the exception handling thread, in response
    2508    to an attempt to execute the UU0 at "pseudo_sigreturn" (e.g.,
    2509    in response to a call to pseudo_sigreturn() from the specified
    2510    user thread.
    2511    Find that context (the user thread's R3 points to it), then
    2512    use that context to set the user thread's state.  When this
    2513    function's caller returns, the Mach kernel will resume the
    2514    user thread.
    2515 */
    2516 
    2517 kern_return_t
    2518 do_pseudo_sigreturn(mach_port_t thread, TCR *tcr)
    2519 {
    2520   ExceptionInformation *xp;
    2521 
    2522 #ifdef DEBUG_MACH_EXCEPTIONS
    2523   fprintf(dbgout, "doing pseudo_sigreturn for 0x%x\n",tcr);
    2524 #endif
    2525   xp = tcr->pending_exception_context;
    2526   if (xp) {
    2527     tcr->pending_exception_context = NULL;
    2528     tcr->valence = TCR_STATE_LISP;
    2529     restore_mach_thread_state(thread, xp);
    2530     raise_pending_interrupt(tcr);
    2531   } else {
    2532     Bug(NULL, "no xp here!\n");
    2533   }
    2534 #ifdef DEBUG_MACH_EXCEPTIONS
    2535   fprintf(dbgout, "did pseudo_sigreturn for 0x%x\n",tcr);
    2536 #endif
    2537   return KERN_SUCCESS;
    2538 
    2539 
    2540 ExceptionInformation *
    2541 create_thread_context_frame(mach_port_t thread,
    2542                             natural *new_stack_top)
    2543 {
    2544 #ifdef PPC64
    2545   ppc_thread_state64_t ts;
    2546 #else
    2547   ppc_thread_state_t ts;
    2548 #endif
    2549   mach_msg_type_number_t thread_state_count;
    2550   kern_return_t result;
    2551   ExceptionInformation *pseudosigcontext;
    2552   MCONTEXT_T mc;
    2553   natural stackp, backlink;
    2554 
    2555 #ifdef PPC64
    2556   thread_state_count = PPC_THREAD_STATE64_COUNT;
    2557   result = thread_get_state(thread,
    2558                             PPC_THREAD_STATE64,
    2559                             (thread_state_t)&ts,
    2560                             &thread_state_count);
    2561 #else
    2562   thread_state_count = MACHINE_THREAD_STATE_COUNT;
    2563   result = thread_get_state(thread,
    2564                             PPC_THREAD_STATE,   /* GPRs, some SPRs  */
    2565                             (thread_state_t)&ts,
    2566                             &thread_state_count);
    2567 #endif
    2568  
    2569   if (result != KERN_SUCCESS) {
    2570     get_tcr(true);
    2571     Bug(NULL, "Exception thread can't obtain thread state, Mach result = %d", result);
    2572   }
    2573   stackp = ts.__r1;
    2574   backlink = stackp;
    2575   stackp = TRUNC_DOWN(stackp, C_REDZONE_LEN, C_STK_ALIGN);
    2576   stackp -= sizeof(*pseudosigcontext);
    2577   pseudosigcontext = (ExceptionInformation *) ptr_from_lispobj(stackp);
    2578 
    2579   stackp = TRUNC_DOWN(stackp, sizeof(*mc), C_STK_ALIGN);
    2580   mc = (MCONTEXT_T) ptr_from_lispobj(stackp);
    2581   memmove(&(mc->__ss),&ts,sizeof(ts));
    2582 
    2583   thread_state_count = PPC_FLOAT_STATE_COUNT;
    2584   thread_get_state(thread,
    2585                    PPC_FLOAT_STATE,
    2586                    (thread_state_t)&(mc->__fs),
    2587                    &thread_state_count);
    2588 
    2589 
    2590 #ifdef PPC64
    2591   thread_state_count = PPC_EXCEPTION_STATE64_COUNT;
    2592 #else
    2593   thread_state_count = PPC_EXCEPTION_STATE_COUNT;
    2594 #endif
    2595   thread_get_state(thread,
    2596 #ifdef PPC64
    2597                    PPC_EXCEPTION_STATE64,
    2598 #else
    2599                    PPC_EXCEPTION_STATE,
    2600 #endif
    2601                    (thread_state_t)&(mc->__es),
    2602                    &thread_state_count);
    2603 
    2604 
    2605   UC_MCONTEXT(pseudosigcontext) = mc;
    2606   stackp = TRUNC_DOWN(stackp, C_PARAMSAVE_LEN, C_STK_ALIGN);
    2607   stackp -= C_LINKAGE_LEN;
    2608   *(natural *)ptr_from_lispobj(stackp) = backlink;
    2609   if (new_stack_top) {
    2610     *new_stack_top = stackp;
    2611   }
    2612   return pseudosigcontext;
    2613 }
    2614 
    2615 /*
    2616   This code sets up the user thread so that it executes a "pseudo-signal
    2617   handler" function when it resumes.  Create a linux sigcontext struct
    2618   on the thread's stack and pass it as an argument to the pseudo-signal
    2619   handler.
    2620 
    2621   Things are set up so that the handler "returns to" pseudo_sigreturn(),
    2622   which will restore the thread's context.
    2623 
    2624   If the handler invokes code that throws (or otherwise never sigreturn()'s
    2625   to the context), that's fine.
    2626 
    2627   Actually, check that: throw (and variants) may need to be careful and
    2628   pop the tcr's xframe list until it's younger than any frame being
    2629   entered.
    2630 */
    2631 
    2632 int
    2633 setup_signal_frame(mach_port_t thread,
    2634                    void *handler_address,
    2635                    int signum,
    2636                    int code,
    2637                    TCR *tcr)
    2638 {
    2639 #ifdef PPC64
    2640   ppc_thread_state64_t ts;
    2641 #else
    2642   ppc_thread_state_t ts;
    2643 #endif
    2644   ExceptionInformation *pseudosigcontext;
    2645   int old_valence = tcr->valence;
    2646   natural stackp;
    2647 
    2648 #ifdef DEBUG_MACH_EXCEPTIONS
    2649   fprintf(dbgout,"Setting up exception handling for 0x%x\n", tcr);
    2650 #endif
    2651   pseudosigcontext = create_thread_context_frame(thread, &stackp);
    2652   pseudosigcontext->uc_onstack = 0;
    2653   pseudosigcontext->uc_sigmask = (sigset_t) 0;
    2654   tcr->pending_exception_context = pseudosigcontext;
    2655   tcr->valence = TCR_STATE_EXCEPTION_WAIT;
    2656  
    2657 
    2658   /*
    2659      It seems like we've created a  sigcontext on the thread's
    2660      stack.  Set things up so that we call the handler (with appropriate
    2661      args) when the thread's resumed.
    2662   */
    2663 
    2664   ts.__srr0 = (natural) handler_address;
    2665   ts.__srr1 = (int) xpMSR(pseudosigcontext) & ~MSR_FE0_FE1_MASK;
    2666   ts.__r1 = stackp;
    2667   ts.__r3 = signum;
    2668   ts.__r4 = (natural)pseudosigcontext;
    2669   ts.__r5 = (natural)tcr;
    2670   ts.__r6 = (natural)old_valence;
    2671   ts.__lr = (natural)pseudo_sigreturn;
    2672 
    2673 
    2674 #ifdef PPC64
    2675   ts.__r13 = xpGPR(pseudosigcontext,13);
    2676   thread_set_state(thread,
    2677                    PPC_THREAD_STATE64,
    2678                    (thread_state_t)&ts,
    2679                    PPC_THREAD_STATE64_COUNT);
    2680 #else
    2681   thread_set_state(thread,
    2682                    MACHINE_THREAD_STATE,
    2683                    (thread_state_t)&ts,
    2684                    MACHINE_THREAD_STATE_COUNT);
    2685 #endif
    2686 #ifdef DEBUG_MACH_EXCEPTIONS
    2687   fprintf(dbgout,"Set up exception context for 0x%x at 0x%x\n", tcr, tcr->pending_exception_context);
    2688 #endif
    2689   return 0;
    2690 }
    2691 
    2692 
    2693 void
    2694 pseudo_signal_handler(int signum, ExceptionInformation *context, TCR *tcr, int old_valence)
    2695 {
    2696   signal_handler(signum, NULL, context, tcr, old_valence);
    2697 }
    2698 
    2699 
    2700 int
    2701 thread_set_fp_exceptions_enabled(mach_port_t thread, Boolean enabled)
    2702 {
    2703 #ifdef PPC64
    2704   ppc_thread_state64_t ts;
    2705 #else
    2706   ppc_thread_state_t ts;
    2707 #endif
    2708   mach_msg_type_number_t thread_state_count;
    2709 
    2710 #ifdef PPC64
    2711   thread_state_count = PPC_THREAD_STATE64_COUNT;
    2712 #else
    2713   thread_state_count = PPC_THREAD_STATE_COUNT;
    2714 #endif
    2715   thread_get_state(thread,
    2716 #ifdef PPC64
    2717                    PPC_THREAD_STATE64,  /* GPRs, some SPRs  */
    2718 #else
    2719                    PPC_THREAD_STATE,    /* GPRs, some SPRs  */
    2720 #endif
    2721                    (thread_state_t)&ts,
    2722                    &thread_state_count);
    2723   if (enabled) {
    2724     ts.__srr1 |= MSR_FE0_FE1_MASK;
    2725   } else {
    2726     ts.__srr1 &= ~MSR_FE0_FE1_MASK;
    2727   }
    2728  
    2729   ts.__srr0 += 4;
    2730   thread_set_state(thread,
    2731 #ifdef PPC64
    2732                    PPC_THREAD_STATE64,  /* GPRs, some SPRs  */
    2733 #else
    2734                    PPC_THREAD_STATE,    /* GPRs, some SPRs  */
    2735 #endif
    2736                    (thread_state_t)&ts,
    2737 #ifdef PPC64
    2738                    PPC_THREAD_STATE64_COUNT
    2739 #else
    2740                    PPC_THREAD_STATE_COUNT
    2741 #endif
    2742                    );
    2743 
    2744   return 0;
    2745 }
    2746 
    2747 /*
    2748   This function runs in the exception handling thread.  It's
    2749   called (by this precise name) from the library function "exc_server()"
    2750   when the thread's exception ports are set up.  (exc_server() is called
    2751   via mach_msg_server(), which is a function that waits for and dispatches
    2752   on exception messages from the Mach kernel.)
    2753 
    2754   This checks to see if the exception was caused by a pseudo_sigreturn()
    2755   UUO; if so, it arranges for the thread to have its state restored
    2756   from the specified context.
    2757 
    2758   Otherwise, it tries to map the exception to a signal number and
    2759   arranges that the thread run a "pseudo signal handler" to handle
    2760   the exception.
    2761 
    2762   Some exceptions could and should be handled here directly.
    2763 */
    2764 
    2765 kern_return_t
    2766 catch_exception_raise(mach_port_t exception_port,
    2767                       mach_port_t thread,
    2768                       mach_port_t task,
    2769                       exception_type_t exception,
    2770                       exception_data_t code_vector,
    2771                       mach_msg_type_number_t code_count)
    2772 {
    2773   int signum = 0, code = *code_vector, code1;
    2774   TCR *tcr = TCR_FROM_EXCEPTION_PORT(exception_port);
    2775   kern_return_t kret;
    2776 
    2777 #ifdef DEBUG_MACH_EXCEPTIONS
    2778   fprintf(dbgout, "obtaining Mach exception lock in exception thread\n");
    2779 #endif
    2780 
    2781   if (tcr->flags & (1<<TCR_FLAG_BIT_PENDING_EXCEPTION)) {
    2782     CLR_TCR_FLAG(tcr,TCR_FLAG_BIT_PENDING_EXCEPTION);
    2783   }
    2784   if ((exception == EXC_BAD_INSTRUCTION) &&
    2785       (code_vector[0] == EXC_PPC_UNIPL_INST) &&
    2786       (((code1 = code_vector[1]) == (int)pseudo_sigreturn) ||
    2787        (code1 == (int)enable_fp_exceptions) ||
    2788        (code1 == (int)disable_fp_exceptions))) {
    2789     if (code1 == (int)pseudo_sigreturn) {
    2790       kret = do_pseudo_sigreturn(thread, tcr);
    2791 #if 0
    2792       fprintf(dbgout, "Exception return in 0x%x\n",tcr);
    2793 #endif
    2794        
    2795     } else if (code1 == (int)enable_fp_exceptions) {
    2796       kret = thread_set_fp_exceptions_enabled(thread, true);
    2797     } else kret =  thread_set_fp_exceptions_enabled(thread, false);
    2798   } else if (tcr->flags & (1<<TCR_FLAG_BIT_PROPAGATE_EXCEPTION)) {
    2799     CLR_TCR_FLAG(tcr,TCR_FLAG_BIT_PROPAGATE_EXCEPTION);
    2800     kret = 17;
    2801   } else {
    2802     switch (exception) {
    2803     case EXC_BAD_ACCESS:
    2804       signum = SIGSEGV;
    2805       break;
    2806        
    2807     case EXC_BAD_INSTRUCTION:
    2808       signum = SIGILL;
    2809       break;
    2810      
    2811     case EXC_SOFTWARE:
    2812       if (code == EXC_PPC_TRAP) {
    2813         signum = SIGTRAP;
    2814       }
    2815       break;
    2816      
    2817     case EXC_ARITHMETIC:
    2818       signum = SIGFPE;
    2819       break;
    2820 
    2821     default:
    2822       break;
    2823     }
    2824     if (signum) {
    2825       kret = setup_signal_frame(thread,
    2826                                 (void *)pseudo_signal_handler,
    2827                                 signum,
    2828                                 code,
    2829                                 tcr);
    2830 #if 0
    2831       fprintf(dbgout, "Setup pseudosignal handling in 0x%x\n",tcr);
    2832 #endif
    2833 
    2834     } else {
    2835       kret = 17;
    2836     }
    2837   }
    2838 
    2839   return kret;
    2840 }
    2841 
    2842 
    2843 
    2844 typedef struct {
    2845   mach_msg_header_t Head;
    2846   /* start of the kernel processed data */
    2847   mach_msg_body_t msgh_body;
    2848   mach_msg_port_descriptor_t thread;
    2849   mach_msg_port_descriptor_t task;
    2850   /* end of the kernel processed data */
    2851   NDR_record_t NDR;
    2852   exception_type_t exception;
    2853   mach_msg_type_number_t codeCnt;
    2854   integer_t code[2];
    2855   mach_msg_trailer_t trailer;
    2856 } exceptionRequest;
    2857 
    2858 
    2859 boolean_t
    2860 openmcl_exc_server(mach_msg_header_t *in, mach_msg_header_t *out)
    2861 {
    2862   static NDR_record_t _NDR = {0};
    2863   kern_return_t handled;
    2864   mig_reply_error_t *reply = (mig_reply_error_t *) out;
    2865   exceptionRequest *req = (exceptionRequest *) in;
    2866 
    2867   reply->NDR = _NDR;
    2868 
    2869   out->msgh_bits = in->msgh_bits & MACH_MSGH_BITS_REMOTE_MASK;
    2870   out->msgh_remote_port = in->msgh_remote_port;
    2871   out->msgh_size = sizeof(mach_msg_header_t)+(3 * sizeof(unsigned));
    2872   out->msgh_local_port = MACH_PORT_NULL;
    2873   out->msgh_id = in->msgh_id+100;
    2874 
    2875   /* Could handle other exception flavors in the range 2401-2403 */
    2876 
    2877 
    2878   if (in->msgh_id != 2401) {
    2879     reply->RetCode = MIG_BAD_ID;
    2880     return FALSE;
    2881   }
    2882   handled = catch_exception_raise(req->Head.msgh_local_port,
    2883                                   req->thread.name,
    2884                                   req->task.name,
    2885                                   req->exception,
    2886                                   req->code,
    2887                                   req->codeCnt);
    2888   reply->RetCode = handled;
    2889   return TRUE;
    2890 }
    2891 
    2892 /*
    2893   The initial function for an exception-handling thread.
    2894 */
    2895 
    2896 void *
    2897 exception_handler_proc(void *arg)
    2898 {
    2899   extern boolean_t exc_server();
    2900   mach_port_t p = TCR_TO_EXCEPTION_PORT(arg);
    2901 
    2902   mach_msg_server(openmcl_exc_server, 2048, p, 0);
    2903   /* Should never return. */
    2904   abort();
    2905 }
    2906 
    2907 
    2908 
    2909 mach_port_t
    2910 mach_exception_port_set()
    2911 {
    2912   static mach_port_t __exception_port_set = MACH_PORT_NULL;
    2913   kern_return_t kret; 
    2914   if (__exception_port_set == MACH_PORT_NULL) {
    2915     kret = mach_port_allocate(mach_task_self(),
    2916                               MACH_PORT_RIGHT_PORT_SET,
    2917                               &__exception_port_set);
    2918     MACH_CHECK_ERROR("allocating thread exception_ports",kret);
    2919     create_system_thread(0,
    2920                          NULL,
    2921                          exception_handler_proc,
    2922                          (void *)((natural)__exception_port_set));
    2923   }
    2924   return __exception_port_set;
    2925 }
    2926 
    2927 /*
    2928   Setup a new thread to handle those exceptions specified by
    2929   the mask "which".  This involves creating a special Mach
    2930   message port, telling the Mach kernel to send exception
    2931   messages for the calling thread to that port, and setting
    2932   up a handler thread which listens for and responds to
    2933   those messages.
    2934 
    2935 */
    2936 
    2937 /*
    2938   Establish the lisp thread's TCR as its exception port, and determine
    2939   whether any other ports have been established by foreign code for
    2940   exceptions that lisp cares about.
    2941 
    2942   If this happens at all, it should happen on return from foreign
    2943   code and on entry to lisp code via a callback.
    2944 
    2945   This is a lot of trouble (and overhead) to support Java, or other
    2946   embeddable systems that clobber their caller's thread exception ports.
    2947  
    2948 */
    2949 kern_return_t
    2950 tcr_establish_exception_port(TCR *tcr, mach_port_t thread)
    2951 {
    2952   kern_return_t kret;
    2953   MACH_foreign_exception_state *fxs = (MACH_foreign_exception_state *)tcr->native_thread_info;
    2954   int i;
    2955   unsigned n = NUM_LISP_EXCEPTIONS_HANDLED;
    2956   mach_port_t lisp_port = TCR_TO_EXCEPTION_PORT(tcr), foreign_port;
    2957   exception_mask_t mask = 0;
    2958 
    2959   kret = thread_swap_exception_ports(thread,
    2960                                      LISP_EXCEPTIONS_HANDLED_MASK,
    2961                                      lisp_port,
    2962                                      EXCEPTION_DEFAULT,
    2963                                      THREAD_STATE_NONE,
    2964                                      fxs->masks,
    2965                                      &n,
    2966                                      fxs->ports,
    2967                                      fxs->behaviors,
    2968                                      fxs->flavors);
    2969   if (kret == KERN_SUCCESS) {
    2970     fxs->foreign_exception_port_count = n;
    2971     for (i = 0; i < n; i ++) {
    2972       foreign_port = fxs->ports[i];
    2973 
    2974       if ((foreign_port != lisp_port) &&
    2975           (foreign_port != MACH_PORT_NULL)) {
    2976         mask |= fxs->masks[i];
    2977       }
    2978     }
    2979     tcr->foreign_exception_status = (int) mask;
    2980   }
    2981   return kret;
    2982 }
    2983 
    2984 kern_return_t
    2985 tcr_establish_lisp_exception_port(TCR *tcr)
    2986 {
    2987   return tcr_establish_exception_port(tcr, (mach_port_t)((natural)tcr->native_thread_id));
    2988 }
    2989 
    2990 /*
    2991   Do this when calling out to or returning from foreign code, if
    2992   any conflicting foreign exception ports were established when we
    2993   last entered lisp code.
    2994 */
    2995 kern_return_t
    2996 restore_foreign_exception_ports(TCR *tcr)
    2997 {
    2998   exception_mask_t m = (exception_mask_t) tcr->foreign_exception_status;
    2999  
    3000   if (m) {
    3001     MACH_foreign_exception_state *fxs  =
    3002       (MACH_foreign_exception_state *) tcr->native_thread_info;
    3003     int i, n = fxs->foreign_exception_port_count;
    3004     exception_mask_t tm;
    3005 
    3006     for (i = 0; i < n; i++) {
    3007       if ((tm = fxs->masks[i]) & m) {
    3008         thread_set_exception_ports((mach_port_t)((natural)tcr->native_thread_id),
    3009                                    tm,
    3010                                    fxs->ports[i],
    3011                                    fxs->behaviors[i],
    3012                                    fxs->flavors[i]);
    3013       }
    3014     }
    3015   }
    3016 }
    3017                                    
    3018 
    3019 /*
    3020   This assumes that a Mach port (to be used as the thread's exception port) whose
    3021   "name" matches the TCR's 32-bit address has already been allocated.
    3022 */
    3023 
    3024 kern_return_t
    3025 setup_mach_exception_handling(TCR *tcr)
    3026 {
    3027   mach_port_t
    3028     thread_exception_port = TCR_TO_EXCEPTION_PORT(tcr),
    3029     task_self = mach_task_self();
    3030   kern_return_t kret;
    3031 
    3032   kret = mach_port_insert_right(task_self,
    3033                                 thread_exception_port,
    3034                                 thread_exception_port,
    3035                                 MACH_MSG_TYPE_MAKE_SEND);
    3036   MACH_CHECK_ERROR("adding send right to exception_port",kret);
    3037 
    3038   kret = tcr_establish_exception_port(tcr, (mach_port_t)((natural) tcr->native_thread_id));
    3039   if (kret == KERN_SUCCESS) {
    3040     mach_port_t exception_port_set = mach_exception_port_set();
    3041 
    3042     kret = mach_port_move_member(task_self,
    3043                                  thread_exception_port,
    3044                                  exception_port_set);
    3045   }
    3046   return kret;
    3047 }
    3048 
    3049 void
    3050 darwin_exception_init(TCR *tcr)
    3051 {
    3052   void tcr_monitor_exception_handling(TCR*, Boolean);
    3053   kern_return_t kret;
    3054   MACH_foreign_exception_state *fxs =
    3055     calloc(1, sizeof(MACH_foreign_exception_state));
    3056  
    3057   tcr->native_thread_info = (void *) fxs;
    3058 
    3059   if ((kret = setup_mach_exception_handling(tcr))
    3060       != KERN_SUCCESS) {
    3061     fprintf(dbgout, "Couldn't setup exception handler - error = %d\n", kret);
    3062     terminate_lisp();
    3063   }
    3064 }
    3065 
    3066 /*
    3067   The tcr is the "name" of the corresponding thread's exception port.
    3068   Destroying the port should remove it from all port sets of which it's
    3069   a member (notably, the exception port set.)
    3070 */
    3071 void
    3072 darwin_exception_cleanup(TCR *tcr)
    3073 {
    3074   void *fxs = tcr->native_thread_info;
    3075   extern Boolean use_mach_exception_handling;
    3076 
    3077   if (fxs) {
    3078     tcr->native_thread_info = NULL;
    3079     free(fxs);
    3080   }
    3081   if (use_mach_exception_handling) {
    3082     mach_port_deallocate(mach_task_self(),TCR_TO_EXCEPTION_PORT(tcr));
    3083     mach_port_destroy(mach_task_self(),TCR_TO_EXCEPTION_PORT(tcr));
    3084   }
    3085 }
    3086 
    3087 
    3088 Boolean
    3089 suspend_mach_thread(mach_port_t mach_thread)
    3090 {
    3091   kern_return_t status;
    3092   Boolean aborted = false;
    3093  
    3094   do {
    3095     aborted = false;
    3096     status = thread_suspend(mach_thread);
    3097     if (status == KERN_SUCCESS) {
    3098       status = thread_abort_safely(mach_thread);
    3099       if (status == KERN_SUCCESS) {
    3100         aborted = true;
    3101       } else {
    3102         fprintf(dbgout, "abort failed on thread = 0x%x\n",mach_thread);
    3103         thread_resume(mach_thread);
    3104       }
    3105     } else {
    3106       return false;
    3107     }
    3108   } while (! aborted);
    3109   return true;
    3110 }
    3111 
    3112 /*
    3113   Only do this if pthread_kill indicated that the pthread isn't
    3114   listening to signals anymore, as can happen as soon as pthread_exit()
    3115   is called on Darwin.  The thread could still call out to lisp as it
    3116   is exiting, so we need another way to suspend it in this case.
    3117 */
    3118 Boolean
    3119 mach_suspend_tcr(TCR *tcr)
    3120 {
    3121   mach_port_t mach_thread = (mach_port_t)((natural)( tcr->native_thread_id));
    3122   ExceptionInformation *pseudosigcontext;
    3123   Boolean result = false;
    3124  
    3125   result = suspend_mach_thread(mach_thread);
    3126   if (result) {
    3127     pseudosigcontext = create_thread_context_frame(mach_thread, NULL);
    3128     pseudosigcontext->uc_onstack = 0;
    3129     pseudosigcontext->uc_sigmask = (sigset_t) 0;
    3130     tcr->suspend_context = pseudosigcontext;
    3131   }
    3132   return result;
    3133 }
    3134 
    3135 void
    3136 mach_resume_tcr(TCR *tcr)
    3137 {
    3138   ExceptionInformation *xp;
    3139   mach_port_t mach_thread = (mach_port_t)((natural)(tcr->native_thread_id));
    3140  
    3141   xp = tcr->suspend_context;
    3142 #ifdef DEBUG_MACH_EXCEPTIONS
    3143   fprintf(dbgout, "resuming TCR 0x%x, pending_exception_context = 0x%x\n",
    3144           tcr, tcr->pending_exception_context);
    3145 #endif
    3146   tcr->suspend_context = NULL;
    3147   restore_mach_thread_state(mach_thread, xp);
    3148 #ifdef DEBUG_MACH_EXCEPTIONS
    3149   fprintf(dbgout, "restored state in TCR 0x%x, pending_exception_context = 0x%x\n",
    3150           tcr, tcr->pending_exception_context);
    3151 #endif
    3152   thread_resume(mach_thread);
    3153 }
    3154 
    3155 void
    3156 fatal_mach_error(char *format, ...)
    3157 {
    3158   va_list args;
    3159   char s[512];
    3160  
    3161 
    3162   va_start(args, format);
    3163   vsnprintf(s, sizeof(s),format, args);
    3164   va_end(args);
    3165 
    3166   Fatal("Mach error", s);
    3167 }
    3168 
    3169 void
    3170 pseudo_interrupt_handler(int signum, ExceptionInformation *context)
    3171 {
    3172   interrupt_handler(signum, NULL, context);
    3173 }
    3174 
    3175 int
    3176 mach_raise_thread_interrupt(TCR *target)
    3177 {
    3178   mach_port_t mach_thread = (mach_port_t)((natural)(target->native_thread_id));
    3179   kern_return_t kret;
    3180   Boolean result = false;
    3181   TCR *current = get_tcr(false);
    3182   thread_basic_info_data_t info;
    3183   mach_msg_type_number_t info_count = THREAD_BASIC_INFO_COUNT;
    3184 
    3185   LOCK(lisp_global(TCR_AREA_LOCK), current);
    3186 
    3187   if (suspend_mach_thread(mach_thread)) {
    3188     if (thread_info(mach_thread,
    3189                     THREAD_BASIC_INFO,
    3190                     (thread_info_t)&info,
    3191                     &info_count) == KERN_SUCCESS) {
    3192       if (info.suspend_count == 1) {
    3193         if ((target->valence == TCR_STATE_LISP) &&
    3194             (!target->unwinding) &&
    3195             (TCR_INTERRUPT_LEVEL(target) >= 0)) {
    3196           kret = setup_signal_frame(mach_thread,
    3197                                     (void *)pseudo_interrupt_handler,
    3198                                     SIGNAL_FOR_PROCESS_INTERRUPT,
    3199                                     0,
    3200                                     target);
    3201           if (kret == KERN_SUCCESS) {
    3202             result = true;
    3203           }
    3204         }
    3205       }
    3206     }
    3207     if (! result) {
    3208       target->interrupt_pending = 1 << fixnumshift;
    3209     }
    3210     thread_resume(mach_thread);
    3211    
    3212   }
    3213   UNLOCK(lisp_global(TCR_AREA_LOCK), current);
    3214   return 0;
    3215 }
    3216 
    3217 #endif
  • trunk/source/lisp-kernel/ppc-exceptions.h

    r13637 r15470  
    278278int altivec_available;
    279279
    280 #ifdef DARWIN
    281 #include <mach/mach.h>
    282 #include <mach/mach_error.h>
    283 #include <mach/machine/thread_state.h>
    284 #include <mach/machine/thread_status.h>
    285 
    286 #endif
    287 
    288280/* Yet another way to look at a branch instruction ... */
    289281typedef union {
  • trunk/source/lisp-kernel/thread_manager.c

    r15463 r15470  
    171171{
    172172  pthread_t thread = (pthread_t)TCR_AUX(target)->osid;
    173 #ifdef DARWIN_not_yet
    174   if (use_mach_exception_handling) {
    175     return mach_raise_thread_interrupt(target);
    176   }
    177 #endif
    178173  if (thread != (pthread_t) 0) {
    179174    return pthread_kill(thread, SIGNAL_FOR_PROCESS_INTERRUPT);
     
    828823  TCR *tcr,  *next;
    829824#ifdef DARWIN
    830   extern Boolean use_mach_exception_handling;
    831 #ifdef DARWIN
    832825  extern TCR* darwin_allocate_tcr(void);
    833826  extern void darwin_free_tcr(TCR *);
    834 #endif
    835   kern_return_t kret;
    836   mach_port_t
    837     thread_exception_port,
    838     task_self = mach_task_self();
    839827#endif
    840828  for (;;) {
     
    843831#else
    844832    tcr = calloc(1, sizeof(TCR));
    845 #endif
    846 #ifdef DARWIN
    847     if (use_mach_exception_handling) {
    848       if (mach_port_allocate(task_self,
    849                              MACH_PORT_RIGHT_RECEIVE,
    850                              &thread_exception_port) == KERN_SUCCESS) {
    851         tcr->io_datum = (void *)((natural)thread_exception_port);
    852         associate_tcr_with_exception_port(thread_exception_port,tcr);
    853       } else {
    854         Fatal("Can't allocate Mach exception port for thread.", "");
    855       }
    856     }
    857 
    858833#endif
    859834    return tcr;
     
    13581333
    13591334  area *vs, *ts, *cs;
    1360 #ifdef DARWIN
    1361   mach_port_t kernel_thread;
    1362 #endif
    13631335 
    13641336  if (current == NULL) {
     
    13761348    }
    13771349#ifdef DARWIN
    1378     darwin_exception_cleanup(tcr);
    1379     kernel_thread = (mach_port_t) (uint32_t)(natural)( TCR_AUX(tcr)->native_thread_id);
    13801350#endif
    13811351    LOCK(lisp_global(TCR_AREA_LOCK),current);
     
    14041374       free that alternate stack here.
    14051375    */
    1406 #ifdef ARM
    1407 #if defined(LINUX)
     1376#ifdef SEPARATE_ALTSTACK
    14081377    {
    14091378      stack_t new, current;
     
    14131382      }
    14141383    }
    1415 #endif
    14161384#endif
    14171385    destroy_semaphore(&TCR_AUX(tcr)->suspend);
     
    14501418#endif
    14511419    UNLOCK(lisp_global(TCR_AREA_LOCK),current);
    1452 #ifdef DARWIN
    1453     {
    1454       mach_port_urefs_t nrefs;
    1455       ipc_space_t task = mach_task_self();
    1456 
    1457       if (mach_port_get_refs(task,kernel_thread,MACH_PORT_RIGHT_SEND,&nrefs) == KERN_SUCCESS) {
    1458         if (nrefs > 1) {
    1459           mach_port_mod_refs(task,kernel_thread,MACH_PORT_RIGHT_SEND,-(nrefs-1));
    1460         }
    1461       }
    1462     }
    1463 #endif
    14641420  } else {
    14651421    tsd_set(lisp_global(TCR_KEY), TCR_TO_TSD(tcr));
     
    15451501  TCR_AUX(tcr)->errno_loc = (int *)(&errno);
    15461502  tsd_set(lisp_global(TCR_KEY), TCR_TO_TSD(tcr));
    1547 #ifdef DARWIN
    1548   extern Boolean use_mach_exception_handling;
    1549   if (use_mach_exception_handling) {
    1550     darwin_exception_init(tcr);
    1551   }
    1552 #endif
    15531503#ifdef LINUX
    15541504  linux_exception_init(tcr);
  • trunk/source/lisp-kernel/x86-asmutils32.s

    r15229 r15470  
    110110
    111111
    112         __ifdef(`DARWIN')
    113 _exportfn(C(pseudo_sigreturn))
    114         __(hlt)
    115         __(jmp C(pseudo_sigreturn))
    116 _endfn
    117         __endif   
    118112
    119113/* int cpuid (int code, int *pebx, int *pecx, int *pedx)  */
     
    177171        __ifdef(`DARWIN')
    178172_exportfn(C(darwin_sigreturn))
    179 /* Need to set the sigreturn 'infostyle' argument, which is mostly
    180    undocumented.  On x8632 Darwin, sigtramp() sets it to 0x1e, and
    181    since we're trying to do what sigtramp() would do if we'd returned
    182    to it ... */
    183         __(movl $0x1e,8(%esp))
    184173        __(movl $0xb8,%eax)     /* SYS_sigreturn */
    185174        __(int $0x80)
  • trunk/source/lisp-kernel/x86-asmutils64.s

    r15233 r15470  
    125125
    126126
    127         __ifdef(`DARWIN')
    128 _exportfn(C(pseudo_sigreturn))
    129         __(hlt)
    130         __(jmp C(pseudo_sigreturn))
    131 _endfn
    132         __endif                       
    133127
    134128/* int cpuid (natural code, natural *pebx, natural *pecx, natural *pedx)  */
     
    177171_exportfn(C(darwin_sigreturn))
    178172        .globl C(sigreturn)
    179 /* Need to set the sigreturn 'infostyle' argument, which is mostly
    180    undocumented.  On x8664 Darwin, sigtramp() sets it to 0x1e, and
    181    since we're trying to do what sigtramp() would do if we'd returned
    182    to it ... */
    183         __(movl $0x1e,%esi)
    184173        __(movl $0x20000b8,%eax)
    185174        __(syscall)
  • trunk/source/lisp-kernel/x86-exceptions.c

    r15468 r15470  
    3535#ifdef DARWIN
    3636#include <sysexits.h>
    37 #include <dlfcn.h>
    38 #include <mach/machine/vm_types.h>
    3937#endif
    4038#ifndef WINDOWS
     
    9391}
    9492
    95 #ifdef DARWIN
    96  typedef kern_return_t (*like_mach_port_get_context)(mach_port_t,mach_port_t,mach_vm_address_t *);
    97 
    98 typedef kern_return_t (*like_mach_port_set_context)(mach_port_t,mach_port_t,mach_vm_address_t);
    99 
    100 like_mach_port_get_context Pmach_port_get_context = NULL;
    101 like_mach_port_set_context Pmach_port_set_context = NULL;
    102 
    103 Boolean use_mach_port_context_functions = false;
    104 
    105 void
    106 darwin_early_exception_init()
    107 {
    108   Pmach_port_get_context = dlsym(RTLD_DEFAULT, "mach_port_get_context");
    109   Pmach_port_set_context = dlsym(RTLD_DEFAULT, "mach_port_set_context");
    110   if ((Pmach_port_set_context != NULL) &&
    111       (Pmach_port_get_context != NULL)) {
    112     use_mach_port_context_functions = true;
    113   }
    114 }
    115 #endif
    11693
    11794void
     
    695672  LispObj *vsp = (LispObj *)xpGPR(xp, Isp);
    696673#endif
    697 
    698674  set_mxcsr(0x1f80);
    699675
     
    14651441
    14661442void
    1467 signal_handler(int signum, siginfo_t *info, ExceptionInformation  *context
    1468 #ifdef DARWIN
    1469                , TCR *tcr, int old_valence
    1470 #endif
    1471 )
     1443signal_handler(int signum, siginfo_t *info, ExceptionInformation  *context)
    14721444{
    14731445  xframe_list xframe_link;
    1474 #ifndef DARWIN
    14751446  TCR *tcr = get_tcr(false);
    14761447
     1448  ResetAltStack();
     1449
    14771450  int old_valence = prepare_to_wait_for_exception_lock(tcr, context);
    1478 #endif
    14791451  if (tcr->flags & (1<<TCR_FLAG_BIT_PENDING_SUSPEND)) {
    14801452    CLR_TCR_FLAG(tcr, TCR_FLAG_BIT_PENDING_SUSPEND);
     
    14951467  }
    14961468  unlock_exception_lock_in_handler(tcr);
    1497 #ifndef DARWIN_USE_PSEUDO_SIGRETURN
    14981469  exit_signal_handler(tcr, old_valence);
    1499 #endif
    15001470  /* raise_pending_interrupt(tcr); */
    1501 #ifndef DARWIN_USE_PSEUDO_SIGRETURN
    15021471  SIGRETURN(context);
    1503 #endif
    15041472}
    15051473#endif
     
    17401708                                     );
    17411709    } else {
    1742       signal_handler(signum, info, context, tcr, 0);
     1710      signal_handler(signum, info, context);
    17431711    }
    17441712  }
     
    17611729#endif
    17621730     
    1763   /* Because of signal chaining - and the possibility that libaries
     1731  /* Because of signal chaining - and the possibility that libraries
    17641732     that use it ignore sigaltstack-related issues - we have to check
    17651733     to see if we're actually on the altstack.
     
    18221790    } else {
    18231791      LispObj cmain = nrs_CMAIN.vcell;
    1824 
     1792     
     1793      ResetAltStack();
    18251794      if ((fulltag_of(cmain) == fulltag_misc) &&
    18261795          (header_subtag(header_of(cmain)) == subtag_macptr)) {
     
    21702139#endif
    21712140
    2172 #ifndef DARWIN
    21732141  install_signal_handler(SIGILL, handler, RESERVE_FOR_LISP|ON_ALTSTACK);
    21742142  install_signal_handler(SIGBUS, handler, RESERVE_FOR_LISP|ON_ALTSTACK);
    21752143  install_signal_handler(SIGSEGV, handler, RESERVE_FOR_LISP|ON_ALTSTACK);
    21762144  install_signal_handler(SIGFPE, handler, RESERVE_FOR_LISP|ON_ALTSTACK);
    2177 #endif
    21782145 
    21792146  install_signal_handler(SIGNAL_FOR_PROCESS_INTERRUPT, interrupt_handler,
     
    21832150#endif
    21842151
    2185 #ifndef WINDOWS
    2186 #ifndef USE_SIGALTSTACK
    2187 void
    2188 arbstack_suspend_resume_handler(int signum, siginfo_t *info, ExceptionInformation  *context)
    2189 {
    2190   TCR *tcr = get_interrupt_tcr(false);
    2191   if (tcr != NULL) {
    2192     area *vs = tcr->vs_area;
    2193     BytePtr current_sp = (BytePtr) current_stack_pointer();
    2194    
    2195     if ((current_sp >= vs->low) &&
    2196         (current_sp < vs->high)) {
    2197       return
    2198         handle_signal_on_foreign_stack(tcr,
    2199                                        suspend_resume_handler,
    2200                                        signum,
    2201                                        info,
    2202                                        context,
    2203                                        (LispObj)__builtin_return_address(0)
    2204                                        );
    2205     } else {
    2206       /* If we're not on the value stack, we pretty much have to be on
    2207          the C stack.  Just run the handler. */
    2208     }
    2209   }
    2210   suspend_resume_handler(signum, info, context);
    2211 }
    2212 
    2213 
    2214 #else /* altstack works */
    2215 
    2216 void
    2217 altstack_suspend_resume_handler(int signum, siginfo_t *info, ExceptionInformation  *context)
    2218 {
    2219   TCR* tcr = get_tcr(true);
    2220   handle_signal_on_foreign_stack(tcr,
    2221                                  suspend_resume_handler,
    2222                                  signum,
    2223                                  info,
    2224                                  context,
    2225                                  (LispObj)__builtin_return_address(0)
    2226                                  );
    2227 }
    2228 #endif
    2229 #endif
    22302152
    22312153
     
    23172239
    23182240#ifdef USE_SIGALTSTACK
    2319 #define SUSPEND_RESUME_HANDLER altstack_suspend_resume_handler
    23202241#define THREAD_KILL_HANDLER altstack_thread_kill_handler
    23212242#else
    2322 #define SUSPEND_RESUME_HANDLER arbstack_suspend_resume_handler
    23232243#define THREAD_KILL_HANDLER arbstack_thread_kill_handler
    23242244#endif
     
    23362256  thread_kill_signal = SIG_KILL_THREAD;
    23372257
    2338   install_signal_handler(thread_suspend_signal, (void *)SUSPEND_RESUME_HANDLER,
    2339                          RESERVE_FOR_LISP|ON_ALTSTACK|RESTART_SYSCALLS);
     2258  install_signal_handler(thread_suspend_signal, (void *)suspend_resume_handler,
     2259                         RESERVE_FOR_LISP|RESTART_SYSCALLS);
    23402260  install_signal_handler(thread_kill_signal, (void *)THREAD_KILL_HANDLER,
    23412261                         RESERVE_FOR_LISP|ON_ALTSTACK);
     
    23522272{
    23532273  x86_early_exception_init();
    2354 #ifdef DARWIN
    2355   darwin_early_exception_init();
    2356 #endif
    23572274  install_pmcl_exception_handlers();
    23582275}
     
    23702287
    23712288void
    2372 
    23732289adjust_soft_protection_limit(area *a)
    23742290{
     
    24042320{
    24052321  stack_t stack;
     2322#ifdef SEPARATE_ALTSTACK
     2323  stack.ss_sp = mmap(NULL,SIGSTKSZ*8, PROT_READ|PROT_WRITE|PROT_EXEC,MAP_ANON|MAP_PRIVATE,-1,0);
     2324#else
    24062325  stack.ss_sp = a->low;
    24072326  a->low += SIGSTKSZ*8;
     2327  mmap(stack.ss_sp,stack.ss_size, PROT_READ|PROT_WRITE|PROT_EXEC,MAP_FIXED|MAP_ANON|MAP_PRIVATE,-1,0);
     2328#endif
    24082329  stack.ss_size = SIGSTKSZ*8;
    24092330  stack.ss_flags = 0;
    2410   mmap(stack.ss_sp,stack.ss_size, PROT_READ|PROT_WRITE|PROT_EXEC,MAP_FIXED|MAP_ANON|MAP_PRIVATE,-1,0);
    24112331#ifdef LINUX
    24122332  /* The ucontext pushed on the altstack may not contain the (largish)
     
    29372857}
    29382858
    2939 #ifdef DARWIN
    2940 
    2941 #ifdef X8664
    2942 #define ts_pc(t) t->__rip
    2943 typedef x86_thread_state64_t native_thread_state_t;
    2944 #define NATIVE_THREAD_STATE_COUNT x86_THREAD_STATE64_COUNT
    2945 #define NATIVE_THREAD_STATE_FLAVOR x86_THREAD_STATE64
    2946 typedef x86_float_state64_t native_float_state_t;
    2947 #define NATIVE_FLOAT_STATE_COUNT x86_FLOAT_STATE64_COUNT
    2948 #define NATIVE_FLOAT_STATE_FLAVOR x86_FLOAT_STATE64
    2949 typedef x86_exception_state64_t native_exception_state_t;
    2950 #define NATIVE_EXCEPTION_STATE_COUNT x86_EXCEPTION_STATE64_COUNT
    2951 #define NATIVE_EXCEPTION_STATE_FLAVOR x86_EXCEPTION_STATE64
    2952 #else
    2953 #define ts_pc(t) t->__eip
    2954 typedef x86_thread_state32_t native_thread_state_t;
    2955 #define NATIVE_THREAD_STATE_COUNT x86_THREAD_STATE32_COUNT
    2956 #define NATIVE_THREAD_STATE_FLAVOR x86_THREAD_STATE32
    2957 typedef x86_float_state32_t native_float_state_t;
    2958 #define NATIVE_FLOAT_STATE_COUNT x86_FLOAT_STATE32_COUNT
    2959 #define NATIVE_FLOAT_STATE_FLAVOR x86_FLOAT_STATE32
    2960 typedef x86_exception_state32_t native_exception_state_t;
    2961 #define NATIVE_EXCEPTION_STATE_COUNT x86_EXCEPTION_STATE32_COUNT
    2962 #define NATIVE_EXCEPTION_STATE_FLAVOR x86_EXCEPTION_STATE32
    2963 #endif
    2964 
    2965 #define TCR_FROM_EXCEPTION_PORT(p) find_tcr_from_exception_port(p)
    2966 #define TCR_TO_EXCEPTION_PORT(t) (mach_port_name_t)((natural) (((TCR *)t)->io_datum))
    2967 
    2968 
    2969 extern void pseudo_sigreturn(void);
    2970 
    2971 
    2972 
    2973 #define LISP_EXCEPTIONS_HANDLED_MASK \
    2974  (EXC_MASK_SOFTWARE | EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC)
    2975 
    2976 /* (logcount LISP_EXCEPTIONS_HANDLED_MASK) */
    2977 #define NUM_LISP_EXCEPTIONS_HANDLED 4
    2978 
    2979 typedef struct {
    2980   int foreign_exception_port_count;
    2981   exception_mask_t         masks[NUM_LISP_EXCEPTIONS_HANDLED];
    2982   mach_port_t              ports[NUM_LISP_EXCEPTIONS_HANDLED];
    2983   exception_behavior_t behaviors[NUM_LISP_EXCEPTIONS_HANDLED];
    2984   thread_state_flavor_t  flavors[NUM_LISP_EXCEPTIONS_HANDLED];
    2985 } MACH_foreign_exception_state;
    2986 
    2987 
    2988 
    2989 
    2990 /*
    2991   Mach's exception mechanism works a little better than its signal
    2992   mechanism (and, not incidentally, it gets along with GDB a lot
    2993   better.
    2994 
    2995   Initially, we install an exception handler to handle each native
    2996   thread's exceptions.  This process involves creating a distinguished
    2997   thread which listens for kernel exception messages on a set of
    2998   0 or more thread exception ports.  As threads are created, they're
    2999   added to that port set; a thread's exception port is destroyed
    3000   (and therefore removed from the port set) when the thread exits.
    3001 
    3002   A few exceptions can be handled directly in the handler thread;
    3003   others require that we resume the user thread (and that the
    3004   exception thread resumes listening for exceptions.)  The user
    3005   thread might eventually want to return to the original context
    3006   (possibly modified somewhat.)
    3007 
    3008   As it turns out, the simplest way to force the faulting user
    3009   thread to handle its own exceptions is to do pretty much what
    3010   signal() does: the exception handlng thread sets up a sigcontext
    3011   on the user thread's stack and forces the user thread to resume
    3012   execution as if a signal handler had been called with that
    3013   context as an argument.  We can use a distinguished UUO at a
    3014   distinguished address to do something like sigreturn(); that'll
    3015   have the effect of resuming the user thread's execution in
    3016   the (pseudo-) signal context.
    3017 
    3018   Since:
    3019     a) we have miles of code in C and in Lisp that knows how to
    3020     deal with Linux sigcontexts
    3021     b) Linux sigcontexts contain a little more useful information
    3022     (the DAR, DSISR, etc.) than their Darwin counterparts
    3023     c) we have to create a sigcontext ourselves when calling out
    3024     to the user thread: we aren't really generating a signal, just
    3025     leveraging existing signal-handling code.
    3026 
    3027   we create a Linux sigcontext struct.
    3028 
    3029   Simple ?  Hopefully from the outside it is ...
    3030 
    3031   We want the process of passing a thread's own context to it to
    3032   appear to be atomic: in particular, we don't want the GC to suspend
    3033   a thread that's had an exception but has not yet had its user-level
    3034   exception handler called, and we don't want the thread's exception
    3035   context to be modified by a GC while the Mach handler thread is
    3036   copying it around.  On Linux (and on Jaguar), we avoid this issue
    3037   because (a) the kernel sets up the user-level signal handler and
    3038   (b) the signal handler blocks signals (including the signal used
    3039   by the GC to suspend threads) until tcr->xframe is set up.
    3040 
    3041   The GC and the Mach server thread therefore contend for the lock
    3042   "mach_exception_lock".  The Mach server thread holds the lock
    3043   when copying exception information between the kernel and the
    3044   user thread; the GC holds this lock during most of its execution
    3045   (delaying exception processing until it can be done without
    3046   GC interference.)
    3047 
    3048 */
    3049 
    3050 #ifdef PPC64
    3051 #define C_REDZONE_LEN           320
    3052 #define C_STK_ALIGN             32
    3053 #else
    3054 #define C_REDZONE_LEN           224
    3055 #define C_STK_ALIGN             16
    3056 #endif
    3057 #define C_PARAMSAVE_LEN         64
    3058 #define C_LINKAGE_LEN           48
    3059 
    3060 #define TRUNC_DOWN(a,b,c)  (((((natural)a)-(b))/(c)) * (c))
    3061 
    3062 void
    3063 fatal_mach_error(char *format, ...);
    3064 
    3065 #define MACH_CHECK_ERROR(context,x) if (x != KERN_SUCCESS) {fatal_mach_error("Mach error while %s : %d", context, x);}
    3066 
    3067 
    3068 void
    3069 restore_mach_thread_state(mach_port_t thread, ExceptionInformation *pseudosigcontext, native_thread_state_t *ts)
    3070 {
    3071   kern_return_t kret;
    3072 #if WORD_SIZE == 64
    3073   MCONTEXT_T mc = UC_MCONTEXT(pseudosigcontext);
    3074 #else
    3075   mcontext_t mc = UC_MCONTEXT(pseudosigcontext);
    3076 #endif
    3077 
    3078   /* Set the thread's FP state from the pseudosigcontext */
    3079   kret = thread_set_state(thread,
    3080                           NATIVE_FLOAT_STATE_FLAVOR,
    3081                           (thread_state_t)&(mc->__fs),
    3082                           NATIVE_FLOAT_STATE_COUNT);
    3083   MACH_CHECK_ERROR("setting thread FP state", kret);
    3084   *ts = mc->__ss;
    3085 
    3086 
    3087 kern_return_t
    3088 do_pseudo_sigreturn(mach_port_t thread, TCR *tcr, native_thread_state_t *out)
    3089 {
    3090   ExceptionInformation *xp;
    3091 
    3092 #ifdef DEBUG_MACH_EXCEPTIONS
    3093   fprintf(dbgout, "doing pseudo_sigreturn for 0x%x\n",tcr);
    3094 #endif
    3095   xp = tcr->pending_exception_context;
    3096   if (xp) {
    3097     tcr->pending_exception_context = NULL;
    3098     tcr->valence = TCR_STATE_LISP;
    3099     restore_mach_thread_state(thread, xp, out);
    3100     raise_pending_interrupt(tcr);
    3101   } else {
    3102     Bug(NULL, "no xp here!\n");
    3103   }
    3104 #ifdef DEBUG_MACH_EXCEPTIONS
    3105   fprintf(dbgout, "did pseudo_sigreturn for 0x%x\n",tcr);
    3106 #endif
    3107   return KERN_SUCCESS;
    3108 
    3109 
    3110 ExceptionInformation *
    3111 create_thread_context_frame(mach_port_t thread,
    3112                             natural *new_stack_top,
    3113                             siginfo_t **info_ptr,
    3114                             TCR *tcr,
    3115                             native_thread_state_t *ts
    3116                             )
    3117 {
    3118   mach_msg_type_number_t thread_state_count;
    3119   ExceptionInformation *pseudosigcontext;
    3120 #ifdef X8664
    3121   MCONTEXT_T mc;
    3122 #else
    3123   mcontext_t mc;
    3124 #endif
    3125   natural stackp;
    3126 
    3127 #ifdef X8664 
    3128   stackp = (LispObj) find_foreign_rsp(ts->__rsp,tcr->cs_area,tcr);
    3129   stackp = TRUNC_DOWN(stackp, C_REDZONE_LEN, C_STK_ALIGN);
    3130 #else
    3131   stackp = (LispObj) find_foreign_rsp(ts->__esp, tcr->cs_area, tcr);
    3132 #endif
    3133   stackp = TRUNC_DOWN(stackp, sizeof(siginfo_t), C_STK_ALIGN);
    3134   if (info_ptr) {
    3135     *info_ptr = (siginfo_t *)stackp;
    3136   }
    3137   stackp = TRUNC_DOWN(stackp,sizeof(*pseudosigcontext), C_STK_ALIGN);
    3138   pseudosigcontext = (ExceptionInformation *) ptr_from_lispobj(stackp);
    3139 
    3140   stackp = TRUNC_DOWN(stackp, sizeof(*mc), C_STK_ALIGN);
    3141   mc = (MCONTEXT_T) ptr_from_lispobj(stackp);
    3142  
    3143   memmove(&(mc->__ss),ts,sizeof(*ts));
    3144 
    3145   thread_state_count = NATIVE_FLOAT_STATE_COUNT;
    3146   thread_get_state(thread,
    3147                    NATIVE_FLOAT_STATE_FLAVOR,
    3148                    (thread_state_t)&(mc->__fs),
    3149                    &thread_state_count);
    3150 
    3151   thread_state_count = NATIVE_EXCEPTION_STATE_COUNT;
    3152   thread_get_state(thread,
    3153                    NATIVE_EXCEPTION_STATE_FLAVOR,
    3154                    (thread_state_t)&(mc->__es),
    3155                    &thread_state_count);
    3156 
    3157 
    3158   UC_MCONTEXT(pseudosigcontext) = mc;
    3159   if (new_stack_top) {
    3160     *new_stack_top = stackp;
    3161   }
    3162   return pseudosigcontext;
    3163 }
    3164 
    3165 /*
    3166   This code sets up the user thread so that it executes a "pseudo-signal
    3167   handler" function when it resumes.  Create a fake ucontext struct
    3168   on the thread's stack and pass it as an argument to the pseudo-signal
    3169   handler.
    3170 
    3171   Things are set up so that the handler "returns to" pseudo_sigreturn(),
    3172   which will restore the thread's context.
    3173 
    3174   If the handler invokes code that throws (or otherwise never sigreturn()'s
    3175   to the context), that's fine.
    3176 
    3177   Actually, check that: throw (and variants) may need to be careful and
    3178   pop the tcr's xframe list until it's younger than any frame being
    3179   entered.
    3180 */
    3181 
    3182 int
    3183 setup_signal_frame(mach_port_t thread,
    3184                    void *handler_address,
    3185                    int signum,
    3186                    int code,
    3187                    TCR *tcr,
    3188                    native_thread_state_t *ts,
    3189                    native_thread_state_t *new_ts
    3190                    )
    3191 {
    3192   ExceptionInformation *pseudosigcontext;
    3193   int  old_valence = tcr->valence;
    3194   natural stackp, *stackpp;
    3195   siginfo_t *info;
    3196 
    3197 #ifdef DEBUG_MACH_EXCEPTIONS
    3198   fprintf(dbgout,"Setting up exception handling for 0x%x\n", tcr);
    3199 #endif
    3200   pseudosigcontext = create_thread_context_frame(thread, &stackp, &info, tcr,  ts);
    3201   bzero(info, sizeof(*info));
    3202   info->si_code = code;
    3203   info->si_addr = (void *)(UC_MCONTEXT(pseudosigcontext)->__es.__faultvaddr);
    3204   info->si_signo = signum;
    3205   pseudosigcontext->uc_onstack = 0;
    3206   pseudosigcontext->uc_sigmask = (sigset_t) 0;
    3207   pseudosigcontext->uc_stack.ss_sp = 0;
    3208   pseudosigcontext->uc_stack.ss_size = 0;
    3209   pseudosigcontext->uc_stack.ss_flags = 0;
    3210   pseudosigcontext->uc_link = NULL;
    3211   pseudosigcontext->uc_mcsize = sizeof(*UC_MCONTEXT(pseudosigcontext));
    3212   tcr->pending_exception_context = pseudosigcontext;
    3213   tcr->valence = TCR_STATE_EXCEPTION_WAIT;
    3214  
    3215 
    3216   /*
    3217      It seems like we've created a  sigcontext on the thread's
    3218      stack.  Set things up so that we call the handler (with appropriate
    3219      args) when the thread's resumed.
    3220   */
    3221 
    3222 #ifdef X8664
    3223   new_ts->__rip = (natural) handler_address;
    3224   stackpp = (natural *)stackp;
    3225   *--stackpp = (natural)pseudo_sigreturn;
    3226   stackp = (natural)stackpp;
    3227   new_ts->__rdi = signum;
    3228   new_ts->__rsi = (natural)info;
    3229   new_ts->__rdx = (natural)pseudosigcontext;
    3230   new_ts->__rcx = (natural)tcr;
    3231   new_ts->__r8 = (natural)old_valence;
    3232   new_ts->__rsp = stackp;
    3233   new_ts->__rflags = ts->__rflags;
    3234 #else
    3235   bzero(new_ts, sizeof(*new_ts));
    3236   new_ts->__cs = ts->__cs;
    3237   new_ts->__ss = ts->__ss;
    3238   new_ts->__ds = ts->__ds;
    3239   new_ts->__es = ts->__es;
    3240   new_ts->__fs = ts->__fs;
    3241   new_ts->__gs = ts->__gs;
    3242 
    3243   new_ts->__eip = (natural)handler_address;
    3244   stackpp = (natural *)stackp;
    3245   *--stackpp = 0;               /* alignment */
    3246   *--stackpp = 0;
    3247   *--stackpp = 0;
    3248   *--stackpp = (natural)old_valence;
    3249   *--stackpp = (natural)tcr;
    3250   *--stackpp = (natural)pseudosigcontext;
    3251   *--stackpp = (natural)info;
    3252   *--stackpp = (natural)signum;
    3253   *--stackpp = (natural)pseudo_sigreturn;
    3254   stackp = (natural)stackpp;
    3255   new_ts->__esp = stackp;
    3256   new_ts->__eflags = ts->__eflags;
    3257 #endif
    3258 #ifdef DEBUG_MACH_EXCEPTIONS
    3259   fprintf(dbgout,"Set up exception context for 0x%x at 0x%x\n", tcr, tcr->pending_exception_context);
    3260 #endif
    3261   return 0;
    3262 }
    3263 
    3264 
    3265 
    3266 
    3267 
    3268 
    3269 /*
    3270   This function runs in the exception handling thread.  It's
    3271   called (by this precise name) from the library function "exc_server()"
    3272   when the thread's exception ports are set up.  (exc_server() is called
    3273   via mach_msg_server(), which is a function that waits for and dispatches
    3274   on exception messages from the Mach kernel.)
    3275 
    3276   This checks to see if the exception was caused by a pseudo_sigreturn()
    3277   UUO; if so, it arranges for the thread to have its state restored
    3278   from the specified context.
    3279 
    3280   Otherwise, it tries to map the exception to a signal number and
    3281   arranges that the thread run a "pseudo signal handler" to handle
    3282   the exception.
    3283 
    3284   Some exceptions could and should be handled here directly.
    3285 */
    3286 
    3287 
    3288 
    3289 
    3290 #define DARWIN_EXCEPTION_HANDLER signal_handler
    3291 
    3292 #define EXCEPTION_PORT_BUCKETS 109
    3293 
    3294 TCR *
    3295 exception_port_map[EXCEPTION_PORT_BUCKETS];
    3296 
    3297 pthread_mutex_t
    3298 exception_port_map_lock = PTHREAD_MUTEX_INITIALIZER;
    3299 
    3300 TCR *
    3301 find_tcr_from_exception_port(mach_port_t port)
    3302 {
    3303   if (use_mach_port_context_functions) {
    3304     mach_vm_address_t addr = 0;
    3305     kern_return_t kret;
    3306 
    3307     kret = Pmach_port_get_context(mach_task_self(),port,&addr);
    3308     MACH_CHECK_ERROR("finding TCR from exception port",kret);
    3309     return (TCR *)((natural)addr);
    3310   } else {
    3311     TCR *tcr = NULL;
    3312     pthread_mutex_lock(&exception_port_map_lock);
    3313 
    3314     tcr = exception_port_map[(unsigned)port % EXCEPTION_PORT_BUCKETS];
    3315 
    3316     while (tcr) {
    3317       if (TCR_TO_EXCEPTION_PORT(tcr) == port) {
    3318         break;
    3319       }
    3320       tcr = (TCR *)tcr->pending_io_info;
    3321     }
    3322     pthread_mutex_unlock(&exception_port_map_lock);
    3323     return tcr;
    3324   }
    3325 }
    3326 
    3327 void
    3328 associate_tcr_with_exception_port(mach_port_t port, TCR *tcr)
    3329 {
    3330   if (use_mach_port_context_functions) {
    3331     kern_return_t kret;
    3332    
    3333     kret = Pmach_port_set_context(mach_task_self(),port,(mach_vm_address_t)((natural)tcr));
    3334     MACH_CHECK_ERROR("associating TCR with exception port",kret);
    3335   } else {
    3336     int b = (unsigned)port % EXCEPTION_PORT_BUCKETS;
    3337     pthread_mutex_lock(&exception_port_map_lock);
    3338    
    3339     tcr->pending_io_info = (void *)(exception_port_map[b]);
    3340     exception_port_map[b] = tcr;
    3341     pthread_mutex_unlock(&exception_port_map_lock);
    3342   }
    3343 }
    3344 
    3345 void
    3346 disassociate_tcr_from_exception_port(mach_port_t port)
    3347 {
    3348   if (use_mach_port_context_functions) {
    3349     TCR **prev = &(exception_port_map[(unsigned)port % EXCEPTION_PORT_BUCKETS]),
    3350       *tcr;
    3351     pthread_mutex_lock(&exception_port_map_lock);
    3352 
    3353     while((tcr = *prev) != NULL) {
    3354       if (TCR_TO_EXCEPTION_PORT(tcr) == port) {
    3355         *prev = (TCR *)tcr->pending_io_info;
    3356         break;
    3357       }
    3358       prev = (TCR **)&(tcr->pending_io_info);
    3359     }
    3360     pthread_mutex_unlock(&exception_port_map_lock);
    3361   }
    3362 }
    3363  
    3364 
    3365 kern_return_t
    3366 catch_exception_raise_state(mach_port_t exception_port,
    3367                             exception_type_t exception,
    3368                             exception_data_t code_vector,
    3369                             mach_msg_type_number_t code_count,
    3370                             int * flavor,
    3371                             thread_state_t in_state,
    3372                             mach_msg_type_number_t in_state_count,
    3373                             thread_state_t out_state,
    3374                             mach_msg_type_number_t * out_state_count)
    3375 {
    3376   int signum = 0, code = *code_vector;
    3377   TCR *tcr = TCR_FROM_EXCEPTION_PORT(exception_port);
    3378   mach_port_t thread = (mach_port_t)((natural)tcr->native_thread_id);
    3379   kern_return_t kret, call_kret;
    3380 
    3381   native_thread_state_t
    3382     *ts = (native_thread_state_t *)in_state,
    3383     *out_ts = (native_thread_state_t*)out_state;
    3384   mach_msg_type_number_t thread_state_count;
    3385 
    3386 
    3387 
    3388   if (1) {
    3389     if (tcr->flags & (1<<TCR_FLAG_BIT_PENDING_EXCEPTION)) {
    3390       CLR_TCR_FLAG(tcr,TCR_FLAG_BIT_PENDING_EXCEPTION);
    3391     }
    3392     if ((code == EXC_I386_GPFLT) &&
    3393         ((natural)(ts_pc(ts)) == (natural)pseudo_sigreturn)) {
    3394       kret = do_pseudo_sigreturn(thread, tcr, out_ts);
    3395 #if 0
    3396       fprintf(dbgout, "Exception return in 0x%x\n",tcr);
    3397 #endif
    3398     } else if (tcr->flags & (1<<TCR_FLAG_BIT_PROPAGATE_EXCEPTION)) {
    3399       CLR_TCR_FLAG(tcr,TCR_FLAG_BIT_PROPAGATE_EXCEPTION);
    3400       kret = 17;
    3401     } else {
    3402       switch (exception) {
    3403       case EXC_BAD_ACCESS:
    3404         if (code == EXC_I386_GPFLT) {
    3405           signum = SIGSEGV;
    3406         } else {
    3407           signum = SIGBUS;
    3408         }
    3409         break;
    3410        
    3411       case EXC_BAD_INSTRUCTION:
    3412         if (code == EXC_I386_GPFLT) {
    3413           signum = SIGSEGV;
    3414         } else {
    3415           signum = SIGILL;
    3416         }
    3417         break;
    3418          
    3419       case EXC_SOFTWARE:
    3420         signum = SIGILL;
    3421         break;
    3422        
    3423       case EXC_ARITHMETIC:
    3424         signum = SIGFPE;
    3425         if (code == EXC_I386_DIV)
    3426           code = FPE_INTDIV;
    3427         break;
    3428        
    3429       default:
    3430         break;
    3431       }
    3432 #if WORD_SIZE==64
    3433       if ((signum==SIGFPE) &&
    3434           (code != FPE_INTDIV) &&
    3435           (tcr->valence != TCR_STATE_LISP)) {
    3436         mach_msg_type_number_t thread_state_count = x86_FLOAT_STATE64_COUNT;
    3437         x86_float_state64_t fs;
    3438 
    3439         thread_get_state(thread,
    3440                          x86_FLOAT_STATE64,
    3441                          (thread_state_t)&fs,
    3442                          &thread_state_count);
    3443        
    3444         if (! (tcr->flags & (1<<TCR_FLAG_BIT_FOREIGN_FPE))) {
    3445           tcr->flags |= (1<<TCR_FLAG_BIT_FOREIGN_FPE);
    3446           tcr->lisp_mxcsr = (fs.__fpu_mxcsr & ~MXCSR_STATUS_MASK);
    3447         }
    3448         fs.__fpu_mxcsr &= ~MXCSR_STATUS_MASK;
    3449         fs.__fpu_mxcsr |= MXCSR_CONTROL_MASK;
    3450         thread_set_state(thread,
    3451                          x86_FLOAT_STATE64,
    3452                          (thread_state_t)&fs,
    3453                          x86_FLOAT_STATE64_COUNT);
    3454         *out_state_count = NATIVE_THREAD_STATE_COUNT;
    3455         *out_ts = *ts;
    3456         return KERN_SUCCESS;
    3457       }
    3458 #endif
    3459       if (signum) {
    3460         kret = setup_signal_frame(thread,
    3461                                   (void *)DARWIN_EXCEPTION_HANDLER,
    3462                                   signum,
    3463                                   code,
    3464                                   tcr,
    3465                                   ts,
    3466                                   out_ts);
    3467        
    3468       } else {
    3469         kret = 17;
    3470       }
    3471     }
    3472   }
    3473   if (kret) {
    3474     *out_state_count = 0;
    3475     *flavor = 0;
    3476   } else {
    3477     *out_state_count = NATIVE_THREAD_STATE_COUNT;
    3478   }
    3479   return kret;
    3480 }
    3481 
    3482 
    3483 
    3484 
    3485 static mach_port_t mach_exception_thread = (mach_port_t)0;
    3486 
    3487 
    3488 /*
    3489   The initial function for an exception-handling thread.
    3490 */
    3491 
    3492 void *
    3493 exception_handler_proc(void *arg)
    3494 {
    3495   extern boolean_t exc_server();
    3496   mach_port_t p = (mach_port_t)((natural)arg);
    3497 
    3498   mach_exception_thread = pthread_mach_thread_np(pthread_self());
    3499   mach_msg_server(exc_server, 256, p, 0);
    3500   /* Should never return. */
    3501   abort();
    3502 }
    3503 
    3504 
    3505 
    3506 void
    3507 mach_exception_thread_shutdown()
    3508 {
    3509   kern_return_t kret;
    3510 
    3511   fprintf(dbgout, "terminating Mach exception thread, 'cause exit can't\n");
    3512   kret = thread_terminate(mach_exception_thread);
    3513   if (kret != KERN_SUCCESS) {
    3514     fprintf(dbgout, "Couldn't terminate exception thread, kret = %d\n",kret);
    3515   }
    3516 }
    3517 
    3518 
    3519 mach_port_t
    3520 mach_exception_port_set()
    3521 {
    3522   static mach_port_t __exception_port_set = MACH_PORT_NULL;
    3523   kern_return_t kret; 
    3524   if (__exception_port_set == MACH_PORT_NULL) {
    3525 
    3526     kret = mach_port_allocate(mach_task_self(),
    3527                               MACH_PORT_RIGHT_PORT_SET,
    3528                               &__exception_port_set);
    3529     MACH_CHECK_ERROR("allocating thread exception_ports",kret);
    3530     create_system_thread(0,
    3531                          NULL,
    3532                          exception_handler_proc,
    3533                          (void *)((natural)__exception_port_set));
    3534   }
    3535   return __exception_port_set;
    3536 }
    3537 
    3538 /*
    3539   Setup a new thread to handle those exceptions specified by
    3540   the mask "which".  This involves creating a special Mach
    3541   message port, telling the Mach kernel to send exception
    3542   messages for the calling thread to that port, and setting
    3543   up a handler thread which listens for and responds to
    3544   those messages.
    3545 
    3546 */
    3547 
    3548 /*
    3549   Establish the lisp thread's TCR as its exception port, and determine
    3550   whether any other ports have been established by foreign code for
    3551   exceptions that lisp cares about.
    3552 
    3553   If this happens at all, it should happen on return from foreign
    3554   code and on entry to lisp code via a callback.
    3555 
    3556   This is a lot of trouble (and overhead) to support Java, or other
    3557   embeddable systems that clobber their caller's thread exception ports.
    3558  
    3559 */
    3560 kern_return_t
    3561 tcr_establish_exception_port(TCR *tcr, mach_port_t thread)
    3562 {
    3563   kern_return_t kret;
    3564   MACH_foreign_exception_state *fxs = (MACH_foreign_exception_state *)tcr->native_thread_info;
    3565   int i;
    3566   unsigned n = NUM_LISP_EXCEPTIONS_HANDLED;
    3567   mach_port_t lisp_port = TCR_TO_EXCEPTION_PORT(tcr), foreign_port;
    3568   exception_mask_t mask = 0;
    3569 
    3570   kret = thread_swap_exception_ports(thread,
    3571                                      LISP_EXCEPTIONS_HANDLED_MASK,
    3572                                      lisp_port,
    3573                                      EXCEPTION_STATE,
    3574 #if WORD_SIZE==64
    3575                                      x86_THREAD_STATE64,
    3576 #else
    3577                                      x86_THREAD_STATE32,
    3578 #endif
    3579                                      fxs->masks,
    3580                                      &n,
    3581                                      fxs->ports,
    3582                                      fxs->behaviors,
    3583                                      fxs->flavors);
    3584   if (kret == KERN_SUCCESS) {
    3585     fxs->foreign_exception_port_count = n;
    3586     for (i = 0; i < n; i ++) {
    3587       foreign_port = fxs->ports[i];
    3588 
    3589       if ((foreign_port != lisp_port) &&
    3590           (foreign_port != MACH_PORT_NULL)) {
    3591         mask |= fxs->masks[i];
    3592       }
    3593     }
    3594     tcr->foreign_exception_status = (int) mask;
    3595   }
    3596   return kret;
    3597 }
    3598 
    3599 kern_return_t
    3600 tcr_establish_lisp_exception_port(TCR *tcr)
    3601 {
    3602   return tcr_establish_exception_port(tcr, (mach_port_t)((natural)tcr->native_thread_id));
    3603 }
    3604 
    3605 /*
    3606   Do this when calling out to or returning from foreign code, if
    3607   any conflicting foreign exception ports were established when we
    3608   last entered lisp code.
    3609 */
    3610 kern_return_t
    3611 restore_foreign_exception_ports(TCR *tcr)
    3612 {
    3613   exception_mask_t m = (exception_mask_t) tcr->foreign_exception_status;
    3614   kern_return_t kret;
    3615 
    3616   if (m) {
    3617     MACH_foreign_exception_state *fxs  =
    3618       (MACH_foreign_exception_state *) tcr->native_thread_info;
    3619     int i, n = fxs->foreign_exception_port_count;
    3620     exception_mask_t tm;
    3621 
    3622     for (i = 0; i < n; i++) {
    3623       if ((tm = fxs->masks[i]) & m) {
    3624         kret = thread_set_exception_ports((mach_port_t)((natural)tcr->native_thread_id),
    3625                                    tm,
    3626                                    fxs->ports[i],
    3627                                    fxs->behaviors[i],
    3628                                    fxs->flavors[i]);
    3629         MACH_CHECK_ERROR("restoring thread exception ports", kret);
    3630       }
    3631     }
    3632   }
    3633   return KERN_SUCCESS;
    3634 }
    3635                                    
    3636 
    3637 /*
    3638   This assumes that a Mach port (to be used as the thread's exception port) whose
    3639   "name" matches the TCR's 32-bit address has already been allocated.
    3640 */
    3641 
    3642 kern_return_t
    3643 setup_mach_exception_handling(TCR *tcr)
    3644 {
    3645   mach_port_t
    3646     thread_exception_port = TCR_TO_EXCEPTION_PORT(tcr),
    3647     task_self = mach_task_self();
    3648   kern_return_t kret;
    3649 
    3650   kret = mach_port_insert_right(task_self,
    3651                                 thread_exception_port,
    3652                                 thread_exception_port,
    3653                                 MACH_MSG_TYPE_MAKE_SEND);
    3654   MACH_CHECK_ERROR("adding send right to exception_port",kret);
    3655 
    3656   kret = tcr_establish_exception_port(tcr, (mach_port_t)((natural) tcr->native_thread_id));
    3657   if (kret == KERN_SUCCESS) {
    3658     mach_port_t exception_port_set = mach_exception_port_set();
    3659 
    3660     kret = mach_port_move_member(task_self,
    3661                                  thread_exception_port,
    3662                                  exception_port_set);
    3663   }
    3664   return kret;
    3665 }
    3666 
    3667 void
    3668 darwin_exception_init(TCR *tcr)
    3669 {
    3670   void tcr_monitor_exception_handling(TCR*, Boolean);
    3671   kern_return_t kret;
    3672   MACH_foreign_exception_state *fxs =
    3673     calloc(1, sizeof(MACH_foreign_exception_state));
    3674  
    3675   tcr->native_thread_info = (void *) fxs;
    3676 
    3677   if ((kret = setup_mach_exception_handling(tcr))
    3678       != KERN_SUCCESS) {
    3679     fprintf(dbgout, "Couldn't setup exception handler - error = %d\n", kret);
    3680     terminate_lisp();
    3681   }
    3682 }
    3683 
    3684 /*
    3685   The tcr is the "name" of the corresponding thread's exception port.
    3686   Destroying the port should remove it from all port sets of which it's
    3687   a member (notably, the exception port set.)
    3688 */
    3689 void
    3690 darwin_exception_cleanup(TCR *tcr)
    3691 {
    3692   void *fxs = tcr->native_thread_info;
    3693   extern Boolean use_mach_exception_handling;
    3694 
    3695   if (fxs) {
    3696     tcr->native_thread_info = NULL;
    3697     free(fxs);
    3698   }
    3699   if (use_mach_exception_handling) {
    3700     mach_port_t task_self = mach_task_self(),
    3701       exception_port = TCR_TO_EXCEPTION_PORT(tcr);
    3702 
    3703     disassociate_tcr_from_exception_port(exception_port);
    3704     mach_port_deallocate(task_self,exception_port);
    3705     mach_port_destroy(task_self,exception_port);
    3706   }
    3707 }
    3708 
    3709 
    3710 
    3711 
    3712 void
    3713 fatal_mach_error(char *format, ...)
    3714 {
    3715   va_list args;
    3716   char s[512];
    3717  
    3718 
    3719   va_start(args, format);
    3720   vsnprintf(s, sizeof(s),format, args);
    3721   va_end(args);
    3722 
    3723   Fatal("Mach error", s);
    3724 }
    3725 
    3726 
    3727 
    3728 
    3729 #endif
    37302859
    37312860/* watchpoint stuff */
  • trunk/source/lisp-kernel/x86-exceptions.h

    r15462 r15470  
    6666
    6767/* sigaltstack isn't thread-specific on The World's Most Advanced OS */
    68 #ifdef DARWIN
    69 #undef USE_SIGALTSTACK
    70 #else
    7168#ifdef WINDOWS
    7269#undef USE_SIGALTSTACK
     
    7471#define USE_SIGALTSTACK 1
    7572#endif
    76 #endif
    7773
    7874#ifdef USE_SIGALTSTACK
    7975void setup_sigaltstack(area *);
     76#ifdef DARWIN
     77/* Apple's sigaltstack mechanism is still broken; a thread is considered
     78to be running on the alternate signal stack until a signal handler called
     79on the altstack returns.  (OSX is too advanced to look at the damned
     80stack pointer ...)  We can reset a thread's notion of whether or not it's
     81on the altstack by calling sigreturn with a second argument of #x80000000
     82(the first argument is ignored in this case.)
     83
     84This is supported thru 10.8.1; hopefully, it'll be ignored if Apple ever
     85fixes their sigaltstack implementation.
     86
     87Recall that in 10.4 and earlier, Apple's sigaltstack implementation tried
     88to make all threads use the same alternate stack (whatever one was most
     89recently specified in a call to sigaltstack()), so this nonsense is actually
     90a step forward.  A sysctl was supported thru 10.6.8 (at least) to revert
     91to this behavior, which was presumably useful to people who wanted to see
     92what would happen if multiple threads ran signal handlers on the same stack
     93at the same time.
     94
     95(Whenever I rant about this sort of thing, I feel like a political satirist
     96during the Bush administration: every day new material is written for you,
     97and it's much better than anything that you could invent yourself.)
     98*/
     99#define ResetAltStack() darwin_sigreturn(NULL, 0x80000000)
     100#endif
     101#endif
     102
     103#ifndef ResetAltStack
     104#define ResetAltStack()
    80105#endif
    81106
     
    86111void callback_for_gc_notification(ExceptionInformation *xp, TCR *tcr);
    87112
    88 #ifdef DARWIN
    89 TCR *
    90 find_tcr_from_exception_port(mach_port_t);
    91 
    92 void
    93 associate_tcr_with_exception_port(mach_port_t, TCR *);
    94 
    95 void
    96 disassociate_tcr_from_exception_port(mach_port_t);
    97 #endif
    98113
    99114
Note: See TracChangeset for help on using the changeset viewer.