These tests demonstrate what happens when the debuggee causes an exception (either intentionally or unintentionally).
To run a test, start Testbug as the debuggee and press F9 (run in background) from "START". Then choose one of Testbug's menu items in "Use of GoBug, exceptions ..".
The unintentional exceptions (usually memory page faults or divide by zero)
make GoBug's exception dialog appear, and details also appear in the log.
With these exceptions the instruction has not been carried out, and
the system gives the debugger an early chance to deal with the matter.
The exception dialog gives you a choice of
jumping over the exception (the instruction causing the exception is
not executed at all), ignoring the exception (the instruction is executed),
or time to think (GoBug just waits in the debug loop for the next
action key).
Other types of exceptions are deliberate, for example, an INT3 in the
code, or DebugBreak which is the system's own equivalent (it just
calls code with an INT3!). These exceptions act as breakpoints -
the code just stops at the breakpoint and the debuggee goes into the
debug loop.
Another deliberate exception is RaiseException, which can be used by an application to call its own exception handling code or a debugger. Note that in this case the exception occurs after RaiseException has returned.
Note that Testbug does not have an exception handler, so these tests do not cover what happens if an application's own handler deals with an exception. For that, see my article in Except.zip available for download from my web site.
   Exception 80000005h
(memory read/write)
   Exception 0C0000094h (divide by zero)
   Exception 0C0000095h (divide resulting
in overflow)
   Unaligned MOVDQA exception
   INT3 in code (probably put in
intentionally)
   Calling RaiseException (definitely put in
intentionally)
   Calling DebugBreak (definitely put in
intentionally)
   Own single-stepping test (using an
INT3 and handler combination)
   Output string sent by calling
