+{-# LANGUAGE CPP #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ForeignFunctionInterface #-}
+#include "debug.h"
+module Mate.X86TrapHandling where
+
+import qualified Data.Map as M
+
+import Foreign
+import Foreign.C.Types
+
+import Mate.Types
+import Mate.MethodPool
+import Mate.ClassPool
+
+
+foreign import ccall "register_signal"
+ register_signal :: IO ()
+
+
+getTrapType :: CUInt -> CUInt -> IO CUInt
+getTrapType signal_from from2 = do
+ tmap <- getTrapMap
+ case M.lookup (fromIntegral signal_from) tmap of
+ (Just (MI _)) -> return 0
+ (Just (SFI _)) -> return 2
+ (Just _) -> error "getTrapMap: doesn't happen"
+ -- maybe we've a hit on the second `from' value
+ Nothing -> case M.lookup (fromIntegral from2) tmap of
+ (Just (VI _)) -> return 1
+ (Just (II _)) -> return 4
+ (Just _) -> error "getTrapType: abort #1 :-("
+ Nothing -> error "getTrapType: abort #2 :-("
+
+foreign export ccall mateHandler :: CUInt -> CUInt -> CUInt -> CUInt -> IO CUInt
+mateHandler :: CUInt -> CUInt -> CUInt -> CUInt -> IO CUInt
+mateHandler eip eax ebx esp = do
+ callerAddr <- callerAddrFromStack esp
+ blah <- getTrapType eip (callerAddr - 3)
+ case blah of
+ 0 -> staticCallHandler eip
+ 1 -> invokeHandler eax eax esp
+ 4 -> invokeHandler eax ebx esp
+ 2 -> staticFieldHandler eip
+ x -> error $ "wtf: " ++ (show x)
+
+staticCallHandler :: CUInt -> IO CUInt
+staticCallHandler eip = do
+ -- the actual insn to patch is displaced by two bytes
+ let insn_ptr = intPtrToPtr (fromIntegral (eip - 2)) :: Ptr CUChar
+ -- call offset is displaced by one byte
+ let imm_ptr = intPtrToPtr (fromIntegral (eip - 1)) :: Ptr CUInt
+ -- in codegen we set the immediate to some magic value
+ -- in order to produce a SIGILL signal. we also do a safety
+ -- check here, if we're really the "owner" of this signal.
+ checkMe <- peek imm_ptr
+ case checkMe == 0x90ffff90 of
+ True -> do
+ entryAddr <- getMethodEntry eip 0
+ poke insn_ptr 0xe8 -- call opcode
+ -- it's a relative call, so we have to calculate the offset. why "+ 3"?
+ -- (1) the whole insn is 5 bytes long
+ -- (2) begin of insn is displaced by 2 bytes
+ -- (3) offset is calculated wrt to the beginning of the next insn
+ poke imm_ptr (entryAddr - (eip + 3))
+ return (eip - 2)
+ False -> error "staticCallHandler: something is wrong here. abort\n"
+
+staticFieldHandler :: CUInt -> IO CUInt
+staticFieldHandler eip = do
+ -- patch the offset here, first two bytes are part of the insn (opcode + reg)
+ let imm_ptr = intPtrToPtr (fromIntegral (eip + 2)) :: Ptr CUInt
+ checkMe <- peek imm_ptr
+ case checkMe == 0x00000000 of
+ True -> do
+ getStaticFieldAddr eip >>= poke imm_ptr
+ return eip
+ False -> error "staticFieldHandler: something is wrong here. abort.\n"
+
+invokeHandler :: CUInt -> CUInt -> CUInt -> IO CUInt
+invokeHandler method_table table2patch esp = do
+ -- table2patch: note, that can be a method-table or a interface-table
+ callerAddr <- callerAddrFromStack esp
+ offset <- offsetOfCallInsn esp
+ entryAddr <- getMethodEntry (callerAddr - 3) method_table
+ let call_insn = intPtrToPtr (fromIntegral $ table2patch + (fromIntegral offset))
+ poke call_insn entryAddr
+ return entryAddr
+
+
+callerAddrFromStack :: CUInt -> IO CUInt
+callerAddrFromStack = peek . intPtrToPtr . fromIntegral
+
+offsetOfCallInsn :: CUInt -> IO CUChar
+offsetOfCallInsn esp = do
+ let ret_ptr = intPtrToPtr (fromIntegral esp) :: Ptr CUInt
+ ret <- peek ret_ptr
+ peek (intPtrToPtr $ fromIntegral (ret - 1))