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
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
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
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.
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.
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..
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
Ok, think it's fixed.. comments, feedback.. improvements?
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 Thing
TM) 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
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).
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.
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!