Win32 Exception handling for assembler programmers by Jeremy Gordon -

http://www.GoDevTool.com


CONTENTS (click to go there)
1. Background
2. Exception handling in practice
3. Setting up simple exception handlers
4. Stack unwinds
5. The information sent to the handlers
6. Recovering from and Repairing an exception
7. Continuing execution after final handler called
8. Single-stepping by setting the trap flag within the handler
9. Exception handling in multi-threaded applications
10. Except1 program (simple example)
11. Except2 program (complex example)
 

Background

We're going to examine how to make an application more robust by handling its own exceptions, rather than permitting the system to do so. An "exception" is an offence committed by the program, which would otherwise result in the embarrassing appearance of the dreaded closure message box:-
wpe3.gif (5741 bytes)
or its more elaborate counterpart in Windows NT.

What exception handling does ...

The idea of exception handling (often called "Structured Exception Handling") is that your application instals one or more callback routines called "exception handlers" at run-time and then, if an exception occurs, the system will call the routine to let the application deal with the exception. The hope would be that the exception handler may be able to repair the exception and continue running either from the same area of code where the exception occurred, or from a "safe place" in the code as if nothing had happened. No closure message box would then be displayed and the user would be done the wiser. As part of this repair it may be necessary to close handles, close temporary files, free device contexts, free memory areas, inform other threads, then unwind the stack or close down the offending thread. During this process the exception handler may make a record of what it is doing and save this to a file for later analysis.

If a repair cannot be achieved, exception handling allows your application to close gracefully, having done as much clearing up, saving of data, and apologising as it can.

Planned exceptions

The Windows SDK suggests another use for exception handling. It is suggested as a way to keep track of memory usage. The idea is that an exception will occur if you need to commit more memory: you intercept it and carry out the memory allocation. This can be done by intercepting a memory access violation [exception number 0C0000005h], which would occur if your code tries to read from, or write to, memory which had not been committed.

Another way suggested to keep track of memory usage is to set the guard page flag in a call to VirtualAlloc when committing the memory, or later using VirtualProtect. This causes a guard page exception [080000001h] if an attempt was made to read to, or write from a guarded area of memory, after which the guard page flag is released. The exception handler would therefore be kept informed of the memory requirements and could reset the flag if required.

These methods are widely used throughout the system, for example, as more stack is required by a thread, it is automatically enlarged.

An application, however, usually knows what it hopes to do next, so it is much simpler and quicker to keep track of memory requirements by keeping the top of the memory area as a data variable, and to check before the start of each series of memory read/write operations whether the memory area needs to be enlarged or diminished.

This works even if more than one thread uses the same area of memory, since the same data variable can be used by each thread. In that case, handling the 0C0000005h exception might only be a backup in case your code went wrong.

And what exception handling cannot do ...

Apart from divide by zero [exception code 0C0000094h] which can easily be avoided by protective coding, the most common type of exception is an attempt to read from, or write to, an illegal memory address [0C0000005h]. There are several ways that the second (illegal address) can arise. For example:- It can be seen from this list that exceptions may occur in unexpected circumstances for a variety of reasons. And it will be precisely this type of exception which may terminate your program despite the best efforts of your exception handler. In these circumstances at the very least, the exception handler should try to save important data which would otherwise be lost, and then retire gracefully, with suitable apologies.

Other program failures

Your program may fail for other reasons which will not result in an exception at all.

The usual cause of this is:-

The result is that your program will not be able to respond to system messages it will appear to the user simply to have stopped. Luckily, however, because it runs in its own virtual address space other programs will not be affected, although the whole system may appear to run a little more slowly.

Utterly fatal exceptions

Some errors are so bad that the system cannot even manage to call your exception handler. Then only if the user is lucky will the system's closure message box appear, or the devastating bright blue error screen will appear, showing that a "fatal" error has occurred. Almost inevitably this is a result of a total crash of the system and a reboot is the only remedy. Fortunately in Win32 you have to try quite hard to produce such errors, but they can still occur.

... and where exception handling really scores

Having spent some time on what exception handling cannot do, let’s review the instances where it is invaluable:-

Exception handling in practice

The Windows sequence

In order to understand what your code can or should do when handling exceptions, you need to know in some more detail what the system does when an exception occurs. If you are new to the subject, the following may not yet be clear. However it is necessary to know these steps to understand the subject. The steps are as follows:-
    1. Windows decides first whether it is an exception which it is willing to send to the program's exception handler. If so, if the program is being debugged, Windows will notify the debugger of the exception by suspending the program and sending EXCEPTION_DEBUG_EVENT (value 1h) to the debugger.
    2. If the program is not being debugged or if the exception is not dealt with by the debugger, the system sends the exception to your per-thread exception handler if you have installed one. A per-thread handler is installed at run-time and is pointed to by the first dword in the Thread Information Block whose address is at FS:[0].
    3. The per-thread exception handler can try to deal with the exception, or it may not do so, leaving it for handlers further up the chain, if there are any more handlers installed.
    4. Eventually if none of the per-thread handlers deal with the exception, if the program is being debugged the system will again suspend the program and notify the debugger.
    5. If the program is not being debugged or if the exception is still not dealt with by the debugger, the system will call your final handler if one is installed. This will be a final handler installed at run-time by the application using the API SetUnhandledExceptionFilter.
    6. If your final handler does not deal with the exception after it returns, the system final handler will be called. Optionally it will show the system’s closure message box. Depending on the registry settings, this box may give the user a chance to attach a debugger to the program. If no debugger can be attached or if the debugger is powerless to assist, the program is doomed and the system will call ExitProcess to terminate the program.
    7. Before finally terminating the program, though, the system will cause a "final unwind" of the stack for the thread in which the exception occurred.

