Implement the "ignoresegv" feature from SheepShaver. This is Unix-specific

so far. Target platform is currently Linux/x86.
This commit is contained in:
gbeauche 2002-05-12 11:10:50 +00:00
parent d9892009cc
commit 266fc18f8a
10 changed files with 321 additions and 3 deletions

View File

@ -574,6 +574,12 @@ Unix:
the number of key events sent to MacOS for each wheel movement (the the number of key events sent to MacOS for each wheel movement (the
number of lines to scroll). number of lines to scroll).
ignoresegv <"true" or "false">
Set this to "true" to ignore illegal memory accesses. The default
is "false". This feature is only implemented on the following
platforms: Linux/x86.
AmigaOS: AmigaOS:
sound <sound output description> sound <sound output description>

View File

@ -62,6 +62,9 @@
/* Define if we know a hack to replace siginfo_t::si_addr member */ /* Define if we know a hack to replace siginfo_t::si_addr member */
#undef HAVE_SIGCONTEXT_SUBTERFUGE #undef HAVE_SIGCONTEXT_SUBTERFUGE
/* Define if we can ignore the fault (instruction skipping in SIGSEGV handler */
#undef HAVE_SIGSEGV_SKIP_INSTRUCTION
/* Define if your system has a working vm_allocate()-based memory allocator */ /* Define if your system has a working vm_allocate()-based memory allocator */
#undef HAVE_MACH_VM #undef HAVE_MACH_VM

View File

@ -608,6 +608,25 @@ if [[ "x$ac_cv_have_extended_signals" = "xno" ]]; then
AC_TRANSLATE_DEFINE(HAVE_SIGCONTEXT_SUBTERFUGE, "$ac_cv_have_sigcontext_hack") AC_TRANSLATE_DEFINE(HAVE_SIGCONTEXT_SUBTERFUGE, "$ac_cv_have_sigcontext_hack")
fi fi
dnl Check if we can ignore the fault (instruction skipping in SIGSEGV handler)
AC_CACHE_CHECK("whether we can skip instruction in SIGSEGV handler",
ac_cv_have_skip_instruction, [
AC_LANG_SAVE
AC_LANG_CPLUSPLUS
AC_TRY_RUN([
#define HAVE_SIGSEGV_SKIP_INSTRUCTION 1
#define CONFIGURE_TEST_SIGSEGV_RECOVERY
#include "vm_alloc.cpp"
#include "sigsegv.cpp"
], ac_cv_have_skip_instruction=yes, ac_cv_have_skip_instruction=no,
dnl When cross-compiling, do not assume anything.
ac_cv_have_skip_instruction=no
)
AC_LANG_RESTORE
]
)
AC_TRANSLATE_DEFINE(HAVE_SIGSEGV_SKIP_INSTRUCTION, "$ac_cv_have_skip_instruction")
dnl Can we do Video on SEGV Signals ? dnl Can we do Video on SEGV Signals ?
CAN_VOSF=no CAN_VOSF=no
if [[ "$ac_cv_have_extended_signals" = "yes" -o "$ac_cv_have_sigcontext_hack" = "yes" ]]; then if [[ "$ac_cv_have_extended_signals" = "yes" -o "$ac_cv_have_sigcontext_hack" = "yes" ]]; then

View File

