invokevirtual: implemented. not very well tested though
authorBernhard Urban <lewurm@gmail.com>
Wed, 25 Apr 2012 15:31:28 +0000 (17:31 +0200)
committerBernhard Urban <lewurm@gmail.com>
Wed, 25 Apr 2012 21:53:09 +0000 (23:53 +0200)
TODO: cleaner code ;-(

Makefile
Mate/ClassPool.hs
Mate/Types.hs
Mate/Utilities.hs
Mate/X86CodeGen.hs
ffi/trap.c
tests/Instance2.java
tests/Instance3.java [new file with mode: 0644]

index 4a5863d35d0f0a83cd2d2e782883aec1871ac07b..ceeb809633e338a15e7892e179263a1a1a00d1fd 100644 (file)
--- 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) $<
index 44ff8acfa1bb19921b93c95647c0012d92a02edb..ac8ca411b3ac8d5ab9e720dfdcd7ea8832fc1f02 100644 (file)
@@ -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 &&
+                         ((/=) "<init>" . 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
index ec5b977b32def5b243d640f272106af54bce98cc..521a269133c1383a754ec0a547233458c1dd372d 100644 (file)
@@ -51,6 +51,8 @@ data ClassInfo = ClassInfo {
   clFile :: Class Resolved,
   clStaticMap  :: FieldMap,
   clFieldMap :: FieldMap,
+  clMethodMap :: FieldMap,
+  clMethodBase :: Word32,
   clInitDone :: Bool }
 
 data MethodInfo = MethodInfo {
index 7a75466e9af87e8c8f1ee0b7051198779e3c7efc..4ede7ba61b0779871b7b28ca3e04a769fb299b8f 100644 (file)
@@ -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
index 70ee0136e60c0cc0bdd482e823f252b9ad195707..7ddcb6a8b0cd257068f9477092db51d9f731f803 100644 (file)
@@ -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)
index a2af9708299ce884c30d1fb70d8220842c9c6453..0a766094351c10a19affd234a859497882caff05 100644 (file)
@@ -1,6 +1,9 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+/* 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 <asm/ucontext.h>
+#include <sys/ucontext.h>
 
 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)
index 6f25480569516fbbaf8e4f5a3aaaddd59f811f16..687d7f93c5d4e77e4715ec617a94bb8dd2965a66 100644 (file)
@@ -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 (file)
index 0000000..f5ca160
--- /dev/null
@@ -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
+       }
+}