Advantages of using assembler for exception handling

Win32 provides only the framework for exception handling, using a handful of APIs. So most of the code required for exception handling has to be coded by hand.

"C" programmers will use various shortcuts provided by their compilers by including in their source code statements such as _try, _except, _finally, _catch and _throw.

One real disadvantage in relying on the compiler’s code is that it can enlarge the final exe file enormously.

Also most C programmers would have no idea what code is produced by the compiler when exception handling is used, and this is a real disadvantage because to handle exceptions properly you need flexibility, understanding and control. This is because exceptions can be intercepted and handled in various ways and at various different levels in your code. Using assembler you can produce tight, reliable and flexible code which you can tailor closely to your own application.

Multi-threaded applications need particularly careful treatment and assembler provides a simple and versatile way to add exception handling to such programs.

Information about exception handling at a low level is hard to get hold of, and the samples in the Win32 Software Development Kit (SDK) concentrate on how to use the "C" compiler statements rather than how to hard-wire a program to use the Win32 framework itself.

The information in this article was obtained using a test program and a debugger, and by disassembling code produced by "C" compilers. The accompanying programs, Except1.exe and Except2.exe, demonstrate the techniques described here.
 

Setting up simple exception handlers

I hope you will be pleasantly surprised to see in practice how easy it is in assembler to add exception handling to your programs.

The two types of exception handlers

As you have seen above, there are two types of exception handlers.

Type 1 – the "final" exception handler

The "final" exception handler is called by the system if your program is doomed to close. Because this handler is process-specific it is called irrespective of which thread caused the exception.

Establishing a final exception handler

Typically, this is established in the main thread as soon as possible after the program entry point by calling the API SetUnhandledExceptionFilter. It therefore covers the whole program from that point until termination. There is no need to remove the handler on termination - this is done automatically by windows.

Example
START:
PUSH ADDR FINAL_HANDLER
CALL SetUnhandledExceptionFilter
; ...
; ...
; ...
CALL ExitProcess
;************************************
FINAL_HANDLER:
; ...
; ...
; ...
;(eax=-1 reload context and continue)
MOV EAX,1
RET
;program entry point
;
;
;
;code covered by final handler
;
;
;
;
;
;code to provide a polite exit
;
;
;eax=1 stops display of closure box
;eax=0 enables display of the box

No chaining of final exception handlers

There can only be one application-defined final exception handler in the process at any one time. If SetUnhandledExceptionFilter is called a second time in your code the address of the final exception handler is simply changed to the new value, and the previous one is discarded.

Type 2 – the "per-thread" exception handler

This type of handler is typically used to guard certain areas of code and is established by altering the value held by the system at FS:[0]. Each thread in your program has a different value for the segment register FS, so this exception handler will be thread specific. It will be called if an exception occurs during the execution of code protected by the handler.

The value in FS is a 16-bit selector which points to the "Thread Information Block", a structure which contains important information about each thread. The very first dword in the Thread Information Block points to a structure which we are going to call an "ERR" structure.

The "ERR" structure is at least 2 dwords as follows:-

1st dword +0
Pointer to next ERR structure
2nd dword +4
Pointer to own exception handler

Establishing a "per-thread" exception handler

So now we can see how easy it is to establish this type of exception handler:-

Example
PUSH ADDR HANDLER
FS PUSH [0]
FS MOV [0],ESP
...
...
...
FS POP [0]
ADD ESP,4h
RET
;***********************
HANDLER:
...
...
...
MOV EAX,1
RET
;
;address of next ERR structure
;give FS:[0] the ERR address just made
;
;the code protected by the handler goes here
;
;restore next ERR structure to FS:[0]
;throw away rest of ERR structure
;
;
;
;
;exception handler code goes here
;
;eax=1 go to next handler
;eax=0 reload context & continue execution

Chaining of per-thread exception handlers

In the above code we can see that the 2nd dword of the ERR structure, which is the address of your handler, is put on the stack first, then the 1st dword of the next ERR structure is put on the stack by the instruction FS PUSH [0]. Suppose the code which was then protected by this handler called other functions which needed their own individual protection. Then you may create another ERR structure and handler to protect that code in exactly the same way. This is called chaining. In practice this means that when an exception occurs the system will walk the handler chain by first calling the exception handler most recently established before the code where the exception occurred. If that handler does not deal with the exception (returning EAX=1), then the system calls the next handler up the chain. Since each ERR structure contains the address of the next handler up the chain, any number of such handlers can be established in this way. Each handler might guard against or deal with particular types of exceptions depending on what is foreseeable in your code. The stack is used to keep the ERR structure, to avoid write-overs. However there is nothing to stop you using other parts of memory for the ERR structures if you prefer.
 

Stack unwinds

We’re going to look at with stack unwinds at this point because they shouldn’t keep their mystery any longer! A "stack unwind" sounds very dramatic, but in practice it’s simply all about calling the exception handlers whose local data is held further down the stack and then (probably) continuing execution from another stack frame. In other words the program gets ready to ignore the stack contents between these two positions.

