Пример #1
0
static bool initFieldOffset(ClassObject* clazz, int *pOffset,
        const char* name, const char* type) {
    int offset = dvmFindFieldOffset(clazz, name, type);
    if (offset < 0) {
        ALOGE("Could not find essential field %s.%s of type %s", clazz->descriptor, name, type);
        return false;
    }

    *pOffset = offset;
    return true;
}
Пример #2
0
/*
 * Initialize string globals.
 *
 * This isn't part of the VM init sequence because it's hard to get the
 * timing right -- we need it to happen after java/lang/String has been
 * loaded, but before anybody wants to use a string.  It's easiest to
 * just initialize it on first use.
 *
 * In some unusual circumstances (e.g. trying to throw an exception because
 * String implements java/lang/CharSequence, but CharSequence doesn't exist)
 * we can try to create an exception string internally before anything has
 * really tried to use String.  In that case we basically self-destruct.
 */
static bool stringStartup()
{
    if (gDvm.javaLangStringReady < 0) {
        LOGE("ERROR: reentrant string initialization\n");
        assert(false);
        return false;
    }
    assert(gDvm.javaLangStringReady == 0);

    gDvm.javaLangStringReady = -1;

    if (gDvm.classJavaLangString == NULL)
        gDvm.classJavaLangString =
            dvmFindSystemClassNoInit("Ljava/lang/String;");

    gDvm.offJavaLangString_value =
        dvmFindFieldOffset(gDvm.classJavaLangString, "value", "[C");
    gDvm.offJavaLangString_count =
        dvmFindFieldOffset(gDvm.classJavaLangString, "count", "I");
    gDvm.offJavaLangString_offset =
        dvmFindFieldOffset(gDvm.classJavaLangString, "offset", "I");
    gDvm.offJavaLangString_hashCode =
        dvmFindFieldOffset(gDvm.classJavaLangString, "hashCode", "I");

    if (gDvm.offJavaLangString_value < 0 ||
            gDvm.offJavaLangString_count < 0 ||
            gDvm.offJavaLangString_offset < 0 ||
            gDvm.offJavaLangString_hashCode < 0)
    {
        LOGE("VM-required field missing from java/lang/String\n");
        return false;
    }

    gDvm.javaLangStringReady = 1;

    return true;
}
Пример #3
0
/*
 * This is the constructor for a generated proxy object.
 */
static void proxyConstructor(const u4* args, JValue* pResult,
    const Method* method, Thread* self)
{
    Object* obj = (Object*) args[0];
    Object* handler = (Object*) args[1];
    ClassObject* clazz = obj->clazz;
    int fieldOffset;

    fieldOffset = dvmFindFieldOffset(clazz, "h",
                    "Ljava/lang/reflect/InvocationHandler;");
    if (fieldOffset < 0) {
        LOGE("Unable to find 'h' in Proxy object\n");
        //dvmDumpClass(clazz, kDumpClassFullDetail);
        dvmAbort();     // this should never happen
    }
    dvmSetFieldObject(obj, fieldOffset, handler);
}
Пример #4
0
/*
 * This is the common message body for proxy methods.
 *
 * The method we're calling looks like:
 *   public Object invoke(Object proxy, Method method, Object[] args)
 *
 * This means we have to create a Method object, box our arguments into
 * a new Object[] array, make the call, and unbox the return value if
 * necessary.
 */
