實(shí)現(xiàn)原理核心代碼詳解(davlik部分)
1蹋笼、c++背景知識(shí)介紹
- extern關(guān)鍵字
extern可置于變量或者函數(shù)前钠绍,以表示變量或者函數(shù)的定義在別的文件中拨匆,提示編譯器遇到此變量或函數(shù)時(shí)端壳,在其它模塊中尋找其定義
//該方法聲明在頭文件
extern void dalvik_setFieldFlag(JNIEnv* env, jobject field) {}
-
__attribute__
關(guān)鍵字
GNU C的一大特色就是
__attribute__
機(jī)制,__attribute__
可以設(shè)置函數(shù)屬性(Function Attribute)谦秧、變量屬性(Variable Attribute)和類型屬性(Type Attribute),語法格式:__attribute__ ( ( attribute-list ) )
//設(shè)置dalvik_setup函數(shù)屬性為hidden
extern jboolean __attribute__ ((visibility ("hidden"))) dalvik_setup(JNIEnv* env, int apilevel) {}
-
dlopen
函數(shù)
- dlopen()函數(shù)以指定模式打開指定的動(dòng)態(tài)鏈接庫文件,并返回一個(gè)句柄給dlsym()的調(diào)用進(jìn)程搞莺。
- RTLD_NOW:需要在dlopen返回前,解析出所有未定義符號(hào)掂咒,如果解析不出來才沧,在dlopen會(huì)返回NULL。
- 句柄和指針的區(qū)別:我們調(diào)用句柄就是調(diào)用句柄所提供的服務(wù)绍刮,即句柄已經(jīng)把它能做的操作都設(shè)定好了温圆,我們只能在句柄所提供的操作范圍內(nèi)進(jìn)行操作,但是普通指針的操作卻多種多樣孩革,不受限制岁歉。
//打開libdvm.so文件,并返還句柄給后面dlsym()使用
void* dvm_hand = dlopen("libdvm.so", RTLD_NOW);
-
dlsym
函數(shù)
根據(jù)動(dòng)態(tài)鏈接庫操作句柄與符號(hào),返回符號(hào)對(duì)應(yīng)的地址锅移,使用這個(gè)函數(shù)不但可以獲取函數(shù)地址熔掺,也可以獲取變量地址。
//獲取函數(shù)地址方法
static void* dvm_dlsym(void *hand, const char *name) {
//根據(jù)動(dòng)態(tài)鏈接庫操作句柄與符號(hào)非剃,返回符號(hào)對(duì)應(yīng)的地址置逻。
void* ret = dlsym(hand, name); return ret;
}
-
typedef
關(guān)鍵字>typedef關(guān)鍵字使用可自行了解,這里解釋的是復(fù)雜的變量聲明备绽。結(jié)合下面代碼分析說明
typedef int (*dvmComputeMethodArgsSize_func)(void*);
//首先找到變量名dvmComputeMethodArgsSize_func券坞,
//外面有一對(duì)圓括號(hào),而且左邊是一個(gè)*號(hào)肺素,說明這它是一個(gè)指針恨锚;
//然后跳出這個(gè)圓括號(hào),先看右邊又遇到圓括號(hào)倍靡,
//說明(*dvmComputeMethodArgsSize_func)是一個(gè)函數(shù)
//所以dvmComputeMethodArgsSize_func即是函數(shù)指針猴伶,
//具有void*類型的形參,返回值類型為int菌瘫。
當(dāng)調(diào)用了dlsym方法獲取函數(shù)指針后繼給它們做相應(yīng)的引用蜗顽。
- 方法調(diào)用表各方法可到davlik源碼查,
這里有個(gè)比較方便的源碼查尋:Android cross;
符號(hào)標(biāo)示 | 對(duì)應(yīng)源碼函數(shù) | 函數(shù)功能 |
---|---|---|
dvmComputeMethodArgsSize | int dvmComputeMethodArgsSize(const Method* method) |
計(jì)算指定函數(shù)的參數(shù)個(gè)數(shù)方法. |
dvmCallMethod | void dvmCallMethod(Thread* self, const Method* method, Object* obj, JValue* pResult, ...) |
dalvik執(zhí)行指定函數(shù)的方法. |
dexProtoGetParameterCount | size_t dexProtoGetParameterCount(const DexProto* pProto) |
獲取原型類型的參數(shù)個(gè)數(shù). |
dvmAllocArrayByClass | ArrayObject* dvmAllocArrayByClass(ClassObject* arrayClass,size_t length, int allocFlags) |
dalvik通過class申請(qǐng)數(shù)組函數(shù). |
dvmWrapPrimitive雨让、dvmBoxPrimitive | DataObject* dvmBoxPrimitive(JValue value, ClassObject* returnType) |
創(chuàng)建一個(gè)適配對(duì)象給一個(gè)原型數(shù)據(jù)類型,如果返回類型不是原型,僅僅把值強(qiáng)轉(zhuǎn)為object返回. |
dvmFindPrimitiveClass | ClassObject* dvmFindPrimitiveClass(char type) |
dalvik虛擬機(jī)查找原型類函數(shù) |
dvmReleaseTrackedAlloc | void dvmReleaseTrackedAlloc(Object* obj, Thread* self) |
dalvik釋放tracked內(nèi)存 |
dvmCheckException | bool dvmCheckException(Thread* self) |
檢測異常 |
dvmGetException | Object* dvmGetException(Thread* self) |
獲取異常 |
dvmFindArrayClass | ClassObject* dvmFindArrayClass(const char* descriptor, Object* loader) |
查找Class對(duì)象 |
dvmCreateReflectMethodObject | Object* dvmCreateReflectMethodObject(const Method* meth) |
創(chuàng)建反射方法對(duì)象雇盖,即:java/lang/reflect/Method |
dvmGetBoxedReturnType | ClassObject* dvmGetBoxedReturnType(const Method* meth) |
獲取封箱返回類型 |
dvmUnwrapPrimitive、dvmUnboxPrimitive | bool dvmUnboxPrimitive(Object* value, ClassObject* returnType,JValue* pResult) |
獲取原型類型 |
dvmDecodeIndirectRef | Object* dvmDecodeIndirectRef(Thread* self, jobject jobj) |
轉(zhuǎn)換一個(gè)間接引用為對(duì)象引用 |
dvmThreadSelf | Thread* dvmThreadSelf() |
獲得線程自身的ID |
具體方法代碼詳見
方法源碼附錄
栖忠。
大家讀源碼的時(shí)候可能注意到_Z20dvmDecodeIndirectRefP6ThreadP8_jobject類似函數(shù)還附加了些特殊符號(hào)崔挖,這是因?yàn)榫幾g時(shí)區(qū)分不同版本,c++做了命名重載庵寞。
2狸相、字節(jié)碼的文件結(jié)構(gòu)
字段訪問標(biāo)識(shí)
-
class 訪問標(biāo)識(shí)
-
field訪問標(biāo)識(shí)
-
method訪問標(biāo)識(shí)
字段的描述符
上面的當(dāng)我們分析andfix時(shí)遇到符號(hào)查表對(duì)應(yīng)即可。
3捐川、函數(shù)核心方法分析
3.1 replaceMethod
extern void __attribute__ ((visibility ("hidden")))
dalvik_replaceMethod( JNIEnv* env, jobject src, jobject dest) {
//src:待修復(fù)的函數(shù)脓鹃。
//dest:修復(fù)函數(shù)。
//jni調(diào)用反射包下Method類getDeclaringclass方法,獲得class對(duì)象clazz jobject clazz = env->CallObjectMethod(dest, jClassMethod);
//將clazz對(duì)象轉(zhuǎn)換為dalvik直接引用,返回ClassObject*引用它.
//因?yàn)閐avlik是操作ClassObject的古沥。
ClassObject* clz = (ClassObject*) dvmDecodeIndirectRef_fnPtr( dvmThreadSelf_fnPtr(), clazz);
//參見http://osxr.org/android/source/dalvik/vm/oo/Object.h 不過andfix jni目錄下copy一份放在dalvik.h
//標(biāo)示clz為初始化完畢的ready狀態(tài)瘸右。
clz->status = CLASS_INITIALIZED;
//轉(zhuǎn)換java.lang.reflect.Method或java.lang.reflect.Constructor對(duì)象為函數(shù)地址。
Method* meth = (Method*) env->FromReflectedMethod(src);
//修復(fù)函數(shù)地址岩齿。
Method* target = (Method*) env->FromReflectedMethod(dest);
//打印待修復(fù)方法名稱 LOGD("dalvikMethod: %s", meth->name);
//這個(gè)變量記錄了一些預(yù)先計(jì)算好的信息太颤,從而不需要在調(diào)用的時(shí)候再通過方法的參數(shù)和返回值實(shí)時(shí)計(jì)算了,方便了JNI的調(diào)用盹沈,提高了調(diào)用的速度龄章。
//如果第一位為1(即0x80000000),則Dalvik虛擬機(jī)會(huì)忽略后面的所有信息,強(qiáng)制在調(diào)用時(shí)實(shí)時(shí)計(jì)算做裙;
meth->jniArgInfo = 0x80000000;
//方法的訪問標(biāo)識(shí)置為native岗憋,即將meth的訪問標(biāo)識(shí)設(shè)為native方式。
meth->accessFlags |= ACC_NATIVE;
//獲取方法形參個(gè)數(shù)
int argsSize = dvmComputeMethodArgsSize_fnPtr(meth);
if (!dvmIsStaticMethod(meth)) argsSize++;
//非靜態(tài)方法參數(shù)個(gè)數(shù)加一,引用改方法的對(duì)象this也默認(rèn)當(dāng)作一個(gè)參數(shù)菇用,且放到第0位澜驮。
//registersSize:該方法總共用到的寄存器個(gè)數(shù),包含入口參數(shù)所用到的寄存器惋鸥,還有方法內(nèi)部自己所用到的其它本地寄存器杂穷;
//insSize:作為調(diào)用該方法時(shí),參數(shù)傳遞而使用到的寄存器個(gè)數(shù)卦绣;
//outsSize:當(dāng)該方法要調(diào)用其它方法時(shí)耐量,用作參數(shù)傳遞而使用的寄存器個(gè)數(shù);
meth->registersSize = meth->insSize = argsSize;
//如果這個(gè)方法不是Native的話滤港,則這里存放了指向方法具體的Dalvik指令的指針(這個(gè)變量指向的是實(shí)際加載到內(nèi)存中的Dalvik
//指令廊蜒,而不是在Dex文件中的)。如果這個(gè)方法是一個(gè)Dalvik虛擬機(jī)自帶的Native函數(shù)(Internal Native)的話溅漾,則這個(gè)變量
//會(huì)是Null山叮。如果這個(gè)方法是一個(gè)普通的Native函數(shù)的話,則這里存放了指向JNI實(shí)際函數(shù)機(jī)器碼的首地址添履;
meth->insns = (void*) target;
//指向替換src的target方法.
//如果這個(gè)方法是一個(gè)Dalvik虛擬機(jī)自帶的Native函數(shù)(Internal Native)的話屁倔,則這里存放了指向JNI實(shí)際函數(shù)機(jī)器碼的首地址。
//如果這個(gè)方法是一個(gè)普通的Native函數(shù)的話暮胧,則這里將指向一個(gè)中間的跳轉(zhuǎn)JNI橋(Bridge)代碼锐借;
meth->nativeFunc = dalvik_dispatcher;
}
上面即完成了待修復(fù)函數(shù)的替換.
實(shí)際執(zhí)行將通過jni橋代碼執(zhí)行。
static void dalvik_dispatcher(const u4* args, jvalue* pResult, const Method* method, void* self) {
//返回對(duì)象類型
ClassObject* returnType;
//返回值 ArrayObject* argArray;//對(duì)象數(shù)組往衷,這里表示為參數(shù)數(shù)組钞翔。
jvalue result;
//打印一下,函數(shù)名稱和簡短的函數(shù)名席舍,這里輸出待修復(fù)方法
LOGD("dalvik_dispatcher source method: %s %s", method->name,method->shorty);
//賦值為修復(fù)函數(shù)
Method* meth = (Method*) method->insns;
//訪問標(biāo)識(shí)置為
public meth->accessFlags = meth->accessFlags | ACC_PUBLIC;
//打印修復(fù)函數(shù)的名稱和簡短名稱
LOGD("dalvik_dispatcher target method: %s %s", method->name, method->shorty);
//調(diào)用返回值裝箱類型函數(shù)布轿,
returnType = dvmGetBoxedReturnType_fnPtr(method);
//處理返回值類型裝箱異常
if (returnType == NULL) {
assert(dvmCheckException_fnPtr(self));
goto bail;
}
//函數(shù)開始調(diào)用
LOGD("dalvik_dispatcher start call->");
if (!dvmIsStaticMethod(meth)) {
//如果不是靜態(tài)函數(shù)
//獲取持有該方法的對(duì)象。注意這個(gè)object是dalvik中typedef的来颤。
Object* thisObj = (Object*) args[0];
//獲取tmp臨時(shí)ClassObject臨時(shí)較好對(duì)象汰扭。
ClassObject* tmp = thisObj->clazz;
thisObj->clazz = meth->clazz;
//將方法參數(shù)都封裝到array中
argArray = boxMethodArgs(meth, args + 1);
if (dvmCheckException_fnPtr(self))
goto bail;
//執(zhí)行該方法
//1、self:線程id
//2脚曾、jInvokeMethod:指向invoke函數(shù),public Object invoke(Object receiver, Object... args)
//3东且、創(chuàng)建一個(gè)新的Method對(duì)象.使用Meth地址來構(gòu)造出它.
//4启具、result返回值的地址
//5本讥、持有該方法的對(duì)象
//6、函數(shù)參數(shù)數(shù)組
dvmCallMethod_fnPtr(self, (Method*) jInvokeMethod, dvmCreateReflectMethodObject_fnPtr(meth), &result, thisObj, argArray);
//恢復(fù)引用
thisObj->clazz = tmp;
}
else
{
//靜態(tài)函數(shù)
argArray = boxMethodArgs(meth, args);
if (dvmCheckException_fnPtr(self))
goto bail;
//不用傳遞持有該函數(shù)的對(duì)象,因?yàn)楹瘮?shù)是靜態(tài)的
dvmCallMethod_fnPtr(self, (Method*) jInvokeMethod, dvmCreateReflectMethodObject_fnPtr(meth), &result, NULL, argArray);
}
if (dvmCheckException_fnPtr(self)) {
Object* excep = dvmGetException_fnPtr(self);
jni_env->Throw((jthrowable) excep); goto bail;
}
//檢測返回類型和返回值拷沸。
if (returnType->primitiveType == PRIM_VOID) {
//返回類型為void
LOGD("+++ ignoring return to void");
} else if (result.l == NULL) {
//返回值為null
if (dvmIsPrimitiveClass(returnType)) {
//檢測返回類型是否是原型類型色查。
jni_env->ThrowNew(NPEClazz, "null result when primitive expected"); goto bail;
}
pResult->l = NULL;
//非原型類型,即引用類型返回null值撞芍。
} else {
//解析拆箱后的類型秧了,該方法無論原型或非原型類型的拆箱。
if (!dvmUnboxPrimitive_fnPtr(result.l, returnType, pResult)) {
char msg[1024] = { 0 };
snprintf(msg, sizeof(msg) - 1, "%s!=%s\0", ((Object*) result.l)->clazz->descriptor, returnType->descriptor);
jni_env->ThrowNew(CastEClazz, msg); goto bail;
}
}
//出異常時(shí)dalvik釋放tracked內(nèi)存
bail: dvmReleaseTrackedAlloc_fnPtr((Object*) argArray, self);
}
下面是獲取參數(shù)時(shí)的裝箱方法序无,比較簡單就不詳細(xì)的介紹了验毡。
static ArrayObject* boxMethodArgs(const Method* method, const u4* args) {
const char* desc = &method->shorty[1];
// [0] is the return type.
/* count args */
size_t argCount = dexProtoGetParameterCount_fnPtr(&method->prototype);
/* allocate storage */
ArrayObject* argArray = dvmAllocArrayByClass_fnPtr(classJavaLangObjectArray, argCount, ALLOC_DEFAULT);
if (argArray == NULL)
return NULL;
Object** argObjects = (Object**) (void*) argArray->contents;
/* * Fill in the array. */
size_t srcIndex = 0;
size_t dstIndex = 0;
while (*desc != '\0')
{
char descChar = *(desc++);
jvalue value;
switch (descChar) {
case 'Z':
case 'C':
case 'F':
case 'B':
case 'S':
case 'I':
value.i = args[srcIndex++];
argObjects[dstIndex] = (Object*) dvmBoxPrimitive_fnPtr(value,
dvmFindPrimitiveClass_fnPtr(descChar));
/* argObjects is tracked, don't need to hold this too */
dvmReleaseTrackedAlloc_fnPtr(argObjects[dstIndex], NULL);
dstIndex++;
break;
case 'D':
case 'J':
value.j = dvmGetArgLong(args, srcIndex);
srcIndex += 2;
argObjects[dstIndex] = (Object*) dvmBoxPrimitive_fnPtr(value, dvmFindPrimitiveClass_fnPtr(descChar));
dvmReleaseTrackedAlloc_fnPtr(argObjects[dstIndex], NULL);
dstIndex++;
break;
case '[':
case 'L':
argObjects[dstIndex++] = (Object*) args[srcIndex++];
LOGD("boxMethodArgs object: index = %d", dstIndex - 1); break; }
}
return argArray;
}
上述的核心代碼流程分析完畢,相信大家做下分析下都能了解方法替換的實(shí)現(xiàn)了帝嗡。需要學(xué)習(xí)xpose機(jī)制晶通,熟悉dalvik結(jié)構(gòu),然后吸收成自己的理解創(chuàng)出這個(gè)實(shí)現(xiàn)方案哟玷。