Suppose you have a chain of per-thread handlers established as in this arrangement, where Function A calls Function B which calls Function C:-

wpe5.gif (2556 bytes)
Then the stack will look something like this:-
 
stackâ +ve
3rd

Stack

Frame

Use of stack by Function C
Handler 3
Local Data Function C
2nd

Stack

Frame

Return address Function C
Use of stack by Function B
Handler 2
Local Data Function B
1st

Stack

Frame

Return address Function B
Use of stack by Function A
Handler 1
Local Data Function A
Return address Function A
Stackâ +ve

Here as each function is called things are PUSHed onto the stack: firstly the return address, then local data, and then the exception handler (this is the "ERR" structure referred to earlier).
Then suppose that an exception occurs in Function C. As we have seen, the system will cause a walk of the handler chain. Handler 3 will be called first. Suppose Handler 3 does not deal with the exception (returning EAX=1), then Handler 2 will be called. Suppose Handler 2 also returns EAX=1 so that Handler 1 is called. If Handler 1 deals with the exception, it may need to cause a clear-up using local data in the stack frames created by Functions B and C.
It can do so by causing an Unwind.

This simply repeats the walk of the handler chain again, causing first Handler 3 then Handler 2, then Handler 1 to be called in turn.

The differences between this type of handler chain walk and the walk initiated by the system when the exception first occurred are as follows:-

    1. This handler walk is initiated by your handler rather than by the system
    2. The exception flag in the EXCEPTION_RECORD should be set to 2h (EH_UNWINDING). This indicates to the per-thread handler that it is being called by another handler higher in the chain to clear-up using local data. It should not attempt to do any more than that and it must return EAX=1.
    3. The handler walk stops at the handler immediately before the caller. For example in the diagram, if Handler 1 initiates the unwind, the last Handler to be called during the unwind is Handler 2. There is no need for Handler 1 to be called from within itself because it has access to its own local data to clear-up.
You can see below ("Providing access to local data") how the handler is able to find local data during the handler walk.

How the unwind is done

The handler can initiate an unwind using the API RtlUnwind or, as we shall see, it can also easily be done using your own code. This API can be called as follows:- PUSH Return value
PUSH pExceptionRecord
PUSH ADDR CodeLabel
PUSH LastStackFrame
CALL RtlUnwind

Where:-

Return value is said to give a return value after the unwind (you would probably not use this)

pExceptionRecord is a pointer to the exception record, which is one of the structures sent to the handler when an exception occurs

CodeLabel is a place from which execution should continue after the unwind and is typically the code address immediately after the call to RtlUnwind. If this is not specified the API appears to return in the normal way, however the SDK suggests that it should be used and it is better to play safe with this type of API

LastStackFrame is the stack frame at which the unwind should stop. Typically this will be the stack address of the ERR structure which contains the address of the handler which is initiating the unwind

Unlike other APIs you cannot rely on RtlUnwind saving the EBX, ESI or EDI registers – if you are using these in your code you should ensure that they are saved prior to PUSHing the first parameter and restored after the CodeLabel

Own-code Unwind

The following code simulates the unwind (where ebx holds the address of the EXCEPTION_RECORD structure sent to the handler):-
MOV D[EBX+4],2h
FS MOV EDI,[0]
L2:
CMP D[EDI],-1
JZ >L3
PUSH EDI,EBX
CALL [EDI+4]
ADD ESP,8h
MOV EDI,[EDI]
JMP L2
L3:
;make the exception flag EH_UNWINDING
;get 1st per-thread handler address
;
;see if it’s the last one
;yes, so finish
;push ERR structure, EXCEPTION_RECORD
;call handler to run clear-up code
;remove the two parameters pushed
;get pointer to next ERR structure
;and do next if not at end
;code label when finished
Here each handler is called in turn with the ExceptionFlag set to 2h until the last handler is reached (the system has a value of –1 in the last ERR structure).

The above code does not check for corruption of the values at [EDI] and at [EDI+4]. The first is a stack address and could be checked by ensuring that it is above the thread’s stack base given by FS:[8] and below the thread’s stack top given by FS:[4]. The second is a code address and so you could check that it lies within two code labels, one at the start of your code and one at the end of it. Alternatively you could check that [EDI] and [EDI+4] could be read by calling the API IsBadReadPtr.

Unwind by final handler then continue

It is not just a per-thread handler which can initiate a stack unwind. It can also be done in your final handler by calling either RtlUnwind or an own-code unwind and then returning EAX= –1. (See "Continuing execution after final handler called").

Final unwind then terminate

If a final handler is installed and it returns either EAX=0 or EAX=1, the system will cause the process to terminate. However, before final termination something interesting happens. The system does a final unwind by going back to the very first handler in the chain (that is to say, the handler guarding the code in which the exception occurred). This is the very last opportunity for your handler to execute the clear-up code necessary within each stack frame. You can see this final unwind clearly occurring if you set the accompanying demo program Except2.exe to allow the exception to go to the final handler and press either F3 or F5 when there. It also happens in the simpler Except1.exe program.
 

The information sent to the handlers

Clearly sufficient information must be sent to the handlers for them to be able to try to repair the exception, make error logs, or report to the user. As we shall see, this information is sent by the system itself on the stack, when the handlers are called. In addition to this you can send your own information to the handlers by enlarging the ERR structure so that it contains more information.

