News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Simple Exception Handler

Started by HooKooDooKu, February 10, 2012, 06:15:36 PM

Previous topic - Next topic

HooKooDooKu

Ok, so in the last week or so I'm getting a handle on how to properly code an ASM PROC.  But now I'm running into a brick wall on what seems like should be a simple Exception Handler. 

At this point, all I want to do if an exception happens in my PROC is to unwind the stack from before my PROC was called and return an error code.  From what I've read, it should be as simple as calling RtlUnwindEx in the exception handler, passing in the return code as one of the arguments, and then moving ExceptionContinueExecution into rax.  Based on setting a break point in my exception handler, all that is happening is the exception handler is getting called over and over.

Here's what I've got so far in some test code: (NOTE: Exception.inc defines the exception structures as found at http://blogs.msdn.com/b/geoffda/archive/2010/03/10/how-to-implement-try-except-in-ml64-masm.aspx)

option casemap :none

include ksamd64.inc
include Exception.inc

.data

.code

Test_Frame struct
   ;Storage for 128-bit non-volatile registers
   RegXMM6      OWORD   ?   
   RegXMM7      OWORD   ?   

   ;Storage for LOCAL variables
   LocalVar1    BYTE   ?   
   Filler1       BYTE*3 ?
   LocalVar2    WORD   ?   
   Filler2       WORD   ? 
   LocalVar3   DWORD   ?   
   LocalVar4   DWORD   ?   
   LocalVar5   QWORD   ?   
   LocalVar6   QWORD   ?
   LocalVar7   OWORD   ?
   ;Filler7      QWORD   ?
Test_Frame ends

Test_eh PROC ;ExceptionRecord:PEXCEPTION_RECORD, ExtablisherFrame:QWORD, ContextRecord:PCONTEXT, DispatcherContext:PDISPATCHER_CONTEXT
   
   push rbp      ;get the stack 16 byte aligned
   sub rsp, 30h   ;allocate shadow space

   mov rax, 0            ;Load parameters for RtlUnwindeEx
   mov [rsp+28h], rax
   mov [rsp+20h], r9
   mov r9, 012345678h         ;Return Value
   mov r8, rcx
   mov rdx, 0
   mov rcx, rdx

   call RtlUnwindEx

   mov rax, ExceptionContinueExecution

   add rsp, 30h
   pop rbp
   ret
Test_eh ENDP

public Test_asm
Test_asm PROC FRAME : Test_eh ;Parm1:BYTE, Parm2:WORD, Parm3:DWORD, Parm4:QWORD, Parm5:DWORD, Parm6:QWORD

   ;Push non-volatile registers
   push_reg rbx
   push_reg rsi
   push_reg rsi

   ;Alloc Frame Struct and Set Frame Pointer
   alloc_stack( sizeof Test_Frame )
    set_frame rsp, 0                  ;only needed for dynamic stack allocation (alloca)

   ;Save 128-bit non-volatile registers
   save_xmm128 xmm6, Test_Frame.RegXMM6
   save_xmm128 xmm7, Test_Frame.RegXMM7

   SizeOfStackFrame EQU <(sizeof Test_Frame) +8 +8 +8 +8>

   ;Parameters                              
   Parm1   EQU < BYTE PTR [rsp + SizeOfStackFrame +00h]>   
   Parm2   EQU < WORD PTR [rsp + SizeOfStackFrame +08h]>   
   Parm3   EQU <DWORD PTR [rsp + SizeOfStackFrame +10h]>   
   Parm4   EQU <QWORD PTR [rsp + SizeOfStackFrame +18h]>
   Parm5   EQU <DWORD PTR [rsp + SizeOfStackFrame +28h]>
   Parm6   EQU <QWORD PTR [rsp + SizeOfStackFrame +28h]>

   .ENDPROLOG

   ;Dump register parameters to their shadow space
   mov Parm1, cl
   mov Parm2, dx
   mov Parm3, r8d
   mov Parm4, r9

   ;LOCAL variables
   Var1   EQU <[rbp].Test_Frame.LocalVar1>
   Var2   EQU <[rbp].Test_Frame.LocalVar2>
   Var3   EQU <[rbp].Test_Frame.LocalVar3>
   Var4   EQU <[rbp].Test_Frame.LocalVar4>
   Var5   EQU <[rbp].Test_Frame.LocalVar5>
   Var6   EQU <[rbp].Test_Frame.LocalVar6>

   ;Your Code Goes HERE
   mov al, Parm1
   mov Var1, 01h
   mov Var2, 0123h
   mov Var3, 4567h
   mov Var4, 012345678h
   mov rbx, 0          ;!!! Cause an excepetion !!!
   mov eax, [rbx]

   ;EPILOG
   movdqa XMM7, [rbp].Test_Frame.RegXMM7
   movdqu XMM6, [rbp].Test_Frame.RegXMM6
   add rsp, ( sizeof Test_Frame )
   pop rsi
   pop rbx

   ret

Test_asm ENDP


What am I missing?

tofu-sensei

first off, you need to make sure your exception handler also has xdata associated with it.

baltoro

HOOKOODOOKU,
EDGAR (donkey) had a similar problem with Vectored Exception Handlers in 64-Bit Windows,...
The thread is here: AddVectoredExceptionHandler Single Stepping
Baltoro

HooKooDooKu

Quote from: tofu-sensei on February 10, 2012, 10:17:28 PM
first off, you need to make sure your exception handler also has xdata associated with it.

That's what I thought the Exception Handling MASM macros (the prolog) was supposed to do:

   ;Push non-volatile registers
   push_reg rbx
   push_reg rsi
   push_reg rsi

   ;Alloc Frame Struct and Set Frame Pointer
   alloc_stack( sizeof Test_Frame )
   set_frame rsp, 0                  ;only needed for dynamic stack allocation (alloca)

   ;Save 128-bit non-volatile registers
   save_xmm128 xmm6, Test_Frame.RegXMM6
   save_xmm128 xmm7, Test_Frame.RegXMM7

   .ENDPROLOG

    ...


Is there something else I have to add?

mineiro

Follow 2 simplest codes, I have tested it only using XP, so I do not know if this will work in others O.S.
.code
align 16
start proc FRAME:Structured_error
.ENDPROLOG
mov rax,0 ;intentional error
mov rax,[rax]
continue_program::
;int 3h
xor rcx,rcx
call ExitProcess
start endp

align 16
Structured_error proc
jmp continue_program
ret
Structured_error endp
END


public handle_error
handle_error PROTO
.code
align 16
start proc uses rbx rsi rdi hInstance:qword,command_line:qword
FROM_HERE::
    mov rax,cr4 ;intentional error
TO_HERE::
xor rcx,rcx
call ExitProcess
start endp

align 16
handle_error proc
jmp TO_HERE
handle_error endp

pdata segment readonly ALIAS (".pdata")
align 8
dd imagerel FROM_HERE
dd imagerel TO_HERE
dd imagerel SCOPE ;look below
pdata ends

align 8
xdata segment read ALIAS (".xdata")
SCOPE db 19h,0,0,0
dd imagerel handle_error ;procedure to watch
xdata ends
END

tofu-sensei

Quote from: HooKooDooKu on February 10, 2012, 10:49:28 PM
That's what I thought the Exception Handling MASM macros (the prolog) was supposed to do:
yes, you need to use them in your exception handler function (Test_eh) as well.

HooKooDooKu

Quote from: baltoro on February 10, 2012, 10:26:51 PM
HOOKOODOOKU,
EDGAR (donkey) had a similar problem with Vectored Exception Handlers in 64-Bit Windows,...
The thread is here: AddVectoredExceptionHandler Single Stepping

It doesn't appear donkey's situation applies to me.  I don't have an application for which I'm attempting to install a program level exception handler.  I simply have a function that I'm using the exception macros defined by Microsoft to allow the stack to be unwound using simple prolog/epilog code.  Where as donkey had to call a function to add a vectored exception handler, I'm simply using the FRAME keyword for PROC to specify the exception function.

I'm not having any problem getting the logic to jump to the exception function.  And from what I THINK I understand, all I need to do is simply call RtlUnwindEx and it should unwind the stack for my back to the start of my function and set a return value for the function. 

So I guess one question is whether I'm even attempting to go about this correctly.  But assuming that I am, I can not find any useful example code that shows RtlUnwindEx being called (especially from an exception function).  So far, here's the best logic I've been able to come up with, but when RtlUnwindEx is called, it simply jumps back to the start of my excption handler (i.e. apparently an exception is occuring inside of RtlUnwindEx).

Test_eh PROC FRAME ;ExceptionRecord:PEXCEPTION_RECORD, EstablisherFrame:QWORD, ContextRecord:PCONTEXT, DispatcherContext:PDISPATCHER_CONTEXT
   
   push_reg rbx      ;get the stack 16 byte aligned
   alloc_stack( 30h )   ;allocate shadow space for call to RtlUnwindEx
   set_frame rsp, 0
   .ENDPROLOG

   ;Parameters               
   pExceptionRecord   EQU <[rsp +30h +8+8 +00h]>
   pEstablisherFrame   EQU <[rsp +30h +8+8 +08h]>
   pContextRecord      EQU <[rsp +30h +8+8 +10h]>
   pDispatcherContext   EQU <[rsp +30h +8+8 +18h]>

   ;Dump register parameters to their shadow space
   mov pExceptionRecord,   rcx
   mov pEstablisherFrame,   rdx
   mov pContextRecord,      r8
   mov pDispatcherContext, r9

   ;Call RtlUnwindEx with a Return code of 12345678h
   mov rbx, pDispatcherContext
   mov rax, [rbx].DISPATCHER_CONTEXT.HistoryTable
   mov [rsp+28h], rax
   mov rax, pContextRecord
   mov [rsp+20h], rax
   mov r9, 012345678h            ;Return Value
   mov r8, pExceptionRecord
   mov rdx, [rbx].DISPATCHER_CONTEXT.TargetIp
   mov rcx, pEstablisherFrame

   call RtlUnwindEx

   ;RtlUnwindEx isn't supposet to 'return' back to here
   ;   so there doesn't seem be be a need for any epilog
Test_eh ENDP

tofu-sensei

Check EXCEPTION_RECORD.ExceptionFlags for EXCEPTION_UNWIND, if that flag is set return ExceptionContinueSearch.