vm: signal handling cleanup
parent
88d0793946
commit
11ddbc03a4
|
@ -13,7 +13,7 @@ char *factor_vm::pinned_alien_offset(cell obj)
|
|||
{
|
||||
alien *ptr = untag<alien>(obj);
|
||||
if(to_boolean(ptr->expired))
|
||||
general_error(ERROR_EXPIRED,obj,false_object,NULL);
|
||||
general_error(ERROR_EXPIRED,obj,false_object);
|
||||
if(to_boolean(ptr->base))
|
||||
type_error(ALIEN_TYPE,obj);
|
||||
else
|
||||
|
|
|
@ -18,11 +18,17 @@ callstack *factor_vm::allot_callstack(cell size)
|
|||
return stack;
|
||||
}
|
||||
|
||||
stack_frame *factor_vm::fix_callstack_top(stack_frame *top, stack_frame *bottom)
|
||||
/* If 'stack' points into the middle of the frame, find the nearest valid stack
|
||||
pointer where we can resume execution and hope to capture the call trace without
|
||||
crashing. Also, make sure we have at least 'stack_reserved' bytes available so
|
||||
that we don't run out of callstack space while handling the error. */
|
||||
stack_frame *factor_vm::fix_callstack_top(stack_frame *stack)
|
||||
{
|
||||
stack_frame *frame = bottom - 1;
|
||||
stack_frame *frame = ctx->callstack_bottom - 1;
|
||||
|
||||
while(frame >= top)
|
||||
while(frame >= stack
|
||||
&& frame >= ctx->callstack_top
|
||||
&& (cell)frame >= ctx->callstack_seg->start + stack_reserved)
|
||||
frame = frame_successor(frame);
|
||||
|
||||
return frame + 1;
|
||||
|
|
|
@ -144,7 +144,7 @@ void factor_vm::update_word_references(code_block *compiled, bool reset_inline_c
|
|||
image load */
|
||||
void factor_vm::undefined_symbol()
|
||||
{
|
||||
general_error(ERROR_UNDEFINED_SYMBOL,false_object,false_object,NULL);
|
||||
general_error(ERROR_UNDEFINED_SYMBOL,false_object,false_object);
|
||||
}
|
||||
|
||||
void undefined_symbol()
|
||||
|
|
|
@ -44,6 +44,17 @@ void context::reset()
|
|||
reset_context_objects();
|
||||
}
|
||||
|
||||
void context::fix_stacks()
|
||||
{
|
||||
if(datastack + sizeof(cell) < datastack_seg->start
|
||||
|| datastack + stack_reserved >= datastack_seg->end)
|
||||
reset_datastack();
|
||||
|
||||
if(retainstack + sizeof(cell) < retainstack_seg->start
|
||||
|| retainstack + stack_reserved >= retainstack_seg->end)
|
||||
reset_retainstack();
|
||||
}
|
||||
|
||||
context::~context()
|
||||
{
|
||||
delete datastack_seg;
|
||||
|
@ -167,13 +178,13 @@ bool factor_vm::stack_to_array(cell bottom, cell top)
|
|||
void factor_vm::primitive_datastack()
|
||||
{
|
||||
if(!stack_to_array(ctx->datastack_seg->start,ctx->datastack))
|
||||
general_error(ERROR_DS_UNDERFLOW,false_object,false_object,NULL);
|
||||
general_error(ERROR_DATASTACK_UNDERFLOW,false_object,false_object);
|
||||
}
|
||||
|
||||
void factor_vm::primitive_retainstack()
|
||||
{
|
||||
if(!stack_to_array(ctx->retainstack_seg->start,ctx->retainstack))
|
||||
general_error(ERROR_RS_UNDERFLOW,false_object,false_object,NULL);
|
||||
general_error(ERROR_RETAINSTACK_UNDERFLOW,false_object,false_object);
|
||||
}
|
||||
|
||||
/* returns pointer to top of stack */
|
||||
|
|
|
@ -8,6 +8,8 @@ enum context_object {
|
|||
OBJ_CATCHSTACK,
|
||||
};
|
||||
|
||||
static const cell stack_reserved = 1024;
|
||||
|
||||
struct context {
|
||||
|
||||
// First 4 fields accessed directly by compiler. See basis/vm/vm.factor
|
||||
|
@ -41,6 +43,7 @@ struct context {
|
|||
void reset_callstack();
|
||||
void reset_context_objects();
|
||||
void reset();
|
||||
void fix_stacks();
|
||||
|
||||
cell peek()
|
||||
{
|
||||
|
@ -64,19 +67,6 @@ struct context {
|
|||
datastack += sizeof(cell);
|
||||
replace(tagged);
|
||||
}
|
||||
|
||||
static const cell stack_reserved = (64 * sizeof(cell));
|
||||
|
||||
void fix_stacks()
|
||||
{
|
||||
if(datastack + sizeof(cell) < datastack_seg->start
|
||||
|| datastack + stack_reserved >= datastack_seg->end)
|
||||
reset_datastack();
|
||||
|
||||
if(retainstack + sizeof(cell) < retainstack_seg->start
|
||||
|| retainstack + stack_reserved >= retainstack_seg->end)
|
||||
reset_retainstack();
|
||||
}
|
||||
};
|
||||
|
||||
VM_C_API context *new_context(factor_vm *parent);
|
||||
|
|
|
@ -27,8 +27,10 @@ void out_of_memory()
|
|||
exit(1);
|
||||
}
|
||||
|
||||
void factor_vm::throw_error(cell error, stack_frame *callstack_top)
|
||||
void factor_vm::throw_error(cell error, stack_frame *stack)
|
||||
{
|
||||
assert(stack);
|
||||
|
||||
/* If the error handler is set, we rewind any C stack frames and
|
||||
pass the error to user-space. */
|
||||
if(!current_gc && to_boolean(special_objects[ERROR_HANDLER_QUOT]))
|
||||
|
@ -41,22 +43,13 @@ void factor_vm::throw_error(cell error, stack_frame *callstack_top)
|
|||
bignum_roots.clear();
|
||||
code_roots.clear();
|
||||
|
||||
/* If we had an underflow or overflow, stack pointers might be
|
||||
out of bounds */
|
||||
/* If we had an underflow or overflow, data or retain stack
|
||||
pointers might be out of bounds */
|
||||
ctx->fix_stacks();
|
||||
|
||||
ctx->push(error);
|
||||
|
||||
/* Errors thrown from C code pass NULL for this parameter.
|
||||
Errors thrown from Factor code, or signal handlers, pass the
|
||||
actual stack pointer at the time, since the saved pointer is
|
||||
not necessarily up to date at that point. */
|
||||
if(callstack_top)
|
||||
callstack_top = fix_callstack_top(callstack_top,ctx->callstack_bottom);
|
||||
else
|
||||
callstack_top = ctx->callstack_top;
|
||||
|
||||
unwind_native_frames(special_objects[ERROR_HANDLER_QUOT],callstack_top);
|
||||
unwind_native_frames(special_objects[ERROR_HANDLER_QUOT],stack);
|
||||
}
|
||||
/* Error was thrown in early startup before error handler is set, just
|
||||
crash. */
|
||||
|
@ -70,62 +63,55 @@ void factor_vm::throw_error(cell error, stack_frame *callstack_top)
|
|||
}
|
||||
}
|
||||
|
||||
void factor_vm::general_error(vm_error_type error, cell arg1, cell arg2, stack_frame *callstack_top)
|
||||
void factor_vm::general_error(vm_error_type error, cell arg1, cell arg2, stack_frame *stack)
|
||||
{
|
||||
throw_error(allot_array_4(special_objects[OBJ_ERROR],
|
||||
tag_fixnum(error),arg1,arg2),callstack_top);
|
||||
tag_fixnum(error),arg1,arg2),stack);
|
||||
}
|
||||
|
||||
void factor_vm::general_error(vm_error_type error, cell arg1, cell arg2)
|
||||
{
|
||||
throw_error(allot_array_4(special_objects[OBJ_ERROR],
|
||||
tag_fixnum(error),arg1,arg2),ctx->callstack_top);
|
||||
}
|
||||
|
||||
void factor_vm::type_error(cell type, cell tagged)
|
||||
{
|
||||
general_error(ERROR_TYPE,tag_fixnum(type),tagged,NULL);
|
||||
general_error(ERROR_TYPE,tag_fixnum(type),tagged);
|
||||
}
|
||||
|
||||
void factor_vm::not_implemented_error()
|
||||
{
|
||||
general_error(ERROR_NOT_IMPLEMENTED,false_object,false_object,NULL);
|
||||
general_error(ERROR_NOT_IMPLEMENTED,false_object,false_object);
|
||||
}
|
||||
|
||||
/* Test if 'fault' is in the guard page at the top or bottom (depending on
|
||||
offset being 0 or -1) of area+area_size */
|
||||
bool factor_vm::in_page(cell fault, cell area, cell area_size, int offset)
|
||||
void factor_vm::memory_protection_error(cell addr, stack_frame *stack)
|
||||
{
|
||||
int pagesize = getpagesize();
|
||||
area += area_size;
|
||||
area += offset * pagesize;
|
||||
|
||||
return fault >= area && fault <= area + pagesize;
|
||||
}
|
||||
|
||||
void factor_vm::memory_protection_error(cell addr, stack_frame *native_stack)
|
||||
{
|
||||
if(in_page(addr, ctx->datastack_seg->start, 0, -1))
|
||||
general_error(ERROR_DS_UNDERFLOW,false_object,false_object,native_stack);
|
||||
else if(in_page(addr, ctx->datastack_seg->start, datastack_size, 0))
|
||||
general_error(ERROR_DS_OVERFLOW,false_object,false_object,native_stack);
|
||||
else if(in_page(addr, ctx->retainstack_seg->start, 0, -1))
|
||||
general_error(ERROR_RS_UNDERFLOW,false_object,false_object,native_stack);
|
||||
else if(in_page(addr, ctx->retainstack_seg->start, retainstack_size, 0))
|
||||
general_error(ERROR_RS_OVERFLOW,false_object,false_object,native_stack);
|
||||
else if(in_page(addr, nursery.end, 0, 0))
|
||||
critical_error("allot_object() missed GC check",0);
|
||||
if(ctx->datastack_seg->underflow_p(addr))
|
||||
general_error(ERROR_DATASTACK_UNDERFLOW,false_object,false_object,stack);
|
||||
else if(ctx->datastack_seg->overflow_p(addr))
|
||||
general_error(ERROR_DATASTACK_OVERFLOW,false_object,false_object,stack);
|
||||
else if(ctx->retainstack_seg->underflow_p(addr))
|
||||
general_error(ERROR_RETAINSTACK_UNDERFLOW,false_object,false_object,stack);
|
||||
else if(ctx->retainstack_seg->overflow_p(addr))
|
||||
general_error(ERROR_RETAINSTACK_OVERFLOW,false_object,false_object,stack);
|
||||
else
|
||||
general_error(ERROR_MEMORY,allot_cell(addr),false_object,native_stack);
|
||||
general_error(ERROR_MEMORY,allot_cell(addr),false_object,stack);
|
||||
}
|
||||
|
||||
void factor_vm::signal_error(cell signal, stack_frame *native_stack)
|
||||
void factor_vm::signal_error(cell signal, stack_frame *stack)
|
||||
{
|
||||
general_error(ERROR_SIGNAL,allot_cell(signal),false_object,native_stack);
|
||||
general_error(ERROR_SIGNAL,allot_cell(signal),false_object,stack);
|
||||
}
|
||||
|
||||
void factor_vm::divide_by_zero_error()
|
||||
{
|
||||
general_error(ERROR_DIVIDE_BY_ZERO,false_object,false_object,NULL);
|
||||
general_error(ERROR_DIVIDE_BY_ZERO,false_object,false_object);
|
||||
}
|
||||
|
||||
void factor_vm::fp_trap_error(unsigned int fpu_status, stack_frame *signal_callstack_top)
|
||||
void factor_vm::fp_trap_error(unsigned int fpu_status, stack_frame *stack)
|
||||
{
|
||||
general_error(ERROR_FP_TRAP,tag_fixnum(fpu_status),false_object,signal_callstack_top);
|
||||
general_error(ERROR_FP_TRAP,tag_fixnum(fpu_status),false_object,stack);
|
||||
}
|
||||
|
||||
void factor_vm::primitive_call_clear()
|
||||
|
|
|
@ -14,10 +14,10 @@ enum vm_error_type
|
|||
ERROR_C_STRING,
|
||||
ERROR_FFI,
|
||||
ERROR_UNDEFINED_SYMBOL,
|
||||
ERROR_DS_UNDERFLOW,
|
||||
ERROR_DS_OVERFLOW,
|
||||
ERROR_RS_UNDERFLOW,
|
||||
ERROR_RS_OVERFLOW,
|
||||
ERROR_DATASTACK_UNDERFLOW,
|
||||
ERROR_DATASTACK_OVERFLOW,
|
||||
ERROR_RETAINSTACK_UNDERFLOW,
|
||||
ERROR_RETAINSTACK_OVERFLOW,
|
||||
ERROR_MEMORY,
|
||||
ERROR_FP_TRAP,
|
||||
};
|
||||
|
|
|
@ -28,7 +28,7 @@ void factor_vm::io_error()
|
|||
return;
|
||||
#endif
|
||||
|
||||
general_error(ERROR_IO,tag_fixnum(errno),false_object,NULL);
|
||||
general_error(ERROR_IO,tag_fixnum(errno),false_object);
|
||||
}
|
||||
|
||||
FILE *factor_vm::safe_fopen(char *filename, char *mode)
|
||||
|
|
|
@ -35,19 +35,9 @@ void factor_vm::call_fault_handler(
|
|||
MACH_THREAD_STATE_TYPE *thread_state,
|
||||
MACH_FLOAT_STATE_TYPE *float_state)
|
||||
{
|
||||
/* There is a race condition here, but in practice an exception
|
||||
delivered during stack frame setup/teardown or while transitioning
|
||||
from Factor to C is a sign of things seriously gone wrong, not just
|
||||
a divide by zero or stack underflow in the listener */
|
||||
MACH_STACK_POINTER(thread_state) = (cell)fix_callstack_top((stack_frame *)MACH_STACK_POINTER(thread_state));
|
||||
|
||||
/* Are we in compiled Factor code? Then use the current stack pointer */
|
||||
if(in_code_heap_p(MACH_PROGRAM_COUNTER(thread_state)))
|
||||
signal_callstack_top = (stack_frame *)MACH_STACK_POINTER(thread_state);
|
||||
/* Are we in C? Then use the saved callstack top */
|
||||
else
|
||||
signal_callstack_top = NULL;
|
||||
|
||||
MACH_STACK_POINTER(thread_state) = align_stack_pointer(MACH_STACK_POINTER(thread_state));
|
||||
|
||||
/* Now we point the program counter at the right handler function. */
|
||||
if(exception == EXC_BAD_ACCESS)
|
||||
|
|
|
@ -246,7 +246,7 @@ cell factor_vm::unbox_array_size_slow()
|
|||
}
|
||||
}
|
||||
|
||||
general_error(ERROR_ARRAY_SIZE,ctx->pop(),tag_fixnum(array_size_max),NULL);
|
||||
general_error(ERROR_ARRAY_SIZE,ctx->pop(),tag_fixnum(array_size_max));
|
||||
return 0; /* can't happen */
|
||||
}
|
||||
|
||||
|
|
|
@ -10,9 +10,4 @@ void early_init();
|
|||
const char *vm_executable_path();
|
||||
const char *default_image_path();
|
||||
|
||||
template<typename Type> Type align_stack_pointer(Type sp)
|
||||
{
|
||||
return sp;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -62,11 +62,6 @@ inline static unsigned int uap_fpu_status(void *uap)
|
|||
return mach_fpu_status(UAP_FS(uap));
|
||||
}
|
||||
|
||||
template<typename Type> Type align_stack_pointer(Type sp)
|
||||
{
|
||||
return sp;
|
||||
}
|
||||
|
||||
inline static void mach_clear_fpu_status(ppc_float_state_t *float_state)
|
||||
{
|
||||
FPSCR(float_state) &= 0x0007f8ff;
|
||||
|
|
|
@ -64,11 +64,6 @@ inline static unsigned int uap_fpu_status(void *uap)
|
|||
return mach_fpu_status(UAP_FS(uap));
|
||||
}
|
||||
|
||||
template<typename Type> Type align_stack_pointer(Type sp)
|
||||
{
|
||||
return (Type)((((cell)sp + 4) & ~15) - 4);
|
||||
}
|
||||
|
||||
inline static void mach_clear_fpu_status(i386_float_state_t *float_state)
|
||||
{
|
||||
MXCSR(float_state) &= 0xffffffc0;
|
||||
|
|
|
@ -62,11 +62,6 @@ inline static unsigned int uap_fpu_status(void *uap)
|
|||
return mach_fpu_status(UAP_FS(uap));
|
||||
}
|
||||
|
||||
template<typename Type> Type align_stack_pointer(Type sp)
|
||||
{
|
||||
return (Type)((((cell)sp + 8) & ~15) - 8);
|
||||
}
|
||||
|
||||
inline static void mach_clear_fpu_status(x86_float_state64_t *float_state)
|
||||
{
|
||||
MXCSR(float_state) &= 0xffffffc0;
|
||||
|
|
|
@ -85,7 +85,7 @@ void *factor_vm::ffi_dlsym(dll *dll, symbol_char *symbol)
|
|||
void factor_vm::ffi_dlclose(dll *dll)
|
||||
{
|
||||
if(dlclose(dll->handle))
|
||||
general_error(ERROR_FFI,false_object,false_object,NULL);
|
||||
general_error(ERROR_FFI,false_object,false_object);
|
||||
dll->handle = NULL;
|
||||
}
|
||||
|
||||
|
@ -103,7 +103,7 @@ void factor_vm::move_file(const vm_char *path1, const vm_char *path2)
|
|||
ret = rename((path1),(path2));
|
||||
} while(ret < 0 && errno == EINTR);
|
||||
if(ret < 0)
|
||||
general_error(ERROR_IO,tag_fixnum(errno),false_object,NULL);
|
||||
general_error(ERROR_IO,tag_fixnum(errno),false_object);
|
||||
}
|
||||
|
||||
segment::segment(cell size_, bool executable_p)
|
||||
|
@ -141,16 +141,7 @@ segment::~segment()
|
|||
|
||||
void factor_vm::dispatch_signal(void *uap, void (handler)())
|
||||
{
|
||||
if(in_code_heap_p(UAP_PROGRAM_COUNTER(uap)))
|
||||
{
|
||||
stack_frame *ptr = (stack_frame *)UAP_STACK_POINTER(uap);
|
||||
assert(ptr);
|
||||
signal_callstack_top = ptr;
|
||||
}
|
||||
else
|
||||
signal_callstack_top = NULL;
|
||||
|
||||
UAP_STACK_POINTER(uap) = align_stack_pointer(UAP_STACK_POINTER(uap));
|
||||
UAP_STACK_POINTER(uap) = fix_callstack_top((stack_frame *)UAP_STACK_POINTER(uap));
|
||||
UAP_PROGRAM_COUNTER(uap) = (cell)handler;
|
||||
}
|
||||
|
||||
|
|
|
@ -74,6 +74,8 @@ LONG factor_vm::exception_handler(PEXCEPTION_POINTERS pe)
|
|||
PEXCEPTION_RECORD e = (PEXCEPTION_RECORD)pe->ExceptionRecord;
|
||||
CONTEXT *c = (CONTEXT*)pe->ContextRecord;
|
||||
|
||||
c->ESP = (cell)fix_callstack_top((stack_frame *)c->ESP);
|
||||
|
||||
if(in_code_heap_p(c->EIP))
|
||||
signal_callstack_top = (stack_frame *)c->ESP;
|
||||
else
|
||||
|
|
|
@ -140,7 +140,7 @@ long getpagesize()
|
|||
void factor_vm::move_file(const vm_char *path1, const vm_char *path2)
|
||||
{
|
||||
if(MoveFileEx((path1),(path2),MOVEFILE_REPLACE_EXISTING) == false)
|
||||
general_error(ERROR_IO,tag_fixnum(GetLastError()),false_object,NULL);
|
||||
general_error(ERROR_IO,tag_fixnum(GetLastError()),false_object);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,6 +15,16 @@ struct segment {
|
|||
|
||||
explicit segment(cell size, bool executable_p);
|
||||
~segment();
|
||||
|
||||
bool underflow_p(cell addr)
|
||||
{
|
||||
return (addr >= start - getpagesize() && addr < start);
|
||||
}
|
||||
|
||||
bool overflow_p(cell addr)
|
||||
{
|
||||
return (addr >= end && addr < end + getpagesize());
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
16
vm/vm.hpp
16
vm/vm.hpp
|
@ -160,20 +160,20 @@ struct factor_vm
|
|||
void primitive_profiling();
|
||||
|
||||
// errors
|
||||
void throw_error(cell error, stack_frame *callstack_top);
|
||||
void throw_error(cell error, stack_frame *stack);
|
||||
void general_error(vm_error_type error, cell arg1, cell arg2, stack_frame *stack);
|
||||
void general_error(vm_error_type error, cell arg1, cell arg2);
|
||||
void type_error(cell type, cell tagged);
|
||||
void not_implemented_error();
|
||||
bool in_page(cell fault, cell area, cell area_size, int offset);
|
||||
void memory_protection_error(cell addr, stack_frame *native_stack);
|
||||
void signal_error(cell signal, stack_frame *native_stack);
|
||||
void memory_protection_error(cell addr, stack_frame *stack);
|
||||
void signal_error(cell signal, stack_frame *stack);
|
||||
void divide_by_zero_error();
|
||||
void fp_trap_error(unsigned int fpu_status, stack_frame *signal_callstack_top);
|
||||
void fp_trap_error(unsigned int fpu_status, stack_frame *stack);
|
||||
void primitive_call_clear();
|
||||
void primitive_unimplemented();
|
||||
void memory_signal_handler_impl();
|
||||
void misc_signal_handler_impl();
|
||||
void fp_signal_handler_impl();
|
||||
void type_error(cell type, cell tagged);
|
||||
void general_error(vm_error_type error, cell arg1, cell arg2, stack_frame *native_stack);
|
||||
|
||||
// bignum
|
||||
int bignum_equal_p(bignum * x, bignum * y);
|
||||
|
@ -582,7 +582,7 @@ struct factor_vm
|
|||
template<typename Iterator> void iterate_callstack_object(callstack *stack_, Iterator &iterator);
|
||||
void check_frame(stack_frame *frame);
|
||||
callstack *allot_callstack(cell size);
|
||||
stack_frame *fix_callstack_top(stack_frame *top, stack_frame *bottom);
|
||||
stack_frame *fix_callstack_top(stack_frame *top);
|
||||
stack_frame *second_from_top_stack_frame();
|
||||
void primitive_callstack();
|
||||
code_block *frame_code(stack_frame *frame);
|
||||
|
|
Loading…
Reference in New Issue