added back sigsegv handler

This commit is contained in:
Anthony Pesch 2015-09-26 21:26:54 -07:00
parent 99c19a05e9
commit ad3435a720
11 changed files with 487 additions and 0 deletions

View File

@ -140,11 +140,20 @@ set(DREAVM_SOURCES
src/renderer/gl_shader.cc
src/sys/files.cc
src/sys/keys.cc
src/sys/sigsegv_handler.cc
src/sys/system.cc
src/trace/trace.cc
src/trace/trace_viewer.cc
src/main.cc)
if(WIN32)
list(APPEND DREAVM_SOURCES src/sys/sigsegv_handler_win.cc)
elseif(APPLE)
list(APPEND DREAVM_SOURCES src/sys/sigsegv_handler_mac.cc)
else()
list(APPEND DREAVM_SOURCES src/sys/sigsegv_handler_linux.cc)
endif()
# assign source groups for visual studio projects
source_group_by_dir(DREAVM_SOURCES)

View File

@ -103,6 +103,10 @@ Dreamcast::~Dreamcast() {
}
bool Dreamcast::Init() {
if (!(sigsegv_ = SIGSEGVHandler::Install())) {
return false;
}
MapMemory();
if (!aica_->Init()) {

View File

@ -16,6 +16,7 @@
#include "jit/frontend/frontend.h"
#include "jit/runtime.h"
#include "renderer/backend.h"
#include "sys/sigsegv_handler.h"
#include "trace/trace.h"
namespace dreavm {
@ -120,6 +121,8 @@ class Dreamcast {
hw::holly::TextureCache *texcache() { return texcache_; }
hw::holly::TileRenderer *tile_renderer() { return tile_renderer_; }
sys::SIGSEGVHandler *sigsegv() { return sigsegv_; }
renderer::Backend *rb() { return rb_; }
void set_rb(renderer::Backend *rb) { rb_ = rb; }
@ -179,6 +182,7 @@ class Dreamcast {
hw::holly::TileRenderer *tile_renderer_;
// not owned by us
sys::SIGSEGVHandler *sigsegv_;
renderer::Backend *rb_;
trace::TraceWriter *trace_writer_;
};

View File

@ -0,0 +1,75 @@
#include "core/core.h"
#include "core/interval_tree.h"
#include "emu/profiler.h"
#include "sys/sigsegv_handler.h"
using namespace dreavm::sys;
SIGSEGVHandler *dreavm::sys::SIGSEGVHandler::global_handler_ = nullptr;
SIGSEGVHandler *SIGSEGVHandler::Install() {
if (global_handler_) {
return global_handler_;
}
global_handler_ = CreateSIGSEGVHandler();
if (!global_handler_->Init()) {
LOG_WARNING("Failed to install SIGSEGV handler");
delete global_handler_;
global_handler_ = nullptr;
}
return global_handler_;
}
SIGSEGVHandler::~SIGSEGVHandler() { global_handler_ = nullptr; }
void SIGSEGVHandler::AddWriteWatch(void *ptr, int size,
WriteWatchHandler handler, void *ctx,
void *data) {
int page_size = GetPageSize();
uintptr_t physical_start = dreavm::align(reinterpret_cast<uintptr_t>(ptr),
static_cast<uintptr_t>(page_size));
uintptr_t physical_end =
dreavm::align(reinterpret_cast<uintptr_t>(ptr) + size,
static_cast<uintptr_t>(page_size));
// write protect the pages
CHECK(Protect(reinterpret_cast<void *>(physical_start),
static_cast<int>(physical_end - physical_start), ACC_READONLY));
write_watches_.Insert(
physical_start, physical_end - 1,
WriteWatch(handler, ctx, data, physical_start, physical_end));
// track number of watches
PROFILER_COUNT("WriteWatches", write_watches_.Size());
}
bool SIGSEGVHandler::HandleAccessFault(uintptr_t rip, uintptr_t fault_addr) {
WatchTree::node_type *node = write_watches_.Find(fault_addr, fault_addr);
bool handled = node != nullptr;
while (node) {
WriteWatch &watch = node->value;
watch.handler(watch.ctx, watch.data);
// remove write protection
CHECK(Protect(reinterpret_cast<void *>(watch.physical_start),
static_cast<int>(watch.physical_end - watch.physical_start),
ACC_READWRITE));
write_watches_.Remove(node);
node = write_watches_.Find(fault_addr, fault_addr);
}
// track number of watches
PROFILER_COUNT("WriteWatches", write_watches_.Size());
return handled;
}

61
src/sys/sigsegv_handler.h Normal file
View File

@ -0,0 +1,61 @@
#ifndef SIGSEGV_HANDLER_H
#define SIGSEGV_HANDLER_H
#include <functional>
#include <memory>
#include "core/interval_tree.h"
namespace dreavm {
namespace sys {
// implemented in the platform specific souce file
class SIGSEGVHandler;
extern SIGSEGVHandler *CreateSIGSEGVHandler();
enum PageAccess { ACC_READONLY, ACC_READWRITE };
typedef std::function<void(void *, void *)> WriteWatchHandler;
struct WriteWatch {
WriteWatch(WriteWatchHandler handler, void *ctx, void *data,
uintptr_t physical_start, uintptr_t physical_end)
: handler(handler),
ctx(ctx),
data(data),
physical_start(physical_start),
physical_end(physical_end) {}
WriteWatchHandler handler;
void *ctx;
void *data;
uintptr_t physical_start;
uintptr_t physical_end;
};
typedef IntervalTree<WriteWatch> WatchTree;
class SIGSEGVHandler {
public:
static SIGSEGVHandler *global_handler() { return global_handler_; }
static SIGSEGVHandler *Install();
virtual ~SIGSEGVHandler();
void AddWriteWatch(void *ptr, int size, WriteWatchHandler handler, void *ctx,
void *data);
bool HandleAccessFault(uintptr_t rip, uintptr_t fault_addr);
protected:
static SIGSEGVHandler *global_handler_;
virtual bool Init() = 0;
virtual int GetPageSize() = 0;
virtual bool Protect(void *ptr, int size, PageAccess access) = 0;
WatchTree write_watches_;
};
}
}
#endif

View File

@ -0,0 +1,61 @@
#include <sys/mman.h>
#include <signal.h>
#include <unistd.h>
#include "core/core.h"
#include "sys/sigsegv_handler_linux.h"
using namespace dreavm::sys;
SIGSEGVHandler *dreavm::sys::CreateSIGSEGVHandler() {
return new SIGSEGVHandlerLinux();
}
static struct sigaction old_sa;
static void SignalHandler(int signo, siginfo_t *info, void *ctx) {
ucontext_t *uctx = reinterpret_cast<ucontext_t *>(ctx);
uintptr_t rip = uctx->uc_mcontext.gregs[REG_RIP];
uintptr_t fault_addr = reinterpret_cast<uintptr_t>(info->si_addr);
bool handled =
SIGSEGVHandler::global_handler()->HandleAccessFault(rip, fault_addr);
if (!handled) {
// call into the original handler if the installed handler fails to handle
// the signal
(*old_sa.sa_sigaction)(signo, info, ctx);
}
}
SIGSEGVHandlerLinux::~SIGSEGVHandlerLinux() {
sigaction(SIGSEGV, &old_sa, nullptr);
}
bool SIGSEGVHandlerLinux::Init() {
struct sigaction new_sa;
new_sa.sa_flags = SA_SIGINFO;
sigemptyset(&new_sa.sa_mask);
new_sa.sa_sigaction = &SignalHandler;
if (sigaction(SIGSEGV, &new_sa, &old_sa) != 0) {
return false;
}
return true;
}
int SIGSEGVHandlerLinux::GetPageSize() { return getpagesize(); }
bool SIGSEGVHandlerLinux::Protect(void *ptr, int size, PageAccess access) {
int prot = PROT_NONE;
switch (access) {
case ACC_READONLY:
prot = PROT_READ;
break;
case ACC_READWRITE:
prot = PROT_READ | PROT_WRITE;
break;
}
return mprotect(ptr, size, prot) == 0;
}

View File

@ -0,0 +1,24 @@
#ifndef SIGSEGV_HANDLER_LINUX
#define SIGSEGV_HANDLER_LINUX
#include <thread>
#include "sys/sigsegv_handler.h"
namespace dreavm {
namespace sys {
class SIGSEGVHandlerLinux : public SIGSEGVHandler {
public:
~SIGSEGVHandlerLinux();
protected:
bool Init();
int GetPageSize();
bool Protect(void *ptr, int size, PageAccess access);
private:
};
}
}
#endif

View File

@ -0,0 +1,141 @@
#include <mach/mach.h>
#include <sys/mman.h>
#include <unistd.h>
#include "core/core.h"
#include "sys/sigsegv_handler_mac.h"
using namespace dreavm::sys;
SIGSEGVHandler *dreavm::sys::CreateSIGSEGVHandler() {
return new SIGSEGVHandlerMac();
}
// http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/exc_server.html
extern "C" boolean_t exc_server(mach_msg_header_t *request_msg,
mach_msg_header_t *reply_msg);
// http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/catch_exception_raise.html
extern "C" kern_return_t catch_exception_raise(
mach_port_t exception_port, mach_port_t thread, mach_port_t task,
exception_type_t exception, exception_data_t code,
mach_msg_type_number_t code_count) {
// get exception state
mach_msg_type_number_t state_count = x86_EXCEPTION_STATE64_COUNT;
x86_exception_state64_t exc_state;
if (thread_get_state(thread, x86_EXCEPTION_STATE64,
reinterpret_cast<thread_state_t>(&exc_state),
&state_count) != KERN_SUCCESS) {
return KERN_FAILURE;
}
// get thread state
state_count = x86_THREAD_STATE64_COUNT;
x86_thread_state64_t thread_state;
if (thread_get_state(thread, x86_THREAD_STATE64,
reinterpret_cast<thread_state_t>(&thread_state),
&state_count) != KERN_SUCCESS) {
return KERN_FAILURE;
}
uintptr_t rip = thread_state.__rip;
uintptr_t fault_addr = exc_state.__faultvaddr;
bool handled =
SIGSEGVHandler::global_handler()->HandleAccessFault(rip, fault_addr);
if (!handled) {
return KERN_FAILURE;
}
// reset thread state
if (thread_set_state(thread, x86_THREAD_STATE64,
reinterpret_cast<thread_state_t>(&thread_state),
state_count) != KERN_SUCCESS) {
return KERN_FAILURE;
}
return KERN_SUCCESS;
}
SIGSEGVHandlerMac::~SIGSEGVHandlerMac() {
task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS, 0,
EXCEPTION_DEFAULT, 0);
mach_port_deallocate(mach_task_self(), listen_port_);
}
bool SIGSEGVHandlerMac::Init() {
// allocate port to listen for exceptions
if (mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE,
&listen_port_) != KERN_SUCCESS) {
return false;
}
// http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/mach_port_insert_right.html
if (mach_port_insert_right(mach_task_self(), listen_port_, listen_port_,
MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS) {
return false;
}
// filter out any exception other than EXC_BAD_ACCESS
// http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/task_set_exception_ports.html
if (task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS,
listen_port_, EXCEPTION_DEFAULT,
MACHINE_THREAD_STATE) != KERN_SUCCESS) {
return false;
}
// launch thread to listen for exceptions
thread_ = std::thread([this] { ThreadEntry(); });
thread_.detach();
return true;
}
int SIGSEGVHandlerMac::GetPageSize() { return getpagesize(); }
bool SIGSEGVHandlerMac::Protect(void *ptr, int size, PageAccess access) {
int prot = PROT_NONE;
switch (access) {
case ACC_READONLY:
prot = PROT_READ;
break;
case ACC_READWRITE:
prot = PROT_READ | PROT_WRITE;
break;
}
return mprotect(ptr, size, prot) == 0;
}
void SIGSEGVHandlerMac::ThreadEntry() {
while (true) {
struct {
mach_msg_header_t head;
mach_msg_body_t msgh_body;
char data[1024];
} msg;
struct {
mach_msg_header_t head;
char data[1024];
} reply;
// wait for a message on the exception port
mach_msg_return_t ret =
mach_msg(&msg.head, MACH_RCV_MSG | MACH_RCV_LARGE, 0, sizeof(msg),
listen_port_, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
if (ret != MACH_MSG_SUCCESS) {
LOG_INFO("mach_msg receive failed with %d %s", ret,
mach_error_string(ret));
break;
}
// call exc_server, which will call back into catch_exception_raise
exc_server(&msg.head, &reply.head);
// send the reply
ret = mach_msg(&reply.head, MACH_SEND_MSG, reply.head.msgh_size, 0,
MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
if (ret != MACH_MSG_SUCCESS) {
LOG_INFO("mach_msg send failed with %d %s", ret, mach_error_string(ret));
break;
}
}
}

View File

@ -0,0 +1,28 @@
#ifndef SIGSEGV_HANDLER_MAC
#define SIGSEGV_HANDLER_MAC
#include <thread>
#include "sys/sigsegv_handler.h"
namespace dreavm {
namespace sys {
class SIGSEGVHandlerMac : public SIGSEGVHandler {
public:
~SIGSEGVHandlerMac();
protected:
bool Init();
int GetPageSize();
bool Protect(void *ptr, int size, PageAccess access);
private:
void ThreadEntry();
mach_port_t listen_port_;
std::thread thread_;
};
}
}
#endif

View File

@ -0,0 +1,56 @@
#include <windows.h>
#include "core/core.h"
#include "sys/sigsegv_handler_win.h"
using namespace dreavm::sys;
SIGSEGVHandler *dreavm::sys::CreateSIGSEGVHandler() {
return new SIGSEGVHandlerWin();
}
static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ex_info) {
auto code = ex_info->ExceptionRecord->ExceptionCode;
if (code != STATUS_ACCESS_VIOLATION) {
return EXCEPTION_CONTINUE_SEARCH;
}
uintptr_t rip = ex_info->ContextRecord->Rip;
uintptr_t fault_addr = ex_info->ExceptionRecord->ExceptionInformation[1];
bool handled =
SIGSEGVHandler::global_handler()->HandleAccessFault(rip, fault_addr);
if (!handled) {
return EXCEPTION_CONTINUE_SEARCH;
}
return EXCEPTION_CONTINUE_EXECUTION;
}
SIGSEGVHandlerWin::~SIGSEGVHandlerWin() {
RemoveVectoredExceptionHandler(ExceptionHandler);
}
bool SIGSEGVHandlerWin::Init() {
return AddVectoredExceptionHandler(1, ExceptionHandler) != nullptr;
}
int SIGSEGVHandlerWin::GetPageSize() {
SYSTEM_INFO system_info;
GetSystemInfo(&system_info);
return system_info.dwPageSize;
}
bool SIGSEGVHandlerWin::Protect(void *ptr, int size, PageAccess access) {
DWORD new_protect = PAGE_NOACCESS;
DWORD old_protect;
switch (access) {
case ACC_READONLY:
new_protect = PAGE_READONLY;
break;
case ACC_READWRITE:
new_protect = PAGE_READWRITE;
break;
}
return VirtualProtect(ptr, size, new_protect, &old_protect);
}

View File

@ -0,0 +1,24 @@
#ifndef SIGSEGV_HANDLER_WIN
#define SIGSEGV_HANDLER_WIN
#include <thread>
#include "sys/sigsegv_handler.h"
namespace dreavm {
namespace sys {
class SIGSEGVHandlerWin : public SIGSEGVHandler {
public:
~SIGSEGVHandlerWin();
protected:
bool Init();
int GetPageSize();
bool Protect(void *ptr, int size, PageAccess access);
private:
};
}
}
#endif