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