This is the mail archive of the cygwin@cygwin.com mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Simple SEH using win32 api


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 <http://sources.redhat.com/ml/cygwin/1999-08/msg00362.html> and drmingw <http://mefriss1.swan.ac.uk/~jfonseca/gnu-win32/software/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 <http://www.microsoft.com/msj/0197/exception/exception.htm>, 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 <windows.h>
#include <stdio.h>
#include <setjmp.h>

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/


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]