Hi all,
Here goes the v2 of the seh giga macros. Wear sunglasses.
* __finally blocks don't eat the exception anymore.
* The filter in __except (filter), is now really an
expression, thanks to gcc's preprocessor varargs (__VA_ARGS__).
* Nested blocks work for the testcase included. I didn't
test anything else but what is there.
* The __seh_restore_context function is about my second or third
attempt to write ARM assembly. I've written about 20 or so lines
of ARM asm ever. I'm not even sure it is doing what it is
attempting to do. I'd appreciate if someone would comment in it.
The goal is to do something like a longjmp, but to restore the
full context including the scratch registers and the Psr. It
takes a CONTEXT* arg.
* It doesn't support all the features of a proper
compiler implementation. For example, using goto/return
from inside a __try block in a __try/__finally construct doesn't
do what you'd except. In fact, it would pretty much corrupt the
hack. __leave is missing.
* I'm sure it is full of bugs. Reports welcome, and patches
more even so.
* Thanks Kevin, for the wonderful idea of using setjmp/longjmp
and Tls.
* No garanties, yada, yada.
* I wish I had time and resources to do it properly in
the compiler - it is much more complicated than this.
* This is a bullet.
Enjoy!
Cheers,
Pedro Alves
#ifndef SEH_EXCEPTIONS_H
#define SEH_EXCEPTIONS_H
#include <setjmp.h>
#include <windows.h>
#ifdef __cplusplus
extern "C" {
#endif
struct __seh_data
{
struct __seh_data *prev;
jmp_buf env;
EXCEPTION_RECORD erec;
CONTEXT ctx;
struct _EXCEPTION_POINTERS ep;
};
#define GetExceptionCode() (__ehd.erec.ExceptionCode)
#define GetExceptionInformation() (&__ehd.ep)
int __seh_init (void);
void __seh_push_handler (struct __seh_data *d);
void __seh_pop_handler (struct __seh_data *d);
void __seh_restore_context (CONTEXT* cxt);
#define __TRY \
do { \
struct __seh_data __ehd; \
memset (&__ehd, 0, sizeof (__ehd)); \
volatile int __in_else = 1; \
volatile int __in_finally = 0; \
volatile int __have_finally = 0; \
(void)__have_finally; \
(void)__in_finally; \
while (1) /* 1 */ \
if (!__in_else) /* 2 */ { \
do {
#define __EXCEPT(...) \
} while(0); \
__seh_pop_handler (&__ehd); \
break; \
} else /* 2 */ { \
__in_else = 0; \
__seh_push_handler (&__ehd); \
if (setjmp(__ehd.env)) /* 3 */ { \
int filter_res; \
__ehd.ep.ContextRecord = &__ehd.ctx; \
__ehd.ep.ExceptionRecord = &__ehd.erec; \
filter_res = (__VA_ARGS__); \
if (filter_res == EXCEPTION_CONTINUE_EXECUTION) \
__seh_restore_context (&__ehd.ctx); \
__seh_pop_handler(&__ehd); \
if (filter_res == EXCEPTION_CONTINUE_SEARCH) \
__seh_restore_context (&__ehd.ctx); \
else if (filter_res == EXCEPTION_EXECUTE_HANDLER) \
do {
#define __FINALLY \
} while(0); \
__seh_pop_handler(&__ehd); \
__in_finally = 1; \
__in_else = 1; \
} else /* 2 */ { \
__in_else = 0; \
__have_finally = 1; \
if (!__in_finally) \
__seh_push_handler (&__ehd); \
if (__in_finally || setjmp(__ehd.env)) /* 3 */ { \
__seh_pop_handler (&__ehd); \
do {
#define __END_TRY \
} while (0); \
if (__have_finally && !__in_finally) \
__seh_restore_context (&__ehd.ctx); \
break /* 1 */; \
} /* 3 */ \
} /* 2 */ \
} while (0);
#ifdef __cplusplus
}
#endif
#endif
#include <windows.h>
#include "exceptions.h"
#define UNUSED __attribute__((unused))
static DWORD handler_tls = TLS_OUT_OF_INDEXES;
void
__seh_push_handler (struct __seh_data *d)
{
d->prev = TlsGetValue (handler_tls);
TlsSetValue (handler_tls, d);
}
void
__seh_pop_handler (struct __seh_data *d)
{
TlsSetValue (handler_tls, d->prev);
}
int
__seh_init (void)
{
handler_tls = TlsAlloc ();
if (handler_tls == TLS_OUT_OF_INDEXES)
/* GetLastError for more info. */
return -1;
return 0;
}
struct _DISPATCHER_CONTEXT;
/* Mark it as used, as we register it for use in the __asm__ block below. */
static EXCEPTION_DISPOSITION
eh_handler (struct _EXCEPTION_RECORD *,
void *,
struct _CONTEXT *,
struct _DISPATCHER_CONTEXT *)
__attribute__((used));
static EXCEPTION_DISPOSITION
eh_handler (struct _EXCEPTION_RECORD *ExceptionRecord UNUSED,
void *EstablisherFrame UNUSED,
struct _CONTEXT *ContextRecord,
struct _DISPATCHER_CONTEXT *DispatcherContext UNUSED)
{
struct __seh_data *d = TlsGetValue (handler_tls);
if (d != NULL)
{
d->ctx = *ContextRecord;
d->erec = *ExceptionRecord;
ContextRecord->Pc = (DWORD)longjmp;
ContextRecord->R0 = (DWORD)d->env;
ContextRecord->R1 = 1;
return ExceptionContinueExecution;
}
/* Unexpected, perhaps there is another handler installed. */
return ExceptionContinueSearch;
}
__asm__(
/* Data to be placed at start of .text section. The .init section
is placed before .text with the default linker script. */
"\t.section .init\n"
"\t.word eh_handler\n"
"\t.word 0\n"
"start_eh_text:\n"
/* Data for exception handler. */
"\t.section .pdata\n"
"\t.word start_eh_text\n"
/* max of 22 bits for number of instructions. */
"\t.word 0xc0000002 | (0xFFFFF << 8)\n"
/* Switch back. */
"\t.text\n"
);
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include "exceptions.h"
static void
bad ()
{
int *zero;
int result;
zero = 0;
result = *zero + *zero;
}
static int
filter (unsigned int code, struct _EXCEPTION_POINTERS* ep)
{
puts("in filter.");
printf("Exception: Code:%x Flags:%x Addr:%x "
"SP:%x LR:%x R0:%x R1:%x R2:%x R3:%x R4:%x R5:%x R12:%x Pc:%x\n\n",
code,
(unsigned)ep->ExceptionRecord->ExceptionFlags,
(unsigned)ep->ExceptionRecord->ExceptionAddress,
(unsigned)ep->ContextRecord->Sp,
(unsigned)ep->ContextRecord->Lr,
(unsigned)ep->ContextRecord->R0,
(unsigned)ep->ContextRecord->R1,
(unsigned)ep->ContextRecord->R2,
(unsigned)ep->ContextRecord->R3,
(unsigned)ep->ContextRecord->R4,
(unsigned)ep->ContextRecord->R5,
(unsigned)ep->ContextRecord->R12,
(unsigned)ep->ContextRecord->Pc
);
if (code == EXCEPTION_ACCESS_VIOLATION)
{
puts("caught AV as expected.\n");
return EXCEPTION_EXECUTE_HANDLER;
}
puts("didn't catch AV, unexpected.\n");
return EXCEPTION_CONTINUE_SEARCH;
}
#define __try __TRY
#define __finally __FINALLY
#define __except __EXCEPT
#define __endtry __END_TRY
static void
test_eh (int i)
{
/* The ; at the end of endtry isn't strictly need, but I put it to
keep emacs happy. */
printf ("test 1\n");
printf ("arg i = %d\n", i);
__TRY
{
__TRY
{
bad();
}
__EXCEPT (filter (GetExceptionCode (), GetExceptionInformation ()))
{
printf ("except block 1 reached\n");
}
__END_TRY;
}
__EXCEPT (EXCEPTION_EXECUTE_HANDLER)
{
printf ("except block 2 reached ????\n");
}
__END_TRY;
printf ("test 2\n");
__TRY
{
__TRY
{
printf ("bad try block\n");
bad();
printf ("bad try block ended ?\n");
}
__EXCEPT (EXCEPTION_CONTINUE_SEARCH)
{
printf ("in cont search block!?!\n");
}
__END_TRY;
}
__EXCEPT (EXCEPTION_EXECUTE_HANDLER)
{
printf ("except block 2 reached\n");
}
__END_TRY;
printf ("arg i = %d\n", i);
__try
{
__try
{
printf ("bad try block\n");
bad();
printf ("bad try block ended ?\n");
}
__finally
{
printf ("in finally\n");
}
__endtry;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
printf ("except block 2 reached\n");
}
__endtry;
printf ("arg i = %d\n", i);
printf ("tests done\n");
}
int
main ()
{
__seh_init ();
test_eh (123);
return 0;
}
/* ANSI concatenation macros. */
#define CONCAT(a, b) CONCAT2(a, b)
#define CONCAT2(a, b) a##b
#ifndef __USER_LABEL_PREFIX__
#error __USER_LABEL_PREFIX__ not defined
#endif
#define SYM(x) CONCAT (__USER_LABEL_PREFIX__, x)
#ifdef __ELF__
#define TYPE(x) .type SYM(x),function
#define SIZE(x) .size SYM(x), . - SYM(x)
#else
#define TYPE(x) .def SYM(x); .scl 2; .type 32; .endef
#define SIZE(x)
#endif
.macro FUNC_START name
.text
.align 2
.globl SYM (\name)
TYPE (\name)
SYM (\name):
.endm
.macro FUNC_END name
SIZE (\name)
.endm
@ Args: r0: context pointer
FUNC_START __seh_restore_context
@Skip ContextFlags
add r0, r0, #4
@Restore CPSR (offsetof (Context, Psr) == 16)
mov r1, r0
add r1, r1, #64
ldmfd r1, {r1}
msr SPSR, r1
@Post-incremental load, and restore CPSR
ldmfd r0, {r0-r15}^
FUNC_END __seh_restore_context
TARGET=arm-wince-mingw32ce
CC=$(TARGET)-gcc
CXX=$(TARGET)-g++
STRIP=$(TARGET)-strip
WARNFLAGS=-Wall -Wextra
#CFLAGS=-g0 -O0
CXXFLAGS=$(CFLAGS)
ALLCFLAGS=$(WARNFLAGS) $(CFLAGS)
ALLCXXFLAGS=$(WARNFLAGS) $(CXXFLAGS)
SOURCES=test_eh.cc Makefile exceptions.c exceptions.h restore_context.S
OBJECTS=test_eh.o exceptions.o restore_context.o
all: test_eh.exe
clean:
rm -f $(OBJECTS) test_eh.exe
test_eh_unstripped.exe: $(OBJECTS)
$(CXX) $(OBJECTS) -o $@ $(ALLCXXFLAGS) $(LDFLAGS)
test_eh.exe: test_eh_unstripped.exe
$(STRIP) $< -o $@
exceptions.o: exceptions.c exceptions.h
test_eh.o: test_eh.cc exceptions.h
restore_context.o: restore_context.S
.S.o:
$(CC) -c $< -o $@ $(ALLCFLAGS)
.c.o:
$(CC) -c $< -o $@ $(ALLCFLAGS)
.cpp.o:
$(CXX) -c $< -o $@ $(ALLCXXFLAGS)
.cc.o:
$(CXX) -c $< -o $@ $(ALLCXXFLAGS)
download: test_eh.exe
pcp test_eh.exe ":/test_eh.exe"
dist: test_eh.tar.gz
test_eh.tar.gz: all $(SOURCES)
rm -f $@
tar cfz $@ $(SOURCES)
-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
Cegcc-devel mailing list
Cegcc-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/cegcc-devel