static void proxyInvoker(const u4* args, JValue* pResult,
    const Method* method, Thread* self)
{
    Object* thisObj = (Object*) args[0];
    Object* methodObj = NULL;
    ArrayObject* argArray = NULL;
    Object* handler;
    Method* invoke;
    ClassObject* returnType;
    int hOffset;
    JValue invokeResult;

    /*
     * Retrieve handler object for this proxy instance.
     */
    hOffset = dvmFindFieldOffset(thisObj->clazz, "h",
                    "Ljava/lang/reflect/InvocationHandler;");
    if (hOffset < 0) {
        LOGE("Unable to find 'h' in Proxy object\n");
        dvmAbort();
    }
    handler = dvmGetFieldObject(thisObj, hOffset);

    /*
     * Find the invoke() method, looking in "this"s class.  (Because we
     * start here we don't have to convert it to a vtable index and then
     * index into this' vtable.)
     */
    invoke = dvmFindVirtualMethodHierByDescriptor(handler->clazz, "invoke",
            "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;");
    if (invoke == NULL) {
        LOGE("Unable to find invoke()\n");
        dvmAbort();
    }

    LOGV("invoke: %s.%s, this=%p, handler=%s\n",
        method->clazz->descriptor, method->name,
        thisObj, handler->clazz->descriptor);

    /*
     * Create a java.lang.reflect.Method object for this method.
     *
     * We don't want to use "method", because that's the concrete
     * implementation in the proxy class.  We want the abstract Method
     * from the declaring interface.  We have a pointer to it tucked
     * away in the "insns" field.
     *
     * TODO: this could be cached for performance.
     */
    methodObj = dvmCreateReflectMethodObject((Method*) method->insns);
    if (methodObj == NULL) {
        assert(dvmCheckException(self));
        goto bail;
    }

    /*
     * Determine the return type from the signature.
     *
     * TODO: this could be cached for performance.
     */
    returnType = dvmGetBoxedReturnType(method);
    if (returnType == NULL) {
        char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
        LOGE("Could not determine return type for '%s'\n", desc);
        free(desc);
        assert(dvmCheckException(self));
        goto bail;
    }
    LOGV("  return type will be %s\n", returnType->descriptor);

    /*
     * Convert "args" array into Object[] array, using the method
     * signature to determine types.  If the method takes no arguments,
     * we must pass null.
     */
    argArray = boxMethodArgs(method, args+1);
    if (dvmCheckException(self))
        goto bail;

    /*
     * Call h.invoke(proxy, method, args).
     *
     * We don't need to repackage exceptions, so if one has been thrown
     * just jump to the end.
     */
    dvmCallMethod(self, invoke, handler, &invokeResult,
        thisObj, methodObj, argArray);
    if (dvmCheckException(self))
        goto bail;

    /*
     * Unbox the return value.  If it's the wrong type, throw a
     * ClassCastException.  If it's a null pointer and we need a
     * primitive type, throw a NullPointerException.
     */
    if (returnType->primitiveType == PRIM_VOID) {
        LOGVV("+++ ignoring return to void\n");
    } else if (invokeResult.l == NULL) {
        if (dvmIsPrimitiveClass(returnType)) {
            dvmThrowException("Ljava/lang/NullPointerException;",
                "null result when primitive expected");
            goto bail;
        }
        pResult->l = NULL;
    } else {
        if (!dvmUnwrapPrimitive(invokeResult.l, returnType, pResult)) {
            dvmThrowExceptionWithClassMessage("Ljava/lang/ClassCastException;",
                ((Object*)invokeResult.l)->clazz->descriptor);
            goto bail;
        }
    }

bail:
    dvmReleaseTrackedAlloc(methodObj, self);
    dvmReleaseTrackedAlloc((Object*)argArray, self);
}
Пример #5
0
/*
 * "buf" contains a full JDWP packet, possibly with multiple chunks.  We
 * need to process each, accumulate the replies, and ship the whole thing
 * back.
 *
 * Returns "true" if we have a reply.  The reply buffer is newly allocated,
 * and includes the chunk type/length, followed by the data.
 *
 * TODO: we currently assume that the request and reply include a single
 * chunk.  If this becomes inconvenient we will need to adapt.
 */
