X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=Mate%2FX86TrapHandling.hs;h=23be786ee65cfce828afe2bebd6651c78dd831af;hb=c00b8e4b5d8179be5c34b9d7b3fcfc3cb364b86b;hp=325f7928c44eb82401d32a44b957ad9a416336ea;hpb=8981d77c48cc12673620159b4f6b8274b07abddf;p=mate.git diff --git a/Mate/X86TrapHandling.hs b/Mate/X86TrapHandling.hs index 325f792..23be786 100644 --- a/Mate/X86TrapHandling.hs +++ b/Mate/X86TrapHandling.hs @@ -1,133 +1,122 @@ -{-# 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 + 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 - | InstanceOfMiss B.ByteString - | NoKnownTrap String - -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 (InstanceOf cn)) -> InstanceOfMiss cn - (Just _) -> NoKnownTrap "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 _) -> NoKnownTrap "getTrapType: abort #1 :-(" - Nothing -> NoKnownTrap $ "getTrapType: abort #2 :-(" ++ show signal_from ++ ", " ++ show from2 ++ ", " ++ show tmap - -foreign export ccall mateHandler :: CPtrdiff -> CPtrdiff -> CPtrdiff -> CPtrdiff -> CPtrdiff -> IO CPtrdiff -mateHandler :: CPtrdiff -> CPtrdiff -> CPtrdiff -> CPtrdiff -> CPtrdiff -> IO CPtrdiff -mateHandler eip eax ebx esp esi = do - callerAddr <- callerAddrFromStack esp +foreign export ccall mateHandler :: CPtrdiff -> CPtrdiff -> CPtrdiff -> CPtrdiff -> IO CPtrdiff +mateHandler :: CPtrdiff -> CPtrdiff -> CPtrdiff -> CPtrdiff -> IO CPtrdiff +mateHandler reip reax rebx resi = do tmap <- getTrapMap - case getTrapType tmap eip callerAddr of - StaticMethodCall -> staticCallHandler eip - StaticFieldAccess -> staticFieldHandler eip - (InstanceOfMiss cn) -> instanceOfMissHandler eip cn - VirtualMethodCall imm8 -> invokeHandler eax eax esp imm8 - InterfaceMethodCall imm8 -> invokeHandler eax ebx esp imm8 - NoKnownTrap err -> - case esi of - 0x13371234 -> return (-1) - _ -> error err - -staticCallHandler :: CPtrdiff -> IO CPtrdiff -staticCallHandler eip = do - -- the actual insn to patch as pointer - let insn_ptr = intPtrToPtr (fromIntegral eip) :: Ptr CUChar - -- call offset is displaced by one byte (as the first byte is the opcode) - 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 == 0x909090ff 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 "+ 5"? - -- (1) the whole insn is 5 bytes long - -- (2) offset is calculated wrt to the beginning of the next insn - poke imm_ptr (entryAddr - (eip + 5)) - return eip - else error "staticCallHandler: something is wrong here. abort\n" + let reipw32 = fromIntegral reip + (deleteMe, ret_nreip) <- case M.lookup reipw32 tmap of + (Just (StaticMethod _)) -> + patchWithHarpy patchStaticCall reip >>= delTrue + (Just (StaticField _)) -> + staticFieldHandler reip >>= delTrue + (Just (InstanceOf cn)) -> + patchWithHarpy (`patchInstanceOf` cn) reip >>= delFalse + (Just (NewObject cn)) -> + patchWithHarpy (`patchNewObject` cn) reip >>= delTrue + (Just (VirtualCall False _ io_offset)) -> + patchWithHarpy (patchInvoke reax reax io_offset) reip + >>= delTrue + (Just (VirtualCall True _ io_offset)) -> + patchWithHarpy (patchInvoke rebx reax io_offset) reip + >>= delTrue + 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 (False, nreip)) + delFalse = (\nreip -> return (False, nreip)) + + +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 reip + +withDisasm :: CodeGen e s CPtrdiff -> CodeGen e s (CPtrdiff, [Instruction]) +withDisasm patcher = do + reip <- patcher + d <- disassemble + return (reip, d) + +patchStaticCall :: CPtrdiff -> CodeGen e s CPtrdiff +patchStaticCall reip = do + entryAddr <- liftIO $ getMethodEntry reip 0 + call (fromIntegral (entryAddr - (reip + 5)) :: NativeWord) + return reip + 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" -instanceOfMissHandler :: CPtrdiff -> B.ByteString -> IO CPtrdiff -instanceOfMissHandler eip classname = do - -- first byte is going to be the opcode - let insn_ptr = intPtrToPtr (fromIntegral eip) :: Ptr CUChar - -- the next four bytes are the immediate - let imm_ptr = intPtrToPtr (fromIntegral (eip + 1)) :: Ptr CPtrdiff - checkMe <- peek imm_ptr - if checkMe == 0x909090ff then -- safety check... - do - mtable <- getMethodTable classname - poke imm_ptr (fromIntegral mtable) - poke insn_ptr 0xba -- `mov edx' opcode - return eip - else error "instanceOfMissHandler: 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)) +patchInstanceOf :: CPtrdiff -> B.ByteString -> CodeGen e s CPtrdiff +patchInstanceOf reip classname = do + mtable <- liftIO $ getMethodTable classname + mov edx mtable + return reip + +patchNewObject :: CPtrdiff -> B.ByteString -> CodeGen e s CPtrdiff +patchNewObject reip classname = do + objsize <- liftIO $ getObjectSize classname + push32 objsize + callMalloc + mtable <- liftIO $ getMethodTable classname + mov (Disp 0, eax) mtable + return reip + +patchInvoke :: CPtrdiff -> CPtrdiff -> IO NativeWord -> CPtrdiff -> CodeGen e s CPtrdiff +patchInvoke method_table table2patch io_offset reip = do + offset <- liftIO io_offset + entryAddr <- liftIO $ getMethodEntry reip method_table + call32_eax (Disp offset) + -- patch entry in table + let call_insn = intPtrToPtr . fromIntegral $ table2patch + fromIntegral offset + liftIO $ poke call_insn entryAddr + return reip