Mailing-List: contact cygwin-help AT cygwin DOT com; run by ezmlm List-Subscribe: List-Archive: List-Post: List-Help: , Sender: cygwin-owner AT cygwin DOT com Mail-Followup-To: cygwin AT cygwin DOT com Delivered-To: mailing list cygwin AT cygwin DOT com Message-Id: <5.0.2.1.0.20020829212353.009f7070@mail.his.com> X-Sender: gshapiro AT mail DOT his DOT com Date: Thu, 29 Aug 2002 21:24:53 -0400 To: cygwin AT cygwin DOT com From: Gerald Shapiro Subject: Simple SEH using win32 api Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii"; format=flowed The program that follows catches exceptions with gcc using win32 api. I used gcc version 2.95.3-6 on mingw, 2.95.3-5 on cygwin; win32api version 1.5 on mingw, 1.5.1 on cygwin. Although g++ supplies exception handling capability, and and drmingw provide code to catch exceptions, I am porting a c app to mingw, and I didn't want to require the package maintainer to include a lot of extra code that may conflict with changes in gcc, nor did I want to have to compile in c++. So what is attached uses only c, no new headers or macro definitions, and only a few lines of extra code to implement what is essentially a _try/_except construct. My solution is pretty basic. I would advise against installing more than one exception handler at a time using this technique, as there is no provision for 'unwinding' the unused handlers. Most of the ideas came from , but one interesting thing... the return codes do not do what their names would imply. (I think that this is because the win32 exception handling is intimately intertwined with the microsoft c++ compiler. The codes that the handler returns are codes that vc++ uses to return the results of the _except filter. The vc++ exception handler itself returns values from the enumerated type EXCEPTION_DISPOSITION . This however, is *not* part of the win32 api, it is part of the compiler's implementation of seh.) Here is what I found: Return value #defined name 0 EXCEPTION_CONTINUE_SEARCH Action: Returns to the statement that caused the exception and re-executes that statement. (Causes an infinite loop of calling the exception handler if the handler does not fix the problem) 1 EXCEPTION_EXECUTE_HANDLER Action: Passes the exception to the win32 default handler (error box) -1 EXCEPTION_CONTINUE_EXECUTION Action: Returns an "invalid disposition" exception, indicating that this is not a valid return value!!! Anyway... here it is ***************** bare_bones_seh.c ************************ #include #include #include long *fault_addr; int a; long valid_memory_area = 10; int *z = (int *) 0x12345678; jmp_buf environment; int error_val = -1; /* This Handler shows how SEH can modify global variables and/or registers of the faulting routine to "fix" an error It also shows that more than one error can be handled in a single exception handler */ int problem_fixing_seh( struct _EXCEPTION_RECORD *ExceptionRecord, void * EstablisherFrame, struct _CONTEXT *ContextRecord, void * DispatcherContext) { printf("You've raised exception number %#x\n", ExceptionRecord->ExceptionCode); if (ExceptionRecord->ExceptionFlags & 1) { printf("Non-continuable error\n"); exit(1); } switch (ExceptionRecord->ExceptionCode) { case STATUS_INTEGER_DIVIDE_BY_ZERO : fault_addr = (long *) (ExceptionRecord->ExceptionInformation[1]); printf("Divide by zero at addr=%#x\n", fault_addr); /* fix the problem */ a = 5; printf("But don't worry, it is fixed now\n"); return EXCEPTION_CONTINUE_SEARCH; case EXCEPTION_ACCESS_VIOLATION : fault_addr = (long *) (ExceptionRecord->ExceptionInformation[1]); printf("ACCESS VIOLATION at addr=%#x\n", fault_addr); /* Make faulting frame's EAX register point to a valid memory area */ ContextRecord->Eax = (DWORD)&valid_memory_area; printf("But don't worry, it is fixed now\n"); return EXCEPTION_CONTINUE_SEARCH; default: return EXCEPTION_EXECUTE_HANDLER; } } /* This handler uses longjmp to go back to the "except" clause */ int problem_skipping_seh( struct _EXCEPTION_RECORD *ExceptionRecord, void * EstablisherFrame, struct _CONTEXT *ContextRecord, void * DispatcherContext) { printf("You've raised exception number %#x\n", ExceptionRecord->ExceptionCode); if (ExceptionRecord->ExceptionFlags & 1) { printf("Non-continuable error\n"); exit(1); } if (ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) return EXCEPTION_EXECUTE_HANDLER; fault_addr = (long *) (ExceptionRecord->ExceptionInformation[1]); printf("ACCESS VIOLATION at addr=%#x\n", fault_addr); longjmp(environment, error_val); } main(int argc, char *argv[]) { DWORD handler; int error_code; setbuf(stdout, NULL); printf("Loading the error fixing seh\n"); /* Simple structured exception catcher */ handler = (DWORD) problem_fixing_seh ; asm("movl %0, %%eax\n\t" "pushl %%eax": : "r" (handler): "%eax" ); asm("pushl %fs:0"); asm("movl %esp, %fs:0"); /* We have installed our handler */ /* Now generate a divide by zero exception */ /* The handler will replace a with the value 5 and then re-execute the offending division */ a = 0; valid_memory_area = 10 / a; printf("valid_memory_area = %d\n", valid_memory_area); /* Now an invalid memory access exception */ /* The assembler code attempts to store a value (12) in location 0h . Upon detecting the exception the handler changes EAX to point to valid_memory_area */ asm("movl $0, %%eax\n\t" "movl $12, (%%eax)" : : : "%eax", "memory" ); printf("valid_memory_area = %d\n", valid_memory_area); /* Now uninstall this seh */ asm("movl (%%esp), %%eax \n\t" "movl %%eax, %%fs:0" : : : "%eax"); asm("addl $8, %esp"); /* handler is uninstalled */ printf("Error fixing handler removed\n"); /* Now install a different handler that skips offending code */ handler = (DWORD) problem_skipping_seh ; asm("movl %0, %%eax\n\t" "pushl %%eax": : "r" (handler): "%eax" ); asm("pushl %fs:0"); asm("movl %esp, %fs:0"); /* We have installed our handler */ printf("Problem skipping SEH installed\n"); /* The longjmp call in the handler will return here, placing a non-zero return value in setjmp */ error_code = setjmp(environment); if (error_code != 0) { printf ("Skipping invalid memory access\n"); goto NoCanDo; } z = 0; printf("Trying to write to memory location 0\n"); *z = 12; NoCanDo: /* Uninstall the seh */ asm("movl (%%esp), %%eax \n\t" "movl %%eax, %%fs:0" : : : "%eax"); asm("addl $8, %esp"); printf("FINISHED\n"); } -- Unsubscribe info: http://cygwin.com/ml/#unsubscribe-simple Bug reporting: http://cygwin.com/bugs.html Documentation: http://cygwin.com/docs.html FAQ: http://cygwin.com/faq/