@ -75,6 +75,7 @@ struct sigstate {
#include "version.h" #include "version.h"
#include "main.h" #include "main.h"
#include "vm_alloc.h" #include "vm_alloc.h"
#include "sigsegv.h"
#ifdef ENABLE_MON #ifdef ENABLE_MON
# include "mon.h" # include "mon.h"
@ -283,6 +284,12 @@ int main(int argc, char **argv)
if (!PrefsEditor()) if (!PrefsEditor())
QuitEmulator(); QuitEmulator();
// Register request to ignore segmentation faults
#ifdef HAVE_SIGSEGV_SKIP_INSTRUCTION
if (PrefsFindBool("ignoresegv"))
sigsegv_set_ignore_state(true);
#endif
// Read RAM size // Read RAM size
RAMSize = PrefsFindInt32("ramsize") & 0xfff00000; // Round down to 1MB boundary RAMSize = PrefsFindInt32("ramsize") & 0xfff00000; // Round down to 1MB boundary
if (RAMSize < 1024*1024) { if (RAMSize < 1024*1024) {

View File

@ -1124,6 +1124,14 @@ static void create_serial_pane(GtkWidget *top)
static GtkObject *w_ramsize_adj; static GtkObject *w_ramsize_adj;
static GtkWidget *w_rom_file; static GtkWidget *w_rom_file;
// "Ignore SEGV" button toggled
#ifdef HAVE_SIGSEGV_SKIP_INSTRUCTION
static void tb_ignoresegv(GtkWidget *widget)
{
PrefsReplaceBool("ignoresegv", GTK_TOGGLE_BUTTON(widget)->active);
}
#endif
// Model ID selected // Model ID selected
static void mn_modelid_5(...) {PrefsReplaceInt32("modelid", 5);} static void mn_modelid_5(...) {PrefsReplaceInt32("modelid", 5);}
static void mn_modelid_14(...) {PrefsReplaceInt32("modelid", 14);} static void mn_modelid_14(...) {PrefsReplaceInt32("modelid", 14);}
@ -1226,6 +1234,10 @@ static void create_memory_pane(GtkWidget *top)
#endif #endif
w_rom_file = make_file_entry(box, STR_ROM_FILE_CTRL, "rom"); w_rom_file = make_file_entry(box, STR_ROM_FILE_CTRL, "rom");
#ifdef HAVE_SIGSEGV_SKIP_INSTRUCTION
make_checkbox(box, STR_IGNORESEGV_CTRL, "ignoresegv", GTK_SIGNAL_FUNC(tb_ignoresegv));
#endif
} }

View File

@ -33,6 +33,9 @@ prefs_desc platform_prefs_items[] = {
{"fbdevicefile", TYPE_STRING, false, "path of frame buffer device specification file"}, {"fbdevicefile", TYPE_STRING, false, "path of frame buffer device specification file"},
{"mousewheelmode", TYPE_INT32, false, "mouse wheel support mode (0=page up/down, 1=cursor up/down)"}, {"mousewheelmode", TYPE_INT32, false, "mouse wheel support mode (0=page up/down, 1=cursor up/down)"},
{"mousewheellines", TYPE_INT32, false, "number of lines to scroll in mouse wheel mode 1"}, {"mousewheellines", TYPE_INT32, false, "number of lines to scroll in mouse wheel mode 1"},
#ifdef HAVE_SIGSEGV_SKIP_INSTRUCTION
{"ignoresegv", TYPE_BOOLEAN, false, "ignore illegal memory accesses"},
#endif
{NULL, TYPE_END, false, NULL} // End of list {NULL, TYPE_END, false, NULL} // End of list
}; };
@ -98,4 +101,7 @@ void AddPlatformPrefsDefaults(void)
PrefsReplaceString("extfs", "/"); PrefsReplaceString("extfs", "/");
PrefsReplaceInt32("mousewheelmode", 1); PrefsReplaceInt32("mousewheelmode", 1);
PrefsReplaceInt32("mousewheellines", 3); PrefsReplaceInt32("mousewheellines", 3);
#ifdef HAVE_SIGSEGV_SKIP_INSTRUCTION
PrefsAddBool("ignoresegv", false);
#endif
} }

View File

