The MASM Forum

64 bit assembler => 64 bit assembler. Conceptual Issues => Topic started by: johnsa on December 04, 2012, 11:12:22 AM

Title: 64bit Exceptions
Post by: johnsa on December 04, 2012, 11:12:22 AM
Hey,

RaiseException is available as a built in API call which can take a user defined exception code. The MSDN refers to the C++ built-in GetExceptionCode() macro to retrieve it but I don't seem to be able to find any info on how I'd retrieve it from the actual exception handler in asm.
Reason being i've implemented a try/catch/throw set of macros which are working really well.. but I need to be able to pickup that code in the handler as throw uses it to indicate the type of exception.

Any info would be appreciated!
John
Title: Re: 64bit Exceptions
Post by: japheth on December 04, 2012, 07:04:19 PM
IIRC, GetExceptionCode() is a C-thing. It requires the CRT to install an exception handler, which does the "unwinding" and  calls your code. Do you really want this?

A bare exception handler in 64-bit is installed via FRAME:<exception_handler>:


;--- Win64 console application with exception handler.
;--- this variant can be assembled by both JWasm and ML64.
;--- assemble: jwasm -c Win64_5m.asm
;---           ml64 -c Win64_5m.asm
;--- link: link /subsystem:console /Libpath:\WinInc\Lib64 Win64_5m.obj

    option casemap:none

    includelib <msvcrt.lib>

exit   proto
printf proto

;--- CStr(): macro function to simplify defining a string

CStr macro Text:VARARG
local szText
    .const
szText  db Text,0
    .code
    exitm <offset szText>
endm

EXCEPTION_MAXIMUM_PARAMETERS EQU 15
DWORD64 typedef QWORD

EXCEPTION_RECORD64  struct
ExceptionCode       DWORD ?
ExceptionFlags      DWORD ?
ExceptionRecord     DWORD64 ?
ExceptionAddress    DWORD64 ?
NumberParameters    DWORD ?
__unusedAlignment   DWORD ?
ExceptionInformation DWORD64 EXCEPTION_MAXIMUM_PARAMETERS dup (?)
EXCEPTION_RECORD64  ends

M128 struct
Low_     QWORD ?
High_    QWORD ?
M128 ends

LEGACY_SAVE_AREA struct
ControlWord WORD ?
Reserved0   WORD ?
StatusWord  WORD ?
Reserved1   WORD ?
TagWord     WORD ?
Reserved2   WORD ?
ErrorOffset DWORD ?
ErrorSelector WORD ?
ErrorOpcode WORD ?
DataOffset  DWORD ?
DataSelector WORD ?
Reserved3   WORD ?
FloatRegisters BYTE 8*10 dup (?)
LEGACY_SAVE_AREA ends

CONTEXT struct
P1Home  DWORD64 ?
P2Home  DWORD64 ?
P3Home  DWORD64 ?
P4Home  DWORD64 ?
P5Home  DWORD64 ?
P6Home  DWORD64 ?
ContextFlags    DWORD   ?
MxCsr   DWORD   ?
SegCs   WORD    ?
SegDs   WORD    ?
SegEs   WORD    ?
SegFs   WORD    ?
SegGs   WORD    ?
SegSs   WORD    ?
EFlags  DWORD   ?
Dr0_    DWORD64 ?
Dr1_    DWORD64 ?
Dr2_    DWORD64 ?
Dr3_    DWORD64 ?
Dr6_    DWORD64 ?
Dr7_    DWORD64 ?
Rax_    DWORD64 ?
Rcx_    DWORD64 ?
Rdx_    DWORD64 ?
Rbx_    DWORD64 ?
Rsp_    DWORD64 ?
Rbp_    DWORD64 ?
Rsi_    DWORD64 ?
Rdi_    DWORD64 ?
R8_ DWORD64 ?
R9_ DWORD64 ?
R10_    DWORD64 ?
R11_    DWORD64 ?
R12_    DWORD64 ?
R13_    DWORD64 ?
R14_    DWORD64 ?
R15_    DWORD64 ?
Rip_    DWORD64 ?
Xmm0_   M128 <>
Xmm1_   M128 <>
Xmm2_   M128 <>
Xmm3_   M128 <>
Xmm4_   M128 <>
Xmm5_   M128 <>
Xmm6_   M128 <>
Xmm7_   M128 <>
Xmm8_   M128 <>
Xmm9_   M128 <>
Xmm10_  M128 <>
Xmm11_  M128 <>
Xmm12_  M128 <>
Xmm13_  M128 <>
Xmm14_  M128 <>
Xmm15_  M128 <>
FltSave LEGACY_SAVE_AREA    <>
Fill    DWORD   ?
DebugControl    DWORD64 ?
LastBranchToRip DWORD64 ?
LastBranchFromRip   DWORD64 ?
LastExceptionToRip  DWORD64 ?
LastExceptionFromRip    DWORD64 ?
Fill1   DWORD64 ?
CONTEXT ends

    .CODE

