lazy classloading: yet another bug
authorBernhard Urban <lewurm@gmail.com>
Sun, 26 Aug 2012 22:22:26 +0000 (00:22 +0200)
committerBernhard Urban <lewurm@gmail.com>
Mon, 27 Aug 2012 09:35:30 +0000 (11:35 +0200)
by computing the field access offset, the class has to loaded. but this should
happen at runtime, on the first field access, not at compile time. see
tests/Instance7.java

Mate/MethodPool.hs
Mate/Types.hs
Mate/X86CodeGen.hs
Mate/X86TrapHandling.hs
tests/Instance7.java [new file with mode: 0644]

index 223f50909fc6d04f62167be770392f7d21e00aa2..4a7cf7e384929223061888580ea70a877aac3840 100644 (file)
@@ -52,10 +52,6 @@ getMethodEntry signal_from methodtable = do
          (VirtualCall _ (MethodInfo methname _ msig) _) -> newMi methname msig
          _ -> error "getMethodEntry: no TrapCause found. abort."
        where newMi mn = MethodInfo mn (vmap M.! fromIntegral methodtable)
-  -- bernhard (TODO): doesn't work with gnu classpath at some point. didn't
-  --                  figured out the problem yet :/ therefore, I have no
-  --                  testcase for replaying the situation.
-  -- setTrapMap $ M.delete w32_from tmap
   entryaddr <- case M.lookup mi' mmap of
     Nothing -> do
       cls <- getClassFile cm
index 3bcf6570bc899db38635671da99b95d2bd0669a8..060ffb7403e3d1e036bbec9cedbc97eab3659288 100644 (file)
@@ -29,6 +29,9 @@ import qualified Data.ByteString.Lazy as B
 import Data.IORef
 import System.IO.Unsafe
 
+import Harpy
+import Foreign.C.Types
+
 import JVM.ClassFile
 import JVM.Assembler
 