The information sent to the final handler

The final handler is documented in the Windows Software Development Kit ("SDK") as the API "UnhandledExceptionFilter". It receives one parameter only, a pointer to the structure EXCEPTION_POINTERS. This structure is as follows:-
EXCEPTION_POINTERS   +0
Pointer to structure:-
EXCEPTION_RECORD
+4
Pointer to structure:-
CONTEXT record

The structure EXCEPTION_RECORD has these fields:-
EXCEPTION_RECORD  +0
ExceptionCode
                                             +4
ExceptionFlag
                                             +8
NestedExceptionRecord
                                             +C
ExceptionAddress
                                             +10
NumberParameters
                                             +14
AdditionalData

Where

ExceptionCode gives the type of exception which has occurred. There are a number of these listed in the SDK and header files, but in practice, the types which you may come across are:-

C0000005h – Read or write memory violation

C0000094h – Divide by zero

C0000095h – Divide overflow

C00000FDh – The stack went beyond the maximum available size

80000001h – Violation of a guard page in memory set up using Virtual Alloc

The following only occur whilst dealing with exceptions:-

C0000025h – A non-continuable exception – the handler should not try to deal with it

C0000026h – Exception code used the by system during exception handling. This code might be used if the system encounters an unexpected return from a handler. It is also used if no Exception Record is supplied when calling RtlUnwind.

The following are used in debugging:-

80000003h – Breakpoint occurred because there was an INT3 in the code

80000004h – Single step during debugging

The exception codes follow these rules:
Bits 31-30      Bit 29               Bit 28             Bits 27-0
0=success      0=Microsoft     Reserved        For exception
1=information 1=Application  Must be zero  code
2=warning
3=error
A typical own exception code sent by RaiseException might therefore be E0000100h (error, application, code=100h).
Own user code – this would be sent by your own application by calling the API RaiseException. This is a quick way to exit code directly into your handler if required.

Exception flag which gives instructions to the handler. The values can be:-

0 – a continuable exception (can be repaired)

1 – a non-continuable exception (cannot be repaired)

2 – the stack is unwinding - do not try to repair

Nested exception record pointing to another EXCEPTION_RECORD structure if the handler itself has caused another exception

Exception address – the address in code where the exception occurred

NumberParameters – number of dwords to follow in Additional information

Additional information – array of dwords with further information

This can either be information sent by the application itself when calling RaiseException, or, if the exception code is C0000005h it will be as follows:-

1st dword - 0=a read violation, 1=a write violation.

2nd dword – address of access violation

The second part of the EXCEPTION_POINTERS structure which is sent to the final handler points to the CONTEXT record structure which contains the processor-specific values of all the registers at the time of the exception. WINNT.H contains the CONTEXT structures for various processors. Your program can find out what sort of processor is being used by calling GetSystemInfo. CONTEXT is as follows for IA32 (Intel 386 and upwards):- +0 context flags
(used when calling GetThreadContext)
DEBUG REGISTERS
+4 debug register #0
+8 debug register #1
+C debug register #2
+10 debug register #3
+14 debug register #6
+18 debug register #7
FLOATING POINT / MMX registers
+1C ControlWord
+20 StatusWord
+24 TagWord
+28 ErrorOffset
+2C ErrorSelector
+30 DataOffset
+34 DataSelector
+38 FP registers x 8 (10 bytes each)
+88 Cr0NpxState
SEGMENT REGISTERS
+8C gs register
+90 fs register
+94 es register
+98 ds register
ORDINARY REGISTERS
+9C edi register
+A0 esi register
+A4 ebx register
+A8 edx register
+AC ecx register
+B0 eax register
CONTROL REGISTERS
+B4 ebp register
+B8 eip register
+BC cs register
+C0 eflags register
+C4 esp register
+C8 ss register

The information sent to the per-thread handlers

At the time of the call to the per-thread handler, ESP points to three structures as follows:-
ESP+4
Pointer to structure:-
EXCEPTION_RECORD
ESP+8
Pointer to own ERR structure
ESP+C
Pointer to structure:-
CONTEXT record
Unlike usual CALLBACKs in Windows, when the per-thread handler is called, the C calling convention is used (caller to remove the arguments from the stack) not the PASCAL convention (function to do so). This can be seen from the actual Kernel32 code used to make the call:-

PUSH Param, CONTEXT record, ERR, EXCEPTION_RECORD
CALL HANDLER
ADD ESP,10h

In practice the first argument, Param, was not found to contain meaningful information

The EXCEPTION_RECORD and CONTEXT record structures have already been described above.

The ERR structure is the structure you created on the stack when the handler was established and it must contain the pointer to the next ERR structure and the code address of the handler now being installed (see "Setting up simple exception handlers", above). The pointer to the ERR structure passed to the per-thread handler is to the top of this structure. It is possible, therefore, to enlarge the ERR structure so that the handler can receive additional information.

In a typical arrangement the ERR structure might look like this, where [ESP+8h] points to the top of this structure when the handler is called:-

ERR +0
Pointer to next ERR structure
+4
Pointer to own exception handler
+8
Code address of "safe-place" for handler
+C
Information for handler
+10
Area for flags
+14
Value of EBP at safe-place
As we shall see below ("Continuing execution from a safe-place"), the fields at +8 and +14 may be used by the handler to recover from the exception.

