彻底弄懂dalvik字节码【一】
之前曾經(jīng)簡單跟蹤過代碼,知道dalvik的字節(jié)碼是可以支持解釋執(zhí)行的,所謂的解釋執(zhí)行,其實就是c/c++編寫的用于解釋并執(zhí)行dalvik字節(jié)碼的程序,說白了就是dalvik字節(jié)碼到cpu字節(jié)碼的轉換。
之前的理解算是囫圇吞棗,最近有時間,好好跟了一遍dalvik的代碼,算是弄明白了細節(jié)。
我們從dvmCallMethod開始來跟一遍dalvik執(zhí)行方法的過程:
void dvmCallMethod(Thread* self, const Method* method, Object* obj,JValue* pResult, ...) {va_list args;va_start(args, pResult);dvmCallMethodV(self, method, obj, false, pResult, args);va_end(args); }直接調(diào)用dvmCallMethodV:
void dvmCallMethodV(Thread* self, const Method* method, Object* obj,bool fromJni, JValue* pResult, va_list args) {const char* desc = &(method->shorty[1]); // [0] is the return type.int verifyCount = 0;ClassObject* clazz;u4* ins;clazz = callPrep(self, method, obj, false);if (clazz == NULL)return;/* "ins" for new frame start at frame pointer plus locals */ins = ((u4*)self->interpSave.curFrame) +(method->registersSize - method->insSize);//ALOGD(" FP is %p, INs live at >= %p", self->interpSave.curFrame, ins);/* put "this" pointer into in0 if appropriate */if (!dvmIsStaticMethod(method)) { #ifdef WITH_EXTRA_OBJECT_VALIDATIONassert(obj != NULL && dvmIsHeapAddress(obj)); #endif*ins++ = (u4) obj;verifyCount++;}while (*desc != '\0') {switch (*(desc++)) {case 'D': case 'J': {u8 val = va_arg(args, u8);memcpy(ins, &val, 8); // EABI prevents direct storeins += 2;verifyCount += 2;break;}case 'F': {/* floats were normalized to doubles; convert back */float f = (float) va_arg(args, double);*ins++ = dvmFloatToU4(f);verifyCount++;break;}case 'L': { /* 'shorty' descr uses L for all refs, incl array */void* arg = va_arg(args, void*);assert(obj == NULL || dvmIsHeapAddress(obj));jobject argObj = reinterpret_cast<jobject>(arg);if (fromJni)*ins++ = (u4) dvmDecodeIndirectRef(self, argObj);else*ins++ = (u4) argObj;verifyCount++;break;}default: {/* Z B C S I -- all passed as 32-bit integers */*ins++ = va_arg(args, u4);verifyCount++;break;}}}#ifndef NDEBUGif (verifyCount != method->insSize) {ALOGE("Got vfycount=%d insSize=%d for %s.%s", verifyCount,method->insSize, clazz->descriptor, method->name);assert(false);goto bail;} #endif//dvmDumpThreadStack(dvmThreadSelf());if (dvmIsNativeMethod(method)) {TRACE_METHOD_ENTER(self, method);/** Because we leave no space for local variables, "curFrame" points* directly at the method arguments.*/(*method->nativeFunc)((u4*)self->interpSave.curFrame, pResult,method, self);TRACE_METHOD_EXIT(self, method);} else {dvmInterpret(self, method, pResult);}#ifndef NDEBUG bail: #endifdvmPopFrame(self); }解釋一下這個函數(shù):
看一下棧幀分配:
static ClassObject* callPrep(Thread* self, const Method* method, Object* obj,bool checkAccess) {ClassObject* clazz;#ifndef NDEBUGif (self->status != THREAD_RUNNING) {ALOGW("threadid=%d: status=%d on call to %s.%s -",self->threadId, self->status,method->clazz->descriptor, method->name);} #endifassert(self != NULL);assert(method != NULL);if (obj != NULL)clazz = obj->clazz;elseclazz = method->clazz;IF_LOGVV() {char* desc = dexProtoCopyMethodDescriptor(&method->prototype);LOGVV("thread=%d native code calling %s.%s %s", self->threadId,clazz->descriptor, method->name, desc);free(desc);}if (checkAccess) {/* needed for java.lang.reflect.Method.invoke */if (!dvmCheckMethodAccess(dvmGetCaller2Class(self->interpSave.curFrame),method)){/* note this throws IAException, not IAError */dvmThrowIllegalAccessException("access to method denied");return NULL;}}/** Push a call frame on. If there isn't enough room for ins, locals,* outs, and the saved state, it will throw an exception.** This updates self->interpSave.curFrame.*/if (dvmIsNativeMethod(method)) {/* native code calling native code the hard way */if (!dvmPushJNIFrame(self, method)) {assert(dvmCheckException(self));return NULL;}} else {/* native code calling interpreted code */if (!dvmPushInterpFrame(self, method)) {assert(dvmCheckException(self));return NULL;}}return clazz; }核心是dvmPushInterpFrame:
static bool dvmPushInterpFrame(Thread* self, const Method* method) {StackSaveArea* saveBlock;StackSaveArea* breakSaveBlock;int stackReq;u1* stackPtr;assert(!dvmIsNativeMethod(method));assert(!dvmIsAbstractMethod(method));stackReq = method->registersSize * 4 // params + locals+ sizeof(StackSaveArea) * 2 // break frame + regular frame+ method->outsSize * 4; // args to other methodsif (self->interpSave.curFrame != NULL)stackPtr = (u1*) SAVEAREA_FROM_FP(self->interpSave.curFrame);elsestackPtr = self->interpStackStart;if (stackPtr - stackReq < self->interpStackEnd) {/* not enough space */ALOGW("Stack overflow on call to interp ""(req=%d top=%p cur=%p size=%d %s.%s)",stackReq, self->interpStackStart, self->interpSave.curFrame,self->interpStackSize, method->clazz->descriptor, method->name);dvmHandleStackOverflow(self, method);assert(dvmCheckException(self));return false;}/** Shift the stack pointer down, leaving space for the function's* args/registers and save area.*/stackPtr -= sizeof(StackSaveArea);breakSaveBlock = (StackSaveArea*)stackPtr;stackPtr -= method->registersSize * 4 + sizeof(StackSaveArea);saveBlock = (StackSaveArea*) stackPtr;#if !defined(NDEBUG) && !defined(PAD_SAVE_AREA)/* debug -- memset the new stack, unless we want valgrind's help */memset(stackPtr - (method->outsSize*4), 0xaf, stackReq); #endif #ifdef EASY_GDBbreakSaveBlock->prevSave =(StackSaveArea*)FP_FROM_SAVEAREA(self->interpSave.curFrame);saveBlock->prevSave = breakSaveBlock; #endifbreakSaveBlock->prevFrame = self->interpSave.curFrame;breakSaveBlock->savedPc = NULL; // not requiredbreakSaveBlock->xtra.localRefCookie = 0; // not requiredbreakSaveBlock->method = NULL;saveBlock->prevFrame = FP_FROM_SAVEAREA(breakSaveBlock);saveBlock->savedPc = NULL; // not requiredsaveBlock->xtra.currentPc = NULL; // not required?saveBlock->method = method;LOGVV("PUSH frame: old=%p new=%p (size=%d)",self->interpSave.curFrame, FP_FROM_SAVEAREA(saveBlock),(u1*)self->interpSave.curFrame - (u1*)FP_FROM_SAVEAREA(saveBlock));self->interpSave.curFrame = FP_FROM_SAVEAREA(saveBlock);return true; }這段代碼比較長,我分析了一下,繪制了一張圖幫助理解:
即分配一個棧幀時,會從棧頂方向向下依次分配breakSaveBlock、registers(其中又依次是params和本地變量)、saveBlock,然后將curFrame指向saveBlock的高邊緣地址。同時還建立一些prevFrame的索引關系,參見圖。
當棧幀分配后,回到dvmCallMethodV,依次將參數(shù)壓入剛剛分配的棧幀的registers中的params處,參數(shù)序號和地址依次升高。
當壓入?yún)?shù)后,調(diào)用dvmInterpret開啟對方法的解釋執(zhí)行,執(zhí)行完后結果通過pResult獲取,最后調(diào)用dvmPopFrame彈出之前分配的棧幀。
從dvmInterpret開始就是重點了,通過跟一遍代碼,我們可以知道字節(jié)碼的格式,以及dalvik的解釋執(zhí)行過程。
內(nèi)容有點多,分幾篇來寫。
作者:difcareer
鏈接:http://www.jianshu.com/p/02250d7f9ff4
來源:簡書
著作權歸作者所有。商業(yè)轉載請聯(lián)系作者獲得授權,非商業(yè)轉載請注明出處。
總結
以上是生活随笔為你收集整理的彻底弄懂dalvik字节码【一】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 反调试检测之一TracerPid
- 下一篇: 彻底弄懂dalvik字节码【二】