Preface
As shown in the figure below, the OpenNativeLibrary code uses the dlopen
open dynamic library. This article calls dlopen and dlsym in the source code to call the method in so to strengthen the understanding.
Following the above review of the LoadLibrary source code process , this article is to imitate the process. After obtaining the so path to be called in the Java layer, pass in the c layer to call dlopen
open, and end dlsym
after calling the method dlclose
.
Function description
-
The dlopen() function opens the specified dynamic link library file in the specified mode and returns a handle to the calling process.
- RTLD_LAZY
Each external function reference is bound the first time the function is called.
Before dlopen returns, the undefined variables in the dynamic library (such as external variables extern, or functions) are not parsed, that is, the variable is not parsed Until the first call; - RTLD_NOW
All external function references are bound immediately during the call to dlopen().
Before dlopen returns, the address of each undefined variable is parsed. If it can’t be resolved, dlopen will return NULL. The error is:
- RTLD_LAZY
-
dlerror() returns the error that occurred.
-
Use dlsym() to manipulate handles and symbols through the dynamic link library, and return the address corresponding to the symbol.
-
Use dlclose() to unload the opened library.
Example
Step 1: Type the file test.cpplibtest-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);
}
Step 2: The file testdlopen.cpp is printedlibdlopen-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);
}
The third step: imitate System#loadLibrary
Finding the libtest-lib.so
path in the Java code was originally performed by hook DexPathList, and the code was also implemented. Suddenly, I found that the findLibrary in BaseDexClassLoader turned out to be public. Isn't that simple? Call it directly, and I am happy.
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);
Conclusion
This article imitates the System#loadLibrary process, and directly opens the so dynamic library in the c layer and calls the execution method. I am a little familiar with this.
Please correct me if there is an error.