-{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ForeignFunctionInterface #-}
-#include "debug.h"
module Mate.X86TrapHandling (
mateHandler,
register_signal
) where
+import Numeric
import qualified Data.Map as M
+import qualified Data.ByteString.Lazy as B
import Foreign
import Foreign.C.Types
+import Harpy hiding (fst)
+
import Mate.Types
+import Mate.NativeSizes
import {-# SOURCE #-} Mate.MethodPool
import Mate.ClassPool
+import Mate.X86CodeGen
+
+import Mate.Debug
+import Harpy.X86Disassembler
foreign import ccall "register_signal"
register_signal :: IO ()
-data TrapType =
- StaticMethodCall
- | StaticFieldAccess
- | VirtualMethodCall Bool
- | InterfaceMethodCall Bool
-
-getTrapType :: TrapMap -> CPtrdiff -> CPtrdiff -> TrapType
-getTrapType tmap signal_from from2 =
- case M.lookup (fromIntegral signal_from) tmap of
- (Just (StaticMethod _)) -> StaticMethodCall
- (Just (StaticField _)) -> StaticFieldAccess
- (Just _) -> error "getTrapMap: doesn't happen"
- -- maybe we've a hit on the second `from' value
- Nothing -> case M.lookup (fromIntegral from2) tmap of
- (Just (VirtualMethod imm8 _)) -> VirtualMethodCall imm8
- (Just (InterfaceMethod imm8 _)) -> InterfaceMethodCall imm8
- (Just _) -> error "getTrapType: abort #1 :-("
- Nothing -> error $ "getTrapType: abort #2 :-(" ++ show signal_from ++ ", " ++ show from2 ++ ", " ++ show tmap
-
foreign export ccall mateHandler :: CPtrdiff -> CPtrdiff -> CPtrdiff -> CPtrdiff -> IO CPtrdiff
mateHandler :: CPtrdiff -> CPtrdiff -> CPtrdiff -> CPtrdiff -> IO CPtrdiff
-mateHandler eip eax ebx esp = do
- callerAddr <- callerAddrFromStack esp
+mateHandler reip reax rebx resi = do
tmap <- getTrapMap
- case getTrapType tmap eip callerAddr of
- StaticMethodCall -> staticCallHandler eip
- StaticFieldAccess -> staticFieldHandler eip
- VirtualMethodCall imm8 -> invokeHandler eax eax esp imm8
- InterfaceMethodCall imm8 -> invokeHandler eax ebx esp imm8
+ let reipw32 = fromIntegral reip
+ (deleteMe, ret_nreip) <- case M.lookup reipw32 tmap of
+ (Just (StaticMethod patcher)) ->
+ patchWithHarpy patcher reip >>= delFalse
+ (Just (StaticField _)) ->
+ staticFieldHandler reip >>= delTrue
+ (Just (ObjectField patcher)) ->
+ patchWithHarpy patcher reip >>= delTrue
+ (Just (InstanceOf patcher)) ->
+ patchWithHarpy (patcher reax) reip >>= delFalse
+ (Just (NewObject patcher)) ->
+ patchWithHarpy patcher reip >>= delTrue
+ (Just (VirtualCall False mi io_offset)) ->
+ patchWithHarpy (patchInvoke mi reax reax io_offset) reip
+ >>= delFalse
+ (Just (VirtualCall True mi io_offset)) ->
+ patchWithHarpy (patchInvoke mi rebx reax io_offset) reip
+ >>= delFalse
+ Nothing -> case resi of
+ 0x13371234 -> return (-1) >>= delFalse
+ _ -> error $ "getTrapType: abort :-( " ++ (showHex reip ". ")
+ ++ (concatMap (`showHex` ", ") (M.keys tmap))
+ if deleteMe
+ then setTrapMap $ M.delete reipw32 tmap
+ else return ()
+ return ret_nreip
+ where
+ delTrue = (\nreip -> return (True, nreip))
+ delFalse = (\nreip -> return (False, nreip))
-staticCallHandler :: CPtrdiff -> IO CPtrdiff
-staticCallHandler eip = do
- -- the actual insn to patch is displaced by two bytes
- let insn_ptr = intPtrToPtr (fromIntegral (eip - 2)) :: Ptr CUChar
- -- call offset is displaced by one byte
- let imm_ptr = intPtrToPtr (fromIntegral (eip - 1)) :: Ptr CPtrdiff
- -- in codegen we set the immediate to some magic value
- -- in order to produce a SIGILL signal. we also do a safety
- -- check here, if we're really the "owner" of this signal.
- checkMe <- peek imm_ptr
- if checkMe == 0x90ffff90 then
- do
- entryAddr <- getMethodEntry eip 0
- poke insn_ptr 0xe8 -- call opcode
- -- it's a relative call, so we have to calculate the offset. why "+ 3"?
- -- (1) the whole insn is 5 bytes long
- -- (2) begin of insn is displaced by 2 bytes
- -- (3) offset is calculated wrt to the beginning of the next insn
- poke imm_ptr (entryAddr - (eip + 3))
- return (eip - 2)
- else error "staticCallHandler: something is wrong here. abort\n"
+
+patchWithHarpy :: (CPtrdiff -> CodeGen () () CPtrdiff) -> CPtrdiff -> IO CPtrdiff
+patchWithHarpy patcher reip = do
+ -- this is just an upperbound. if the value is to low, patching fails. find
+ -- something better?
+ let fixme = 1024
+ let entry = Just (intPtrToPtr (fromIntegral reip), fixme)
+ let cgconfig = defaultCodeGenConfig { customCodeBuffer = entry }
+ (_, Right right) <- runCodeGenWithConfig (withDisasm $ patcher reip) () () cgconfig
+ if mateDEBUG
+ then mapM_ (printfJit . printf "patched: %s\n" . showAtt) $ snd right
+ else return ()
+ return $ fst right
+
+withDisasm :: CodeGen e s CPtrdiff -> CodeGen e s (CPtrdiff, [Instruction])
+withDisasm patcher = do
+ reip <- patcher
+ d <- disassemble
+ return (reip, d)
staticFieldHandler :: CPtrdiff -> IO CPtrdiff
-staticFieldHandler eip = do
+staticFieldHandler reip = do
-- patch the offset here, first two bytes are part of the insn (opcode + reg)
- let imm_ptr = intPtrToPtr (fromIntegral (eip + 2)) :: Ptr CPtrdiff
+ let imm_ptr = intPtrToPtr (fromIntegral (reip + 2)) :: Ptr CPtrdiff
checkMe <- peek imm_ptr
if checkMe == 0x00000000 then
do
- getStaticFieldAddr eip >>= poke imm_ptr
- return eip
+ getStaticFieldAddr reip >>= poke imm_ptr
+ return reip
else error "staticFieldHandler: something is wrong here. abort.\n"
-invokeHandler :: CPtrdiff -> CPtrdiff -> CPtrdiff -> Bool -> IO CPtrdiff
-invokeHandler method_table table2patch esp imm8 = do
- -- table2patch: note, that can be a method-table or a interface-table
- callerAddr <- callerAddrFromStack esp
- offset <- if imm8 then offsetOfCallInsn8 esp else offsetOfCallInsn32 esp
- entryAddr <- getMethodEntry callerAddr method_table
- let call_insn = intPtrToPtr (fromIntegral $ table2patch + fromIntegral offset)
- poke call_insn entryAddr
- return entryAddr
-
-
-callerAddrFromStack :: CPtrdiff -> IO CPtrdiff
-callerAddrFromStack = peek . intPtrToPtr . fromIntegral
-
-offsetOfCallInsn8 :: CPtrdiff -> IO CPtrdiff
-offsetOfCallInsn8 esp = do
- let ret_ptr = intPtrToPtr (fromIntegral esp) :: Ptr CPtrdiff
- ret <- peek ret_ptr
- retval <- peek (intPtrToPtr (fromIntegral (ret - 1)) :: Ptr CUChar)
- return $ fromIntegral retval
-
-offsetOfCallInsn32 :: CPtrdiff -> IO CPtrdiff
-offsetOfCallInsn32 esp = do
- let ret_ptr = intPtrToPtr (fromIntegral esp) :: Ptr CPtrdiff
- ret <- peek ret_ptr
- peek (intPtrToPtr $ fromIntegral (ret - 4))
+patchInvoke :: MethodInfo -> CPtrdiff -> CPtrdiff -> IO NativeWord -> CPtrdiff -> CodeGen e s CPtrdiff
+patchInvoke (MethodInfo methname _ msig) method_table table2patch io_offset reip = do
+ vmap <- liftIO $ getVirtualMap
+ let newmi = MethodInfo methname (vmap M.! fromIntegral method_table) msig
+ offset <- liftIO io_offset
+ entryAddr <- liftIO $ getMethodEntry newmi
+ call32_eax (Disp offset)
+ -- patch entry in table
+ let call_insn = intPtrToPtr . fromIntegral $ table2patch + fromIntegral offset
+ liftIO $ poke call_insn entryAddr
+ return reip