@@ -63,12 +66,15 @@ data RawMethod = RawMethod {
 -- MethodInfo = relevant information about callee
 type TrapMap = M.Map NativeWord TrapCause
 
+type TrapPatcher = CPtrdiff -> CodeGen () () CPtrdiff
+
 data TrapCause
   = StaticMethod MethodInfo -- for static calls
   | VirtualCall Bool MethodInfo (IO NativeWord) -- for invoke{interface,virtual}
   | InstanceOf B.ByteString -- class name
   | NewObject B.ByteString -- class name
   | StaticField StaticFieldInfo
+  | ObjectField TrapPatcher
 
 data StaticFieldInfo = StaticFieldInfo {
   sfiClassName :: B.ByteString,
index 1a045794f32e33cbc33a144b4f66688a83bb2c95..9b39388d23941b81c6856faddccbb41a29cace8c 100644 (file)
@@ -154,6 +154,7 @@ emitFromBB cls method = do
     emit' (INVOKESTATIC cpidx) = emitInvoke cpidx False
     emit' (INVOKEINTERFACE cpidx _) = virtualCall cpidx True
     emit' (INVOKEVIRTUAL cpidx) = virtualCall cpidx False
+
     emit' (PUTSTATIC cpidx) = do
       pop eax
       trapaddr <- getCurrentOffset
@@ -164,6 +165,31 @@ emitFromBB cls method = do
       mov eax (Addr 0x00000000) -- it's a trap
       push eax
       return $ Just (trapaddr, StaticField $ buildStaticFieldID cls cpidx)
+
+    emit' (GETFIELD x) = do
+      pop eax -- this pointer
+      trapaddr <- getCurrentOffset
+      -- like: 099db064  ff b0 e4 14 00 00 pushl  5348(%eax)
+      emit32 (0x9090ffff :: Word32); nop; nop
+      let patcher reip = do
+            let (cname, fname) = buildFieldOffset cls x
+            offset <- liftIO $ getFieldOffset cname fname
+            push32_rel_eax (Disp (fromIntegral offset)) -- get field
+            return reip
+      return $ Just (trapaddr, ObjectField patcher)
+    emit' (PUTFIELD x) = do
+      pop ebx -- value to write
+      pop eax -- this pointer
+      trapaddr <- getCurrentOffset
+      -- like: 4581fc6b  89 98 30 7b 00 00 movl   %ebx,31536(%eax)
+      emit32 (0x9090ffff :: Word32); nop; nop
+      let patcher reip = do
+            let (cname, fname) = buildFieldOffset cls x
+            offset <- liftIO $ getFieldOffset cname fname
+            mov32_rel_ebx_eax (Disp (fromIntegral offset)) -- set field
+            return reip
+      return $ Just (trapaddr, ObjectField patcher)
+
     emit' (INSTANCEOF cpidx) = do
       pop eax
       mov eax (Disp 0, eax) -- mtable of objectref
@@ -277,13 +303,6 @@ emitFromBB cls method = do
                     (CInteger i) -> liftIO $ return i
                     e -> error $ "LDCI... missing impl.: " ++ show e
       push value
-    emit (GETFIELD x) = do
-      offset <- emitFieldOffset x
-      push (Disp (fromIntegral offset), eax) -- get field
-    emit (PUTFIELD x) = do
-      pop ebx -- value to write
-      offset <- emitFieldOffset x
-      mov (Disp (fromIntegral offset), eax) ebx -- set field
 
     emit IADD = do pop ebx; pop eax; add eax ebx; push eax
     emit ISUB = do pop ebx; pop eax; sub eax ebx; push eax
@@ -319,13 +338,6 @@ emitFromBB cls method = do
     emit IRETURN = do pop eax; emit RETURN
     emit invalid = error $ "insn not implemented yet: " ++ show invalid
 
-    -- TODO(bernhard): delay to runtime (find counter example!)
-    emitFieldOffset :: Word16 -> CodeGen e s Int32
-    emitFieldOffset x = do
-      pop eax -- this pointer
-      let (cname, fname) = buildFieldOffset cls x
-      liftIO $ getFieldOffset cname fname
-
     emitIF :: CMP -> CodeGen e s ()
     emitIF cond = let
       sid = case successor bb of TwoTarget _ t -> t; _ -> error "bad"
@@ -367,11 +379,22 @@ callMalloc = do
   add esp (ptrSize :: Word32)
   push eax
 
--- the regular push implementation, considers the provided immediate and selects
--- a different instruction if it fits in 8bit. but this is not useful for
--- patching.
+
+-- harpy tries to cut immediates (or displacements), if they fit in 8bit.
+-- however, this is bad for patching so we want to put always 32bit.
+
+-- push imm32
 push32 :: Word32 -> CodeGen e s ()
 push32 imm32 = emit8 0x68 >> emit32 imm32
 
+-- call disp32(%eax)
 call32_eax :: Disp -> CodeGen e s ()
 call32_eax (Disp disp32) = emit8 0xff >> emit8 0x90 >> emit32 disp32
+
+-- push disp32(%eax)
+push32_rel_eax :: Disp -> CodeGen e s ()
+push32_rel_eax (Disp disp32) = emit8 0xff >> emit8 0xb0 >> emit32 disp32
+
+-- mov %ebx, disp32(%eax)
+mov32_rel_ebx_eax :: Disp -> CodeGen e s ()
+mov32_rel_ebx_eax (Disp disp32) = emit8 0x89 >> emit8 0x98 >> emit32 disp32
index 23be786ee65cfce828afe2bebd6651c78dd831af..bf4b28831a4ef44f0b07f7f69dbc6b5941ef4ed7 100644 (file)
@@ -36,6 +36,8 @@ mateHandler reip reax rebx resi = do
         patchWithHarpy patchStaticCall reip >>= delTrue
     (Just (StaticField _))  ->
         staticFieldHandler reip >>= delTrue
+    (Just (ObjectField patcher)) ->
+        patchWithHarpy patcher reip >>= delTrue
     (Just (InstanceOf cn))  ->
         patchWithHarpy (`patchInstanceOf` cn) reip >>= delFalse
     (Just (NewObject cn))   ->
@@ -55,7 +57,7 @@ mateHandler reip reax rebx resi = do
     else return ()
   return ret_nreip
   where
-    delTrue = (\nreip -> return (False, nreip))
+    delTrue = (\nreip -> return (False, nreip)) -- TODO: FIXME
     delFalse = (\nreip -> return (False, nreip))
 
 
diff --git a/tests/Instance7.java b/tests/Instance7.java
new file mode 100644 (file)
index 0000000..8c7e3f9
--- /dev/null
@@ -0,0 +1,21 @@
+package tests;
+
+public class Instance7 {
+       public static void main(String []args) {
+               int i_am_null = 0;
+               System.out.printf("before\n");
+               if (i_am_null > 0) {
+                       Instance7_notload a = new Instance7_notload();
+                       System.out.printf("loaded notload stuff o_O: %d\n", a.foo);
+               } else {
+                       System.out.printf("Nothing to do here\n");
+               }
+       }
+}
+
+class Instance7_notload {
+       static {
+               System.out.printf("sup, I'm Instance7_notload\n");
+       }
+       public int foo = 6;
+}