bool dvmDdmHandlePacket(const u1* buf, int dataLen, u1** pReplyBuf,
    int* pReplyLen)
{
    Thread* self = dvmThreadSelf();
    const int kChunkHdrLen = 8;
    ArrayObject* dataArray = NULL;
    bool result = false;

    assert(dataLen >= 0);

    /*
     * Prep DdmServer.  We could throw this in gDvm.
     */
    ClassObject* ddmServerClass;
    Method* dispatch;

    ddmServerClass =
        dvmFindClass("Lorg/apache/harmony/dalvik/ddmc/DdmServer;", NULL);
    if (ddmServerClass == NULL) {
        LOGW("Unable to find org.apache.harmony.dalvik.ddmc.DdmServer\n");
        goto bail;
    }
    dispatch = dvmFindDirectMethodByDescriptor(ddmServerClass, "dispatch",
                    "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;");
    if (dispatch == NULL) {
        LOGW("Unable to find DdmServer.dispatch\n");
        goto bail;
    }

    /*
     * Prep Chunk.
     */
    int chunkTypeOff, chunkDataOff, chunkOffsetOff, chunkLengthOff;
    ClassObject* chunkClass;
    chunkClass = dvmFindClass("Lorg/apache/harmony/dalvik/ddmc/Chunk;", NULL);
    if (chunkClass == NULL) {
        LOGW("Unable to find org.apache.harmony.dalvik.ddmc.Chunk\n");
        goto bail;
    }
    chunkTypeOff = dvmFindFieldOffset(chunkClass, "type", "I");
    chunkDataOff = dvmFindFieldOffset(chunkClass, "data", "[B");
    chunkOffsetOff = dvmFindFieldOffset(chunkClass, "offset", "I");
    chunkLengthOff = dvmFindFieldOffset(chunkClass, "length", "I");
    if (chunkTypeOff < 0 || chunkDataOff < 0 ||
        chunkOffsetOff < 0 || chunkLengthOff < 0)
    {
        LOGW("Unable to find all chunk fields\n");
        goto bail;
    }

    /*
     * The chunk handlers are written in the Java programming language, so
     * we need to convert the buffer to a byte array.
     */
    dataArray = dvmAllocPrimitiveArray('B', dataLen, ALLOC_DEFAULT);
    if (dataArray == NULL) {
        LOGW("array alloc failed (%d)\n", dataLen);
        dvmClearException(self);
        goto bail;
    }
    memcpy(dataArray->contents, buf, dataLen);

    /*
     * Run through and find all chunks.  [Currently just find the first.]
     */
    unsigned int offset, length, type;
    type = get4BE((u1*)dataArray->contents + 0);
    length = get4BE((u1*)dataArray->contents + 4);
    offset = kChunkHdrLen;
    if (offset+length > (unsigned int) dataLen) {
        LOGW("WARNING: bad chunk found (len=%u pktLen=%d)\n", length, dataLen);
        goto bail;
    }

    /*
     * Call the handler.
     */
    JValue callRes;
    dvmCallMethod(self, dispatch, NULL, &callRes, type, dataArray, offset,
        length);
    if (dvmCheckException(self)) {
        LOGI("Exception thrown by dispatcher for 0x%08x\n", type);
        dvmLogExceptionStackTrace();
        dvmClearException(self);
        goto bail;
    }

    Object* chunk;
    ArrayObject* replyData;
    chunk = (Object*) callRes.l;
    if (chunk == NULL)
        goto bail;

    /*
     * Pull the pieces out of the chunk.  We copy the results into a
     * newly-allocated buffer that the caller can free.  We don't want to
     * continue using the Chunk object because nothing has a reference to it.
     * (If we do an alloc in here, we need to dvmAddTrackedAlloc it.)
     *
     * We could avoid this by returning type/data/offset/length and having
     * the caller be aware of the object lifetime issues, but that
     * integrates the JDWP code more tightly into the VM, and doesn't work
     * if we have responses for multiple chunks.
     *
     * So we're pretty much stuck with copying data around multiple times.
     */
    type = dvmGetFieldInt(chunk, chunkTypeOff);
    replyData = (ArrayObject*) dvmGetFieldObject(chunk, chunkDataOff);
    offset = dvmGetFieldInt(chunk, chunkOffsetOff);
    length = dvmGetFieldInt(chunk, chunkLengthOff);

    LOGV("DDM reply: type=0x%08x data=%p offset=%d length=%d\n",
        type, replyData, offset, length);

    if (length == 0 || replyData == NULL)
        goto bail;
    if (offset + length > replyData->length) {
        LOGW("WARNING: chunk off=%d len=%d exceeds reply array len %d\n",
            offset, length, replyData->length);
        goto bail;
    }

    u1* reply;
    reply = (u1*) malloc(length + kChunkHdrLen);
    if (reply == NULL) {
        LOGW("malloc %d failed\n", length+kChunkHdrLen);
        goto bail;
    }
    set4BE(reply + 0, type);
    set4BE(reply + 4, length);
    memcpy(reply+kChunkHdrLen, (const u1*)replyData->contents + offset, length);

    *pReplyBuf = reply;
    *pReplyLen = length + kChunkHdrLen;
    result = true;

    LOGV("dvmHandleDdm returning type=%.4s buf=%p len=%d\n",
        (char*) reply, reply, length);

bail:
    dvmReleaseTrackedAlloc((Object*) dataArray, NULL);
    return result;
}
Пример #6
0
/*
 * Cache pointers to some of the exception classes we use locally.
 *
 * Note this is NOT called during dexopt optimization.  Some of the fields
 * are initialized by the verifier (dvmVerifyCodeFlow).
 */
