mirror of
https://github.com/kanjitalk755/macemu.git
synced 2024-12-20 15:29:48 +00:00
New SIGSEGV API so that skip-instruction requests are more explicit. Yes,
that's api change, but that's cooler now for SheepShaver. ;-)
This commit is contained in:
parent
5b57fde6ce
commit
a48a804c15
@ -209,6 +209,32 @@ char *strdup(const char *s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SIGSEGV handler
|
||||||
|
*/
|
||||||
|
|
||||||
|
static sigsegv_return_t sigsegv_handler(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction)
|
||||||
|
{
|
||||||
|
#if ENABLE_VOSF
|
||||||
|
// Handle screen fault
|
||||||
|
extern bool Screen_fault_handler(sigsegv_address_t, sigsegv_address_t);
|
||||||
|
if (Screen_fault_handler(fault_address, fault_instruction))
|
||||||
|
return SIGSEGV_RETURN_SUCCESS;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SIGSEGV_SKIP_INSTRUCTION
|
||||||
|
// Ignore writes to ROM
|
||||||
|
if (((uintptr)fault_address - (uintptr)ROMBaseHost) < ROMSize)
|
||||||
|
return SIGSEGV_RETURN_SKIP_INSTRUCTION;
|
||||||
|
|
||||||
|
// Ignore all other faults, if requested
|
||||||
|
if (PrefsFindBool("ignoresegv"))
|
||||||
|
return SIGSEGV_RETURN_SKIP_INSTRUCTION;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return SIGSEGV_RETURN_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Dump state when everything went wrong after a SEGV
|
* Dump state when everything went wrong after a SEGV
|
||||||
*/
|
*/
|
||||||
@ -357,12 +383,10 @@ int main(int argc, char **argv)
|
|||||||
if (!PrefsEditor())
|
if (!PrefsEditor())
|
||||||
QuitEmulator();
|
QuitEmulator();
|
||||||
|
|
||||||
// Register request to ignore all segmentation faults
|
// Install the handler for SIGSEGV
|
||||||
#ifdef HAVE_SIGSEGV_SKIP_INSTRUCTION
|
if (!sigsegv_install_handler(sigsegv_handler))
|
||||||
if (PrefsFindBool("ignoresegv"))
|
return false;
|
||||||
sigsegv_add_ignore_range(0, ~(0UL), SIGSEGV_TRANSFER_LOAD | SIGSEGV_TRANSFER_STORE);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Register dump state function when we got mad after a segfault
|
// Register dump state function when we got mad after a segfault
|
||||||
sigsegv_set_dump_state(sigsegv_dump_state);
|
sigsegv_set_dump_state(sigsegv_dump_state);
|
||||||
|
|
||||||
|
@ -45,16 +45,6 @@ using std::list;
|
|||||||
// Type of the system signal handler
|
// Type of the system signal handler
|
||||||
typedef RETSIGTYPE (*signal_handler)(int);
|
typedef RETSIGTYPE (*signal_handler)(int);
|
||||||
|
|
||||||
// Ignore range chain
|
|
||||||
struct ignore_range_t {
|
|
||||||
sigsegv_address_t start;
|
|
||||||
unsigned long length;
|
|
||||||
int transfer_type;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef list<ignore_range_t> ignore_range_list_t;
|
|
||||||
ignore_range_list_t sigsegv_ignore_ranges;
|
|
||||||
|
|
||||||
// User's SIGSEGV handler
|
// User's SIGSEGV handler
|
||||||
static sigsegv_fault_handler_t sigsegv_fault_handler = 0;
|
static sigsegv_fault_handler_t sigsegv_fault_handler = 0;
|
||||||
|
|
||||||
@ -64,16 +54,6 @@ static sigsegv_state_dumper_t sigsegv_state_dumper = 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);
|
||||||
|
|
||||||
// Find ignore range matching address
|
|
||||||
static inline ignore_range_list_t::iterator sigsegv_find_ignore_range(sigsegv_address_t address)
|
|
||||||
{
|
|
||||||
ignore_range_list_t::iterator it;
|
|
||||||
for (it = sigsegv_ignore_ranges.begin(); it != sigsegv_ignore_ranges.end(); it++)
|
|
||||||
if (address >= it->start && address < it->start + it->length)
|
|
||||||
break;
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Instruction decoding aids
|
* Instruction decoding aids
|
||||||
@ -610,12 +590,6 @@ static bool powerpc_skip_instruction(unsigned int * nip_p, unsigned int * regs)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ignore_range_list_t::iterator it = sigsegv_find_ignore_range((sigsegv_address_t)instr.addr);
|
|
||||||
if (it == sigsegv_ignore_ranges.end() || ((it->transfer_type & instr.transfer_type) != instr.transfer_type)) {
|
|
||||||
// Address doesn't fall into ignore ranges list, let it crash.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
printf("%08x: %s %s access", *nip_p,
|
printf("%08x: %s %s access", *nip_p,
|
||||||
instr.transfer_size == SIZE_BYTE ? "byte" : instr.transfer_size == SIZE_WORD ? "word" : "long",
|
instr.transfer_size == SIZE_BYTE ? "byte" : instr.transfer_size == SIZE_WORD ? "word" : "long",
|
||||||
@ -661,22 +635,24 @@ static void sigsegv_handler(SIGSEGV_FAULT_HANDLER_ARGLIST)
|
|||||||
bool fault_recovered = false;
|
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_fault_handler(fault_address, fault_instruction)) {
|
switch (sigsegv_fault_handler(fault_address, fault_instruction)) {
|
||||||
|
case SIGSEGV_RETURN_SUCCESS:
|
||||||
#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;
|
fault_recovered = true;
|
||||||
}
|
break;
|
||||||
#if HAVE_SIGSEGV_SKIP_INSTRUCTION
|
#if HAVE_SIGSEGV_SKIP_INSTRUCTION
|
||||||
else if (sigsegv_ignore_ranges.size() > 0) {
|
case SIGSEGV_RETURN_SKIP_INSTRUCTION:
|
||||||
// Call the instruction skipper with the register file available
|
// Call the instruction skipper with the register file available
|
||||||
if (SIGSEGV_SKIP_INSTRUCTION(SIGSEGV_REGISTER_FILE))
|
if (SIGSEGV_SKIP_INSTRUCTION(SIGSEGV_REGISTER_FILE))
|
||||||
fault_recovered = true;
|
fault_recovered = true;
|
||||||
}
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
if (!fault_recovered) {
|
if (!fault_recovered) {
|
||||||
// FAIL: reinstall default handler for "safe" crash
|
// Failure: 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
|
||||||
@ -760,43 +736,6 @@ void sigsegv_deinstall_handler(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Add SIGSEGV ignore range
|
|
||||||
*/
|
|
||||||
|
|
||||||
void sigsegv_add_ignore_range(sigsegv_address_t address, unsigned long length, int transfer_type)
|
|
||||||
{
|
|
||||||
ignore_range_t ignore_range;
|
|
||||||
ignore_range.start = address;
|
|
||||||
ignore_range.length = length;
|
|
||||||
ignore_range.transfer_type = transfer_type;
|
|
||||||
sigsegv_ignore_ranges.push_front(ignore_range);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Remove SIGSEGV ignore range. Range must match installed one, otherwise FALSE is returned.
|
|
||||||
*/
|
|
||||||
|
|
||||||
bool sigsegv_remove_ignore_range(sigsegv_address_t address, unsigned long length, int transfer_type)
|
|
||||||
{
|
|
||||||
ignore_range_list_t::iterator it;
|
|
||||||
for (it = sigsegv_ignore_ranges.begin(); it != sigsegv_ignore_ranges.end(); it++)
|
|
||||||
if (it->start == address && it->length == length && ((it->transfer_type & transfer_type) == transfer_type))
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (it != sigsegv_ignore_ranges.end()) {
|
|
||||||
if (it->transfer_type != transfer_type)
|
|
||||||
it->transfer_type &= ~transfer_type;
|
|
||||||
else
|
|
||||||
sigsegv_ignore_ranges.erase(it);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set callback function when we cannot handle the fault
|
* Set callback function when we cannot handle the fault
|
||||||
*/
|
*/
|
||||||
@ -822,20 +761,22 @@ static int page_size;
|
|||||||
static volatile char * page = 0;
|
static volatile char * page = 0;
|
||||||
static volatile int handler_called = 0;
|
static volatile int handler_called = 0;
|
||||||
|
|
||||||
static bool sigsegv_test_handler(sigsegv_address_t fault_address, sigsegv_address_t instruction_address)
|
static sigsegv_return_t sigsegv_test_handler(sigsegv_address_t fault_address, sigsegv_address_t instruction_address)
|
||||||
{
|
{
|
||||||
handler_called++;
|
handler_called++;
|
||||||
if ((fault_address - 123) != page)
|
if ((fault_address - 123) != page)
|
||||||
exit(1);
|
exit(1);
|
||||||
if (vm_protect((char *)((unsigned long)fault_address & -page_size), page_size, VM_PAGE_READ | VM_PAGE_WRITE) != 0)
|
if (vm_protect((char *)((unsigned long)fault_address & -page_size), page_size, VM_PAGE_READ | VM_PAGE_WRITE) != 0)
|
||||||
exit(1);
|
exit(1);
|
||||||
return true;
|
return SIGSEGV_RETURN_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_SIGSEGV_SKIP_INSTRUCTION
|
#ifdef HAVE_SIGSEGV_SKIP_INSTRUCTION
|
||||||
static bool sigsegv_insn_handler(sigsegv_address_t fault_address, sigsegv_address_t instruction_address)
|
static sigsegv_return_t sigsegv_insn_handler(sigsegv_address_t fault_address, sigsegv_address_t instruction_address)
|
||||||
{
|
{
|
||||||
return false;
|
if (((unsigned long)fault_address - (unsigned long)page) < page_size)
|
||||||
|
return SIGSEGV_SRETURN_KIP_INSTRUCTION;
|
||||||
|
return SIGSEGV_RETURN_FAILURE;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -873,8 +814,6 @@ int main(void)
|
|||||||
if (vm_protect((char *)page, page_size, VM_PAGE_NOACCESS) < 0)
|
if (vm_protect((char *)page, page_size, VM_PAGE_NOACCESS) < 0)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
sigsegv_add_ignore_range((char *)page, page_size, SIGSEGV_TRANSFER_LOAD | SIGSEGV_TRANSFER_STORE);
|
|
||||||
|
|
||||||
#define TEST_SKIP_INSTRUCTION(TYPE) do { \
|
#define TEST_SKIP_INSTRUCTION(TYPE) do { \
|
||||||
const unsigned int TAG = 0x12345678; \
|
const unsigned int TAG = 0x12345678; \
|
||||||
TYPE data = *((TYPE *)(page + sizeof(TYPE))); \
|
TYPE data = *((TYPE *)(page + sizeof(TYPE))); \
|
||||||
|
@ -34,8 +34,15 @@ enum sigsegv_transfer_type_t {
|
|||||||
SIGSEGV_TRANSFER_STORE = 2,
|
SIGSEGV_TRANSFER_STORE = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// SIGSEGV handler return state
|
||||||
|
enum sigsegv_return_t {
|
||||||
|
SIGSEGV_RETURN_SUCCESS,
|
||||||
|
SIGSEGV_RETURN_FAILURE,
|
||||||
|
SIGSEGV_RETURN_SKIP_INSTRUCTION,
|
||||||
|
};
|
||||||
|
|
||||||
// Type of a SIGSEGV handler. Returns boolean expressing successful operation
|
// Type of a SIGSEGV handler. Returns boolean expressing successful operation
|
||||||
typedef bool (*sigsegv_fault_handler_t)(sigsegv_address_t fault_address, sigsegv_address_t instruction_address);
|
typedef sigsegv_return_t (*sigsegv_fault_handler_t)(sigsegv_address_t fault_address, sigsegv_address_t instruction_address);
|
||||||
|
|
||||||
// Type of a SIGSEGV state dump function
|
// Type of a SIGSEGV state dump function
|
||||||
typedef void (*sigsegv_state_dumper_t)(sigsegv_address_t fault_address, sigsegv_address_t instruction_address);
|
typedef void (*sigsegv_state_dumper_t)(sigsegv_address_t fault_address, sigsegv_address_t instruction_address);
|
||||||
@ -46,12 +53,6 @@ extern bool sigsegv_install_handler(sigsegv_fault_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);
|
||||||
|
|
||||||
// Add SIGSEGV ignore range
|
|
||||||
extern void sigsegv_add_ignore_range(sigsegv_address_t address, unsigned long length, int transfer_type);
|
|
||||||
|
|
||||||
// Remove SIGSEGV ignore range. Range must match installed one, otherwise FALSE is returned.
|
|
||||||
extern bool sigsegv_remove_ignore_range(sigsegv_address_t address, unsigned long length, int transfer_type);
|
|
||||||
|
|
||||||
// Set callback function when we cannot handle the fault
|
// Set callback function when we cannot handle the fault
|
||||||
extern void sigsegv_set_dump_state(sigsegv_state_dumper_t handler);
|
extern void sigsegv_set_dump_state(sigsegv_state_dumper_t handler);
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
#ifndef VIDEO_VOSF_H
|
#ifndef VIDEO_VOSF_H
|
||||||
#define VIDEO_VOSF_H
|
#define VIDEO_VOSF_H
|
||||||
|
|
||||||
// Note: this file is #include'd in video_x.cpp
|
// Note: this file must be #include'd only in video_x.cpp
|
||||||
#ifdef ENABLE_VOSF
|
#ifdef ENABLE_VOSF
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
@ -200,11 +200,6 @@ static uint32 page_extend(uint32 size)
|
|||||||
* Initialize the VOSF system (mainBuffer structure, SIGSEGV handler)
|
* Initialize the VOSF system (mainBuffer structure, SIGSEGV handler)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#if !EMULATED_PPC && !POWERPC_ROM
|
|
||||||
static
|
|
||||||
#endif
|
|
||||||
bool Screen_fault_handler(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction);
|
|
||||||
|
|
||||||
static bool video_vosf_init(X11_MONITOR_INIT)
|
static bool video_vosf_init(X11_MONITOR_INIT)
|
||||||
{
|
{
|
||||||
VIDEO_MODE_INIT;
|
VIDEO_MODE_INIT;
|
||||||
@ -260,12 +255,6 @@ static bool video_vosf_init(X11_MONITOR_INIT)
|
|||||||
if (vm_protect((char *)mainBuffer.memStart, mainBuffer.memLength, VM_PAGE_READ) != 0)
|
if (vm_protect((char *)mainBuffer.memStart, mainBuffer.memLength, VM_PAGE_READ) != 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
#if !EMULATED_PPC && !POWERPC_ROM
|
|
||||||
// Initialize the handler for SIGSEGV
|
|
||||||
if (!sigsegv_install_handler(Screen_fault_handler))
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// The frame buffer is sane, i.e. there is no write to it yet
|
// The frame buffer is sane, i.e. there is no write to it yet
|
||||||
mainBuffer.dirty = false;
|
mainBuffer.dirty = false;
|
||||||
return true;
|
return true;
|
||||||
|
Loading…
Reference in New Issue
Block a user