Providing access to local data

Let’s now consider the best position of the ERR structure on the stack relative to the stack frame, which may well hold local data variables. This is important because the handler may well need access to this local data in order to clear-up properly. Here is some typical code which may be used to establish a per-thread handler where there is local data:-
MYFUNCTION:
PUSH EBP
MOV EBP,ESP
SUB ESP,40h
;******** local data now at
;********** install handler
PUSH EBP
PUSH 0
PUSH 0
PUSH ADDR SAFE_PLACE 
PUSH ADDR HANDLER 
FS PUSH [0]
FS MOV [0],ESP
...
...
...
JMP >L10
SAFE_PLACE:
L10:
FS POP [0]
MOV ESP,EBP
POP EBP
RET
;*****************
HANDLER:
RET
;procedure entry point
;save ebp (used to address stack frame)
;use EBP as stack frame pointer
;make 16 dwords on stack for local data
[EBP-4] to [EBP-40h]
and its ERR structure
;ERR+14h save ebp (being ebp at safe-place)
;ERR+10h area for flags
;ERR+0Ch information for handler
;ERR+8h new eip at safe-place
;ERR+4h address of handler
;ERR+0h keep next ERR up the chain
;point to ERR just made on the stack
;
;code which is protected goes here
;
;normal end if there is no exception
;handler sets eip/esp/ebp for here
;
;restore next ERR up the chain

Using this code, when the handler is called, the following is on the stack, and with [ESP+8h] pointing to the top of the ERR structure (ie. ERR+0):-

stackâ +ve
ERR +0
Pointer to next ERR structure
ERR +4
Pointer to own exception handler
ERR +8
Code address of "safe-place" for handler
ERR +C
Information for handler
ERR +10
Area for flags
ERR +14
Value of EBP at safe-place
+18
Local Data
+1C
Local Data
+20
Local Data
more local data â
You can see from this that since the handler is given a pointer to the ERR structure it can also find the address of local data on the stack. This is because the handler knows the size of the ERR structure and also the position of the local data on the stack. If the EBP field is used at ERR+14h as in the above example, that could also be used as a pointer to the local data.
 

Recovering from and Repairing an exception

Continuing execution from a safe-place

Choosing the safe-place

You need to continue execution from a place in the code which will not cause further problems. The main thing you must bear in mind is that since your program is designed to work within the Windows framework, your aim is to return to the system as soon as possible in a controlled manner, so that you can wait for the next system event. If the exception has occurred during the call by the system to a window procedure, then often a good safe-place will be near the exit point of the window procedure so that control passes back to the system cleanly. In this case it will simply appear to the system that your application has returned from the window procedure in the usual way.

If the exception has occurred, however, in code where there is no window procedure, then you may need to exercise more control. For example, a thread established to do certain tasks will probably need to be terminated, reporting to the main thread that it could not complete the task.

Another major consideration is how easy it is to get the correct EIP, ESP and EBP values at the safe-place. As we can see below, this may not be at all difficult.

There are so many possible permutations here it is probably pointless to postulate them. The precise safe-place will depend on the nature of your code and the use you are making of exception handling.

Example of how to get to safe-place

As an example, though, look again at the code example above in MYFUNCTION. You can see the code label "SAFE-PLACE". This is a code address from which execution could continue safely, the handler having done all necessary clearing up.

In the code example, in order to continue execution successfully, it must be borne in mind that although SAFE-PLACE is within the same stack frame as the exception occurred, the values of ESP and EBP need carefully to be set by the handler before execution continues from EIP.

These 3 registers therefore need to be set and for the following reasons:-

ESP – to enable the FS POP [0] instruction to work and to POP other values if necessary

EBP – to ensure that local data can be addressed within the handler and to restore the correct ESP value to return from MYFUNCTION

EIP – to cause execution to continue from SAFE-PLACE

Now you can see that each of these values is readily available from within the handler function. The correct ESP value is, in fact, exactly the same as the top of the ERR structure itself (given by [ESP+8h] when the handler is called). The correct EBP value is available from ERR+14h, because this was PUSHed onto the stack when the ERR structure was made. And the correct code address of SAFE-PLACE to give to EIP is at ERR+8h.

Now we are ready to see how the handler can ensure that execution continues from a safe-place, instead of allowing the process to close, should an exception occur.
HANDLER:
PUSH EBP
MOV EBP,ESP
;** now [EBP+8]=pointer
;** [EBP+0Ch]=pointer to
;** [EBP+10h]=pointer to
PUSH EBX,EDI,ESI
MOV EBX,[EBP+8]
TEST D[EBX+4],1h
JNZ >L5
TEST D[EBX+4],2h
JZ >L2
...
...
...
JMP >L5
L2:
PUSH 0
PUSH [EBP+8h]
PUSH ADDR UN23
PUSH [EBP+0Ch]
CALL RtlUnwind
UN23:
MOV ESI,[EBP+10h]
MOV EDX,[EBP+0Ch]
MOV [ESI+0C4h],EDX
MOV EAX,[EDX+8]
MOV [ESI+0B8h],EAX
MOV EAX,[EDX+14h]
MOV [ESI+0B4h],EAX
XOR EAX,EAX
JMP >L6
L5:
MOV EAX,1
L6:
POP ESI,EDI,EBX
MOV ESP,EBP
POP EBP
RET
;
;
;
to EXCEPTION_RECORD
ERR structure
CONTEXT record
;save registers as required by windows
;get exception record in ebx
;see if its a non-continuable exception
;yes, so must not deal with it
;see if its EH_UNWINDING (from Unwind)
;no
;
;clear-up code when unwinding
;
;must return 1 to go to next handler
;
;return value (not used)
;pointer to this exception record
;code address for RtlUnwind to return
;pointer to this ERR structure
;
;
;get context record in esi
;get pointer to ERR structure
;use it as new esp
;get safe place given in ERR structure
;insert new eip
;get ebp at safe place given in ERR
;insert new ebp
;reload context & return to system eax=0
;
;
;go to next handler - return eax=1
;ordinary return (no actual arguments)

