ELF Linker学习篇(四)——初始化
来源:互联网 发布:wps for ubuntu 16.04 编辑:程序博客网 时间:2024/06/05 06:28
在上几篇中总结了ELF的加载、链接之后,最后一篇我们继续关于初始化的过程。
正文篇:
首先我们看到源码为:
soinfo* do_dlopen(const char* name, int flags) { if ((flags & ~(RTLD_NOW|RTLD_LAZY|RTLD_LOCAL|RTLD_GLOBAL)) != 0) { DL_ERR("invalid flags to dlopen: %x", flags); return NULL; } set_soinfo_pool_protection(PROT_READ | PROT_WRITE); soinfo* si = find_library(name); if (si != NULL) { si->CallConstructors(); } set_soinfo_pool_protection(PROT_READ); return si;}完成 SO 的装载链接后,返回到 do_dlopen 函数, do_open 获得 find_library 返回的刚刚加载的 SO 的 soinfo,在将 soinfo 返回给其他模块使用之前,最后还需要调用 soinfo 的成员函数 CallConstructors。
看源码为:
void soinfo::CallConstructors() { if (constructors_called) { return; } // We set constructors_called before actually calling the constructors, otherwise it doesn't // protect against recursive constructor calls. One simple example of constructor recursion // is the libc debug malloc, which is implemented in libc_malloc_debug_leak.so: // 1. The program depends on libc, so libc's constructor is called here. // 2. The libc constructor calls dlopen() to load libc_malloc_debug_leak.so. // 3. dlopen() calls the constructors on the newly created // soinfo for libc_malloc_debug_leak.so. // 4. The debug .so depends on libc, so CallConstructors is // called again with the libc soinfo. If it doesn't trigger the early- // out above, the libc constructor will be called again (recursively!). constructors_called = true; if ((flags & FLAG_EXE) == 0 && preinit_array != NULL) { // The GNU dynamic linker silently ignores these, but we warn the developer. PRINT("\"%s\": ignoring %d-entry DT_PREINIT_ARRAY in shared library!", name, preinit_array_count); } if (dynamic != NULL) { for (Elf32_Dyn* d = dynamic; d->d_tag != DT_NULL; ++d) { if (d->d_tag == DT_NEEDED) { const char* library_name = strtab + d->d_un.d_val; TRACE("\"%s\": calling constructors in DT_NEEDED \"%s\"", name, library_name); find_loaded_library(library_name)->CallConstructors(); } } } TRACE("\"%s\": calling constructors", name); // DT_INIT should be called before DT_INIT_ARRAY if both are present. CallFunction("DT_INIT", init_func); CallArray("DT_INIT_ARRAY", init_array, init_array_count, false);}CallConstructors 函数会调用 SO 的首先调用所有依赖的 SO 的 soinfo 的 CallConstructors 函数,接着调用自己的 soinfo 成员变量 init 和 看 init_array 指定的函数,这两个变量在在解析 dynamic section 时赋值。
在这里重点说一下CallFunction()函数:
void soinfo::CallFunction(const char* function_name UNUSED, linker_function_t function) { if (function == NULL || reinterpret_cast<uintptr_t>(function) == static_cast<uintptr_t>(-1)) { return; } TRACE("[ Calling %s @ %p for '%s' ]", function_name, function, name); function(); TRACE("[ Done calling %s @ %p for '%s' ]", function_name, function, name); // The function may have called dlopen(3) or dlclose(3), so we need to ensure our data structures // are still writable. This happens with our debug malloc (see http://b/7941716). set_soinfo_pool_protection(PROT_READ | PROT_WRITE);}这也就明白了之前看的文章中为什么说在linker处下断点时,要搜素“
<span style="font-family:SimSun;">Calling %s @ %p for '%s'</span><span style="font-family: SimSun; background-color: rgb(255, 255, 255);">”这样的字符。</span>
<span style="font-family: SimSun; background-color: rgb(255, 255, 255);"></span>
初始化函数执行完以后,接着执行我们所说的JNI_Onload函数,此函数的作用为:
当Android的VM(Virtual Machine)执行到System.loadLibrary()函数时,首先会去执行C组件里的JNI_OnLoad()函数。
它的用途有二:
(1)告诉VM此C组件使用那一个JNI版本。如果你的*.so档没有提供JNI_OnLoad()函数,VM会默认该*.so档是使用最老的 JNI 1.1版本。由于新版的JNI做了许多扩充,如果需要使用JNI的新版功能,例如JNI 1.4的java.nio.ByteBuffer,就必须藉由JNI_OnLoad()函数来告知VM。
(2)由于VM执行到System.loadLibrary()函数时,就会立即先呼叫JNI_OnLoad(),所以C组件的开发者可以藉由JNI_OnLoad()来进行C组件内的初期值之(Initialization)。
JNI_Onload函数执行完以后,接着执行java_com_XX我们所熟悉的函数的身影。
因此这里也就跟之前所说的下断点有三层.init->.init_array->JNI_Onload->java_com_XX.
总结篇:ELF的加载、链接、算是完了,也算不上很清楚,模模糊糊,以后用到再复习进阶,学东西不就这样嘛
0 0
- ELF Linker学习篇(四)——初始化
- ELF Linker学习篇(二)关于分配soinfo结构
- ELF Linker学习篇(三)关于链接过程
- ELF Linker学习篇(一)关于ELF文件装载进内存
- Android源码学习——linker(3)
- Android源码学习——linker(1)
- Android源码学习——linker(2)
- Android源码学习——linker(4)
- K60学习笔记3——Linker
- ARM Linker的使用——应用程序运行环境的初始化
- OK6410裸机程序(四)——DDR初始化
- Java基础(四)——初始化块
- 《step4:tiny-spring-ioc学习四》——读取xml配置来初始化bean
- spring源码学习笔记-初始化(四)-PostProcessor
- spring源码学习笔记-初始化(四)-PostProcessor
- spring源码学习笔记-初始化(四)-PostProcessor
- C++学习与实践(四):类的初始化
- JVM学习笔记(四):类的初始化
- ContentService源码分析
- bit,byte,编码、解码、字符集的总结
- jquery mobile界面数据刷新
- Spark快速安装与简单入门_Ubuntu14.04 64bitx
- 报错:java.util.Map is an interface, and JAXB can't handle interfaces.
- ELF Linker学习篇(四)——初始化
- js跨域的几种解决方案
- 《OpenCV》Part1 OpenCV3.1.0安装配置与Qt5.5测试
- 每日一个js实例10--回车键
- Random产生随机数,不传入种子和传入种子的区别
- Hello World!
- JSP常见编译及动作指令
- extjs renderer的参数以及对应的值
- struts1的工作原理