Windows CE SEH for ARM with GCC
Friday, April 27, 2007
Structure exception handling (SEH) is a low-level way to handle software
and hardware exceptions in Microsoft Windows. Other exception handling
mechanisms are built on top of SEH. Factor uses SEH in order to report
stack underflow/overflow errors on Windows. Microsoft Visual Studio (VS)
would usually take care of the tricky implementation details by
supplying __try
, __except
, and __finally
keywords, but Factor’s
compiler relies on globally assigning the datastack and retainstack to
specific registers, a feature which VS does not support. Conversely, GCC
does not support __try
, so we are left to implement the exception
handler in assembly.
The internal implementation is both OS and architecture dependent, meaning that we have to support SEH on Windows NT on x86, and Windows CE for x86 and ARM. Digging through MSDN leads to a page called SEH in RISC Environments, where RISC standing for “Reduced Instruction Set Computer”. (You are just supposed to know that ARM is RISC and x86 is CISC). I recommend reading about SEH on MSDN, but it basically says that SEH on RISC uses Virtual Unwinding and that you will need to set the PDATA structure for your function in order to set up the exception handler. Virtual unwinding traverses the framestack until it finds a frame with an exception handler, at which time it will actually unwind the framestack to that point and call the exception handler.
All we need to do is define void run_toplevel(void){...}
to install
our exception_handler
and call Factor’s “main” subroutine, run
.
vm/os-windows-ce.c
long exception_handler(PEXCEPTION_RECORD rec, void *frame, void *ctx, void *disp
atch)
{
memory_protection_error(
rec->ExceptionInformation[1] & 0x1ffffff, // mask off process slot
native_stack_pointer());
return -1; /* unreachable */
}
vm/os-windows-ce-arm.S
.text
.globl run_toplevel
.word exception_handler
.word 0
run_toplevel:
ldr pc, _Prun
_Prun: .word run
.section .pdata
.word run_toplevel
.word 0xc0000002 | (0xFFFFF << 8)
The C code passes the memory fault address, stored in
ExceptionInformation[1]
, and the native stack pointer (another
assembly function that simply moves ESP -> EAX) to a function that
converts the fault address into a Factor stack error message, e.g.
“Datastack underflow”. The fault address is actually a slotized address,
meaning that it is relative to some process slot which must be masked
off. Annoyingly, the exceptions happen at different address ranges on
the emulator and on a real mobile device. Slotized addresses in this
context are not well documented on MSDN, but the Windows CE Blog Team
quickly answered my email. (Thanks!)
The assembly code is mostly boilerplate. The .text directive starts
us in the executable code section. We’re going to be exporting
void run_toplevel(void){...}
in order to call it from C, so we declare
it as a .globl and start the definition. We simply call our “main”
function, void run(void){...}
, which is the platform-dependent run
function to start the Factor interpreter.
Hopefully, if someone else has to do this it will take much less time.