bool dvmExceptionStartup(void)
{
    gDvm.classJavaLangThrowable =
        dvmFindSystemClassNoInit("Ljava/lang/Throwable;");
    gDvm.classJavaLangRuntimeException =
        dvmFindSystemClassNoInit("Ljava/lang/RuntimeException;");
    gDvm.classJavaLangStackOverflowError =
        dvmFindSystemClassNoInit("Ljava/lang/StackOverflowError;");
    gDvm.classJavaLangError =
        dvmFindSystemClassNoInit("Ljava/lang/Error;");
    gDvm.classJavaLangStackTraceElement =
        dvmFindSystemClassNoInit("Ljava/lang/StackTraceElement;");
    gDvm.classJavaLangStackTraceElementArray =
        dvmFindArrayClass("[Ljava/lang/StackTraceElement;", NULL);
    if (gDvm.classJavaLangThrowable == NULL ||
        gDvm.classJavaLangStackTraceElement == NULL ||
        gDvm.classJavaLangStackTraceElementArray == NULL)
    {
        LOGE("Could not find one or more essential exception classes\n");
        return false;
    }

    /*
     * Find the constructor.  Note that, unlike other saved method lookups,
     * we're using a Method* instead of a vtable offset.  This is because
     * constructors don't have vtable offsets.  (Also, since we're creating
     * the object in question, it's impossible for anyone to sub-class it.)
     */
    Method* meth;
    meth = dvmFindDirectMethodByDescriptor(gDvm.classJavaLangStackTraceElement,
        "<init>", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V");
    if (meth == NULL) {
        LOGE("Unable to find constructor for StackTraceElement\n");
        return false;
    }
    gDvm.methJavaLangStackTraceElement_init = meth;

    /* grab an offset for the stackData field */
    gDvm.offJavaLangThrowable_stackState =
        dvmFindFieldOffset(gDvm.classJavaLangThrowable,
            "stackState", "Ljava/lang/Object;");
    if (gDvm.offJavaLangThrowable_stackState < 0) {
        LOGE("Unable to find Throwable.stackState\n");
        return false;
    }

    /* and one for the message field, in case we want to show it */
    gDvm.offJavaLangThrowable_message =
        dvmFindFieldOffset(gDvm.classJavaLangThrowable,
            "detailMessage", "Ljava/lang/String;");
    if (gDvm.offJavaLangThrowable_message < 0) {
        LOGE("Unable to find Throwable.detailMessage\n");
        return false;
    }

    /* and one for the cause field, just 'cause */
    gDvm.offJavaLangThrowable_cause =
        dvmFindFieldOffset(gDvm.classJavaLangThrowable,
            "cause", "Ljava/lang/Throwable;");
    if (gDvm.offJavaLangThrowable_cause < 0) {
        LOGE("Unable to find Throwable.cause\n");
        return false;
    }

    return true;
}
Пример #7
0
/*
 * Initialize string globals.
 *
 * This isn't part of the VM init sequence because it's hard to get the
 * timing right -- we need it to happen after java/lang/String has been
 * loaded, but before anybody wants to use a string.  It's easiest to
 * just initialize it on first use.
 *
 * In some unusual circumstances (e.g. trying to throw an exception because
 * String implements java/lang/CharSequence, but CharSequence doesn't exist)
 * we can try to create an exception string internally before anything has
 * really tried to use String.  In that case we basically self-destruct.
 */
static bool stringStartup()
{
    if (gDvm.javaLangStringReady < 0) {
        LOGE("ERROR: reentrant string initialization\n");
        assert(false);
        return false;
    }
    assert(gDvm.javaLangStringReady == 0);

    gDvm.javaLangStringReady = -1;

    if (gDvm.classJavaLangString == NULL)
        gDvm.classJavaLangString =
            dvmFindSystemClassNoInit("Ljava/lang/String;");

    gDvm.offJavaLangString_value =
        dvmFindFieldOffset(gDvm.classJavaLangString, "value", "[C");
    gDvm.offJavaLangString_count =
        dvmFindFieldOffset(gDvm.classJavaLangString, "count", "I");
    gDvm.offJavaLangString_offset =
        dvmFindFieldOffset(gDvm.classJavaLangString, "offset", "I");
    gDvm.offJavaLangString_hashCode =
        dvmFindFieldOffset(gDvm.classJavaLangString, "hashCode", "I");

    if (gDvm.offJavaLangString_value < 0 ||
        gDvm.offJavaLangString_count < 0 ||
        gDvm.offJavaLangString_offset < 0 ||
        gDvm.offJavaLangString_hashCode < 0)
    {
        LOGE("VM-required field missing from java/lang/String\n");
        return false;
    }

    bool badValue = false;
    if (gDvm.offJavaLangString_value != STRING_FIELDOFF_VALUE) {
        LOGE("InlineNative: String.value offset = %d, expected %d\n",
            gDvm.offJavaLangString_value, STRING_FIELDOFF_VALUE);
        badValue = true;
    }
    if (gDvm.offJavaLangString_count != STRING_FIELDOFF_COUNT) {
        LOGE("InlineNative: String.count offset = %d, expected %d\n",
            gDvm.offJavaLangString_count, STRING_FIELDOFF_COUNT);
        badValue = true;
    }
    if (gDvm.offJavaLangString_offset != STRING_FIELDOFF_OFFSET) {
        LOGE("InlineNative: String.offset offset = %d, expected %d\n",
            gDvm.offJavaLangString_offset, STRING_FIELDOFF_OFFSET);
        badValue = true;
    }
    if (gDvm.offJavaLangString_hashCode != STRING_FIELDOFF_HASHCODE) {
        LOGE("InlineNative: String.hashCode offset = %d, expected %d\n",
            gDvm.offJavaLangString_hashCode, STRING_FIELDOFF_HASHCODE);
        badValue = true;
    }
    if (badValue)
        return false;

    gDvm.javaLangStringReady = 1;

    return true;
}