invokevirtual: implement lazy class loading right
[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 Numeric
11 import qualified Data.Map as M
12 import qualified Data.ByteString.Lazy as B
13
14 import Foreign
15 import Foreign.C.Types
16
17 import Mate.Types
18 import Mate.NativeSizes
19 import {-# SOURCE #-} Mate.MethodPool
20 import Mate.ClassPool
21
22 foreign import ccall "register_signal"
23   register_signal :: IO ()
24
25 foreign export ccall mateHandler :: CPtrdiff -> CPtrdiff -> CPtrdiff -> CPtrdiff -> IO CPtrdiff
26 mateHandler :: CPtrdiff -> CPtrdiff -> CPtrdiff -> CPtrdiff -> IO CPtrdiff
27 mateHandler eip eax ebx esi = do
28   tmap <- getTrapMap
29   case M.lookup (fromIntegral eip) tmap of
30     (Just (StaticMethod _)) -> staticCallHandler eip
31     (Just (StaticField _))  -> staticFieldHandler eip
32     (Just (InstanceOf cn))  -> instanceOfMissHandler eip cn
33     (Just (NewObject cn))   -> newObjectHandler eip cn
34     (Just (VirtualCall False _ io_offset)) -> invokeHandler eax eax eip io_offset
35     (Just (VirtualCall True  _ io_offset)) -> invokeHandler ebx eax eip io_offset
36     Nothing -> case esi of
37         0x13371234 -> return (-1)
38         _ -> error $ "getTrapType: abort :-(" ++ (showHex eip "") ++ ", " ++ show (M.keys tmap)
39
40 staticCallHandler :: CPtrdiff -> IO CPtrdiff
41 staticCallHandler eip = do
42   -- the actual insn to patch as pointer
43   let insn_ptr = intPtrToPtr (fromIntegral eip) :: Ptr CUChar
44   -- call offset is displaced by one byte (as the first byte is the opcode)
45   let imm_ptr = intPtrToPtr (fromIntegral (eip + 1)) :: Ptr CPtrdiff
46   -- in codegen we set the immediate to some magic value
47   -- in order to produce a SIGILL signal. we also do a safety
48   -- check here, if we're really the "owner" of this signal.
49   checkMe <- peek imm_ptr
50   if checkMe == 0x909090ff then
51     do
52       entryAddr <- getMethodEntry eip 0
53       poke insn_ptr 0xe8 -- `call' opcode
54       -- it's a relative call, so we have to calculate the offset. why "+ 5"?
55       -- (1) the whole insn is 5 bytes long
56       -- (2) offset is calculated wrt to the beginning of the next insn
57       poke imm_ptr (entryAddr - (eip + 5))
58       return eip
59     else error "staticCallHandler: something is wrong here. abort\n"
60
61 staticFieldHandler :: CPtrdiff -> IO CPtrdiff
62 staticFieldHandler eip = do
63   -- patch the offset here, first two bytes are part of the insn (opcode + reg)
64   let imm_ptr = intPtrToPtr (fromIntegral (eip + 2)) :: Ptr CPtrdiff
65   checkMe <- peek imm_ptr
66   if checkMe == 0x00000000 then
67     do
68       getStaticFieldAddr eip >>= poke imm_ptr
69       return eip
70     else error "staticFieldHandler: something is wrong here. abort.\n"
71
72 instanceOfMissHandler :: CPtrdiff -> B.ByteString -> IO CPtrdiff
73 instanceOfMissHandler eip classname = do
74   -- first byte is going to be the opcode
75   let insn_ptr = intPtrToPtr (fromIntegral eip) :: Ptr CUChar
76   -- the next four bytes are the immediate
77   let imm_ptr = intPtrToPtr (fromIntegral (eip + 1)) :: Ptr CPtrdiff
78   checkMe <- peek imm_ptr
79   if checkMe == 0x909090ff then -- safety check...
80     do
81       mtable <- getMethodTable classname
82       poke imm_ptr (fromIntegral mtable)
83       poke insn_ptr 0xba -- `mov edx' opcode
84       return eip
85     else error "instanceOfMissHandler: something is wrong here. abort.\n"
86
87 newObjectHandler :: CPtrdiff -> B.ByteString -> IO CPtrdiff
88 newObjectHandler eip classname = do
89   let push_insn_ptr = intPtrToPtr (fromIntegral eip) :: Ptr CUChar
90   let push_imm_ptr = intPtrToPtr (fromIntegral (eip + 1)) :: Ptr CPtrdiff
91   let mov_imm_ptr = intPtrToPtr (fromIntegral (eip + 16)) :: Ptr CPtrdiff
92   checkMe <- peek mov_imm_ptr
93   if checkMe == 0x13371337
94     then do
95       objsize <- getObjectSize classname
96       mtable <- getMethodTable classname
97       poke push_insn_ptr 0x68 -- push_imm insn
98       poke push_imm_ptr (fromIntegral objsize)
99       poke mov_imm_ptr (fromIntegral mtable)
100       return eip
101     else error "newObjectHandler: something is wrong here. abort.\n"
102
103 invokeHandler :: CPtrdiff -> CPtrdiff -> CPtrdiff -> IO NativeWord -> IO CPtrdiff
104 invokeHandler method_table table2patch eip io_offset = do
105   let call0_insn_ptr = intPtrToPtr (fromIntegral eip) :: Ptr CUChar
106   let call1_insn_ptr = intPtrToPtr (fromIntegral (eip + 1)) :: Ptr CUChar
107   let call_imm_ptr = intPtrToPtr (fromIntegral (eip + 2)) :: Ptr CPtrdiff
108   offset <- io_offset
109   -- @table2patch: note, that can be a method-table or a interface-table
110   entryAddr <- getMethodEntry eip method_table
111
112   -- patch table
113   let call_insn = intPtrToPtr . fromIntegral $ table2patch + fromIntegral offset
114   poke call_insn entryAddr
115
116   -- patch insn
117   checkMe <- peek call_imm_ptr
118   if checkMe == 0x90909090
119     then do
120       poke call0_insn_ptr 0xff -- indirect call op[0]
121       poke call1_insn_ptr 0x90 -- indirect call op[1]
122       poke call_imm_ptr (fromIntegral offset)
123       return eip
124     else error "invokeHandler: something is wrong here. abort\n"