2 {-# LANGUAGE OverloadedStrings #-}
3 {-# LANGUAGE ForeignFunctionInterface #-}
5 module Mate.X86TrapHandling (
10 import qualified Data.Map as M
13 import Foreign.C.Types
16 import {-# SOURCE #-} Mate.MethodPool
19 foreign import ccall "register_signal"
20 register_signal :: IO ()
25 | VirtualMethodCall Bool
26 | InterfaceMethodCall Bool
28 getTrapType :: TrapMap -> CUInt -> CUInt -> TrapType
29 getTrapType tmap signal_from from2 =
30 case M.lookup (fromIntegral signal_from) tmap of
31 (Just (StaticMethod _)) -> StaticMethodCall
32 (Just (StaticField _)) -> StaticFieldAccess
33 (Just _) -> error "getTrapMap: doesn't happen"
34 -- maybe we've a hit on the second `from' value
35 Nothing -> case M.lookup (fromIntegral from2) tmap of
36 (Just (VirtualMethod imm8 _)) -> VirtualMethodCall imm8
37 (Just (InterfaceMethod imm8 _)) -> InterfaceMethodCall imm8
38 (Just _) -> error "getTrapType: abort #1 :-("
39 Nothing -> error $ "getTrapType: abort #2 :-(" ++ show signal_from ++ ", " ++ show from2 ++ ", " ++ show tmap
41 foreign export ccall mateHandler :: CUInt -> CUInt -> CUInt -> CUInt -> IO CUInt
42 mateHandler :: CUInt -> CUInt -> CUInt -> CUInt -> IO CUInt
43 mateHandler eip eax ebx esp = do
44 callerAddr <- callerAddrFromStack esp
46 case getTrapType tmap eip callerAddr of
47 StaticMethodCall -> staticCallHandler eip
48 StaticFieldAccess -> staticFieldHandler eip
49 VirtualMethodCall imm8 -> invokeHandler eax eax esp imm8
50 InterfaceMethodCall imm8 -> invokeHandler eax ebx esp imm8
52 staticCallHandler :: CUInt -> IO CUInt
53 staticCallHandler eip = do
54 -- the actual insn to patch is displaced by two bytes
55 let insn_ptr = intPtrToPtr (fromIntegral (eip - 2)) :: Ptr CUChar
56 -- call offset is displaced by one byte
57 let imm_ptr = intPtrToPtr (fromIntegral (eip - 1)) :: Ptr CUInt
58 -- in codegen we set the immediate to some magic value
59 -- in order to produce a SIGILL signal. we also do a safety
60 -- check here, if we're really the "owner" of this signal.
61 checkMe <- peek imm_ptr
62 if checkMe == 0x90ffff90 then
64 entryAddr <- getMethodEntry eip 0
65 poke insn_ptr 0xe8 -- call opcode
66 -- it's a relative call, so we have to calculate the offset. why "+ 3"?
67 -- (1) the whole insn is 5 bytes long
68 -- (2) begin of insn is displaced by 2 bytes
69 -- (3) offset is calculated wrt to the beginning of the next insn
70 poke imm_ptr (entryAddr - (eip + 3))
72 else error "staticCallHandler: something is wrong here. abort\n"
74 staticFieldHandler :: CUInt -> IO CUInt
75 staticFieldHandler eip = do
76 -- patch the offset here, first two bytes are part of the insn (opcode + reg)
77 let imm_ptr = intPtrToPtr (fromIntegral (eip + 2)) :: Ptr CUInt
78 checkMe <- peek imm_ptr
79 if checkMe == 0x00000000 then
81 getStaticFieldAddr eip >>= poke imm_ptr
83 else error "staticFieldHandler: something is wrong here. abort.\n"
85 invokeHandler :: CUInt -> CUInt -> CUInt -> Bool -> IO CUInt
86 invokeHandler method_table table2patch esp imm8 = do
87 -- table2patch: note, that can be a method-table or a interface-table
88 callerAddr <- callerAddrFromStack esp
89 offset <- if imm8 then offsetOfCallInsn8 esp else offsetOfCallInsn32 esp
90 entryAddr <- getMethodEntry callerAddr method_table
91 let call_insn = intPtrToPtr (fromIntegral $ table2patch + fromIntegral offset)
92 poke call_insn entryAddr
96 callerAddrFromStack :: CUInt -> IO CUInt
97 callerAddrFromStack = peek . intPtrToPtr . fromIntegral
99 offsetOfCallInsn8 :: CUInt -> IO CUInt
100 offsetOfCallInsn8 esp = do
101 let ret_ptr = intPtrToPtr (fromIntegral esp) :: Ptr CUInt
103 retval <- peek (intPtrToPtr (fromIntegral (ret - 1)) :: Ptr CUChar)
104 return $ fromIntegral retval
106 offsetOfCallInsn32 :: CUInt -> IO CUInt
107 offsetOfCallInsn32 esp = do
108 let ret_ptr = intPtrToPtr (fromIntegral esp) :: Ptr CUInt
110 peek (intPtrToPtr $ fromIntegral (ret - 4))