Repairing the exception

In the above example you saw the context being loaded with the new eip, ebp and esp to cause execution to continue from a safe-place. It may be possible using the same method of replacing the values for some of the registers in the context, to "repair" the exception, permitting execution to continue from near the offending code, so that the current task can be continued.

An obvious example would be a divide by zero, which can be repaired by the handler by substituting the value 1 for the divisor, and then a return with EAX=0 (if a "per-thread" handler) causing the system to reload the context and continue execution.

In the case of memory violations, you can make use of the fact that the address of the memory violation is passed as the second dword in the additional information field of the exception record. The handler can use this very same value to pass to VirtualAlloc to commit more memory starting at that place. If this is successful, the handler can then reload the context (unchanged) and return EAX=0 to continue execution (in the case of a "per-thread" handler).
 

Continuing execution after final handler called

If you wish you can deal with exceptions in the final handler. You recall that at the beginning of this article I said that the final handler is called by the system when the process is about to be terminated.

This is true.

The returns in EAX from the final handler are not the same as those from the per-thread handler. If the return is EAX=1 the process terminates without showing the system’s closure message box, and if EAX=0 the box is shown.

However, there is also a third return code, EAX= –1 which is properly described in the SDK as "EXCEPTION_CONTINUE_EXECUTION". This return has the same effect as returning EAX=0 from a per-thread handler, that is, it reloads the context record into the processor and continues execution from the eip given in the context. Of course, the final handler may change the context record before returning to the system, in the same way as a per-thread handler might do so. In this way the final handler can recover from the exception by continuing execution from a suitable safe-place or it may try to repair the exception.

If you use the final handler to deal with all exceptions instead of using per-thread handlers you do lose some flexibility, though.

Firstly, you cannot nest final handlers. You can only have one working final handler established by SetUnhandledExceptionFilter in your code at any one time. You could, if you wished, change the address of the final handler as different parts of your code are being processed. SetUnhandledExceptionFilter returns the address of the final handler being replaced so you could make use of this as follows:-
PUSH ADDR FINAL_HANDLER
CALL SetUnhandledExceptionFilter
PUSH EAX
...
...
...
...
CALL SetUnhandledExceptionFilter
;
;
;keep address of previous handler
;
;this is the code
;being guarded
;
;restore previous handler
Note here that at the time of the second call to SetUnhandledExceptionFilter the address of the previous handler is already on the stack because of the earlier PUSH EAX instruction.

Another difficulty with using the final handler is that the information sent to it is limited to the exception record and the context record. Therefore you will need to keep the code address of the safe-place, and the values of ESP and EBP at that safe-place, in static memory. This can be done easily at run time. For example, when dealing with the WM_COMMAND message within a window procedure,
PROCESS_COMMAND:
MOV EBPSAFE_PLACE,EBP
MOV ESPSAFE_PLACE,ESP
...
...
...
SAFE_PLACE:
XOR EAX,EAX
RET
;called on uMsg=111h (WM_COMMAND)
;keep ebp at safe-place
;keep esp at safe-place
;
;protected code here
;
;code-label for safe-place
;return eax=0=message processed
In the above example, in order to repair the exception by continuing execution from the safe-place, the handler would insert the values of EBPSAFE_PLACE at CONTEXT+0B4h (ebp), ESPSAFE_PLACE at CONTEXT+0C4h (esp), and ADDR SAFE_PLACE into CONTEXT+0B8h (eip) and then return -1.

Note that in a stack unwind forced by the system because of a fatal exit, only the "per-thread" handlers (if any) and not the final handler are called. If there are no "per-thread" handlers, the final handler would have to deal with all clearing-up itself before returning to the system.
 

Single-stepping by setting the trap flag within the handler

You can make a simple single-step tester for your program while it is under development by using the handler's ability to set the trap flag in the register context before returning to the system.  You can arrange for the handler to display the results on the screen, or to dump them to a file.   This may be useful if you suspect that results are being altered under debugger control, or if you need to see quickly how a particular piece of code responds to various inputs.  Insert the following code fragment where you want single-stepping to begin:-

MOV D[SSCOUNT],5
INT 3

