前言
如下圖所示,OpenNativeLibrary代碼中使用了dlopen打開動態庫,本文按照該原始碼中呼叫dlopen、dlsym來呼叫so里的方法,加強一下理解,
本文就是模仿原始碼、模仿System#LoadLibrary流程,在Java層獲取到要呼叫so路徑后,傳入c層呼叫dlopen打開,并通過dlsym呼叫方法后dlclose結束,
函式說明
-
dlopen()函式以指定模式打開指定的動態連接庫檔案,并回傳一個句柄給呼叫行程,
- RTLD_LAZY
Each external function reference is bound the first time the function is called.
在dlopen回傳前,對于動態庫中存在的未定義的變數(如外部變數extern,也可以是函式)不執行決議,就是不決議這個變數的地址,直到首次呼叫;- RTLD_NOW
All external function references are bound immediately during the call to dlopen().
在dlopen回傳前,決議出每個未定義變數的地址,如果決議不出來,在dlopen會回傳NULL,錯誤為:
- RTLD_NOW
- RTLD_LAZY
-
dlerror()回傳出現的錯誤,
-
使用dlsym()通過元件操作句柄與符號,回傳符號對應的地址,
-
使用dlclose()來卸載打開的庫,
示例
第一步:檔案test.cpp打成libtest-lib.so
// 檔案1 test.cpp:
int fibonacci(int n) {
if (n == 1) return 1;
if (n == 2) return 1;
return fibonacci(n - 1) + fibonacci(n - 2);
}
第二步:檔案testdlopen.cpp打出libdlopen-lib.so
// 檔案2:testdlopen.cpp
extern "C"
JNIEXPORT void JNICALL
Java_com_baiiu_jnitest_dlopen_DLOpenFragment_testdlopen(JNIEnv *env, jobject thiz, jstring jstr) {
const char *soPath = env->GetStringUTFChars(jstr, JNI_FALSE);
LOGD("native--> soPath: %s", soPath);
//
void *handle = dlopen(soPath, RTLD_NOW);
char *error_msg = nullptr;
if (handle == nullptr) {
error_msg = strdup(dlerror());
LOGD("%s", error_msg);
}
LOGD("handle is %p", handle);
// void *sym = dlsym(handle, "fibonacci"); // 注意函式名稱,這里是個坑點,readelf so發現名稱變化了
void *sym = dlsym(handle, "_Z9fibonaccii");
if (sym == nullptr) {
LOGD("can not find the function");
return;
}
using FIBONACCI = int (*)(int);
FIBONACCI fibonacci = reinterpret_cast<FIBONACCI>(sym);
int result = (*fibonacci)(5);
LOGD("fibonacci result is: %d", result);
dlclose(handle);
env->ReleaseStringUTFChars(jstr, soPath);
}
第三步:模仿System#loadLibrary
Java代碼中找到libtest-lib.so路徑,本來是hook DexPathList進行查找的,代碼也實作了,突然發現BaseDexClassLoader里的findLibrary竟然是public的,那不就簡單了么,直接呼叫,喜滋滋,
private static final String LIB_NAME = "test-lib";
private void test() {
try {
// 直接呼叫BaseDexClassLoader#findLibrary找到test-lib路徑,傳到c層進行dlopen,模仿System#loadLibrary操作
BaseDexClassLoader dexPathClassLoader = (BaseDexClassLoader) Binder.class.getClassLoader();
String targetSoAbsolutePath = dexPathClassLoader.findLibrary(LIB_NAME);
android.util.Log.e("mLogU", "ClassLoader#findLibrary: " + targetSoAbsolutePath);
testdlopen(targetSoAbsolutePath +"\lib" + LIB_NAME + ".so");
} catch (Exception e) {
android.util.Log.e("mLogU", e.toString());
}
}
private native void testdlopen(String targetSoAbsolutePath);
結語:
本文模仿System#loadLibrary流程,并在c層直接打開so動態庫并呼叫執行方法,對這塊稍微熟悉了點,
若有錯誤請指正,
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/240560.html
標籤:其他
