Well, now I've got this (thanks to Nate Eldredge). If CPU exceptions are first handled by the kernel, then they should be propagated to user space in an OS-specific way. I had hoped it could be as simple as I did in the past with the PDP-11 and RT-11, about 35+ years ago, using a simple trap. But, well, the world nowadays is slightly more complicated.
The OS I'm actually targeting is Windows x64, so this answer is specific to that, using SEH.
After digging through old forums and documentation, I was able to get things done using FASM.
Here is a minimal working example with an SEH handler:
format PE64 CONSOLE entry start include 'seh64.inc' start: SUB RSP,8*(4+1) LEA RCX,[hello] call [printf] .try handler MOV ECX,0x40000000 ; Instructions Counter RDPMC ; Read Performance-Monitoring Counter .end safe_place: LEA RCX, [finish] call [printf] XOR ECX,ECX call [ExitProcess] handler: SUB RSP,8*(4+1) MOV qword [R8+CONTEXT64.Rip],safe_place LEA RCX,[exception] call [printf] XOR EAX,EAX ADD RSP,8*(4+1) retn section '.data' data readable writeable hello db "Hello, SEH!",10,0 finish db "Sucessfully finished",10,0 exception db "Instruction caused exception",10,0 data seh end data section '.idata' import data readable library kernel32,'KERNEL32.DLL', msvcrt, 'msvcrt.dll' import kernel32, ExitProcess,'ExitProcess' import msvcrt, printf, 'printf'
Now exception on RDPMC will not cause application crash, and will be landed into my hands in handler:
>sehtest.exe Hello, SEH! Instruction caused exception Sucessfully finished
Of course, this will work for any kind of exception as well, for example:
.try handler XOR EAX,EAX MOV dword [EAX], 0 .end
or
.try handler XOR EBX,EBX DIV EBX .end
Content of seh64.inc:
include 'INCLUDE\win64a.inc' macro enqueue list,item { match any,list \{ list equ list,item \} match ,list \{ list equ item \} } macro dequeue list,item { done@dequeue equ match first=,rest,list \{ item equ first list equ rest restore done@dequeue \} match :m,done@dequeue:list \{ item equ m list equ restore done@dequeue \} match ,done@dequeue \{ item equ restore done@dequeue \} } macro queue list,index,item { local copy copy equ list rept index+1 \{ dequeue copy,item \} } macro data directory { done@data equ match =3,directory \{ local l_infos,_info,_end l_infos equ align 4 match list,l_handlers \\{ irp _handler,list \\\{ local rva$ rva$ = rva $ enqueue l_infos,rva$ db 19h,0,0,0 dd _handler,0 \\\} \\} data 3 match list,l_begins \\{ irp _begin,list \\\{ dequeue l_ends,_end dequeue l_infos,_info dd _begin dd _end dd _info \\\} \\} restore done@data \} match ,done@data \{ data directory restore done@data \} } l_begins equ l_ends equ l_handlers equ macro .try handler { local ..try __TRY equ ..try local ..end __END equ ..end local ..catch __CATCH equ ..catch __TRY: if ~ handler eq virtual at handler __CATCH: end virtual end if } macro .catch { jmp __END __CATCH: } macro .end { __END: enqueue l_begins,rva __TRY enqueue l_ends,rva __END enqueue l_handlers,rva __CATCH restore __TRY restore __END restore __CATCH } seh equ 3 CONTEXT64.Rip = 0F8h
Be very careful with this code above, it is just "quick and dirty" (but at least functional) example.
Update
And as suggested by Margaret Bloom, here is a code snippet that uses AddVectoredExceptionHandler. This time EuroAssembler:
EUROASM AutoSegment=ON, CPU=X64, SIMD=AVX2 %^SourceName PROGRAM Format=PE, Width=64, Model=Flat, IconFile=, Entry=main INCLUDE winscon.htm, winabi.htm, cpuext64.htm helloMsg D "Hello, Exception!",0 finishMsg D "Sucessfully finished",0 exceptionMsg D "Instruction caused exception",0 Handle DQ 0 main: nop StdOutput helloMsg, Eol=Yes, Console=Yes WinABI AddVectoredExceptionHandler, 1, VectoredHandler MOV [Handle],RAX MOV ECX, 0x40000000 ; Counter RDPMC ; 2 bytes (0F33) EXCEPTION_PRIV_INSTRUCTION WinABI RemoveVectoredExceptionHandler, [Handle] StdOutput finishMsg, Eol=Yes, Console=Yes TerminateProgram VectoredHandler PROC PUSH RBX PUSH RSI PUSH RDI MOV RBX, RCX ; EXCEPTION_POINTERS MOV RDI, [RBX + 8] ; CONTEXT MOV RSI, [RBX] ; EXCEPTION_RECORD MOV EAX, [RSI] ; ExceptionCode CMP EAX, 0C0000096h ; EXCEPTION_PRIV_INSTRUCTION JNE continue_search ADD [RDI + 0F8h], 2, DATA=Q ; Rip += 2 (adjust for RDPMC) StdOutput exceptionMsg, Eol=Yes, Console=Yes MOV EAX, 0FFFFFFFFh ; EXCEPTION_CONTINUE_EXECUTION JMP return continue_search: MOV EAX,1 ; EXCEPTION_CONTINUE_SEARCH return: POP RDI POP RSI POP RBX RET ENDP ENDPROGRAM
and it works as well:
>vecdemo.exe Hello, Exception! Instruction caused exception Sucessfully finished