2 {-# LANGUAGE OverloadedStrings #-}
3 {-# LANGUAGE ForeignFunctionInterface #-}
5 module Mate.X86TrapHandling (
10 import qualified Data.Map as M
11 import qualified Data.ByteString.Lazy as B
14 import Foreign.C.Types
17 import {-# SOURCE #-} Mate.MethodPool
20 foreign import ccall "register_signal"
21 register_signal :: IO ()
26 | VirtualMethodCall Bool
27 | InterfaceMethodCall Bool
28 | InstanceOfMiss B.ByteString
29 | NewObjectTrap B.ByteString
32 getTrapType :: TrapMap -> CPtrdiff -> CPtrdiff -> TrapType
33 getTrapType tmap signal_from from2 =
34 case M.lookup (fromIntegral signal_from) tmap of
35 (Just (StaticMethod _)) -> StaticMethodCall
36 (Just (StaticField _)) -> StaticFieldAccess
37 (Just (InstanceOf cn)) -> InstanceOfMiss cn
38 (Just (NewObject cn)) -> NewObjectTrap cn
39 (Just _) -> NoKnownTrap "getTrapMap: doesn't happen"
40 -- maybe we've a hit on the second `from' value
41 Nothing -> case M.lookup (fromIntegral from2) tmap of
42 (Just (VirtualMethod imm8 _)) -> VirtualMethodCall imm8
43 (Just (InterfaceMethod imm8 _)) -> InterfaceMethodCall imm8
44 (Just _) -> NoKnownTrap "getTrapType: abort #1 :-("
45 Nothing -> NoKnownTrap $ "getTrapType: abort #2 :-(" ++ show signal_from ++ ", " ++ show from2 ++ ", " ++ show tmap
47 foreign export ccall mateHandler :: CPtrdiff -> CPtrdiff -> CPtrdiff -> CPtrdiff -> CPtrdiff -> IO CPtrdiff
48 mateHandler :: CPtrdiff -> CPtrdiff -> CPtrdiff -> CPtrdiff -> CPtrdiff -> IO CPtrdiff
49 mateHandler eip eax ebx esp esi = do
50 callerAddr <- callerAddrFromStack esp
52 case getTrapType tmap eip callerAddr of
53 StaticMethodCall -> staticCallHandler eip
54 StaticFieldAccess -> staticFieldHandler eip
55 (InstanceOfMiss cn) -> instanceOfMissHandler eip cn
56 (NewObjectTrap cn) -> newObjectHandler eip cn
57 VirtualMethodCall imm8 -> invokeHandler eax eax esp imm8
58 InterfaceMethodCall imm8 -> invokeHandler eax ebx esp imm8
61 0x13371234 -> return (-1)
64 staticCallHandler :: CPtrdiff -> IO CPtrdiff
65 staticCallHandler eip = do
66 -- the actual insn to patch as pointer
67 let insn_ptr = intPtrToPtr (fromIntegral eip) :: Ptr CUChar
68 -- call offset is displaced by one byte (as the first byte is the opcode)
69 let imm_ptr = intPtrToPtr (fromIntegral (eip + 1)) :: Ptr CPtrdiff
70 -- in codegen we set the immediate to some magic value
71 -- in order to produce a SIGILL signal. we also do a safety
72 -- check here, if we're really the "owner" of this signal.
73 checkMe <- peek imm_ptr
74 if checkMe == 0x909090ff then
76 entryAddr <- getMethodEntry eip 0
77 poke insn_ptr 0xe8 -- `call' opcode
78 -- it's a relative call, so we have to calculate the offset. why "+ 5"?
79 -- (1) the whole insn is 5 bytes long
80 -- (2) offset is calculated wrt to the beginning of the next insn
81 poke imm_ptr (entryAddr - (eip + 5))
83 else error "staticCallHandler: something is wrong here. abort\n"
85 staticFieldHandler :: CPtrdiff -> IO CPtrdiff
86 staticFieldHandler eip = do
87 -- patch the offset here, first two bytes are part of the insn (opcode + reg)
88 let imm_ptr = intPtrToPtr (fromIntegral (eip + 2)) :: Ptr CPtrdiff
89 checkMe <- peek imm_ptr
90 if checkMe == 0x00000000 then
92 getStaticFieldAddr eip >>= poke imm_ptr
94 else error "staticFieldHandler: something is wrong here. abort.\n"
96 instanceOfMissHandler :: CPtrdiff -> B.ByteString -> IO CPtrdiff
97 instanceOfMissHandler eip classname = do
98 -- first byte is going to be the opcode
99 let insn_ptr = intPtrToPtr (fromIntegral eip) :: Ptr CUChar
100 -- the next four bytes are the immediate
101 let imm_ptr = intPtrToPtr (fromIntegral (eip + 1)) :: Ptr CPtrdiff
102 checkMe <- peek imm_ptr
103 if checkMe == 0x909090ff then -- safety check...
105 mtable <- getMethodTable classname
106 poke imm_ptr (fromIntegral mtable)
107 poke insn_ptr 0xba -- `mov edx' opcode
109 else error "instanceOfMissHandler: something is wrong here. abort.\n"
111 newObjectHandler :: CPtrdiff -> B.ByteString -> IO CPtrdiff
112 newObjectHandler eip classname = do
113 let push_insn_ptr = intPtrToPtr (fromIntegral eip) :: Ptr CUChar
114 let push_imm_ptr = intPtrToPtr (fromIntegral (eip + 1)) :: Ptr CPtrdiff
115 let mov_imm_ptr = intPtrToPtr (fromIntegral (eip + 16)) :: Ptr CPtrdiff
116 checkMe <- peek mov_imm_ptr
117 if checkMe == 0x13371337
119 objsize <- getObjectSize classname
120 mtable <- getMethodTable classname
121 poke push_insn_ptr 0x68 -- push_imm insn
122 poke push_imm_ptr (fromIntegral objsize)
123 poke mov_imm_ptr (fromIntegral mtable)
125 else error "newObjectHandler: something is wrong here. abort.\n"
127 invokeHandler :: CPtrdiff -> CPtrdiff -> CPtrdiff -> Bool -> IO CPtrdiff
128 invokeHandler method_table table2patch esp imm8 = do
129 -- table2patch: note, that can be a method-table or a interface-table
130 callerAddr <- callerAddrFromStack esp
131 offset <- if imm8 then offsetOfCallInsn8 esp else offsetOfCallInsn32 esp
132 entryAddr <- getMethodEntry callerAddr method_table
133 let call_insn = intPtrToPtr (fromIntegral $ table2patch + fromIntegral offset)
134 poke call_insn entryAddr
138 callerAddrFromStack :: CPtrdiff -> IO CPtrdiff
139 callerAddrFromStack = peek . intPtrToPtr . fromIntegral
141 offsetOfCallInsn8 :: CPtrdiff -> IO CPtrdiff
142 offsetOfCallInsn8 esp = do
143 let ret_ptr = intPtrToPtr (fromIntegral esp) :: Ptr CPtrdiff
145 retval <- peek (intPtrToPtr (fromIntegral (ret - 1)) :: Ptr CUChar)
146 return $ fromIntegral retval
148 offsetOfCallInsn32 :: CPtrdiff -> IO CPtrdiff
149 offsetOfCallInsn32 esp = do
150 let ret_ptr = intPtrToPtr (fromIntegral esp) :: Ptr CPtrdiff
152 peek (intPtrToPtr $ fromIntegral (ret - 4))