exchdl proc pRecord:ptr, ulframe:qword, pContext:ptr, x4:ptr

    sub rsp,28h
    add qword ptr [r8].CONTEXT.Rip_, 1  ;1=size of "in EAX, DX" opcode
    mov edx, [rcx].EXCEPTION_RECORD64.ExceptionCode
    mov rcx, CStr("exception code: %X",10)
    call printf
    mov eax, 0  ;0=continue execution?
    add rsp,28h
    ret

exchdl endp

VMwareInstalled proc FRAME:exchdl

    push rbx
    .pushreg rbx
    .endprolog

    mov eax, 0564D5868h
    mov ebx, 08685D465h
    mov ecx, 10
    mov dx, 05658h
    in eax, dx
    cmp ebx, 564D5868h
    setz al
    movzx eax,al

    add rsp,0
    pop rbx
    ret

VMwareInstalled endp

main proc FRAME

    sub rsp,28h
    .allocstack 28h
    .endprolog

    mov rcx, CStr("Testing VMware presence",10)
    call printf

    call VMwareInstalled

    lea rcx, CStr("running in VMware",10)
    and eax, eax
    jnz @F
    lea rcx, CStr("NOT running in VMware",10)
@@:
    call printf

    add rsp,28h
    ret

main endp

mainCRTStartup proc
    sub rsp,28h
    call main
    mov ecx,eax
    call exit
mainCRTStartup endp

    END



Title: Re: 64bit Exceptions
Post by: johnsa on December 04, 2012, 08:34:39 PM
Hi,

Yep I'm using the standard exception handler setup as you've shown. What I've done however is set it up so that the generic exception handler looks at a (per-thread) currently assigned user handler and calls that. This currently assigned handler is setup by the TRY macro and the corresponding handler in the CATCH block. The only reason I've done this is so that one could have different handlers through different parts of a proc rather than just one for the whole thing. It also makes the code tidy in terms of having something like this:



TRY myException
xor ebx,ebx
idiv ebx
CATCH
invoke MessageBox,NULL,CStr("Divide by Zero"),CStr("error"),MB_OK
ENDCATCH



That is working very nicely with basically no real overhead save the move of current handler ptr.
What  I'd like to do now is something like this:



FILE_NOT_FOUND_EXCEPTION equ 1

TRY myException

   ; Try open a file...

   .if (eax == 0)
     THROW FILE_NOT_FOUND_EXCEPTION
   .endif

CATCH
   ; Here I need to pickup the parameter from THROW.. which essentially just wrapse the win32 call RaiseException() .. that takes a parameter, and doesn't seem to be C specific.. so in here I'd like to retrieve that param.
   .if (param == FILE_NOT_FOUND_EXCEPTION)
      ; handle this specific one
   .else
      invoke MessageBox,NULL,CStr("Something went wrong"),CStr("error"),MB_OK
   .endif 
ENDCATCH

Title: Re: 64bit Exceptions
Post by: japheth on December 05, 2012, 01:54:50 AM
Quote from: johnsa on December 04, 2012, 08:34:39 PM
What  I'd like to do now is something like this:
[snip]

I understand ...... or, more exactly: I don't understand at all. Because, you've written the macros, you've written the exception handler - so you should probably be the best person to find a way how to transfer arguments to the CATCH block. In fact, since your macros and your code are not public, you're not only the best but the only person who should know.
Title: Re: 64bit Exceptions
Post by: johnsa on December 05, 2012, 01:56:53 AM
Haha good point.

I'll finish what I'm doing at the moment with it, got the THROW to work. Just want to allow the macros to nest at least 1 level. Then I'll put it up here.
Title: Re: 64bit Exceptions
Post by: johnsa on December 05, 2012, 03:39:50 AM
Ok here it is, should be thread-safe, support up to 256 threads and allow 1 level of nesting.

Use as follows:


        TRY errorLoading    ; errorLoading (must be a unique name.. for the macros to create correct labels)
