|
Re: C++ exceptions: msg#00036gnu.cegcc.devel
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-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@xxxxxxxxxxxxxxxx https://lists.sourceforge.net/lists/listinfo/cegcc-devel |
|
| <Prev in Thread] | Current Thread | [Next in Thread> |
|---|---|---|
| Previous by Date: | Re: C++ exceptions: 00036, Pedro Alves |
|---|---|
| Next by Date: | wxWinCE and cegcc: 00036, Hywel B. Richards |
| Previous by Thread: | Re: C++ exceptionsi: 00036, Pedro Alves |
| Next by Thread: | Re: C++ exceptions: 00036, Danny Backx |
| Indexes: | [Date] [Thread] [Top] [All Lists] |
| News | Mail Home | sitemap | FAQ | advertise |