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; }
/* * 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; }
/* * 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); }
/* * 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); }
/* * "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; }
/* * 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; }
/* * 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; }