THROW 1    ; comment this out for the default handler in catch.
xor ebx,ebx
idiv ebx
CATCH
GETEXCEPTIONCODE    ; This will get the related code from THROW 1 above (or 0 if default)
.if (rax == 1)
invoke MessageBox,NULL,CStr("Bad 1"),CStr("very bad"),MB_OK
invoke ExitProcess,0
.else
invoke MessageBox,NULL,CStr("Bad"),CStr("very bad"),MB_OK
invoke ExitProcess,0
.endif
ENDCATCH


Thoughts , comments, improvements..
Title: Re: 64bit Exceptions
Post by: johnsa on December 05, 2012, 03:43:14 AM
Ok so nesting seems to be a bit broken.. I think the endcatch macro is breaking... but it should allow you to do:



TRY errorLoading
;THROW 1
;xor ebx,ebx
;idiv ebx
TRY errorLoading2
xor ebx,ebx
idiv ebx
CATCH
invoke MessageBox,NULL,CStr("inner exception"),CStr("div 0"),MB_OK
ENDCATCH

CATCH
GETEXCEPTIONCODE
.if (rax == 1)
invoke MessageBox,NULL,CStr("Bad 1"),CStr("very bad"),MB_OK
invoke ExitProcess,0
.else
invoke MessageBox,NULL,CStr("Bad"),CStr("very bad"),MB_OK
invoke ExitProcess,0
.endif
ENDCATCH

Title: Re: 64bit Exceptions
Post by: johnsa on December 05, 2012, 08:45:49 AM
Ok, think it's fixed.. comments, feedback.. improvements?
Title: Re: 64bit Exceptions
Post by: jj2007 on December 05, 2012, 10:53:38 AM
Quote from: johnsa on December 05, 2012, 08:45:49 AM
Ok, think it's fixed.. comments, feedback.. improvements?

Cannot test it with my trusty Win XP, but it does look impressive :t

One question, though: Is THROW FILE_NOT_FOUND_EXCEPTION a C thing? In Basic we distinguish between exceptions (The Real ThingTM) and simple runtime errors. Example:
Quoteinclude \masm32\MasmBasic\MasmBasic.inc   ; download (http://masm32.com/board/index.php?topic=94.0)
   Init tc       ; Initialise with Try/Catch
   ErrLines       ; add extra code to get source code line where error occurred
   Dim My$(3)       ; Create a string array with four elements

; ######   Type 1: catch a runtime error ######

;   con means: if there is an error, print it to the console (default is a MsgBox, key = print to console and wait for a keypress)
TryRTE BadIndex, con
   Let My$(7)="This won't work"       ; seven is too high
   
BadIndex:
   cmp edx, $       ; simple error check
   .if Zero?
        Print "Bad index!!", CrLf$, CrLf$
   .endif

; ######   Type 2: exceptions ######

   xor eax, eax
Try
   div eax
Catch only
   Print "div 0 is a no-no", CrLf$
   Print "The OS reports:", CrLf$, LastEx(info), CrLf$
Finally
   Inkey "bye"
   Exit
   TryCatchEnd
end start

Output:
Line 10: Count too high
or wrong # of dimensions
Bad index

div 0 is a no-no
The OS reports:
{ERRORE DI EXCEPTION}
Divisione intera per zero
EIP     004010B1
Code    C0000094
bye

Title: Re: 64bit Exceptions
Post by: johnsa on December 05, 2012, 08:00:18 PM
Hey,

FILE_NOT_FOUND_EXCEPTION would be your user defined error (in the little example case). It's just a numeric that allows catch to know which error it's handling.

For the most part I'd stick to normal traditional error handling, just as these macros incur some overhead, so wrapping say a 10 line proc wouldn't be ideal.
The best model I think would be the following:
1) Simple error handling where possible
2) If it's a large proc, not performance critical or has lots of different sections, use try/catch
3) If it's a very small proc that realistically can only generate one type of error.. and you really want an exception, use raisexception with the normal jwasm syntax of frame:<handler> (that way it's precompiled).
Title: Re: 64bit Exceptions
Post by: agguro on April 02, 2016, 09:14:00 PM
This is a late "call for answer" but I'm just started with exceptions (and 64 bit coding). Can anyone explain the parameters in

exchdl proc pRecord:ptr, ulframe:qword, pContext:ptr, x4:ptr , please.

I debugged the example in masm 64 bits (on visual studio 2015 if the question rises) and observing the data in the parameters doesn't make any sence nor equality with the contents of rcx and r8.
Title: Re: 64bit Exceptions
Post by: LiaoMi on June 03, 2016, 10:05:09 PM
Hallo,

сan someone tell me what are the methods of interception of exceptions in the program? By not using the debugger, instead inject my own library.

Thanks!