./$< 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) $<
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
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
(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
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
clFile :: Class Resolved,
clStaticMap :: FieldMap,
clFieldMap :: FieldMap,
+ clMethodMap :: FieldMap,
+ clMethodBase :: Word32,
clInitDone :: Bool }
data MethodInfo = MethodInfo {
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
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
-- 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
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
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)
#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
#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*);
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) {
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) {
printf("*to_patch: 0x%08x\n", *to_patch);
*to_patch = patchme;
printf("*to_patch: 0x%08x\n", *to_patch);
+ }
}
void register_signal(void)
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;
}
}
--- /dev/null
+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
+ }
+}