getObjectSize,
getMethodOffset,
getFieldOffset,
- getStaticFieldAddr
+ getStaticFieldAddr,
+ getInterfaceMethodOffset
) where
import Data.Int
import Foreign.Ptr
import Foreign.C.Types
import Foreign.Marshal.Alloc
+import Foreign.Storable
import JVM.ClassFile
import JVM.Converter
getMethodOffset :: B.ByteString -> B.ByteString -> IO (Word32)
getMethodOffset path method = do
ci <- getClassInfo path
- return $ fromIntegral $ (ciMethodMap ci) M.! method
+ -- (4+) one slot for "interface-table-ptr"
+ return $ (+4) $ fromIntegral $ (ciMethodMap ci) M.! method
getMethodTable :: B.ByteString -> IO (Word32)
getMethodTable path = do
getStaticFieldOffset cls field
_ -> error $ "getFieldAddr: no trapInfo. abort"
+-- interface + method + signature plz!
+getInterfaceMethodOffset :: B.ByteString -> B.ByteString -> B.ByteString -> IO (Word32)
+getInterfaceMethodOffset ifname meth sig = do
+ loadInterface ifname
+ ifmmap <- get_interfacemethodmap >>= ptr2interfacemethodmap
+ let k = ifname `B.append` meth `B.append` sig
+ case M.lookup k ifmmap of
+ Just w32 -> return $ (+4) w32
+ Nothing -> error $ "getInterfaceMethodOffset: no offset set"
+
loadClass :: B.ByteString -> IO ClassInfo
loadClass path = do
#ifdef DEBUG
#endif
let rpath = toString $ path `B.append` ".class"
cfile <- parseClassFile rpath
+ -- load all interfaces, which are implemented by this class
+ sequence_ [ loadInterface i | i <- interfaces cfile ]
superclass <- case (path /= "java/lang/Object") of
True -> do
sc <- loadClass $ superClass cfile
(staticmap, fieldmap) <- calculateFields cfile superclass
(methodmap, mbase) <- calculateMethodMap cfile superclass
+ immap <- get_interfacemethodmap >>= ptr2interfacemethodmap
+
+ -- allocate interface offset table for this class
+ -- TODO(bernhard): we have some duplicates in immap (i.e. some
+ -- entries have the same offset), so we could
+ -- save some memory here.
+ iftable <- mallocBytes ((4*) $ M.size immap)
+ let w32_iftable = fromIntegral $ ptrToIntPtr iftable :: Word32
+ -- store interface-table at offset 0 in method-table
+ pokeElemOff (intPtrToPtr $ fromIntegral mbase) 0 w32_iftable
#ifdef DEBUG
printf "staticmap: %s @ %s\n" (show staticmap) (toString path)
printf "fieldmap: %s @ %s\n" (show fieldmap) (toString path)
printf "methodmap: %s @ %s\n" (show methodmap) (toString path)
printf "mbase: 0x%08x\n" mbase
+ printf "interfacemethod: %s @ %s\n" (show immap) (toString path)
+ printf "iftable: 0x%08x\n" w32_iftable
#endif
-
virtual_map <- get_virtualmap >>= ptr2virtualmap
let virtual_map' = M.insert mbase path virtual_map
virtualmap2ptr virtual_map' >>= set_virtualmap
return new_ci
+loadInterface :: B.ByteString -> IO ()
+loadInterface path = do
+ imap <- get_interfacesmap >>= ptr2interfacesmap
+ -- interface already loaded?
+ case M.lookup path imap of
+ Just _ -> return ()
+ Nothing -> do
+#ifdef DEBUG
+ printf "interface: loading \"%s\"\n" $ toString path
+#endif
+ let ifpath = toString $ path `B.append` ".class"
+ cfile <- parseClassFile ifpath
+ -- load "superinterfaces" first
+ sequence_ [ loadInterface i | i <- interfaces cfile ]
+ immap <- get_interfacemethodmap >>= ptr2interfacemethodmap
+
+ -- load map again, because there could be new entries now
+ -- due to loading superinterfaces
+ imap' <- get_interfacesmap >>= ptr2interfacesmap
+ let max_off = fromIntegral $ (M.size immap) * 4
+ -- create index of methods by this interface
+ let mm = zipbase max_off (classMethods cfile)
+
+ -- create for each method from *every* superinterface a entry to,
+ -- but just put in the same offset as it is already in the map
+ let (ifnames, methodnames) = unzip $ concat $
+ [ zip (repeat ifname) (classMethods $ imap' M.! ifname)
+ | ifname <- interfaces cfile ]
+ let sm = zipWith (\x y -> (entry y, immap M.! (getname x y))) ifnames methodnames
+
+ -- merge all offset tables
+ let methodmap = (M.fromList sm) `M.union` (M.fromList mm) `M.union` immap
+ interfacemethodmap2ptr methodmap >>= set_interfacemethodmap
+
+ interfacesmap2ptr (M.insert path cfile imap') >>= set_interfacesmap
+ where
+ zipbase base = zipWith (\x y -> (entry y, x + base)) [0,4..]
+ entry = getname path
+ getname p y = p `B.append` (methodName y) `B.append` (encode $ methodSignature y)
+
+
calculateFields :: Class Resolved -> Maybe ClassInfo -> IO (FieldMap, FieldMap)
calculateFields cf superclass = do
-- TODO(bernhard): correct sizes. int only atm
let mm = zipbase max_off methods
let methodmap = (M.fromList mm) `M.union` sc_mm
- methodbase <- mallocBytes ((fromIntegral $ M.size methodmap) * 4)
+ -- (+1): one slot for the interface-table-ptr
+ methodbase <- mallocBytes (((+1) $ 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)
foreign import ccall "dynamic"
code_void :: FunPtr (IO ()) -> (IO ())
+foreign export ccall getTrapType :: CUInt -> CUInt -> IO CUInt
+getTrapType :: CUInt -> CUInt -> IO CUInt
+getTrapType signal_from from2 = do
+ tmap <- get_trapmap >>= ptr2trapmap
+ case M.lookup (fromIntegral signal_from) tmap of
+ (Just (MI _)) -> return 0
+ (Just (VI _)) -> return 1
+ (Just (SFI _)) -> return 2
+ (Just (II _)) -> return 4
+ -- maybe we've a hit on the second `from' value
+ Nothing -> case M.lookup (fromIntegral from2) tmap of
+ (Just (VI _)) -> return 1
+ (Just (II _)) -> return 4
+ (Just _) -> error $ "getTrapType: abort #1 :-("
+ Nothing -> error $ "getTrapType: abort #2 :-("
foreign export ccall getMethodEntry :: CUInt -> CUInt -> IO CUInt
getMethodEntry :: CUInt -> CUInt -> IO CUInt
(MI x) -> x
(VI (MethodInfo methname _ msig)) ->
(MethodInfo methname (vmap M.! (fromIntegral methodtable)) msig)
+ (II (MethodInfo methname _ msig)) ->
+ (MethodInfo methname (vmap M.! (fromIntegral methodtable)) msig)
_ -> error $ "getMethodEntry: no trapInfo. abort."
case M.lookup mi' mmap of
Nothing -> do
classmap2ptr M.empty >>= set_classmap
virtualmap2ptr M.empty >>= set_virtualmap
stringsmap2ptr M.empty >>= set_stringsmap
+ interfacesmap2ptr M.empty >>= set_interfacesmap
+ interfacemethodmap2ptr M.empty >>= set_interfacemethodmap
addMethodRef :: Word32 -> MethodInfo -> [B.ByteString] -> IO ()
data TrapInfo =
MI MethodInfo | -- for static calls
VI MethodInfo | -- for virtual calls
- SFI StaticFieldInfo
+ II MethodInfo | -- for interface calls
+ SFI StaticFieldInfo deriving Show
data StaticFieldInfo = StaticFieldInfo {
sfiClassName :: B.ByteString,
- sfiFieldName :: B.ByteString }
+ sfiFieldName :: B.ByteString } deriving Show
type VirtualMap = M.Map Word32 B.ByteString
+-- store each parsed Interface upon first loading
+type InterfacesMap = M.Map B.ByteString (Class Resolved)
+
+-- store offset for each <Interface><Method><Signature> pair
+type InterfaceMethodMap = M.Map B.ByteString Word32
+
+
toString :: B.ByteString -> String
toString bstr = decodeString $ map (chr . fromIntegral) $ B.unpack bstr
foreign import ccall "set_stringsmap"
set_stringsmap :: Ptr () -> IO ()
+foreign import ccall "get_interfacesmap"
+ get_interfacesmap :: IO (Ptr ())
+
+foreign import ccall "set_interfacesmap"
+ set_interfacesmap :: Ptr () -> IO ()
+
+foreign import ccall "get_interfacemethodmap"
+ get_interfacemethodmap :: IO (Ptr ())
+
+foreign import ccall "set_interfacemethodmap"
+ set_interfacemethodmap :: Ptr () -> IO ()
+
-- TODO(bernhard): make some typeclass magic 'n stuff
-- or remove that sh**
methodmap2ptr :: MethodMap -> IO (Ptr ())
ptr2stringsmap :: Ptr () -> IO StringsMap
ptr2stringsmap vmap = deRefStablePtr $ ((castPtrToStablePtr vmap) :: StablePtr cmap)
+
+
+interfacesmap2ptr :: InterfacesMap -> IO (Ptr ())
+interfacesmap2ptr cmap = do
+ ptr_cmap <- newStablePtr cmap
+ return $ castStablePtrToPtr ptr_cmap
+
+ptr2interfacesmap :: Ptr () -> IO InterfacesMap
+ptr2interfacesmap vmap = deRefStablePtr $ ((castPtrToStablePtr vmap) :: StablePtr cmap)
+
+
+interfacemethodmap2ptr :: InterfaceMethodMap -> IO (Ptr ())
+interfacemethodmap2ptr cmap = do
+ ptr_cmap <- newStablePtr cmap
+ return $ castStablePtrToPtr ptr_cmap
+
+ptr2interfacemethodmap :: Ptr () -> IO InterfaceMethodMap
+ptr2interfacemethodmap vmap = deRefStablePtr $ ((castPtrToStablePtr vmap) :: StablePtr cmap)
buildMethodID :: Class Resolved -> Word16 -> MethodInfo
buildMethodID cls idx = MethodInfo (ntName nt) rc (ntSignature nt)
- where
- (CMethod rc nt) = (constsPool cls) M.! idx
+ where (rc, nt) = case (constsPool cls) M.! idx of
+ (CMethod rc' nt') -> (rc', nt')
+ (CIfaceMethod rc' nt') -> (rc', nt')
+ _ -> error $ "buildMethodID: something wrong. abort."
buildStaticFieldID :: Class Resolved -> Word16 -> StaticFieldInfo
buildStaticFieldID cls idx = StaticFieldInfo rc (ntName fnt)
methodGetArgsCount :: Class Resolved -> Word16 -> Word32
methodGetArgsCount cls idx = fromIntegral $ length args
where
- (CMethod _ nt) = (constsPool cls) M.! idx
+ nt = case (constsPool cls) M.! idx of
+ (CMethod _ nt') -> nt'
+ (CIfaceMethod _ nt') -> nt'
+ _ -> error $ "methodGetArgsCount: something wrong. abort."
(MethodSignature args _) = ntSignature nt
-- TODO(bernhard): Extend it to more than just int, and provide typeinformation
(Returns (ObjectType _)) -> True;
_ -> error "methodHaveReturnValue: todo"
where
- (CMethod _ nt) = (constsPool cls) M.! idx
+ nt = case (constsPool cls) M.! idx of
+ (CMethod _ nt') -> nt'
+ (CIfaceMethod _ nt') -> nt'
+ _ -> error $ "methodHaveReturnValue: something wrong. abort."
(MethodSignature _ ret) = ntSignature nt
import Foreign hiding (xor)
import Foreign.C.Types
-#ifdef DEFINE
-import Text.Printf
-#endif
-
import qualified JVM.Assembler as J
import JVM.Assembler hiding (Instruction)
import JVM.ClassFile
emit' :: J.Instruction -> CodeGen e s (Maybe (Word32, TrapInfo))
emit' (INVOKESPECIAL cpidx) = emitInvoke cpidx True
emit' (INVOKESTATIC cpidx) = emitInvoke cpidx False
+ emit' (INVOKEINTERFACE cpidx _) = do
+ -- get methodInfo entry
+ let mi@(MethodInfo methodname ifacename 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 method-table-ptr, keep it in eax (for trap handling)
+ mov eax (Disp 0, eax)
+ -- get interface-table-ptr
+ mov ebx (Disp 0, eax)
+ -- get method offset
+ offset <- liftIO $ getInterfaceMethodOffset ifacename methodname (encode msig)
+ -- make actual (indirect) call
+ calladdr <- getCurrentOffset
+ call (Disp offset, ebx)
+ -- 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)
+ -- note, the "mi" has the wrong class reference here.
+ -- we figure that out at run-time, in the methodpool,
+ -- depending on the method-table-ptr
+ return $ Just $ (calladdr, II mi)
emit' (INVOKEVIRTUAL cpidx) = do
-- get methodInfo entry
let mi@(MethodInfo methodname objname msig@(MethodSignature args _)) = buildMethodID cls cpidx
unsigned int getMethodEntry(unsigned int, unsigned int);
unsigned int getStaticFieldAddr(unsigned int, void*);
+unsigned int getTrapType(unsigned int, unsigned int);
#ifdef DEBUG
#define dprintf(args...) do { printf (args); } while (0);
NEW_MAP(class)
NEW_MAP(virtual)
NEW_MAP(strings)
+NEW_MAP(interfaces)
+NEW_MAP(interfacemethod)
void mainresult(unsigned int a)
/* 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 < 0x10000) { // invokevirtual
+ unsigned int *esp = (unsigned int *) mctx->gregs[REG_ESP];
+ /* if from is not *the* eip: get actual eip from stack storage */
+ unsigned int from_stack = (*esp) - 3;
+ unsigned int type = getTrapType(from, from_stack);
+ if (type == 0) {
+ dprintf("something is wrong here: abort\n");
+ exit(1);
+ } else if (type == 1) {
if (from > 0) {
dprintf("from: 0x%08x but should be 0 :-(\n", from);
}
unsigned int method_table_ptr = (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*) (method_table_ptr + offset);
- dprintf("invokevirtual by 0x%08x with offset 0x%08x\n", from, offset);
+ dprintf("invokevirtual by 0x%08x with offset 0x%08x\n", from_stack, offset);
dprintf(" to_patch: 0x%08x\n", (unsigned int) to_patch);
dprintf("*to_patch: 0x%08x\n", *to_patch);
- *to_patch = getMethodEntry(from, method_table_ptr);
+ *to_patch = getMethodEntry(from_stack, method_table_ptr);
mctx->gregs[REG_EIP] = *to_patch;
dprintf("*to_patch: 0x%08x\n", *to_patch);
- } else {
+ } else if (type == 4) {
+ unsigned int method_table_ptr = (unsigned int) mctx->gregs[REG_EAX];
+ unsigned int interface_table_ptr = (unsigned int) mctx->gregs[REG_EBX];
+ unsigned char offset = *((unsigned char *) (*esp) - 1);
+ /* interface entry to patch */
+ unsigned int *to_patch = (unsigned int*) (interface_table_ptr + offset);
+ dprintf("invokeinterface by 0x%08x with offset 0x%08x\n", from_stack, offset);
+ dprintf(" to_patch: 0x%08x\n", (unsigned int) to_patch);
+ dprintf("*to_patch: 0x%08x\n", *to_patch);
+ *to_patch = getMethodEntry(from_stack, method_table_ptr);
+ mctx->gregs[REG_EIP] = *to_patch;
+ dprintf("*to_patch: 0x%08x\n", *to_patch);
+ } else if (type == 2) {
unsigned int *to_patch = (unsigned int *) (from + 2);
dprintf("staticfieldtrap by 0x%08x\n", from);
if (*to_patch != 0x00000000) {
--- /dev/null
+package tests;
+
+public class Interface1 implements Inf1_I1_local {
+ public int x;
+
+ public Interface1() {
+ this.x = 0x1337;
+ }
+
+ public int func1(int a) {
+ this.x = a;
+ return this.x;
+ }
+
+ public static void main(String []args) {
+ Interface1 o1 = new Interface1();
+ System.out.printf("o1.x: 0x%08x\n", o1.x);
+ Inf1_I1_local i1 = o1;
+ System.out.printf("o1.func1(0x11): 0x%08x\n", i1.func1(0x11));
+ }
+}
+
+interface Inf1_I1_local {
+ int func1(int a);
+}
--- /dev/null
+package tests;
+
+public class Interface2 implements Inf2_I1_local, Inf2_I2_local, Inf2_I3_local {
+ public int x;
+
+ public Interface2() {
+ this.x = 0x1337;
+ }
+
+ public int func1(int a) {
+ this.x = a;
+ return this.x;
+ }
+
+ public int func2(int a, int b) {
+ return a + b;
+ }
+
+ public int func3(int a, int b) {
+ return a - b;
+ }
+
+ public static void main(String []args) {
+ Interface2 o1 = new Interface2();
+ Inf2_I1_local i1 = o1;
+ Inf2_I2_local i2 = o1;
+ Inf2_I3_local i3 = o1;
+ System.out.printf("this.x: 0x%08x\n", i1.func1(0x1122));
+ System.out.printf("this.x: 0x%08x\n", i2.func1(0x22));
+ System.out.printf("this.x: 0x%08x\n", i3.func1(0x33));
+
+ System.out.printf("func2: 0x%08x\n", i2.func2(0x22, 0x44));
+ System.out.printf("func2: 0x%08x\n", i3.func2(0x22, 0x44));
+
+ System.out.printf("func3: 0x%08x\n", i3.func3(0x111, 0x11));
+ }
+}
+
+interface Inf2_I1_local {
+ int func1 (int a);
+}
+
+interface Inf2_I2_local {
+ int func1 (int a);
+ int func2 (int a, int b);
+}
+
+interface Inf2_I3_local extends Inf2_I2_local, Inf2_I1_local {
+ int func1 (int a);
+ int func3 (int a, int b);
+}