@ -40,9 +40,15 @@
// Type of the system signal handler // Type of the system signal handler
typedef RETSIGTYPE (*signal_handler)(int); typedef RETSIGTYPE (*signal_handler)(int);
// Is the fault to be ignored?
static bool sigsegv_ignore_fault = false;
// User's SIGSEGV handler // User's SIGSEGV handler
static sigsegv_handler_t sigsegv_user_handler = 0; static sigsegv_handler_t sigsegv_user_handler = 0;
// Function called to dump state if we can't handle the fault
static sigsegv_handler_t sigsegv_dump_state = 0;
// Actual SIGSEGV handler installer // Actual SIGSEGV handler installer
static bool sigsegv_do_install_handler(int sig); static bool sigsegv_do_install_handler(int sig);
@ -64,6 +70,8 @@ static bool sigsegv_do_install_handler(int sig);
#if (defined(i386) || defined(__i386__)) #if (defined(i386) || defined(__i386__))
#include <sys/ucontext.h> #include <sys/ucontext.h>
#define SIGSEGV_FAULT_INSTRUCTION (((ucontext_t *)scp)->uc_mcontext.gregs[14]) /* should use REG_EIP instead */ #define SIGSEGV_FAULT_INSTRUCTION (((ucontext_t *)scp)->uc_mcontext.gregs[14]) /* should use REG_EIP instead */
#define SIGSEGV_REGISTER_FILE (unsigned long *)(((ucontext_t *)scp)->uc_mcontext.gregs)
#define SIGSEGV_SKIP_INSTRUCTION ix86_skip_instruction
#endif #endif
#if (defined(ia64) || defined(__ia64__)) #if (defined(ia64) || defined(__ia64__))
#define SIGSEGV_FAULT_INSTRUCTION (((struct sigcontext *)scp)->sc_ip & ~0x3ULL) /* slot number is in bits 0 and 1 */ #define SIGSEGV_FAULT_INSTRUCTION (((struct sigcontext *)scp)->sc_ip & ~0x3ULL) /* slot number is in bits 0 and 1 */
@ -84,6 +92,8 @@ static bool sigsegv_do_install_handler(int sig);
#define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, struct sigcontext scs #define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, struct sigcontext scs
#define SIGSEGV_FAULT_ADDRESS scs.cr2 #define SIGSEGV_FAULT_ADDRESS scs.cr2
#define SIGSEGV_FAULT_INSTRUCTION scs.eip #define SIGSEGV_FAULT_INSTRUCTION scs.eip
#define SIGSEGV_REGISTER_FILE (unsigned long *)(&scs)
#define SIGSEGV_SKIP_INSTRUCTION ix86_skip_instruction
#endif #endif
#if (defined(sparc) || defined(__sparc__)) #if (defined(sparc) || defined(__sparc__))
#include <asm/sigcontext.h> #include <asm/sigcontext.h>
@ -274,6 +284,178 @@ static sigsegv_address_t get_fault_address(struct sigcontext *scp)
#endif #endif
#endif #endif
#ifdef HAVE_SIGSEGV_SKIP_INSTRUCTION
// Decode and skip X86 instruction
#if (defined(i386) || defined(__i386__))
#if defined(__linux__)
enum {
X86_REG_EIP = 14,
X86_REG_EAX = 11,
X86_REG_ECX = 10,
X86_REG_EDX = 9,
X86_REG_EBX = 8,
X86_REG_ESP = 7,
X86_REG_EBP = 6,
X86_REG_ESI = 5,
X86_REG_EDI = 4
};
#endif
// FIXME: this is partly redundant with the instruction decoding phase
// to discover transfer type and register number
static inline int ix86_step_over_modrm(unsigned char * p)
{
int mod = (p[0] >> 6) & 3;
int rm = p[0] & 7;
int offset = 0;
// ModR/M Byte
switch (mod) {
case 0: // [reg]
if (rm == 5) return 4; // disp32
break;
case 1: // disp8[reg]
offset = 1;
break;
case 2: // disp32[reg]
offset = 4;
break;
case 3: // register
return 0;
}
// SIB Byte
if (rm == 4) {
if (mod == 0 && (p[1] & 7) == 5)
offset = 5; // disp32[index]
else
offset++;
}
return offset;
}
static bool ix86_skip_instruction(sigsegv_address_t fault_instruction, unsigned long * regs)
{
unsigned char * eip = (unsigned char *)fault_instruction;
if (eip == 0)
return false;
// Transfer type
enum {
TYPE_UNKNOWN,
TYPE_LOAD,
TYPE_STORE
} transfer_type = TYPE_UNKNOWN;
// Transfer size
enum {
SIZE_BYTE,
SIZE_WORD,
SIZE_LONG
} transfer_size = SIZE_LONG;
int reg = -1;
int len = 0;
// Operand size prefix
if (*eip == 0x66) {
eip++;
len++;
transfer_size = SIZE_WORD;
}
// Decode instruction
switch (eip[0]) {
case 0x8a: // MOV r8, r/m8
transfer_size = SIZE_BYTE;
case 0x8b: // MOV r32, r/m32 (or 16-bit operation)
switch (eip[1] & 0xc0) {
case 0x80:
reg = (eip[1] >> 3) & 7;
transfer_type = TYPE_LOAD;
break;
case 0x40:
reg = (eip[1] >> 3) & 7;
transfer_type = TYPE_LOAD;
break;
case 0x00:
reg = (eip[1] >> 3) & 7;
transfer_type = TYPE_LOAD;
break;
}
len += 2 + ix86_step_over_modrm(eip + 1);
break;
case 0x88: // MOV r/m8, r8
transfer_size = SIZE_BYTE;
case 0x89: // MOV r/m32, r32 (or 16-bit operation)
switch (eip[1] & 0xc0) {
case 0x80:
reg = (eip[1] >> 3) & 7;
transfer_type = TYPE_STORE;
break;
case 0x40:
reg = (eip[1] >> 3) & 7;
transfer_type = TYPE_STORE;
break;
case 0x00:
reg = (eip[1] >> 3) & 7;
transfer_type = TYPE_STORE;
break;
}
len += 2 + ix86_step_over_modrm(eip + 1);
break;
}
if (transfer_type == TYPE_UNKNOWN) {
// Unknown machine code, let it crash. Then patch the decoder
return false;
}
if (transfer_type == TYPE_LOAD && reg != -1) {
static const int x86_reg_map[8] = {
X86_REG_EAX, X86_REG_ECX, X86_REG_EDX, X86_REG_EBX,
X86_REG_ESP, X86_REG_EBP, X86_REG_ESI, X86_REG_EDI
};
if (reg < 0 || reg >= 8)
return false;
int rloc = x86_reg_map[reg];
switch (transfer_size) {
case SIZE_BYTE:
regs[rloc] = (regs[rloc] & ~0xff);
break;
case SIZE_WORD:
regs[rloc] = (regs[rloc] & ~0xffff);
break;
case SIZE_LONG:
regs[rloc] = 0;
break;
}
}
#if DEBUG
printf("%08x: %s %s access", regs[X86_REG_EIP],
transfer_size == SIZE_BYTE ? "byte" : transfer_size == SIZE_WORD ? "word" : "long",
transfer_type == TYPE_LOAD ? "read" : "write");
if (reg != -1) {
static const char * x86_reg_str_map[8] = {
"eax", "ecx", "edx", "ebx",
"esp", "ebp", "esi", "edi"
};
printf(" %s register %%%s", transfer_type == TYPE_LOAD ? "to" : "from", x86_reg_str_map[reg]);
}
printf(", %d bytes instruction\n", len);
#endif
regs[X86_REG_EIP] += len;
return true;
}
#endif
#endif
// Fallbacks // Fallbacks
#ifndef SIGSEGV_FAULT_INSTRUCTION #ifndef SIGSEGV_FAULT_INSTRUCTION
#define SIGSEGV_FAULT_INSTRUCTION SIGSEGV_INVALID_PC #define SIGSEGV_FAULT_INSTRUCTION SIGSEGV_INVALID_PC
@ -292,17 +474,34 @@ static sigsegv_address_t get_fault_address(struct sigcontext *scp)
#ifdef HAVE_SIGSEGV_RECOVERY #ifdef HAVE_SIGSEGV_RECOVERY
static void sigsegv_handler(SIGSEGV_FAULT_HANDLER_ARGLIST) static void sigsegv_handler(SIGSEGV_FAULT_HANDLER_ARGLIST)
{ {
sigsegv_address_t fault_address = (sigsegv_address_t)SIGSEGV_FAULT_ADDRESS;
sigsegv_address_t fault_instruction = (sigsegv_address_t)SIGSEGV_FAULT_INSTRUCTION;
bool fault_recovered = false;
// Call user's handler and reinstall the global handler, if required // 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 (sigsegv_user_handler(fault_address, fault_instruction)) {
#if (defined(HAVE_SIGACTION) ? defined(SIGACTION_NEED_REINSTALL) : defined(SIGNAL_NEED_REINSTALL)) #if (defined(HAVE_SIGACTION) ? defined(SIGACTION_NEED_REINSTALL) : defined(SIGNAL_NEED_REINSTALL))
sigsegv_do_install_handler(sig); sigsegv_do_install_handler(sig);
#endif #endif
fault_recovered = true;
} }
else { #if HAVE_SIGSEGV_SKIP_INSTRUCTION
else if (sigsegv_ignore_fault) {
// Call the instruction skipper with the register file available
if (SIGSEGV_SKIP_INSTRUCTION(fault_instruction, SIGSEGV_REGISTER_FILE))
fault_recovered = true;
}
#endif
if (!fault_recovered) {
// FAIL: reinstall default handler for "safe" crash // FAIL: reinstall default handler for "safe" crash
#define FAULT_HANDLER(sig) signal(sig, SIG_DFL); #define FAULT_HANDLER(sig) signal(sig, SIG_DFL);
SIGSEGV_ALL_SIGNALS SIGSEGV_ALL_SIGNALS
#undef FAULT_HANDLER #undef FAULT_HANDLER
// We can't do anything with the fault_address, dump state?
if (sigsegv_dump_state != 0)
sigsegv_dump_state(fault_address, fault_instruction);
} }
} }
#endif #endif
@ -379,6 +578,27 @@ void sigsegv_deinstall_handler(void)
#endif #endif
} }
/*
* SIGSEGV ignore state modifier
*/
void sigsegv_set_ignore_state(bool ignore_fault)
{
sigsegv_ignore_fault = ignore_fault;
}
/*
* Set callback function when we cannot handle the fault
*/
void sigsegv_set_dump_state(sigsegv_handler_t handler)
{
sigsegv_dump_state = handler;
}
/* /*
* Test program used for configure/test * Test program used for configure/test
*/ */
@ -404,6 +624,13 @@ static bool sigsegv_test_handler(sigsegv_address_t fault_address, sigsegv_addres
return true; return true;
} }
#ifdef HAVE_SIGSEGV_SKIP_INSTRUCTION
static bool sigsegv_insn_handler(sigsegv_address_t fault_address, sigsegv_address_t instruction_address)
{
return false;
}
#endif
int main(void) int main(void)
{ {
if (vm_init() < 0) if (vm_init() < 0)
@ -425,6 +652,34 @@ int main(void)
if (handler_called != 1) if (handler_called != 1)
return 1; return 1;
#ifdef HAVE_SIGSEGV_SKIP_INSTRUCTION
if (!sigsegv_install_handler(sigsegv_insn_handler))
return 1;
if (vm_protect((char *)page, page_size, VM_PAGE_WRITE) < 0)
return 1;
for (int i = 0; i < page_size; i++)
page[i] = (i + 1) % page_size;
if (vm_protect((char *)page, page_size, VM_PAGE_NOACCESS) < 0)
return 1;
sigsegv_set_ignore_state(true);
#define TEST_SKIP_INSTRUCTION(TYPE) do { \
const unsigned int TAG = 0x12345678; \
TYPE data = *((TYPE *)(page + sizeof(TYPE))); \
volatile unsigned int effect = data + TAG; \
if (effect != TAG) \
return 1; \
} while (0)
TEST_SKIP_INSTRUCTION(unsigned char);
TEST_SKIP_INSTRUCTION(unsigned short);
TEST_SKIP_INSTRUCTION(unsigned int);
#endif
vm_exit(); vm_exit();
return 0; return 0;
} }