SSCOUNT is a data symbol and is set to the number of steps the handler should do before returning to normal operation. The INT 3 causes a 80000003h exception, so your handler is called.
The code in your development program should be protected by a per-thread handler using code like this:-.
SS_HANDLER:
PUSH EBP
MOV EBP,ESP
PUSH EBX,EDI,ESI
MOV EBX,[EBP+8]
TEST D[EBX+4],01h
JNZ >L14
TEST D[EBX+4],02h
JNZ >L14
MOV ESI,[EBP+10h]
MOV EAX,[EBX]
CMP EAX,80000004h
JZ >L10
CMP EAX,80000003h
JNZ >L14
L10:
DEC D[SSCOUNT]
JZ >L12
OR D[ESI+0C0h],100h
L12:
...
...
...
XOR EAX,EAX
JMP >L17
L14:
MOV EAX,1
L17:
POP ESI,EDI,EBX
MOV ESP,EBP
POP EBP
RET
;
;
;
;save registers as required by Windows
;get exception record in ebx
;see if its a non-continuable exception
;yes
;see if EH_UNWINDING
;yes
;get context record in esi
;get ExceptionCode
;see if here because trap flag set
;yes
;see if its own INT 3 inserted to single-step
;no
;
;stop when correct number done
;
;set trap flag in context
;
;
;code here to display results to screen
;
;eax=0 reload context and return to system
;
;
;eax=1 system to go to next handler
Here the first call to the handler is caused by the INT 3 (the system objected strongly to the use of INT 1 when I tried it).  On receipt of this exception, which could only come from the code fragment inserted in the code-to-test, the handler sets the trap flag in the context before returning.  This causes a 80000004h exception to come back to the handler upon the next instruction.  Note that with these exceptions, eip is already at the next instruction ie. one past the INT 3, or past the instruction executed with the trap flag set.  Accordingly all you have to do in the handler to continue single-stepping is to set the trap flag again and return to the system.
* Thanks to G.W.Wilhelm, Jr of IBM for this idea
 

Exception handling in multi-threaded applications

When it comes to exception handling in multi-threaded applications there is little or no help from the system. You will need to plan for likely faults and organise your threads accordingly.

The rules applying to the exception handling provided by the system (in the context of a multi-threaded application) are:-

  1. Only one type 1 (final handler) can be in existence at any one time for each process. If a new thread calls SetUnhandledExceptionFilter, this will simply replace the final handler – there is no chain of final handlers as there is for the type 2 (per-thread) handlers. Therefore the simplest way of using the final handler is still probably the best way in a multi-threaded application – establish it in the main thread as soon as possible after the program start point.
  2. The final handler will be called by the system if the process will be terminating, regardless of which thread caused the exception.
  3. However, there will only be a final unwind (immediately prior to termination) in the per-thread handlers established for the thread which caused the exception. Even if any other (innocent) threads have a window and a message loop, the system will not warn them that the process is about to terminate (no special message will be sent to them other than usual messages arising from the loss of focus of other windows).
  4. Therefore the other (innocent) threads cannot expect a final unwind if the process is to terminate.  And they will remain ignorant of the imminent termination.
  5. If, as is likely, these other innocent threads will also need to clear-up on such termination you will need to inform them from the final handler. The final handler will need to wait until these other threads have completed clearing up before returning to the system.
  6. The way in which the innocent threads are informed of the expected termination of the program depends on the precise make-up of your code. If the innocent thread has a window and message loop, then the final handler can use SendMessage to that window to send an application defined message (must be 400h or above), to inform that thread to terminate gracefully.
    If there is no window and message loop, the final handler could set a public variable flag, polled from time to time by the other thread. Alternatively you could use SetThreadContext to force the thread to execute certain termination code, by setting the value of eip to point to that code. This method would not work if the thread is in an API, for example, waiting for the return from GetMessage. In that case you would need to send a message as well, to make sure the thread returned from the API, so that the new context is set.
  7. RaiseException only works on the calling thread, so this cannot be used as a means of communication between threads to make an innocent thread execute its own exception handler code.
  8. How does the final handler know when it may proceed after informing the other threads that the program is about to terminate? SendMessage will not return until the recipient has returned from its window procedure and the final handler could wait for that return. Alternatively it could poll a flag waiting for a response from the other thread that it has finished clearing up (note you must call the API Sleep in the polling loop to avoid over-using the system). Or better still, the final handler could wait until the other thread has terminated (this can be done using the API WaitForSingleObject or WaitForMultipleObjects if there is more than one thread). Alternatively use could be made of the Event or Semaphore APIs.
  9. For an example of how these procedures could work in practice, suppose a secondary thread has the job of re-organising a database and then writing it to disk.  It may be in the middle of this task when the main thread causes an exception which enters your final handler. Here you could either cause the secondary thread to abort its job, by causing it to unwind and terminate gracefully, leaving the original data on disk or alternatively you could permit it to complete the task, and then inform the handler that it had finished so that the handler could then return to the system. You would need to stop the secondary thread starting any further such jobs if your handler had been called. This could be achieved by the handler setting a flag tested by the secondary thread before it started any job, or by using the Event APIs.
  10. If communication between threads is difficult, there is another way for one thread to access the stack of another thread, and thereby cause an unwind.  This makes use of the fact that whereas each thread has its own stack, the memory reserved for that stack is within the address space for the process itself. You can check this yourself if you watch a multi-threaded application using a debugger. As you move between threads the values of ESP and EBP will change, but they are all kept within the address space of the process itself. The value of FS will also be different between threads and will point to the Thread Information Block for each thread. So if you take the following steps one thread can access the stack and cause an unwind of another:-

  11. a. As each thread is created record in a static variable the value of its FS register.
    b. As each thread closes it returns the static variables to zero.
    c. The handler which needs to unwind other threads should take all the static variables in turn and for those which have a non-zero value (ie. thread was running at the time of the exception) the handlers should be called with the exception flag of 2 (EH_UNWINDING) and, a user flag of say, 400h to show that the per-thread handler is being called by your final handler. You cannot call a per-thread handler in a different thread using RtlUnwind (which is thread-specific) but it can be done using the following code (where ebx holds the address of the EXCEPTION_RECORD):-
