Changeset 4954
- Timestamp:
- Aug 18, 2006, 8:29:04 AM (18 years ago)
- File:
-
- 1 edited
-
trunk/ccl/lisp-kernel/x86-exceptions.c (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/ccl/lisp-kernel/x86-exceptions.c
r4859 r4954 793 793 SIGRETURN(context); 794 794 } 795 796 #ifdef DARWIN 797 #endif 795 798 796 799 #ifdef LINUX … … 1428 1431 return status; 1429 1432 } 1433 1434 #ifdef DARWIN 1435 1436 #define TCR_FROM_EXCEPTION_PORT(p) ((TCR *)((natural)p)) 1437 #define TCR_TO_EXCEPTION_PORT(tcr) ((mach_port_t)((natural)(tcr))) 1438 1439 pthread_mutex_t _mach_exception_lock, *mach_exception_lock; 1440 1441 1442 #define LISP_EXCEPTIONS_HANDLED_MASK \ 1443 (EXC_MASK_SOFTWARE | EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC) 1444 1445 /* (logcount LISP_EXCEPTIONS_HANDLED_MASK) */ 1446 #define NUM_LISP_EXCEPTIONS_HANDLED 4 1447 1448 typedef struct { 1449 int foreign_exception_port_count; 1450 exception_mask_t masks[NUM_LISP_EXCEPTIONS_HANDLED]; 1451 mach_port_t ports[NUM_LISP_EXCEPTIONS_HANDLED]; 1452 exception_behavior_t behaviors[NUM_LISP_EXCEPTIONS_HANDLED]; 1453 thread_state_flavor_t flavors[NUM_LISP_EXCEPTIONS_HANDLED]; 1454 } MACH_foreign_exception_state; 1455 1456 1457 1458 1459 /* 1460 Mach's exception mechanism works a little better than its signal 1461 mechanism (and, not incidentally, it gets along with GDB a lot 1462 better. 1463 1464 Initially, we install an exception handler to handle each native 1465 thread's exceptions. This process involves creating a distinguished 1466 thread which listens for kernel exception messages on a set of 1467 0 or more thread exception ports. As threads are created, they're 1468 added to that port set; a thread's exception port is destroyed 1469 (and therefore removed from the port set) when the thread exits. 1470 1471 A few exceptions can be handled directly in the handler thread; 1472 others require that we resume the user thread (and that the 1473 exception thread resumes listening for exceptions.) The user 1474 thread might eventually want to return to the original context 1475 (possibly modified somewhat.) 1476 1477 As it turns out, the simplest way to force the faulting user 1478 thread to handle its own exceptions is to do pretty much what 1479 signal() does: the exception handlng thread sets up a sigcontext 1480 on the user thread's stack and forces the user thread to resume 1481 execution as if a signal handler had been called with that 1482 context as an argument. We can use a distinguished UUO at a 1483 distinguished address to do something like sigreturn(); that'll 1484 have the effect of resuming the user thread's execution in 1485 the (pseudo-) signal context. 1486 1487 Since: 1488 a) we have miles of code in C and in Lisp that knows how to 1489 deal with Linux sigcontexts 1490 b) Linux sigcontexts contain a little more useful information 1491 (the DAR, DSISR, etc.) than their Darwin counterparts 1492 c) we have to create a sigcontext ourselves when calling out 1493 to the user thread: we aren't really generating a signal, just 1494 leveraging existing signal-handling code. 1495 1496 we create a Linux sigcontext struct. 1497 1498 Simple ? Hopefully from the outside it is ... 1499 1500 We want the process of passing a thread's own context to it to 1501 appear to be atomic: in particular, we don't want the GC to suspend 1502 a thread that's had an exception but has not yet had its user-level 1503 exception handler called, and we don't want the thread's exception 1504 context to be modified by a GC while the Mach handler thread is 1505 copying it around. On Linux (and on Jaguar), we avoid this issue 1506 because (a) the kernel sets up the user-level signal handler and 1507 (b) the signal handler blocks signals (including the signal used 1508 by the GC to suspend threads) until tcr->xframe is set up. 1509 1510 The GC and the Mach server thread therefore contend for the lock 1511 "mach_exception_lock". The Mach server thread holds the lock 1512 when copying exception information between the kernel and the 1513 user thread; the GC holds this lock during most of its execution 1514 (delaying exception processing until it can be done without 1515 GC interference.) 1516 1517 */ 1518 1519 #ifdef PPC64 1520 #define C_REDZONE_LEN 320 1521 #define C_STK_ALIGN 32 1522 #else 1523 #define C_REDZONE_LEN 224 1524 #define C_STK_ALIGN 16 1525 #endif 1526 #define C_PARAMSAVE_LEN 64 1527 #define C_LINKAGE_LEN 48 1528 1529 #define TRUNC_DOWN(a,b,c) (((((natural)a)-(b))/(c)) * (c)) 1530 1531 void 1532 fatal_mach_error(char *format, ...); 1533 1534 #define MACH_CHECK_ERROR(context,x) if (x != KERN_SUCCESS) {fatal_mach_error("Mach error while %s : %d", context, x);} 1535 1536 1537 void 1538 restore_mach_thread_state(mach_port_t thread, ExceptionInformation *pseudosigcontext) 1539 { 1540 int i, j; 1541 kern_return_t kret; 1542 #if WORD_SIZE == 64 1543 struct mcontext64 *mc = UC_MCONTEXT(pseudosigcontext); 1544 #else 1545 struct mcontext * mc = UC_MCONTEXT(pseudosigcontext); 1546 #endif 1547 1548 /* Set the thread's FP state from the pseudosigcontext */ 1549 kret = thread_set_state(thread, 1550 X86_FLOAT_STATE, 1551 (thread_state_t)&(mc->fs), 1552 X86_FLOAT_STATE_COUNT); 1553 1554 MACH_CHECK_ERROR("setting thread FP state", kret); 1555 1556 /* The thread'll be as good as new ... */ 1557 #ifdef PPC64 1558 kret = thread_set_state(thread, 1559 X86_THREAD_STATE64, 1560 (thread_state_t)&(mc->ss), 1561 X86_THREAD_STATE64_COUNT); 1562 #else 1563 kret = thread_set_state(thread, 1564 X86_THREAD_STATE32, 1565 (thread_state_t)&(mc->ss), 1566 X86_THREAD_STATE32_COUNT); 1567 #endif 1568 MACH_CHECK_ERROR("setting thread state", kret); 1569 } 1570 1571 /* This code runs in the exception handling thread, in response 1572 to an attempt to execute the UU0 at "pseudo_sigreturn" (e.g., 1573 in response to a call to pseudo_sigreturn() from the specified 1574 user thread. 1575 Find that context (the user thread's R3 points to it), then 1576 use that context to set the user thread's state. When this 1577 function's caller returns, the Mach kernel will resume the 1578 user thread. 1579 */ 1580 1581 kern_return_t 1582 do_pseudo_sigreturn(mach_port_t thread, TCR *tcr) 1583 { 1584 ExceptionInformation *xp; 1585 1586 #ifdef DEBUG_MACH_EXCEPTIONS 1587 fprintf(stderr, "doing pseudo_sigreturn for 0x%x\n",tcr); 1588 #endif 1589 xp = tcr->pending_exception_context; 1590 if (xp) { 1591 tcr->pending_exception_context = NULL; 1592 tcr->valence = TCR_STATE_LISP; 1593 restore_mach_thread_state(thread, xp); 1594 raise_pending_interrupt(tcr); 1595 } else { 1596 Bug(NULL, "no xp here!\n"); 1597 } 1598 #ifdef DEBUG_MACH_EXCEPTIONS 1599 fprintf(stderr, "did pseudo_sigreturn for 0x%x\n",tcr); 1600 #endif 1601 return KERN_SUCCESS; 1602 } 1603 1604 ExceptionInformation * 1605 create_thread_context_frame(mach_port_t thread, 1606 natural *new_stack_top) 1607 { 1608 #ifdef X8664 1609 x86_thread_state64_t ts; 1610 #else 1611 x86_thread_state_t ts; 1612 #endif 1613 mach_msg_type_number_t thread_state_count; 1614 kern_return_t result; 1615 int i,j; 1616 ExceptionInformation *pseudosigcontext; 1617 #ifdef X8664 1618 struct mcontext64 *mc; 1619 #else 1620 struct mcontext *mc; 1621 #endif 1622 natural stackp, backlink; 1623 1624 #ifdef X8664 1625 thread_state_count = X86_THREAD_STATE64_COUNT; 1626 result = thread_get_state(thread, 1627 X86_THREAD_STATE64, 1628 (thread_state_t)&ts, 1629 &thread_state_count); 1630 #else 1631 thread_state_count = MACHINE_THREAD_STATE_COUNT; 1632 result = thread_get_state(thread, 1633 X86_THREAD_STATE, /* GPRs, some SPRs */ 1634 (thread_state_t)&ts, 1635 &thread_state_count); 1636 #endif 1637 1638 if (result != KERN_SUCCESS) { 1639 get_tcr(true); 1640 Bug(NULL, "Exception thread can't obtain thread state, Mach result = %d", result); 1641 } 1642 stackp = ts.r1; 1643 backlink = stackp; 1644 stackp = TRUNC_DOWN(stackp, C_REDZONE_LEN, C_STK_ALIGN); 1645 stackp -= sizeof(*pseudosigcontext); 1646 pseudosigcontext = (ExceptionInformation *) ptr_from_lispobj(stackp); 1647 1648 stackp = TRUNC_DOWN(stackp, sizeof(*mc), C_STK_ALIGN); 1649 #ifdef X8664 1650 mc = (struct mcontext64 *) ptr_from_lispobj(stackp); 1651 #else 1652 mc = (struct mcontext *) ptr_from_lispobj(stackp); 1653 #endif 1654 bcopy(&ts,&(mc->ss),sizeof(ts)); 1655 1656 thread_state_count = X86_FLOAT_STATE_COUNT; 1657 thread_get_state(thread, 1658 X86_FLOAT_STATE, 1659 (thread_state_t)&(mc->fs), 1660 &thread_state_count); 1661 1662 1663 #ifdef X8664 1664 thread_state_count = X86_EXCEPTION_STATE64_COUNT; 1665 #else 1666 thread_state_count = X86_EXCEPTION_STATE_COUNT; 1667 #endif 1668 thread_get_state(thread, 1669 #ifdef X8664 1670 X86_EXCEPTION_STATE64, 1671 #else 1672 X86_EXCEPTION_STATE, 1673 #endif 1674 (thread_state_t)&(mc->es), 1675 &thread_state_count); 1676 1677 1678 UC_MCONTEXT(pseudosigcontext) = mc; 1679 stackp = TRUNC_DOWN(stackp, C_PARAMSAVE_LEN, C_STK_ALIGN); 1680 stackp -= C_LINKAGE_LEN; 1681 *(natural *)ptr_from_lispobj(stackp) = backlink; 1682 if (new_stack_top) { 1683 *new_stack_top = stackp; 1684 } 1685 return pseudosigcontext; 1686 } 1687 1688 /* 1689 This code sets up the user thread so that it executes a "pseudo-signal 1690 handler" function when it resumes. Create a linux sigcontext struct 1691 on the thread's stack and pass it as an argument to the pseudo-signal 1692 handler. 1693 1694 Things are set up so that the handler "returns to" pseudo_sigreturn(), 1695 which will restore the thread's context. 1696 1697 If the handler invokes code that throws (or otherwise never sigreturn()'s 1698 to the context), that's fine. 1699 1700 Actually, check that: throw (and variants) may need to be careful and 1701 pop the tcr's xframe list until it's younger than any frame being 1702 entered. 1703 */ 1704 1705 int 1706 setup_signal_frame(mach_port_t thread, 1707 void *handler_address, 1708 int signum, 1709 int code, 1710 TCR *tcr) 1711 { 1712 #ifdef X8664 1713 x86_thread_state64_t ts; 1714 #else 1715 x86_thread_state_t ts; 1716 #endif 1717 mach_msg_type_number_t thread_state_count; 1718 ExceptionInformation *pseudosigcontext; 1719 int i, j; 1720 kern_return_t result; 1721 natural stackp; 1722 1723 #ifdef DEBUG_MACH_EXCEPTIONS 1724 fprintf(stderr,"Setting up exception handling for 0x%x\n", tcr); 1725 #endif 1726 pseudosigcontext = create_thread_context_frame(thread, &stackp); 1727 pseudosigcontext->uc_onstack = 0; 1728 pseudosigcontext->uc_sigmask = (sigset_t) 0; 1729 tcr->pending_exception_context = pseudosigcontext; 1730 tcr->valence = TCR_STATE_EXCEPTION_WAIT; 1731 1732 1733 /* 1734 It seems like we've created a sigcontext on the thread's 1735 stack. Set things up so that we call the handler (with appropriate 1736 args) when the thread's resumed. 1737 */ 1738 1739 ts.srr0 = (natural) handler_address; 1740 ts.srr1 = (int) xpMSR(pseudosigcontext) & ~MSR_FE0_FE1_MASK; 1741 ts.r1 = stackp; 1742 ts.r3 = signum; 1743 ts.r4 = (natural)pseudosigcontext; 1744 ts.r5 = (natural)tcr; 1745 ts.lr = (natural)pseudo_sigreturn; 1746 1747 1748 #ifdef X8664 1749 ts.r13 = xpGPR(pseudosigcontext,13); 1750 thread_set_state(thread, 1751 X86_THREAD_STATE64, 1752 (thread_state_t)&ts, 1753 X86_THREAD_STATE64_COUNT); 1754 #else 1755 thread_set_state(thread, 1756 MACHINE_THREAD_STATE, 1757 (thread_state_t)&ts, 1758 MACHINE_THREAD_STATE_COUNT); 1759 #endif 1760 #ifdef DEBUG_MACH_EXCEPTIONS 1761 fprintf(stderr,"Set up exception context for 0x%x at 0x%x\n", tcr, tcr->pending_exception_context); 1762 #endif 1763 return 0; 1764 } 1765 1766 1767 void 1768 pseudo_signal_handler(int signum, ExceptionInformation *context, TCR *tcr) 1769 { 1770 signal_handler(signum, NULL, context, tcr); 1771 } 1772 1773 1774 int 1775 thread_set_fp_exceptions_enabled(mach_port_t thread, Boolean enabled) 1776 { 1777 #ifdef X8664 1778 x86_thread_state64_t ts; 1779 #else 1780 x86_thread_state_t ts; 1781 #endif 1782 mach_msg_type_number_t thread_state_count; 1783 1784 #ifdef X8664 1785 thread_state_count = X86_THREAD_STATE64_COUNT; 1786 #else 1787 thread_state_count = X86_THREAD_STATE_COUNT; 1788 #endif 1789 thread_get_state(thread, 1790 #ifdef X8664 1791 X86_THREAD_STATE64, /* GPRs, some SPRs */ 1792 #else 1793 X86_THREAD_STATE, /* GPRs, some SPRs */ 1794 #endif 1795 (thread_state_t)&ts, 1796 &thread_state_count); 1797 if (enabled) { 1798 ts.srr1 |= MSR_FE0_FE1_MASK; 1799 } else { 1800 ts.srr1 &= ~MSR_FE0_FE1_MASK; 1801 } 1802 /* 1803 Hack-o-rama warning (isn't it about time for such a warning?): 1804 pthread_kill() seems to want to lose the MSR's FE0/FE1 bits. 1805 Our handler for lisp's use of pthread_kill() pushes a phony 1806 lisp frame on the stack and force the context to resume at 1807 the UUO in enable_fp_exceptions(); the "saveLR" field of that 1808 lisp frame contains the -real- address that process_interrupt 1809 should have returned to, and the fact that it's in a lisp 1810 frame should convince the GC to notice that address if it 1811 runs in the tiny time window between returning from our 1812 interrupt handler and ... here. 1813 If the top frame on the stack is a lisp frame, discard it 1814 and set ts.srr0 to the saveLR field in that frame. Otherwise, 1815 just adjust ts.srr0 to skip over the UUO. 1816 */ 1817 { 1818 lisp_frame *tos = (lisp_frame *)ts.r1, 1819 *next_frame = tos->backlink; 1820 1821 if (tos == (next_frame -1)) { 1822 ts.srr0 = tos->savelr; 1823 ts.r1 = (LispObj) next_frame; 1824 } else { 1825 ts.srr0 += 4; 1826 } 1827 } 1828 thread_set_state(thread, 1829 #ifdef X8664 1830 X86_THREAD_STATE64, /* GPRs, some SPRs */ 1831 #else 1832 X86_THREAD_STATE, /* GPRs, some SPRs */ 1833 #endif 1834 (thread_state_t)&ts, 1835 #ifdef X8664 1836 X86_THREAD_STATE64_COUNT 1837 #else 1838 X86_THREAD_STATE_COUNT 1839 #endif 1840 ); 1841 1842 return 0; 1843 } 1844 1845 /* 1846 This function runs in the exception handling thread. It's 1847 called (by this precise name) from the library function "exc_server()" 1848 when the thread's exception ports are set up. (exc_server() is called 1849 via mach_msg_server(), which is a function that waits for and dispatches 1850 on exception messages from the Mach kernel.) 1851 1852 This checks to see if the exception was caused by a pseudo_sigreturn() 1853 UUO; if so, it arranges for the thread to have its state restored 1854 from the specified context. 1855 1856 Otherwise, it tries to map the exception to a signal number and 1857 arranges that the thread run a "pseudo signal handler" to handle 1858 the exception. 1859 1860 Some exceptions could and should be handled here directly. 1861 */ 1862 1863 kern_return_t 1864 catch_exception_raise(mach_port_t exception_port, 1865 mach_port_t thread, 1866 mach_port_t task, 1867 exception_type_t exception, 1868 exception_data_t code_vector, 1869 mach_msg_type_number_t code_count) 1870 { 1871 int signum = 0, code = *code_vector, code1; 1872 TCR *tcr = TCR_FROM_EXCEPTION_PORT(exception_port); 1873 kern_return_t kret; 1874 1875 #ifdef DEBUG_MACH_EXCEPTIONS 1876 fprintf(stderr, "obtaining Mach exception lock in exception thread\n"); 1877 #endif 1878 1879 if (pthread_mutex_trylock(mach_exception_lock) == 0) { 1880 if (tcr->flags & (1<<TCR_FLAG_BIT_PENDING_EXCEPTION)) { 1881 CLR_TCR_FLAG(tcr,TCR_FLAG_BIT_PENDING_EXCEPTION); 1882 } 1883 if ((exception == EXC_BAD_INSTRUCTION) && 1884 (code_vector[0] == EXC_X86_UNIPL_INST) && 1885 (((code1 = code_vector[1]) == (int)pseudo_sigreturn) || 1886 (code1 == (int)enable_fp_exceptions) || 1887 (code1 == (int)disable_fp_exceptions))) { 1888 if (code1 == (int)pseudo_sigreturn) { 1889 kret = do_pseudo_sigreturn(thread, tcr); 1890 #if 0 1891 fprintf(stderr, "Exception return in 0x%x\n",tcr); 1892 #endif 1893 1894 } else if (code1 == (int)enable_fp_exceptions) { 1895 kret = thread_set_fp_exceptions_enabled(thread, true); 1896 } else kret = thread_set_fp_exceptions_enabled(thread, false); 1897 } else if (tcr->flags & (1<<TCR_FLAG_BIT_PROPAGATE_EXCEPTION)) { 1898 CLR_TCR_FLAG(tcr,TCR_FLAG_BIT_PROPAGATE_EXCEPTION); 1899 kret = 17; 1900 } else { 1901 switch (exception) { 1902 case EXC_BAD_ACCESS: 1903 signum = SIGSEGV; 1904 break; 1905 1906 case EXC_BAD_INSTRUCTION: 1907 signum = SIGILL; 1908 break; 1909 1910 case EXC_SOFTWARE: 1911 signum = SIGILL; 1912 break; 1913 1914 case EXC_ARITHMETIC: 1915 signum = SIGFPE; 1916 break; 1917 1918 default: 1919 break; 1920 } 1921 if (signum) { 1922 kret = setup_signal_frame(thread, 1923 (void *)pseudo_signal_handler, 1924 signum, 1925 code, 1926 tcr); 1927 #if 0 1928 fprintf(stderr, "Setup pseudosignal handling in 0x%x\n",tcr); 1929 #endif 1930 1931 } else { 1932 kret = 17; 1933 } 1934 } 1935 #ifdef DEBUG_MACH_EXCEPTIONS 1936 fprintf(stderr, "releasing Mach exception lock in exception thread\n"); 1937 #endif 1938 pthread_mutex_unlock(mach_exception_lock); 1939 } else { 1940 SET_TCR_FLAG(tcr,TCR_FLAG_BIT_PENDING_EXCEPTION); 1941 #if 0 1942 fprintf(stderr, "deferring pending exception in 0x%x\n", tcr); 1943 #endif 1944 kret = 0; 1945 } 1946 return kret; 1947 } 1948 1949 1950 1951 typedef struct { 1952 mach_msg_header_t Head; 1953 /* start of the kernel processed data */ 1954 mach_msg_body_t msgh_body; 1955 mach_msg_port_descriptor_t thread; 1956 mach_msg_port_descriptor_t task; 1957 /* end of the kernel processed data */ 1958 NDR_record_t NDR; 1959 exception_type_t exception; 1960 mach_msg_type_number_t codeCnt; 1961 integer_t code[2]; 1962 mach_msg_trailer_t trailer; 1963 } exceptionRequest; 1964 1965 1966 boolean_t 1967 openmcl_exc_server(mach_msg_header_t *in, mach_msg_header_t *out) 1968 { 1969 static NDR_record_t _NDR = {0}; 1970 kern_return_t handled; 1971 mig_reply_error_t *reply = (mig_reply_error_t *) out; 1972 exceptionRequest *req = (exceptionRequest *) in; 1973 1974 reply->NDR = _NDR; 1975 1976 out->msgh_bits = in->msgh_bits & MACH_MSGH_BITS_REMOTE_MASK; 1977 out->msgh_remote_port = in->msgh_remote_port; 1978 out->msgh_size = sizeof(mach_msg_header_t)+(3 * sizeof(unsigned)); 1979 out->msgh_local_port = MACH_PORT_NULL; 1980 out->msgh_id = in->msgh_id+100; 1981 1982 /* Could handle other exception flavors in the range 2401-2403 */ 1983 1984 1985 if (in->msgh_id != 2401) { 1986 reply->RetCode = MIG_BAD_ID; 1987 return FALSE; 1988 } 1989 handled = catch_exception_raise(req->Head.msgh_local_port, 1990 req->thread.name, 1991 req->task.name, 1992 req->exception, 1993 req->code, 1994 req->codeCnt); 1995 reply->RetCode = handled; 1996 return TRUE; 1997 } 1998 1999 /* 2000 The initial function for an exception-handling thread. 2001 */ 2002 2003 void * 2004 exception_handler_proc(void *arg) 2005 { 2006 extern boolean_t exc_server(); 2007 mach_port_t p = TCR_TO_EXCEPTION_PORT(arg); 2008 2009 mach_msg_server(openmcl_exc_server, 2048, p, 0); 2010 /* Should never return. */ 2011 abort(); 2012 } 2013 2014 2015 2016 mach_port_t 2017 mach_exception_port_set() 2018 { 2019 static mach_port_t __exception_port_set = MACH_PORT_NULL; 2020 kern_return_t kret; 2021 if (__exception_port_set == MACH_PORT_NULL) { 2022 mach_exception_lock = &_mach_exception_lock; 2023 pthread_mutex_init(mach_exception_lock, NULL); 2024 2025 kret = mach_port_allocate(mach_task_self(), 2026 MACH_PORT_RIGHT_PORT_SET, 2027 &__exception_port_set); 2028 MACH_CHECK_ERROR("allocating thread exception_ports",kret); 2029 create_system_thread(0, 2030 NULL, 2031 exception_handler_proc, 2032 (void *)((natural)__exception_port_set)); 2033 } 2034 return __exception_port_set; 2035 } 2036 2037 /* 2038 Setup a new thread to handle those exceptions specified by 2039 the mask "which". This involves creating a special Mach 2040 message port, telling the Mach kernel to send exception 2041 messages for the calling thread to that port, and setting 2042 up a handler thread which listens for and responds to 2043 those messages. 2044 2045 */ 2046 2047 /* 2048 Establish the lisp thread's TCR as its exception port, and determine 2049 whether any other ports have been established by foreign code for 2050 exceptions that lisp cares about. 2051 2052 If this happens at all, it should happen on return from foreign 2053 code and on entry to lisp code via a callback. 2054 2055 This is a lot of trouble (and overhead) to support Java, or other 2056 embeddable systems that clobber their caller's thread exception ports. 2057 2058 */ 2059 kern_return_t 2060 tcr_establish_exception_port(TCR *tcr, mach_port_t thread) 2061 { 2062 kern_return_t kret; 2063 MACH_foreign_exception_state *fxs = (MACH_foreign_exception_state *)tcr->native_thread_info; 2064 int i; 2065 unsigned n = NUM_LISP_EXCEPTIONS_HANDLED; 2066 mach_port_t lisp_port = TCR_TO_EXCEPTION_PORT(tcr), foreign_port; 2067 exception_mask_t mask = 0; 2068 2069 kret = thread_swap_exception_ports(thread, 2070 LISP_EXCEPTIONS_HANDLED_MASK, 2071 lisp_port, 2072 EXCEPTION_DEFAULT, 2073 THREAD_STATE_NONE, 2074 fxs->masks, 2075 &n, 2076 fxs->ports, 2077 fxs->behaviors, 2078 fxs->flavors); 2079 if (kret == KERN_SUCCESS) { 2080 fxs->foreign_exception_port_count = n; 2081 for (i = 0; i < n; i ++) { 2082 foreign_port = fxs->ports[i]; 2083 2084 if ((foreign_port != lisp_port) && 2085 (foreign_port != MACH_PORT_NULL)) { 2086 mask |= fxs->masks[i]; 2087 } 2088 } 2089 tcr->foreign_exception_status = (int) mask; 2090 } 2091 return kret; 2092 } 2093 2094 kern_return_t 2095 tcr_establish_lisp_exception_port(TCR *tcr) 2096 { 2097 return tcr_establish_exception_port(tcr, (mach_port_t)((natural)tcr->native_thread_id)); 2098 } 2099 2100 /* 2101 Do this when calling out to or returning from foreign code, if 2102 any conflicting foreign exception ports were established when we 2103 last entered lisp code. 2104 */ 2105 kern_return_t 2106 restore_foreign_exception_ports(TCR *tcr) 2107 { 2108 exception_mask_t m = (exception_mask_t) tcr->foreign_exception_status; 2109 2110 if (m) { 2111 MACH_foreign_exception_state *fxs = 2112 (MACH_foreign_exception_state *) tcr->native_thread_info; 2113 int i, n = fxs->foreign_exception_port_count; 2114 exception_mask_t tm; 2115 2116 for (i = 0; i < n; i++) { 2117 if ((tm = fxs->masks[i]) & m) { 2118 thread_set_exception_ports((mach_port_t)((natural)tcr->native_thread_id), 2119 tm, 2120 fxs->ports[i], 2121 fxs->behaviors[i], 2122 fxs->flavors[i]); 2123 } 2124 } 2125 } 2126 } 2127 2128 2129 /* 2130 This assumes that a Mach port (to be used as the thread's exception port) whose 2131 "name" matches the TCR's 32-bit address has already been allocated. 2132 */ 2133 2134 kern_return_t 2135 setup_mach_exception_handling(TCR *tcr) 2136 { 2137 mach_port_t 2138 thread_exception_port = TCR_TO_EXCEPTION_PORT(tcr), 2139 target_thread = pthread_mach_thread_np((pthread_t)ptr_from_lispobj(tcr->osid)), 2140 task_self = mach_task_self(); 2141 kern_return_t kret; 2142 2143 kret = mach_port_insert_right(task_self, 2144 thread_exception_port, 2145 thread_exception_port, 2146 MACH_MSG_TYPE_MAKE_SEND); 2147 MACH_CHECK_ERROR("adding send right to exception_port",kret); 2148 2149 kret = tcr_establish_exception_port(tcr, (mach_port_t)((natural) tcr->native_thread_id)); 2150 if (kret == KERN_SUCCESS) { 2151 mach_port_t exception_port_set = mach_exception_port_set(); 2152 2153 kret = mach_port_move_member(task_self, 2154 thread_exception_port, 2155 exception_port_set); 2156 } 2157 return kret; 2158 } 2159 2160 void 2161 darwin_exception_init(TCR *tcr) 2162 { 2163 void tcr_monitor_exception_handling(TCR*, Boolean); 2164 kern_return_t kret; 2165 MACH_foreign_exception_state *fxs = 2166 calloc(1, sizeof(MACH_foreign_exception_state)); 2167 2168 tcr->native_thread_info = (void *) fxs; 2169 2170 if ((kret = setup_mach_exception_handling(tcr)) 2171 != KERN_SUCCESS) { 2172 fprintf(stderr, "Couldn't setup exception handler - error = %d\n", kret); 2173 terminate_lisp(); 2174 } 2175 lisp_global(LISP_EXIT_HOOK) = (LispObj) restore_foreign_exception_ports; 2176 lisp_global(LISP_RETURN_HOOK) = (LispObj) tcr_establish_lisp_exception_port; 2177 } 2178 2179 /* 2180 The tcr is the "name" of the corresponding thread's exception port. 2181 Destroying the port should remove it from all port sets of which it's 2182 a member (notably, the exception port set.) 2183 */ 2184 void 2185 darwin_exception_cleanup(TCR *tcr) 2186 { 2187 void *fxs = tcr->native_thread_info; 2188 extern Boolean use_mach_exception_handling; 2189 2190 if (fxs) { 2191 tcr->native_thread_info = NULL; 2192 free(fxs); 2193 } 2194 if (use_mach_exception_handling) { 2195 mach_port_deallocate(mach_task_self(),TCR_TO_EXCEPTION_PORT(tcr)); 2196 mach_port_destroy(mach_task_self(),TCR_TO_EXCEPTION_PORT(tcr)); 2197 } 2198 } 2199 2200 2201 Boolean 2202 suspend_mach_thread(mach_port_t mach_thread) 2203 { 2204 kern_return_t status; 2205 Boolean aborted = false; 2206 2207 do { 2208 aborted = false; 2209 status = thread_suspend(mach_thread); 2210 if (status == KERN_SUCCESS) { 2211 status = thread_abort_safely(mach_thread); 2212 if (status == KERN_SUCCESS) { 2213 aborted = true; 2214 } else { 2215 fprintf(stderr, "abort failed on thread = 0x%x\n",mach_thread); 2216 thread_resume(mach_thread); 2217 } 2218 } else { 2219 return false; 2220 } 2221 } while (! aborted); 2222 return true; 2223 } 2224 2225 /* 2226 Only do this if pthread_kill indicated that the pthread isn't 2227 listening to signals anymore, as can happen as soon as pthread_exit() 2228 is called on Darwin. The thread could still call out to lisp as it 2229 is exiting, so we need another way to suspend it in this case. 2230 */ 2231 Boolean 2232 mach_suspend_tcr(TCR *tcr) 2233 { 2234 mach_port_t mach_thread = (mach_port_t)((natural)( tcr->native_thread_id)); 2235 ExceptionInformation *pseudosigcontext; 2236 Boolean result = false; 2237 2238 result = suspend_mach_thread(mach_thread); 2239 if (result) { 2240 pseudosigcontext = create_thread_context_frame(mach_thread, NULL); 2241 pseudosigcontext->uc_onstack = 0; 2242 pseudosigcontext->uc_sigmask = (sigset_t) 0; 2243 tcr->suspend_context = pseudosigcontext; 2244 } 2245 return result; 2246 } 2247 2248 void 2249 mach_resume_tcr(TCR *tcr) 2250 { 2251 ExceptionInformation *xp; 2252 mach_port_t mach_thread = (mach_port_t)((natural)(tcr->native_thread_id)); 2253 2254 xp = tcr->suspend_context; 2255 #ifdef DEBUG_MACH_EXCEPTIONS 2256 fprintf(stderr, "resuming TCR 0x%x, pending_exception_context = 0x%x\n", 2257 tcr, tcr->pending_exception_context); 2258 #endif 2259 tcr->suspend_context = NULL; 2260 restore_mach_thread_state(mach_thread, xp); 2261 #ifdef DEBUG_MACH_EXCEPTIONS 2262 fprintf(stderr, "restored state in TCR 0x%x, pending_exception_context = 0x%x\n", 2263 tcr, tcr->pending_exception_context); 2264 #endif 2265 thread_resume(mach_thread); 2266 } 2267 2268 void 2269 fatal_mach_error(char *format, ...) 2270 { 2271 va_list args; 2272 char s[512]; 2273 2274 2275 va_start(args, format); 2276 vsnprintf(s, sizeof(s),format, args); 2277 va_end(args); 2278 2279 Fatal("Mach error", s); 2280 } 2281 2282 2283 2284 2285 #endif
Note:
See TracChangeset
for help on using the changeset viewer.
