lazy classloading: yet another bug
[mate.git] / Mate / X86TrapHandling.hs
1 {-# LANGUAGE OverloadedStrings #-}
2 {-# LANGUAGE ForeignFunctionInterface #-}
3 module Mate.X86TrapHandling (
4   mateHandler,
5   register_signal
6   ) where
7
8 import Numeric
9 import qualified Data.Map as M
10 import qualified Data.ByteString.Lazy as B
11
12 import Foreign
13 import Foreign.C.Types
14
15 import Harpy
16
17 import Mate.Types
18 import Mate.NativeSizes
19 import {-# SOURCE #-} Mate.MethodPool
20 import Mate.ClassPool
21 import Mate.X86CodeGen
22
23 import Mate.Debug
24 import Harpy.X86Disassembler
25
26 foreign import ccall "register_signal"
27   register_signal :: IO ()
28
29 foreign export ccall mateHandler :: CPtrdiff -> CPtrdiff -> CPtrdiff -> CPtrdiff -> IO CPtrdiff
30 mateHandler :: CPtrdiff -> CPtrdiff -> CPtrdiff -> CPtrdiff -> IO CPtrdiff
31 mateHandler reip reax rebx resi = do
32   tmap <- getTrapMap
33   let reipw32 = fromIntegral reip
34   (deleteMe, ret_nreip) <- case M.lookup reipw32 tmap of
35     (Just (StaticMethod _)) ->
36         patchWithHarpy patchStaticCall reip >>= delTrue
37     (Just (StaticField _))  ->
38         staticFieldHandler reip >>= delTrue
39     (Just (ObjectField patcher)) ->
40         patchWithHarpy patcher reip >>= delTrue
41     (Just (InstanceOf cn))  ->
42         patchWithHarpy (`patchInstanceOf` cn) reip >>= delFalse
43     (Just (NewObject cn))   ->
44         patchWithHarpy (`patchNewObject` cn) reip >>= delTrue
45     (Just (VirtualCall False _ io_offset)) ->
46         patchWithHarpy (patchInvoke reax reax io_offset) reip
47         >>= delTrue
48     (Just (VirtualCall True  _ io_offset)) ->
49         patchWithHarpy (patchInvoke rebx reax io_offset) reip
50         >>= delTrue
51     Nothing -> case resi of
52         0x13371234 -> return (-1) >>= delFalse
53         _ -> error $ "getTrapType: abort :-( " ++ (showHex reip ". ")
54              ++ (concatMap (`showHex` ", ") (M.keys tmap))
55   if deleteMe
56     then setTrapMap $ M.delete reipw32 tmap
57     else return ()
58   return ret_nreip
59   where
60     delTrue = (\nreip -> return (False, nreip)) -- TODO: FIXME
61     delFalse = (\nreip -> return (False, nreip))
62
63
64 patchWithHarpy :: (CPtrdiff -> CodeGen () () CPtrdiff) -> CPtrdiff -> IO CPtrdiff
65 patchWithHarpy patcher reip = do
66   -- this is just an upperbound. if the value is to low, patching fails. find
67   -- something better?
68   let fixme = 1024
69   let entry = Just (intPtrToPtr (fromIntegral reip), fixme)
70   let cgconfig = defaultCodeGenConfig { customCodeBuffer = entry }
71   (_, Right right) <- runCodeGenWithConfig (withDisasm $ patcher reip) () () cgconfig
72   if mateDEBUG
73     then mapM_ (printfJit . printf "patched: %s\n" . showAtt) $ snd right
74     else return ()
75   return reip
76
77 withDisasm :: CodeGen e s CPtrdiff -> CodeGen e s (CPtrdiff, [Instruction])
78 withDisasm patcher = do
79   reip <- patcher
80   d <- disassemble
81   return (reip, d)
82
83 patchStaticCall :: CPtrdiff -> CodeGen e s CPtrdiff
84 patchStaticCall reip = do
85   entryAddr <- liftIO $ getMethodEntry reip 0
86   call (fromIntegral (entryAddr - (reip + 5)) :: NativeWord)
87   return reip
88
89
90 staticFieldHandler :: CPtrdiff -> IO CPtrdiff
91 staticFieldHandler reip = do
92   -- patch the offset here, first two bytes are part of the insn (opcode + reg)
93   let imm_ptr = intPtrToPtr (fromIntegral (reip + 2)) :: Ptr CPtrdiff
94   checkMe <- peek imm_ptr
95   if checkMe == 0x00000000 then
96     do
97       getStaticFieldAddr reip >>= poke imm_ptr
98       return reip
99     else error "staticFieldHandler: something is wrong here. abort.\n"
100
101 patchInstanceOf :: CPtrdiff -> B.ByteString -> CodeGen e s CPtrdiff
102 patchInstanceOf reip classname = do
103   mtable <- liftIO $ getMethodTable classname
104   mov edx mtable
105   return reip
106
107 patchNewObject :: CPtrdiff -> B.ByteString -> CodeGen e s CPtrdiff
108 patchNewObject reip classname = do
109   objsize <- liftIO $ getObjectSize classname
110   push32 objsize
111   callMalloc
112   mtable <- liftIO $ getMethodTable classname
113   mov (Disp 0, eax) mtable
114   return reip
115
116 patchInvoke :: CPtrdiff -> CPtrdiff -> IO NativeWord -> CPtrdiff -> CodeGen e s CPtrdiff
117 patchInvoke method_table table2patch io_offset reip = do
118   offset <- liftIO io_offset
119   entryAddr <- liftIO $ getMethodEntry reip method_table
120   call32_eax (Disp offset)
121   -- patch entry in table
122   let call_insn = intPtrToPtr . fromIntegral $ table2patch + fromIntegral offset
123   liftIO $ poke call_insn entryAddr
124   return reip