From 15833bb85e8b1b82f30024ff7261a208327ceb32 Mon Sep 17 00:00:00 2001 From: Bernhard Urban Date: Wed, 25 Apr 2012 17:31:28 +0200 Subject: [PATCH] invokevirtual: implemented. not very well tested though TODO: cleaner code ;-( --- Makefile | 4 ++- Mate/ClassPool.hs | 60 ++++++++++++++++++++++++++++++++++++++------ Mate/Types.hs | 2 ++ Mate/Utilities.hs | 6 ++++- Mate/X86CodeGen.hs | 40 +++++++++++++++++++++++------ ffi/trap.c | 33 ++++++++++++++++++------ tests/Instance2.java | 9 +++++++ tests/Instance3.java | 12 +++++++++ 8 files changed, 143 insertions(+), 23 deletions(-) create mode 100644 tests/Instance3.java diff --git a/Makefile b/Makefile index 4a5863d..ceeb809 100644 --- a/Makefile +++ b/Makefile @@ -53,7 +53,9 @@ test: mate $(CLASS_FILES) ./$< tests/Instance1 | grep mainresult @printf "should be: 0x%08x 0x%08x\n" 0x55 0x11 ./$< tests/Instance2 | grep mainresult - @printf "should be: 0x%08x\n" 0x198 + @printf "should be: 0x%08x 0x%08x\n" 0x198 0x22 + ./$< tests/Instance3 | grep mainresult + @printf "should be: 0x%08x 0x%08x\n" 0x33 0x44 %.class: %.java $(JAVAC) $< diff --git a/Mate/ClassPool.hs b/Mate/ClassPool.hs index 44ff8ac..ac8ca41 100644 --- a/Mate/ClassPool.hs +++ b/Mate/ClassPool.hs @@ -3,11 +3,16 @@ module Mate.ClassPool ( getClassInfo, getClassFile, + getMethodTable, + getMethodSize, + getMethodOffset, getFieldOffset, getStaticFieldAddr ) where import Data.Int +import Data.Word +import Data.Binary import qualified Data.Map as M import qualified Data.Set as S import qualified Data.ByteString.Lazy as B @@ -49,6 +54,24 @@ getFieldOffset path field = do ci <- getClassInfo path return $ (clFieldMap ci) M.! field +-- method + signature plz! +getMethodOffset :: B.ByteString -> B.ByteString -> IO (Word32) +getMethodOffset path method = do + ci <- getClassInfo path + return $ fromIntegral $ (clMethodMap ci) M.! method + +getMethodTable :: B.ByteString -> IO (Word32) +getMethodTable path = do + ci <- getClassInfo path + return $ clMethodBase ci + +getMethodSize :: B.ByteString -> IO (Word32) +getMethodSize path = do + ci <- getClassInfo path + -- TODO(bernhard): correct sizes for different types... + let msize = fromIntegral $ M.size $ clMethodMap ci + return $ (1 + msize) * 4 + foreign export ccall getStaticFieldAddr :: CUInt -> Ptr () -> IO CUInt getStaticFieldAddr :: CUInt -> Ptr () -> IO CUInt getStaticFieldAddr from ptr_trapmap = do @@ -74,9 +97,12 @@ loadClass path = do (staticmap, fieldmap) <- calculateFields cfile superclass printf "staticmap: %s @ %s\n" (show staticmap) (toString path) printf "fieldmap: %s @ %s\n" (show fieldmap) (toString path) + (methodmap, mbase) <- calculateMethodMap cfile superclass + printf "methodmap: %s @ %s\n" (show methodmap) (toString path) + printf "mbase: 0x%08x\n" mbase class_map <- get_classmap >>= ptr2classmap - let new_ci = ClassInfo path cfile staticmap fieldmap False + let new_ci = ClassInfo path cfile staticmap fieldmap methodmap mbase False let class_map' = M.insert path new_ci class_map classmap2ptr class_map' >>= set_classmap return new_ci @@ -91,21 +117,41 @@ calculateFields cf superclass = do staticbase <- mallocBytes ((fromIntegral $ length sfields) * 4) let i_sb = fromIntegral $ ptrToIntPtr $ staticbase let sm = zipbase i_sb sfields - let sc_sm = getsupermap clStaticMap + let sc_sm = getsupermap superclass clStaticMap -- new fields "overwrite" old ones, if they have the same name let staticmap = (M.fromList sm) `M.union` sc_sm - let sc_im = getsupermap clFieldMap - -- TODO(bernhard): not efficient :-( - let max_off = if (M.size sc_im) > 0 then maximum $ M.elems sc_im else 0 - let im = zipbase (max_off + 4) ifields + let sc_im = getsupermap superclass clFieldMap + -- "+ 4" for the method table pointer + let max_off = (fromIntegral $ (M.size sc_im) * 4) + 4 + let im = zipbase max_off ifields -- new fields "overwrite" old ones, if they have the same name let fieldmap = (M.fromList im) `M.union` sc_im return (staticmap, fieldmap) where zipbase base = zipWith (\x y -> (fieldName y, x + base)) [0,4..] - getsupermap getter = case superclass of Just x -> getter x; Nothing -> M.empty + +-- helper +getsupermap :: Maybe ClassInfo -> (ClassInfo -> FieldMap) -> FieldMap +getsupermap superclass getter = case superclass of Just x -> getter x; Nothing -> M.empty + + +calculateMethodMap :: Class Resolved -> Maybe ClassInfo -> IO (FieldMap, Word32) +calculateMethodMap cf superclass = do + let methods = filter + (\x -> (not . S.member ACC_STATIC . methodAccessFlags) x && + ((/=) "" . methodName) x) + (classMethods cf) + let sc_mm = getsupermap superclass clMethodMap + let max_off = fromIntegral $ (M.size sc_mm) * 4 + let mm = zipbase max_off methods + let methodmap = (M.fromList mm) `M.union` sc_mm + + methodbase <- mallocBytes ((fromIntegral $ M.size methodmap) * 4) + return (methodmap, fromIntegral $ ptrToIntPtr $ methodbase) + where zipbase base = zipWith (\x y -> (entry y, x + base)) [0,4..] + where entry y = (methodName y) `B.append` (encode $ methodSignature y) loadAndInitClass :: B.ByteString -> IO ClassInfo diff --git a/Mate/Types.hs b/Mate/Types.hs index ec5b977..521a269 100644 --- a/Mate/Types.hs +++ b/Mate/Types.hs @@ -51,6 +51,8 @@ data ClassInfo = ClassInfo { clFile :: Class Resolved, clStaticMap :: FieldMap, clFieldMap :: FieldMap, + clMethodMap :: FieldMap, + clMethodBase :: Word32, clInitDone :: Bool } data MethodInfo = MethodInfo { diff --git a/Mate/Utilities.hs b/Mate/Utilities.hs index 7a75466..4ede7ba 100644 --- a/Mate/Utilities.hs +++ b/Mate/Utilities.hs @@ -29,9 +29,13 @@ buildStaticFieldID cls idx = StaticFieldInfo rc (ntName fnt) where (CField rc fnt) = (constsPool cls) M.! idx buildFieldOffset :: Class Resolved -> Word16 -> (B.ByteString, B.ByteString) -buildFieldOffset cls idx = (thisClass cls, ntName fnt) +buildFieldOffset cls idx = (rc, ntName fnt) where (CField rc fnt) = (constsPool cls) M.! idx +buildClassID :: Class Resolved -> Word16 -> B.ByteString +buildClassID cls idx = cl + where (CClass cl) = (constsPool cls) M.! idx + methodGetArgsCount :: Class Resolved -> Word16 -> Word32 methodGetArgsCount cls idx = fromIntegral $ length args where diff --git a/Mate/X86CodeGen.hs b/Mate/X86CodeGen.hs index 70ee013..7ddcb6a 100644 --- a/Mate/X86CodeGen.hs +++ b/Mate/X86CodeGen.hs @@ -169,9 +169,8 @@ emitFromBB method cls hmap = do offset <- getCodeOffset return $ w32_ep + (fromIntegral offset) - emit' :: J.Instruction -> CodeGen e s (Maybe (Word32, TrapInfo)) - emit' (INVOKESPECIAL cpidx) = emit' (INVOKESTATIC cpidx) - emit' (INVOKESTATIC cpidx) = do + emitInvoke :: Word16 -> Bool -> CodeGen e s (Maybe (Word32, TrapInfo)) + emitInvoke cpidx hasThis = do let l = buildMethodID cls cpidx calladdr <- getCurrentOffset newNamedLabel (show l) >>= defineLabel @@ -179,11 +178,35 @@ emitFromBB method cls hmap = do -- place a nop at the end, therefore the disasm doesn't screw up emit32 (0xffff9090 :: Word32) >> emit8 (0x90 :: Word8) -- discard arguments on stack - let argcnt = (methodGetArgsCount cls cpidx) * 4 + let argcnt = ((if hasThis then 1 else 0) + (methodGetArgsCount cls cpidx)) * 4 when (argcnt > 0) (add esp argcnt) -- push result on stack if method has a return value when (methodHaveReturnValue cls cpidx) (push eax) return $ Just $ (calladdr, MI l) + + emit' :: J.Instruction -> CodeGen e s (Maybe (Word32, TrapInfo)) + emit' (INVOKESPECIAL cpidx) = emitInvoke cpidx True + emit' (INVOKESTATIC cpidx) = emitInvoke cpidx False + emit' (INVOKEVIRTUAL cpidx) = do + -- get methodInfo entry + let mi@(MethodInfo methodname objname msig@(MethodSignature args _)) = buildMethodID cls cpidx + newNamedLabel (show mi) >>= defineLabel + -- objref lives somewhere on the argument stack + mov eax (Disp ((*4) $ fromIntegral $ length args), esp) + -- get methodtable ref + mov eax (Disp 0, eax) + -- get method offset + let nameAndSig = methodname `B.append` (encode msig) + let offset = unsafePerformIO $ getMethodOffset objname nameAndSig + -- make actual (indirect) call + calladdr <- getCurrentOffset + call (Disp offset, eax) + -- discard arguments on stack (+4 for "this") + let argcnt = 4 + ((methodGetArgsCount cls cpidx) * 4) + when (argcnt > 0) (add esp argcnt) + -- push result on stack if method has a return value + when (methodHaveReturnValue cls cpidx) (push eax) + return $ Just $ (calladdr, MI mi) emit' (PUTSTATIC cpidx) = do pop eax trapaddr <- getCurrentOffset @@ -204,10 +227,10 @@ emitFromBB method cls hmap = do let trapaddr = (fromIntegral getaddr :: Word32) call (trapaddr - w32_calladdr) add esp (4 :: Word32) - emit DUP = pop (Disp 0, esp) + emit DUP = push (Disp 0, esp) emit (NEW objidx) = do - -- TODO(bernhard): determine right amount... - let amount = 0x20 + let objname = buildClassID cls objidx + let amount = unsafePerformIO $ getMethodSize objname push (amount :: Word32) calladdr <- getCurrentOffset let w32_calladdr = 5 + calladdr @@ -216,6 +239,9 @@ emitFromBB method cls hmap = do add esp (4 :: Word32) push eax -- TODO(bernhard): save reference somewhere for GC + -- set method table pointer + let mtable = unsafePerformIO $ getMethodTable objname + mov (Disp 0, eax) mtable emit (BIPUSH val) = push ((fromIntegral val) :: Word32) emit (SIPUSH val) = push ((fromIntegral $ ((fromIntegral val) :: Int16)) :: Word32) emit (ICONST_0) = push (0 :: Word32) diff --git a/ffi/trap.c b/ffi/trap.c index a2af970..0a76609 100644 --- a/ffi/trap.c +++ b/ffi/trap.c @@ -1,6 +1,9 @@ #include #include +/* TODO(bernhard): use {u,}int* types */ + +#define __USE_GNU // Note by hs: my signal.h includes sys/uconctext which conflicts with // asm/ucontext - this hack kinda solves the problem for me ;-) // so feel free to blame me for that s**t @@ -14,7 +17,7 @@ #define __USE_XOPEN2K8 #endif -#include +#include unsigned int getMethodEntry(unsigned int, void *, void *); unsigned int getStaticFieldAddr(unsigned int, void*); @@ -44,8 +47,8 @@ void mainresult(unsigned int a) void callertrap(int nSignal, siginfo_t *info, void *ctx) { - struct ucontext *uctx = (struct ucontext *) ctx; - unsigned int from = (unsigned int) uctx->uc_mcontext.eip - 2; + mcontext_t *mctx = &((ucontext_t *) ctx)->uc_mcontext; + unsigned int from = (unsigned int) mctx->gregs[REG_EIP] - 2; unsigned int *to_patch = (unsigned int *) (from + 1); printf("callertrap(mctx) by 0x%08x\n", from); if (*to_patch != 0x90ffff90) { @@ -60,14 +63,29 @@ void callertrap(int nSignal, siginfo_t *info, void *ctx) printf("*to_patch: 0x%08x\n", *to_patch); *to_patch = patchme - (from + 5); printf("*to_patch: 0x%08x\n", *to_patch); - uctx->uc_mcontext.eip = (unsigned long) insn; - // while (1) ; + mctx->gregs[REG_EIP] = (unsigned long) insn; } void staticfieldtrap(int nSignal, siginfo_t *info, void *ctx) { - struct ucontext *uctx = (struct ucontext *) ctx; - unsigned int from = (unsigned int) uctx->uc_mcontext.eip; + /* TODO(bernhard): more generic and cleaner please... */ + mcontext_t *mctx = &((ucontext_t *) ctx)->uc_mcontext; + unsigned int from = (unsigned int) mctx->gregs[REG_EIP]; + if (from == 0) { // invokevirtual + unsigned int eax = (unsigned int) mctx->gregs[REG_EAX]; + unsigned int *esp = (unsigned int *) mctx->gregs[REG_ESP]; + /* get actual eip from stack storage */ + unsigned int from = (*esp) - 3; + unsigned char offset = *((unsigned char *) (*esp) - 1); + /* method entry to patch */ + unsigned int *to_patch = (unsigned int*) (eax + offset); + printf("invokevirtual by 0x%08x with offset 0x%08x\n", from, offset); + printf(" to_patch: 0x%08x\n", (unsigned int) to_patch); + printf("*to_patch: 0x%08x\n", *to_patch); + *to_patch = getMethodEntry(from, method_map, trap_map); + mctx->gregs[REG_EIP] = *to_patch; + printf("*to_patch: 0x%08x\n", *to_patch); + } else { unsigned int *to_patch = (unsigned int *) (from + 2); printf("staticfieldtrap by 0x%08x\n", from); if (*to_patch != 0x00000000) { @@ -80,6 +98,7 @@ void staticfieldtrap(int nSignal, siginfo_t *info, void *ctx) printf("*to_patch: 0x%08x\n", *to_patch); *to_patch = patchme; printf("*to_patch: 0x%08x\n", *to_patch); + } } void register_signal(void) diff --git a/tests/Instance2.java b/tests/Instance2.java index 6f25480..687d7f9 100644 --- a/tests/Instance2.java +++ b/tests/Instance2.java @@ -19,5 +19,14 @@ public class Instance2 extends Instance1 { b.x = 0x22; sum += b.x; // 0x22 b.y = 0x33; sum += b.y; // 0x33 Instance1.id(sum); // 0x198 + b.getX(); // 0x22 + } + + public int getX() { + return this.x; + } + + public void setX(int a) { + this.x = a; } } diff --git a/tests/Instance3.java b/tests/Instance3.java new file mode 100644 index 0000000..f5ca160 --- /dev/null +++ b/tests/Instance3.java @@ -0,0 +1,12 @@ +package tests; + +public class Instance3 extends Instance2 { + public static void main(String []args) { + int sum = 0; + Instance3 a = new Instance3(); + a.setX(0x33); + a.getX(); // 0x33 + a.setX(0x44); + a.getX(); // 0x44 + } +} -- 2.25.1