493a3ceaf1b1a3a4a45c2d29bcacbeb6034a7a2e
[mate.git] / Mate / X86TrapHandling.hs
1 {-# LANGUAGE CPP #-}
2 {-# LANGUAGE OverloadedStrings #-}
3 {-# LANGUAGE ForeignFunctionInterface #-}
4 #include "debug.h"
5 module Mate.X86TrapHandling (
6   mateHandler,
7   register_signal
8   ) where
9
10 import qualified Data.Map as M
11 import qualified Data.ByteString.Lazy as B
12
13 import Foreign
14 import Foreign.C.Types
15
16 import Mate.Types
17 import {-# SOURCE #-} Mate.MethodPool
18 import Mate.ClassPool
19
20 foreign import ccall "register_signal"
21   register_signal :: IO ()
22
23 data TrapType =
24     StaticMethodCall
25   | StaticFieldAccess
26   | VirtualMethodCall Bool
27   | InterfaceMethodCall Bool
28   | InstanceOfMiss B.ByteString
29   | NewObjectTrap B.ByteString
30   | NoKnownTrap String
31
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
46
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
51   tmap <- getTrapMap
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
59     NoKnownTrap err ->
60       case esi of
61         0x13371234 -> return (-1)
62         _ -> error err
63
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
75     do
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))
82       return eip
83     else error "staticCallHandler: something is wrong here. abort\n"
84
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
91     do
92       getStaticFieldAddr eip >>= poke imm_ptr
93       return eip
94     else error "staticFieldHandler: something is wrong here. abort.\n"
95
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...
104     do
105       mtable <- getMethodTable classname
106       poke imm_ptr (fromIntegral mtable)
107       poke insn_ptr 0xba -- `mov edx' opcode
108       return eip
109     else error "instanceOfMissHandler: something is wrong here. abort.\n"
110
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
118     then do
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)
124       return eip
125     else error "newObjectHandler: something is wrong here. abort.\n"
126
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
135   return entryAddr
136
137
138 callerAddrFromStack :: CPtrdiff -> IO CPtrdiff
139 callerAddrFromStack = peek . intPtrToPtr . fromIntegral
140
141 offsetOfCallInsn8 :: CPtrdiff -> IO CPtrdiff
142 offsetOfCallInsn8 esp = do
143   let ret_ptr = intPtrToPtr (fromIntegral esp) :: Ptr CPtrdiff
144   ret <- peek ret_ptr
145   retval <- peek (intPtrToPtr (fromIntegral (ret - 1)) :: Ptr CUChar)
146   return $ fromIntegral retval
147
148 offsetOfCallInsn32 :: CPtrdiff -> IO CPtrdiff
149 offsetOfCallInsn32 esp = do
150   let ret_ptr = intPtrToPtr (fromIntegral esp) :: Ptr CPtrdiff
151   ret <- peek ret_ptr
152   peek (intPtrToPtr $ fromIntegral (ret - 4))