From: Slava Pestov Date: Thu, 8 Apr 2010 17:32:14 +0000 (-0700) Subject: vm: implement frame-based SEH for 64-bit Windows X-Git-Tag: 0.97~4720^2~22^2~28 X-Git-Url: https://gitweb.factorcode.org/gitweb.cgi?p=factor.git;a=commitdiff_plain;h=e5e51c40a1917e5676062b37c4a9e63b6caf63d8 vm: implement frame-based SEH for 64-bit Windows --- diff --git a/Nmakefile b/Nmakefile index 02d2b5f1ed..b85197a776 100755 --- a/Nmakefile +++ b/Nmakefile @@ -11,6 +11,7 @@ ML_FLAGS = /nologo /safeseh EXE_OBJS = vm\main-windows-nt.obj vm\factor.res DLL_OBJS = vm\os-windows-nt.obj \ + vm\os-windows-nt-x86.32.obj \ vm\os-windows.obj \ vm\aging_collector.obj \ vm\alien.obj \ diff --git a/vm/Config.windows.nt.x86.32 b/vm/Config.windows.nt.x86.32 index d27629fe83..73bf064ce5 100644 --- a/vm/Config.windows.nt.x86.32 +++ b/vm/Config.windows.nt.x86.32 @@ -1,3 +1,4 @@ +PLAF_DLL_OBJS += vm/os-windows-nt-x86.32.o DLL_PATH=http://factorcode.org/dlls WINDRES=windres include vm/Config.windows.nt diff --git a/vm/Config.windows.nt.x86.64 b/vm/Config.windows.nt.x86.64 index ddb61480e5..495a3ccac9 100644 --- a/vm/Config.windows.nt.x86.64 +++ b/vm/Config.windows.nt.x86.64 @@ -1,3 +1,4 @@ +PLAF_DLL_OBJS += vm/os-windows-nt-x86.64.o DLL_PATH=http://factorcode.org/dlls/64 CC=$(WIN64_PATH)-gcc.exe WINDRES=$(WIN64_PATH)-windres.exe diff --git a/vm/code_heap.cpp b/vm/code_heap.cpp index 40fe00b0e9..96d9541665 100755 --- a/vm/code_heap.cpp +++ b/vm/code_heap.cpp @@ -7,8 +7,14 @@ code_heap::code_heap(cell size) { if(size > ((u64)1 << (sizeof(cell) * 8 - 6))) fatal_error("Heap too large",size); seg = new segment(align_page(size),true); - if(!seg) fatal_error("Out of memory in heap allocator",size); - allocator = new free_list_allocator(size,seg->start); + if(!seg) fatal_error("Out of memory in code_heap constructor",size); + + cell start = seg->start + seh_area_size; + + allocator = new free_list_allocator(seg->end - start,start); + + /* See os-windows-nt-x86.64.cpp for seh_area usage */ + seh_area = (char *)seg->start; } code_heap::~code_heap() diff --git a/vm/code_heap.hpp b/vm/code_heap.hpp index 78ffa6c76a..20ce03c835 100755 --- a/vm/code_heap.hpp +++ b/vm/code_heap.hpp @@ -1,10 +1,19 @@ namespace factor { +#if defined(WINDOWS) && defined(FACTOR_64) + const cell seh_area_size = 1024; +#else + const cell seh_area_size = 0; +#endif + struct code_heap { /* The actual memory area */ segment *seg; + /* Memory area reserved for SEH. Only used on Windows */ + char *seh_area; + /* Memory allocator */ free_list_allocator *allocator; diff --git a/vm/image.cpp b/vm/image.cpp index c74351c191..ccce96a952 100755 --- a/vm/image.cpp +++ b/vm/image.cpp @@ -258,7 +258,7 @@ void factor_vm::load_image(vm_parameters *p) init_objects(&h); cell data_offset = data->tenured->start - h.data_relocation_base; - cell code_offset = code->seg->start - h.code_relocation_base; + cell code_offset = code->allocator->start - h.code_relocation_base; fixup_data(data_offset,code_offset); fixup_code(data_offset,code_offset); @@ -285,7 +285,7 @@ bool factor_vm::save_image(const vm_char *saving_filename, const vm_char *filena h.version = image_version; h.data_relocation_base = data->tenured->start; h.data_size = data->tenured->occupied_space(); - h.code_relocation_base = code->seg->start; + h.code_relocation_base = code->allocator->start; h.code_size = code->allocator->occupied_space(); h.true_object = true_object; diff --git a/vm/os-windows-nt-x86.32.cpp b/vm/os-windows-nt-x86.32.cpp new file mode 100644 index 0000000000..61cf9f6c4e --- /dev/null +++ b/vm/os-windows-nt-x86.32.cpp @@ -0,0 +1,12 @@ +#include "master.hpp" + +namespace factor +{ + +void factor_vm::c_to_factor_toplevel(cell quot) +{ + /* 32-bit Windows SEH is set up in basis/cpu/x86/32/winnt/bootstrap.factor */ + c_to_factor(quot); +} + +} diff --git a/vm/os-windows-nt-x86.64.cpp b/vm/os-windows-nt-x86.64.cpp new file mode 100644 index 0000000000..876d0c5771 --- /dev/null +++ b/vm/os-windows-nt-x86.64.cpp @@ -0,0 +1,85 @@ +#include "master.hpp" + +namespace factor { + +typedef unsigned char UBYTE; + +const UBYTE UNW_FLAG_EHANDLER = 0x1; + +struct UNWIND_INFO { + UBYTE Version:3; + UBYTE Flags:5; + UBYTE SizeOfProlog; + UBYTE CountOfCodes; + UBYTE FrameRegister:4; + UBYTE FrameOffset:4; + ULONG ExceptionHandler; + ULONG ExceptionData[1]; +}; + +struct seh_data { + UNWIND_INFO unwind_info; + RUNTIME_FUNCTION func; + UBYTE handler[32]; +}; + +void factor_vm::c_to_factor_toplevel(cell quot) +{ + /* The annoying thing about Win64 SEH is that the offsets in + * function tables are 32-bit integers, and the exception handler + * itself must reside between the start and end pointers, so + * we stick everything at the beginning of the code heap and + * generate a small trampoline that jumps to the real + * exception handler. */ + + seh_data *seh_area = (seh_data *)code->seh_area; + cell base = code->seg->start; + + /* Should look at generating this with the Factor assembler */ + + /* mov rax,0 */ + seh_area->handler[0] = 0x48; + seh_area->handler[1] = 0xb8; + seh_area->handler[2] = 0x0; + seh_area->handler[3] = 0x0; + seh_area->handler[4] = 0x0; + seh_area->handler[5] = 0x0; + seh_area->handler[6] = 0x0; + seh_area->handler[7] = 0x0; + seh_area->handler[8] = 0x0; + seh_area->handler[9] = 0x0; + + /* jmp rax */ + seh_area->handler[10] = 0x48; + seh_area->handler[11] = 0xff; + seh_area->handler[12] = 0xe0; + + /* Store address of exception handler in the operand of the 'mov' */ + cell handler = (cell)&factor::exception_handler; + memcpy(&seh_area->handler[2],&handler,sizeof(cell)); + + UNWIND_INFO *unwind_info = &seh_area->unwind_info; + unwind_info->Version = 1; + unwind_info->Flags = UNW_FLAG_EHANDLER; + unwind_info->SizeOfProlog = 0; + unwind_info->CountOfCodes = 0; + unwind_info->FrameRegister = 0; + unwind_info->FrameOffset = 0; + unwind_info->ExceptionHandler = (DWORD)((cell)&seh_area->handler[0] - base); + unwind_info->ExceptionData[0] = 0; + + RUNTIME_FUNCTION *func = &seh_area->func; + func->BeginAddress = 0; + func->EndAddress = (DWORD)(code->seg->end - base); + func->UnwindData = (DWORD)((cell)&seh_area->unwind_info - base); + + if(!RtlAddFunctionTable(func,1,base)) + fatal_error("RtlAddFunctionTable() failed",0); + + c_to_factor(quot); + + if(!RtlDeleteFunctionTable(func)) + fatal_error("RtlDeleteFunctionTable() failed",0); +} + +} diff --git a/vm/os-windows-nt.cpp b/vm/os-windows-nt.cpp index 0d43cdecc2..4fea294a12 100755 --- a/vm/os-windows-nt.cpp +++ b/vm/os-windows-nt.cpp @@ -92,11 +92,6 @@ VM_C_API LONG exception_handler(PEXCEPTION_RECORD e, void *frame, PCONTEXT c, vo return current_vm()->exception_handler(e,frame,c,dispatch); } -void factor_vm::c_to_factor_toplevel(cell quot) -{ - c_to_factor(quot); -} - void factor_vm::open_console() { }