MOV D[EBX+4],402h
L1:
PUSH ES
MOV AX,[FS_VALUE]
MOV ES,AX
ES MOV EDI,[0]
POP ES
L2:
CMP D[EDI],-1
JZ >L3
PUSH EDI,EBX
CALL [EDI+4]
ADD ESP,8h
MOV EDI,[EDI]
JMP L2
L3:
;make the exception flag EH_UNWINDING + 400h
;
;
;get FS value of thread to unwind
;
;get 1st per-thread handler address
;
;
;see if it’s the last one
;yes, so finish
;push ERR structure, EXCEPTION_RECORD
;call handler to run clear-up code
;remove the two parameters pushed
;get pointer to next ERR structure
;and do next if not at end
;code label when finished
;now loop back to L1 with a new FS_VALUE until all threads done

Here you see that the Thread Information Block of each innocent thread is read using the ES register, which is temporarily given the value of the thread’s FS register.

Instead of using FS to find the Thread Information Block you could use the following code to get a 32-bit linear address for it. In this code LDT_ENTRY is a structure of 2 dwords, ax holds the 16-bit selector value (FS_VALUE) to be converted and hThread is any valid thread handle:-
AND EAX,0FFFFh
PUSH ADDR LDT_ENTRY,EAX,[hThread]
CALL GetThreadSelectorEntry
OR EAX,EAX
JZ >L300
MOV EAX,ADDR LDT_ENTRY
MOV DH,[EAX+7]
MOV DL,[EAX+4]
SHL EDX,16D
MOV DX,[EAX+2]
OR EDX,EDX
L300:
;
;
;
;see if failed
;yes so return zero
;
;get base high
;get base mid
;shift to top of edx
;and get base low
;edx now=linear 32 bit address)
;return nz on success

The reason why it is important (using the flag 400h) to inform the handler being called that it is being called by another thread (the final handler) is that the thread being called is still running because the exception occurred in a different thread. The handler may well need to suspend the thread in these circumstances, so that the clear-up job can be achieved by the calling thread. The innocent thread would then be given a safe-place to go to before calling ResumeThread.  All this must be done before the final handler is allowed to return to the system because on return the system will simply terminate all threads by brute force.

Except1

This program provides a simple example of how exception handling can be used in practice in Windows programs written in assembler. The source code is contained in Except1.asm. This is written in GoAsm syntax. Although the program is a Windows GDI program, it only relies on message boxes, which is why there is no message loop.
The program has two exception handlers, a final exception handler and a per-thread exception handler. The final exception handler is created first, then a procedure is called which is in code protected by the per-thread exception handler. An exception occurs within that procedure and the per-thread handler is called. Within the handler, the user is asked whether the handler should swallow the exception or not. If the user decides to swallow the exception, the program would be able to continue to run, but actually in this case it terminates normally. If the user decides that the exception should not be swallowed by the handler, then the final exception handler is called (on the way to program closure). In real life, this handler would be responsible for completing logs and records, closing file handles, releasing memory etc. But before the program finally finishes, something interesting happens. The system calls the per-thread exception handler in case there is more clearing up to do in that particular stack frame using local data. This is the system unwind. All these events are followed from the various message boxes which appear on the screen.

Except2

This is a more complex program which is intended to demonstrate in more detail the contents of this article.

The source code for Except2.Exe (Except2.asm and Except2.RC) is also provided and again it is in GoAsm syntax.

The main window is actually a modal dialog. A final handler is set up very early in the process. When the "Cause Exception" button is clicked, first the dialog procedure is called with the command, then 2 further routines are called, the third routine causing an exception of the type chosen by the radiobuttons. As execution passes through this code, 3 per-thread exception handlers are created.

Seh8.bmp (20494 bytes)
The exception is either repaired in situ if possible, or the program recovers in the chosen handler from a safe-place. If the exception is allowed to go to the final handler you can either exit by pressing F3 or F5, or if you press F7 the final handler will try to recover from the exception.

You can follow events as they occur because each handler displays various messages in the listbox. There is a slight delay between each message so that you can follow more easily what is happening, or you can scroll the messages to get them back into view.

When the program is about to terminate, something interesting happens. The system causes a final unwind with the exception flag set to 2h. The messages sent to the listbox are slowed down even further because the program will be terminating soon!

You will see that the same type of unwind occurs if you specify that execution should continue from a "safe-place" or if F7 is pressed from the final handler. This unwind is initiating by the handler itself.

WB01512_.gif (115 bytes)COPYRIGHT NOTE - this article, Except1.asm, Except2.asm, Except2.RC and Except2.Exe are all
Copyright © Jeremy Gordon 1996-2002
[MrDuck Software]
http://www.GoDevTool.com
LEGAL NOTICE - The author accepts no responsibility for losses of any type arising from this article. Whereas the author has used his best endeavours to ensure that the contents of this article are correct, you should not rely on this and you should do your own tests.