From 98192c4ba9d670749d032a1843b4ccced90bd100 Mon Sep 17 00:00:00 2001 From: gbeauche <> Date: Sun, 20 May 2001 20:31:50 +0000 Subject: [PATCH] - new and updated SIGSEGV support functions: + configure script cleanups + possible support for Direct Addressing / VOSF on other platforms --- BasiliskII/ChangeLog | 3 + BasiliskII/src/Unix/Makefile.in | 4 +- BasiliskII/src/Unix/acconfig.h | 6 + BasiliskII/src/Unix/configure.in | 209 +++++++++-------------- BasiliskII/src/Unix/sigsegv.cpp | 278 +++++++++++++++++++++++++++++++ BasiliskII/src/Unix/sigsegv.h | 42 +++++ BasiliskII/src/Unix/video_vosf.h | 109 +----------- BasiliskII/src/Unix/video_x.cpp | 7 +- 8 files changed, 420 insertions(+), 238 deletions(-) create mode 100644 BasiliskII/src/Unix/sigsegv.cpp create mode 100644 BasiliskII/src/Unix/sigsegv.h diff --git a/BasiliskII/ChangeLog b/BasiliskII/ChangeLog index 68b4e6b1..37c65498 100644 --- a/BasiliskII/ChangeLog +++ b/BasiliskII/ChangeLog @@ -5,6 +5,9 @@ V0.9 - [Kolja Waschk] - Unix: some performance improvements to VOSF screen update code [Brian J. Johnson] + - Unix: renewed SIGSEGV support functions. Side effect: configure + script cleanups and probable Direct Addressing/VOSF support for + other platforms (NetBSD, AIX, OSF/1, Irix) [Gwenole Beauchesne] - Unix: -Ofast option is supplied to MIPSPro compiler [Brian J. Johnson] - Unix: workaround for IRIX pthreads bug in Delay_usec() [Brian J. Johnson] diff --git a/BasiliskII/src/Unix/Makefile.in b/BasiliskII/src/Unix/Makefile.in index 90fad151..1b8a4adc 100644 --- a/BasiliskII/src/Unix/Makefile.in +++ b/BasiliskII/src/Unix/Makefile.in @@ -33,8 +33,8 @@ SRCS = ../main.cpp main_unix.cpp ../prefs.cpp ../prefs_items.cpp prefs_unix.cpp ../emul_op.cpp ../macos_util.cpp ../xpram.cpp xpram_unix.cpp ../timer.cpp \ timer_unix.cpp clip_unix.cpp ../adb.cpp ../serial.cpp ../ether.cpp \ ../sony.cpp ../disk.cpp ../cdrom.cpp ../scsi.cpp ../video.cpp video_blit.cpp \ - video_x.cpp ../audio.cpp ../extfs.cpp extfs_unix.cpp ../user_strings.cpp \ - user_strings_unix.cpp \ + video_x.cpp sigsegv.cpp ../audio.cpp ../extfs.cpp extfs_unix.cpp \ + ../user_strings.cpp user_strings_unix.cpp \ $(SYSSRCS) $(CPUSRCS) APP = BasiliskII diff --git a/BasiliskII/src/Unix/acconfig.h b/BasiliskII/src/Unix/acconfig.h index 5049c6e3..8f1a34e6 100644 --- a/BasiliskII/src/Unix/acconfig.h +++ b/BasiliskII/src/Unix/acconfig.h @@ -44,6 +44,12 @@ /* Define if using video enabled on SEGV signals */ #undef ENABLE_VOSF +/* Define if your system requires signals to be reinstalled */ +#undef SIGNAL_NEED_REINSTALL + +/* Define if your system requires sigactions to be reinstalled */ +#undef SIGACTION_NEED_REINSTALL + /* Define if your system support extended signals */ #undef HAVE_SIGINFO_T diff --git a/BasiliskII/src/Unix/configure.in b/BasiliskII/src/Unix/configure.in index 6f122a52..4a962d40 100644 --- a/BasiliskII/src/Unix/configure.in +++ b/BasiliskII/src/Unix/configure.in @@ -212,6 +212,7 @@ AC_STRUCT_TM dnl Checks for library functions. AC_CHECK_FUNCS(strdup cfmakeraw) AC_CHECK_FUNCS(clock_gettime timer_create) +AC_CHECK_FUNCS(sigaction signal) dnl Select system-dependant source files. SERIALSRC=serial_unix.cpp @@ -328,33 +329,74 @@ AC_CACHE_CHECK("whether we can map Low Memory area 0x0000-0x2000", ] ) +dnl Check signal handlers need to be reinstalled +AC_CACHE_CHECK("whether signal handlers need to be reinstalled", + ac_cv_signal_need_reinstall, [ + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_RUN([ + #include + #ifdef HAVE_UNISTD_H + #include + #endif + #include + static int handled_signal = 0; + RETSIGTYPE sigusr1_handler(int) { handled_signal++; } + int main(void) { /* returns 0 if signals need not to be reinstalled */ + signal(SIGUSR1, sigusr1_handler); raise(SIGUSR1); raise(SIGUSR1); + exit(handled_signal == 2); + } + ], + [ac_cv_signal_need_reinstall=yes], + [ac_cv_signal_need_reinstall=no] + ) + AC_LANG_RESTORE + ] +) +AC_TRANSLATE_DEFINE(SIGNAL_NEED_REINSTALL, "$ac_cv_signal_need_reinstall") + +dnl Check if sigaction handlers need to be reinstalled +AC_CACHE_CHECK("whether sigaction handlers need to be reinstalled", + ac_cv_sigaction_need_reinstall, [ + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_RUN([ + #include + #ifdef HAVE_UNISTD_H + #include + #endif + #include + static int handled_signal = 0; + RETSIGTYPE sigusr1_handler(int) { handled_signal++; } + typedef RETSIGTYPE (*signal_handler)(int); + static signal_handler mysignal(int sig, signal_handler handler) { + struct sigaction old_sa; + struct sigaction new_sa; + new_sa.sa_handler = handler; + return ((sigaction(sig,&new_sa,&old_sa) < 0) ? SIG_IGN : old_sa.sa_handler); + } + int main(void) { /* returns 0 if signals need not to be reinstalled */ + mysignal(SIGUSR1, sigusr1_handler); raise(SIGUSR1); raise(SIGUSR1); + exit(handled_signal == 2); + } + ], + [ac_cv_sigaction_need_reinstall=yes], + [ac_cv_sigaction_need_reinstall=no] + ) + AC_LANG_RESTORE + ] +) +AC_TRANSLATE_DEFINE(SIGACTION_NEED_REINSTALL, "$ac_cv_sigaction_need_reinstall") + dnl Check if extended signals are supported. AC_CACHE_CHECK("whether your system supports extended signal handlers", ac_cv_have_extended_signals, [ AC_LANG_SAVE AC_LANG_CPLUSPLUS AC_TRY_RUN([ - #include - #include - #include - #include - #include - - static volatile caddr_t mem = 0; - static int zero_fd = -1; - - static RETSIGTYPE segfault_handler(int, siginfo_t * sip, void *) - { if ((caddr_t)(sip->si_addr) != mem) exit(1); - munmap(mem, getpagesize()); close(zero_fd); exit(0); } - - int main() - { if ((zero_fd = open("/dev/zero", O_RDWR)) < 0) exit(1); - if ((mem = (caddr_t)mmap(0, getpagesize(), PROT_READ, MAP_PRIVATE, zero_fd, 0)) == (caddr_t)MAP_FAILED) exit(1); - struct sigaction sa; sa.sa_sigaction = segfault_handler; sa.sa_flags = SA_SIGINFO; - sigaction(SIGSEGV, &sa, 0); - mem[0] = 0; - exit(1); // should not be reached - } + #define HAVE_SIGINFO_T 1 + #define CONFIGURE_TEST + #include "sigsegv.cpp" ], [ac_cv_have_extended_signals=yes], [ac_cv_have_extended_signals=no] @@ -366,116 +408,21 @@ AC_TRANSLATE_DEFINE(HAVE_SIGINFO_T, "$ac_cv_have_extended_signals") dnl Otherwise, check for subterfuges. if [[ "x$ac_cv_have_extended_signals" = "xno" ]]; then - case "$target_os" in - linux*) - if [[ "x$HAVE_I386" = "xyes" ]]; then - AC_CACHE_CHECK("whether we then have a subterfuge for your system", - ac_cv_have_sigcontext_hack, [ - AC_LANG_SAVE - AC_LANG_CPLUSPLUS - AC_TRY_RUN([ - #include - #include - #include - #include - - static volatile caddr_t mem = 0; - static int zero_fd = -1; - - static RETSIGTYPE segfault_handler(int, struct sigcontext scs) - { if ((caddr_t)(scs.cr2) != mem) exit(1); - munmap(mem, getpagesize()); close(zero_fd); exit(0); } - - int main() - { if ((zero_fd = open("/dev/zero", O_RDWR)) < 0) exit(1); - if ((mem = (caddr_t)mmap(0, getpagesize(), PROT_READ, MAP_PRIVATE, zero_fd, 0)) == (caddr_t)MAP_FAILED) exit(1); - struct sigaction sa; sa.sa_flags = 0; - sa.sa_handler = (RETSIGTYPE (*)(int))segfault_handler; - sigaction(SIGSEGV, &sa, 0); - mem[0] = 0; - exit(1); // should not be reached - } - ], - [ac_cv_have_sigcontext_hack=yes], - [ac_cv_have_sigcontext_hack=no] - ) - AC_LANG_RESTORE - ]) - AC_TRANSLATE_DEFINE(HAVE_SIGCONTEXT_SUBTERFUGE, $ac_cv_have_sigcontext_hack) - elif [[ "x$HAVE_POWERPC" = "xyes" ]]; then - AC_CACHE_CHECK("whether we then have a subterfuge for your system", - ac_cv_have_sigcontext_hack, [ - AC_LANG_SAVE - AC_LANG_CPLUSPLUS - AC_TRY_RUN([ - #include - #include - #include - #include - - static volatile caddr_t mem = 0; - static int zero_fd = -1; - - static RETSIGTYPE segfault_handler(int, struct sigcontext_struct *scs) - { if ((caddr_t)(scs->regs->dar) != mem) exit(1); - munmap(mem, getpagesize()); close(zero_fd); exit(0); } - - int main() - { if ((zero_fd = open("/dev/zero", O_RDWR)) < 0) exit(1); - if ((mem = (caddr_t)mmap(0, getpagesize(), PROT_READ, MAP_PRIVATE, zero_fd, 0)) == (caddr_t)MAP_FAILED) exit(1); - struct sigaction sa; sa.sa_flags = 0; - sa.sa_handler = (RETSIGTYPE (*)(int))segfault_handler; - sigaction(SIGSEGV, &sa, 0); - mem[0] = 0; - exit(1); // should not be reached - } - ], - [ac_cv_have_sigcontext_hack=yes], - [ac_cv_have_sigcontext_hack=no] - ) - AC_LANG_RESTORE - ]) - AC_TRANSLATE_DEFINE(HAVE_SIGCONTEXT_SUBTERFUGE, $ac_cv_have_sigcontext_hack) - fi - ;; - netbsd*) - if [[ "x$HAVE_M68K" = "xyes" ]]; then - AC_CACHE_CHECK("whether we then have a subterfuge for your system", - ac_cv_have_sigcontext_hack, [ - AC_LANG_SAVE - AC_LANG_CPLUSPLUS - AC_TRY_RUN([ - #include - #include - #include - #include - - static volatile caddr_t mem = 0; - static int zero_fd = -1; - - static RETSIGTYPE segfault_handler(int, int code) - { if ((caddr_t)code != mem) exit(1); - munmap(mem, getpagesize()); close(zero_fd); exit(0); } - - int main() - { if ((zero_fd = open("/dev/zero", O_RDWR)) < 0) exit(1); - if ((mem = (caddr_t)mmap(0, getpagesize(), PROT_READ, MAP_PRIVATE, zero_fd, 0)) == (caddr_t)MAP_FAILED) exit(1); - struct sigaction sa; sa.sa_flags = 0; - sa.sa_handler = (RETSIGTYPE (*)(int))segfault_handler; - sigaction(SIGSEGV, &sa, 0); - mem[0] = 0; - exit(1); // should not be reached - } - ], - [ac_cv_have_sigcontext_hack=yes], - [ac_cv_have_sigcontext_hack=no] - ) - AC_LANG_RESTORE - ]) - AC_TRANSLATE_DEFINE(HAVE_SIGCONTEXT_SUBTERFUGE, $ac_cv_have_sigcontext_hack) - fi - ;; - esac + AC_CACHE_CHECK("whether we then have a subterfuge for your system", + ac_cv_have_sigcontext_hack, [ + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_RUN([ + #define HAVE_SIGCONTEXT_SUBTERFUGE 1 + #define CONFIGURE_TEST + #include "sigsegv.cpp" + ], + [ac_cv_have_sigcontext_hack=yes], + [ac_cv_have_sigcontext_hack=no] + ) + AC_LANG_RESTORE + ]) + AC_TRANSLATE_DEFINE(HAVE_SIGCONTEXT_SUBTERFUGE, "$ac_cv_have_sigcontext_hack") fi dnl Can we do Video on SEGV Signals ? diff --git a/BasiliskII/src/Unix/sigsegv.cpp b/BasiliskII/src/Unix/sigsegv.cpp new file mode 100644 index 00000000..a379f4f9 --- /dev/null +++ b/BasiliskII/src/Unix/sigsegv.cpp @@ -0,0 +1,278 @@ +/* + * sigsegv.cpp - SIGSEGV signals support + * + * Derived from Bruno Haible's work on his SIGSEGV library for clisp + * + * + * Basilisk II (C) 1997-2001 Christian Bauer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "sigsegv.h" + +// Return value type of a signal handler (standard type if not defined) +#ifndef RETSIGTYPE +#define RETSIGTYPE void +#endif + +// Type of the system signal handler +typedef RETSIGTYPE (*signal_handler)(int); + +// User's SIGSEGV handler +static sigsegv_handler_t sigsegv_user_handler = 0; + +// Actual SIGSEGV handler installer +static bool sigsegv_do_install_handler(int sig); + + +/* + * OS-dependant SIGSEGV signals support section + */ + +#if HAVE_SIGINFO_T +// Generic extended signal handler +#define SIGSEGV_ALL_SIGNALS FAULT_HANDLER(SIGSEGV) +#define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, siginfo_t *sip, void * +#define SIGSEGV_FAULT_ADDRESS sip->si_addr +#endif + +#if HAVE_SIGCONTEXT_SUBTERFUGE +// Linux kernels prior to 2.4 ? +#if defined(__linux__) +#define SIGSEGV_ALL_SIGNALS FAULT_HANDLER(SIGSEGV) +#if (defined(i386) || defined(__i386__)) +#include +#define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, struct sigcontext scs +#define SIGSEGV_FAULT_ADDRESS scs.cr2 +#define SIGSEGV_FAULT_INSTRUCTION scs.eip +#endif +#if (defined(sparc) || defined(__sparc__)) +#include +#define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, int code, struct sigcontext* scp, char* addr +#define SIGSEGV_FAULT_ADDRESS addr +#endif +#if (defined(powerpc) || defined(__powerpc__)) +#include +#define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, struct sigcontext* scp +#define SIGSEGV_FAULT_ADDRESS scp->regs->dar +#define SIGSEGV_FAULT_INSTRUCTION scp->regs->nip +#endif +#endif + +// Irix 5 or 6 on MIPS +#if (defined(sgi) || defined(__sgi)) && (defined(SYSTYPE_SVR4) || defined(__SYSTYPE_SVR4)) +#define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, int code, struct sigcontext *scp +#define SIGSEGV_FAULT_ADDRESS scp->sc_badvaddr +#define SIGSEGV_ALL_SIGNALS FAULT_HANDLER(SIGSEGV) +#endif + +// OSF/1 on Alpha +#if defined(__osf__) +#define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, int code, struct sigcontext *scp +#define SIGSEGV_FAULT_ADDRESS scp->sc_traparg_a0 +#define SIGSEGV_ALL_SIGNALS FAULT_HANDLER(SIGSEGV) +#endif + +// AIX +#if defined(_AIX) +#define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, int code, struct sigcontext *scp +#define SIGSEGV_FAULT_ADDRESS scp->sc_jmpbuf.jmp_context.o_vaddr +#define SIGSEGV_ALL_SIGNALS FAULT_HANDLER(SIGSEGV) +#endif + +// NetBSD or FreeBSD +#if defined(__NetBSD__) || defined(__FreeBSD__) +#if (defined(m68k) || defined(__m68k__)) +#include +#define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, int code, struct sigcontext *scp +#define SIGSEGV_FAULT_ADDRESS ({ \ + struct sigstate { \ + int ss_flags; \ + struct frame ss_frame; \ + }; \ + struct sigstate *state = (struct sigstate *)scp->sc_ap; \ + char *fault_addr; \ + switch (state->ss_frame.f_format) { \ + case 7: /* 68040 access error */ \ + /* "code" is sometimes unreliable (i.e. contains NULL or a bogus address), reason unknown */ \ + fault_addr = state->ss_frame.f_fmt7.f_fa; \ + break; \ + default: \ + fault_addr = (char *)code; \ + break; \ + } \ + fault_addr; \ +}) +#define SIGSEGV_ALL_SIGNALS FAULT_HANDLER(SIGSEGV) +#else +#define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, int code, void *scp, char *addr +#define SIGSEGV_FAULT_ADDRESS addr +#define SIGSEGV_ALL_SIGNALS FAULT_HANDLER(SIGBUS) +#endif +#endif +#endif + +// Fallbacks +#ifndef SIGSEGV_FAULT_INSTRUCTION +#define SIGSEGV_FAULT_INSTRUCTION SIGSEGV_INVALID_PC +#endif + + +/* + * SIGSEGV global handler + */ + +static void sigsegv_handler(SIGSEGV_FAULT_HANDLER_ARGLIST) +{ + // Call user's handler and reinstall the global handler, if required + if (sigsegv_user_handler((sigsegv_address_t)SIGSEGV_FAULT_ADDRESS, (sigsegv_address_t)SIGSEGV_FAULT_INSTRUCTION)) { +#if (defined(HAVE_SIGACTION) ? defined(SIGACTION_NEED_REINSTALL) : defined(SIGNAL_NEED_REINSTALL)) + sigsegv_do_install_handler(sig); +#endif + } + else { + // FAIL: reinstall default handler for "safe" crash +#define FAULT_HANDLER(sig) signal(sig, SIG_DFL); + SIGSEGV_ALL_SIGNALS +#undef FAULT_HANDLER + } +} + + +/* + * SIGSEGV handler initialization + */ + +#if defined(HAVE_SIGINFO_T) +static bool sigsegv_do_install_handler(int sig) +{ + // Setup SIGSEGV handler to process writes to frame buffer +#ifdef HAVE_SIGACTION + struct sigaction vosf_sa; + sigemptyset(&vosf_sa.sa_mask); + vosf_sa.sa_sigaction = sigsegv_handler; + vosf_sa.sa_flags = SA_SIGINFO; + return (sigaction(sig, &vosf_sa, 0) == 0); +#else + return (signal(sig, (signal_handler)sigsegv_handler) != SIG_ERR); +#endif +} +#elif defined(HAVE_SIGCONTEXT_SUBTERFUGE) +static bool sigsegv_do_install_handler(int sig) +{ + // Setup SIGSEGV handler to process writes to frame buffer +#ifdef HAVE_SIGACTION + struct sigaction vosf_sa; + sigemptyset(&vosf_sa.sa_mask); + vosf_sa.sa_handler = (signal_handler)sigsegv_handler; +#if !EMULATED_68K && defined(__NetBSD__) + sigaddset(&vosf_sa.sa_mask, SIGALRM); + vosf_sa.sa_flags = SA_ONSTACK; +#else + vosf_sa.sa_flags = 0; +#endif + return (sigaction(sig, &vosf_sa, 0) == 0); +#else + return (signal(sig, (signal_handler)sigsegv_handler) != SIG_ERR); +#endif +} +#endif + +bool sigsegv_install_handler(sigsegv_handler_t handler) +{ +#if defined(HAVE_SIGINFO_T) || defined(HAVE_SIGCONTEXT_SUBTERFUGE) + sigsegv_user_handler = handler; + bool success = true; +#define FAULT_HANDLER(sig) success = success && sigsegv_do_install_handler(sig); + SIGSEGV_ALL_SIGNALS +#undef FAULT_HANDLER + return success; +#else + // FAIL: no siginfo_t nor sigcontext subterfuge is available + return false; +#endif +} + + +/* + * SIGSEGV handler deinitialization + */ + +void sigsegv_deinstall_handler(void) +{ + sigsegv_user_handler = 0; +#define FAULT_HANDLER(sig) signal(sig, SIG_DFL); + SIGSEGV_ALL_SIGNALS +#undef FAULT_HANDLER +} + +/* + * Test program used for configure/test + */ + +#ifdef CONFIGURE_TEST +#include +#include +#include +#include +#include + +static caddr_t page = 0; +static int page_size; +static int handler_called = 0; + +static bool sigsegv_test_handler(sigsegv_address_t fault_address, sigsegv_address_t instruction_address) +{ + handler_called++; + if ((fault_address - 123) != page) + exit(1); + if (mprotect((caddr_t)((unsigned long)fault_address & -page_size), page_size, PROT_READ | PROT_WRITE) != 0) + exit(1); + return true; +} + +int main(void) +{ + int zero_fd = open("/dev/zero", O_RDWR); + if (zero_fd < 0) + return 1; + + page_size = getpagesize(); + page = (caddr_t)mmap(0, page_size, PROT_READ, MAP_PRIVATE, zero_fd, 0); + if (page == MAP_FAILED) + return 1; + + if (!sigsegv_install_handler(sigsegv_test_handler)) + return 1; + + page[123] = 45; + page[123] = 45; + + if (handler_called != 1) + return 1; + + return 0; +} +#endif diff --git a/BasiliskII/src/Unix/sigsegv.h b/BasiliskII/src/Unix/sigsegv.h new file mode 100644 index 00000000..0f23fbcc --- /dev/null +++ b/BasiliskII/src/Unix/sigsegv.h @@ -0,0 +1,42 @@ +/* + * sigsegv.h - SIGSEGV signals support + * + * Derived from Bruno Haible's work on his SIGSEGV library for clisp + * + * + * Basilisk II (C) 1997-2001 Christian Bauer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef SIGSEGV_H +#define SIGSEGV_H + +// Address type +typedef char * sigsegv_address_t; + +// Type of a SIGSEGV handler. Returns boolean expressing successful operation +typedef bool (*sigsegv_handler_t)(sigsegv_address_t fault_address, sigsegv_address_t instruction_address); + +// Install a SIGSEGV handler. Returns boolean expressing success +extern bool sigsegv_install_handler(sigsegv_handler_t handler); + +// Remove the user SIGSEGV handler, revert to default behavior +extern void sigsegv_uninstall_handler(void); + +// Define an address that is bound to be invalid for a program counter +const sigsegv_address_t SIGSEGV_INVALID_PC = (sigsegv_address_t)(-1); + +#endif /* SIGSEGV_H */ diff --git a/BasiliskII/src/Unix/video_vosf.h b/BasiliskII/src/Unix/video_vosf.h index bde296ed..77e71220 100644 --- a/BasiliskII/src/Unix/video_vosf.h +++ b/BasiliskII/src/Unix/video_vosf.h @@ -43,15 +43,12 @@ static void * allocate_framebuffer(uint32 size, uint8 * hint = 0) return mmap((caddr_t)hint, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, zero_fd, 0); } - -/* - * Screen fault handler - */ - -const uintptr INVALID_PC = (uintptr)-1; - -static inline void do_handle_screen_fault(uintptr addr, uintptr pc = INVALID_PC) +// Screen fault handler +static bool screen_fault_handler(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction) { + D(bug("screen_fault_handler: ADDR=0x%08X from IP=0x%08X\n", fault_address, fault_instruction)); + const uintptr addr = (uintptr)fault_address; + /* Someone attempted to write to the frame buffer. Make it writeable * now so that the data could actually be written. It will be made * read-only back in one of the screen update_*() functions. @@ -64,105 +61,17 @@ static inline void do_handle_screen_fault(uintptr addr, uintptr pc = INVALID_PC) mprotect(page_ad, mainBuffer.pageSize, PROT_READ | PROT_WRITE); mainBuffer.dirty = true; UNLOCK_VOSF; - return; + return true; } /* Otherwise, we don't know how to handle the fault, let it crash */ fprintf(stderr, "do_handle_screen_fault: unhandled address 0x%08X", addr); - if (pc != INVALID_PC) - fprintf(stderr, " [IP=0x%08X]", pc); + if (fault_instruction != SIGSEGV_INVALID_PC) + fprintf(stderr, " [IP=0x%08X]", fault_instruction); fprintf(stderr, "\n"); - - signal(SIGSEGV, SIG_DFL); + return false; } -#if defined(HAVE_SIGINFO_T) - -static void Screen_fault_handler(int, siginfo_t * sip, void *) -{ - D(bug("Screen_fault_handler: ADDR=0x%08X\n", sip->si_addr)); - do_handle_screen_fault((uintptr)sip->si_addr); -} - -#elif defined(HAVE_SIGCONTEXT_SUBTERFUGE) - -# if defined(__i386__) && defined(__linux__) -static void Screen_fault_handler(int, struct sigcontext scs) -{ - D(bug("Screen_fault_handler: ADDR=0x%08X from IP=0x%08X\n", scs.cr2, scs.eip)); - do_handle_screen_fault((uintptr)scs.cr2, (uintptr)scs.eip); -} - -# elif defined(__m68k__) && defined(__NetBSD__) - -# include -static void Screen_fault_handler(int, int code, struct sigcontext *scp) -{ - D(bug("Screen_fault_handler: ADDR=0x%08X\n", code)); - struct sigstate { - int ss_flags; - struct frame ss_frame; - }; - struct sigstate *state = (struct sigstate *)scp->sc_ap; - uintptr fault_addr; - switch (state->ss_frame.f_format) { - case 7: // 68040 access error - // "code" is sometimes unreliable (i.e. contains NULL or a bogus address), reason unknown - fault_addr = state->ss_frame.f_fmt7.f_fa; - break; - default: - fault_addr = (uintptr)code; - break; - } - do_handle_screen_fault(fault_addr); -} - -# elif defined(__powerpc__) && defined(__linux__) - -static void Screen_fault_handler(int, struct sigcontext_struct *scs) -{ - D(bug("Screen_fault_handler: ADDR=0x%08X from IP=0x%08X\n", scs->regs->dar, scs->regs->nip)); - do_handle_screen_fault((uintptr)scs->regs->dar, (uintptr)scs->regs->nip); -} - -# else -# error "No suitable subterfuge for Video on SEGV signals" -# endif -#else -# error "Can't do Video on SEGV signals" -#endif - - -/* - * Screen fault handler initialization - */ - -#if defined(HAVE_SIGINFO_T) -static bool Screen_fault_handler_init() -{ - // Setup SIGSEGV handler to process writes to frame buffer - sigemptyset(&vosf_sa.sa_mask); - vosf_sa.sa_sigaction = Screen_fault_handler; - vosf_sa.sa_flags = SA_SIGINFO; - return (sigaction(SIGSEGV, &vosf_sa, NULL) == 0); -} -#elif defined(HAVE_SIGCONTEXT_SUBTERFUGE) -static bool Screen_fault_handler_init() -{ - // Setup SIGSEGV handler to process writes to frame buffer - sigemptyset(&vosf_sa.sa_mask); - vosf_sa.sa_handler = (void (*)(int)) Screen_fault_handler; -#if !EMULATED_68K && defined(__NetBSD__) - sigaddset(&vosf_sa.sa_mask, SIGALRM); - vosf_sa.sa_flags = SA_ONSTACK; -#else - vosf_sa.sa_flags = 0; -#endif - return (sigaction(SIGSEGV, &vosf_sa, NULL) == 0); -} -#endif - - /* * Update display for Windowed mode and VOSF */ diff --git a/BasiliskII/src/Unix/video_x.cpp b/BasiliskII/src/Unix/video_x.cpp index 5f597c47..d250f012 100644 --- a/BasiliskII/src/Unix/video_x.cpp +++ b/BasiliskII/src/Unix/video_x.cpp @@ -53,10 +53,9 @@ #endif #ifdef ENABLE_VOSF -# include -# include # include # include +# include "sigsegv.h" #endif #include "cpu_emulation.h" @@ -278,8 +277,6 @@ static inline int find_next_page_clear(int page) } static int zero_fd = -1; -static bool Screen_fault_handler_init(); -static struct sigaction vosf_sa; #ifdef HAVE_PTHREADS static pthread_mutex_t vosf_lock = PTHREAD_MUTEX_INITIALIZER; // Mutex to protect frame buffer (dirtyPages in fact) @@ -1172,7 +1169,7 @@ bool VideoInit(bool classic) } // Initialize the handler for SIGSEGV - if (!Screen_fault_handler_init()) { + if (!sigsegv_install_handler(screen_fault_handler)) { // TODO: STR_VOSF_INIT_ERR ? ErrorAlert("Could not initialize Video on SEGV signals"); return false;