OutputDebugString (definitely put intentionally)
Exception 80000005h
(memory read/write)
This test uses this code which is self-explanatory:-
EXCEPTION_MEM:
MOV ESI,-1
MOV EAX,[ESI]
RET
Exception 0C0000094h
(divide by zero)
This test uses this code which is self-explanatory:-
EXCEPTION_DIV:
XOR ECX,ECX
XOR ECX,ECX
XOR ECX,ECX
DIV ECX ;divide by zero exception
RET
Avoid such exceptions by always testing the divisor for zero before
the DIV:-
JECXZ >L4
DIV ECX
L4:
Exception 0C0000095h
(divide resulting in overflow)
This test uses this code. There is an overflow because
you are trying to divide an enormous number in edx:eax by 10 and the
result will not fit into a single 32-bit register.
EXCEPTION_DIVOVER:
MOV EDX,-1
MOV EAX,-1
MOV ECX,10D
DIV ECX ;overflow exception
RET
Avoiding these exceptions is more difficult, but when the divisor is
known you should always check the dividend immediately prior to the
divide to make sure there will be no overflow. Also don't forget to
clear edx if you are doing a 32-bit divide. Remember when
using DIV ECX, the dividend is edx:eax not eax.
Unaligned MOVDQA exception
This one only works if your processor supports SSE2
instructions (Intel Pentium 4 and above). When reading from memory, the
fast MOVDQA instruction (move aligned double quadword) requires that the
memory area is aligned on a 16 byte boundary. You would achieve this by
declaring ALIGN 16 just before declaring the data. Here we make sure that
the data is not aligned, causing a type 96h exception. Note that
you can use the slower MOVDQU (move unaligned double quadword) to move
data which is not aligned into the XMM registers. The MOVAPD (move two
aligned double precision values) and MOVUPD (move two unaligned double
precision values) instructions work in the same way.
Set the breakpoint to UNALIGNEDMOVDQA_EXCEPTION for this test:-
DATA SECTION
;
ALIGN 4
DB 0 ;move ahead by one byte
UNALIGNED_DATA DQ 0
DQ 0
;
CODE SECTION
;
UNALIGNEDMOVDQA_EXCEPTION:
MOVDQA XMM0,[UNALIGNED_DATA] ;cause exception with unaligned data
RET
INT3 in code
(probably put in intentionally)
This simply stops the debuggee in its tracks. It comes
into GoBug's debug loop immediately after the INT3.
INT3_TEST:
INT 3
MOV ECX,3
L10:
CMP EAX,EAX
LOOP L10
RET
Calling RaiseException
This API is normally used to send a private exception to the calling application's own handler. For example the handler might be in charge of allocating more memory if required. RaiseException could be called to ask the handler to establish memory in the first place. Under debuggee control, the exception comes into GoBug on the return from the API call.
RAISE_EXCEPTION:
PUSH 0,0,0,22222h
CALL RaiseException
RET
Calling DebugBreak
(definitely put in intentionally)
This is the system's version of INT3. In this case
there is an INT3 actually within the API. GoBug will therefore show
the break in shared code, inside the API. You will have to
single-step out of the API to get back to Testbug's code.
DEBUGBREAK_TEST:
CALL DebugBreak
MOV ECX,3
L12:
CMP EAX,EAX
LOOP L12
RET
Own single-stepping test (using an INT3 and handler combination)
Thanks to Bill Welhelm, Jr (May 1999) for this idea.
Using this code you don't need a debugger at all to make your code
single-step. This works by establishing an exception handler and
inserting an INT3 at the beginning of the code you wish to check. The
system calls the handler when the INT3 is reached. The handler then
sets the trap flag in the register context and returns to the system.
Since the trap flag is now set, the next instruction also comes into
the handler. And so on until the correct number of instructions have
past. Here the handler's output is drawn to the screen using a tester
window (details of this tester code are in GoBug's help "other techniques
to try"). For more information about exception handlers
see my paper Except.zip.
SS_HANDLER:
PUSH EBP
MOV EBP,ESP
PUSH EBX,EDI,ESI ;save registers as required by Windows
MOV EBX,[EBP+8] ;get exception record in ebx
TEST D[EBX+4],01h ;see if its a non-continuable exception
JNZ >L14 ;yes
TEST D[EBX+4],02h ;see if EH_UNWINDING
JNZ >L14 ;yes
MOV ESI,[EBP+10h] ;get context record in esi
MOV EAX,[EBX] ;get ExceptionCode
CMP EAX,80000004h ;see if here because trap flag set
JZ >L10 ;yes
CMP EAX,80000003h ;see if its own INT 3 inserted to single-step
JNZ >L14 ;no
;***** note eip is now one after INT 3 - so may just continue ...
L10:
DEC D[SSCOUNT] ;stop when correct number done
JZ >L12
OR D[ESI+0C0h],100h ;set trap flag in context
L12:
MOV EAX,[ESI+0B8h] ;get eip
CALL TESTER ;write eip to the screen
XOR EAX,EAX ;eax=0 reload context and return to system
JMP >L17
L14:
MOV EAX,1 ;eax=1 system to go to next handler
L17:
POP ESI,EDI,EBX
MOV ESP,EBP
POP EBP
RET
;
SINGLE_STEPTEST:
PUSH EBP,0,0 ;save ebp, area for flags, no safe place message pointer required
PUSH ADDR SAFE_PLACE
PUSH ADDR SS_HANDLER
FS PUSH [0]
FS MOV [0],ESP ;point to structure just established on the stack
;*******************
MOV D[SSCOUNT],5 ;do 5 visits to the handler
INT 3 ;cause visit to the handler now
MOV EAX,4444h ;
MOV ECX,EAX ; here is the code
SHL ECX,2 ; which will be
PUSH ECX ; single-stepped
POP ECX ; by the handler
;*******************
SAFE_PLACE:
FS POP [0] ;restore original exception handler from stack
ADD ESP,14h ;throw away remainder of ERR structure made earlier
RET
Output string sent by calling OutputDebugString (definitely put intentionally)
I suppose this might be useful if you wanted quickly to
receive some information from the debuggee during testing that was
difficult to obtain in other ways.
OUTPUT_STRING:
PUSH 'Hello from debuggee! (ansi string)'
CALL OutputDebugStringA
RET