View File

@ -36,6 +36,12 @@ extern bool sigsegv_install_handler(sigsegv_handler_t handler);
// Remove the user SIGSEGV handler, revert to default behavior // Remove the user SIGSEGV handler, revert to default behavior
extern void sigsegv_uninstall_handler(void); extern void sigsegv_uninstall_handler(void);
// Set SIGSEGV ignore state
extern void sigsegv_set_ignore_state(bool ignore_fault);
// Set callback function when we cannot handle the fault
extern void sigsegv_set_dump_state(sigsegv_handler_t handler);
// Define an address that is bound to be invalid for a program counter // Define an address that is bound to be invalid for a program counter
const sigsegv_address_t SIGSEGV_INVALID_PC = (sigsegv_address_t)(-1); const sigsegv_address_t SIGSEGV_INVALID_PC = (sigsegv_address_t)(-1);

View File

@ -75,6 +75,8 @@ user_string_def platform_strings[] = {
{STR_MOUSEWHEELMODE_CURSOR_LAB, "Cursor Up/Down"}, {STR_MOUSEWHEELMODE_CURSOR_LAB, "Cursor Up/Down"},
{STR_MOUSEWHEELLINES_CTRL, "Lines To Scroll"}, {STR_MOUSEWHEELLINES_CTRL, "Lines To Scroll"},
{STR_IGNORESEGV_CTRL, "Ignore Illegal Memory Accesses"},
{STR_WINDOW_TITLE_GRABBED, "Basilisk II (mouse grabbed, press Ctrl-F5 to release)"}, {STR_WINDOW_TITLE_GRABBED, "Basilisk II (mouse grabbed, press Ctrl-F5 to release)"},
{-1, NULL} // End marker {-1, NULL} // End marker

View File

@ -66,6 +66,8 @@ enum {
STR_MOUSEWHEELMODE_CURSOR_LAB, STR_MOUSEWHEELMODE_CURSOR_LAB,
STR_MOUSEWHEELLINES_CTRL, STR_MOUSEWHEELLINES_CTRL,
STR_IGNORESEGV_CTRL,
STR_WINDOW_TITLE_GRABBED STR_WINDOW_TITLE_GRABBED
}; };