dlopen的源码分析
在Android系统中,我们知道对so的加载是通过两个API调用实现的,这两个API是:
- System.loadLibrary(String libName)
- System.load(String pathName)
以上两个API的底层实现都是基于dlopen实现的.要想弄明白dlopen的实现,必须研究dlopen的源码实现.
首先dlopen的实现定义在源码(/bionic/linker/dlfcn.cpp, 本文讨论的源码是基于Android5.0.0的版本)
void* dlopen(const char* filename, int flags) { return dlopen_ext(filename, flags, NULL); }
dlopen直接调用dlopenext函数.
static void* dlopen_ext(const char* filename, int flags, const android_dlextinfo* extinfo) { ScopedPthreadMutexLocker locker(&g_dl_mutex); soinfo* result = do_dlopen(filename, flags, extinfo); if (result == NULL) { __bionic_format_dlerror("dlopen failed", linker_get_error_buffer()); return NULL; } return result; }
dlopenext函数主要用来调用dodlopen函数.
soinfo* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo) { if ((flags & ~(RTLD_NOW|RTLD_LAZY|RTLD_LOCAL|RTLD_GLOBAL|RTLD_NOLOAD)) != 0) { DL_ERR("invalid flags to dlopen: %x", flags); return NULL; } if (extinfo != NULL && ((extinfo->flags & ~(ANDROID_DLEXT_VALID_FLAG_BITS)) != 0)) { DL_ERR("invalid extended flags to android_dlopen_ext: %" PRIx64, extinfo->flags); return NULL; } protect_data(PROT_READ | PROT_WRITE); // 生成soinfo结构体 soinfo* si = find_library(name, flags, extinfo); if (si != NULL) { //调用so中的构造方法 si->CallConstructors(); } protect_data(PROT_READ); return si; }
以上的代码中关键的函数是findlibrary.该函数的定义如下:
static soinfo* find_library(const char* name, int dlflags, const android_dlextinfo* extinfo) { soinfo* si = find_library_internal(name, dlflags, extinfo); if (si != NULL) { si->ref_count++; } return si; }
关键的代码在 findlibraryinternal函数中.
static soinfo* find_library_internal(const char* name, int dlflags, const android_dlextinfo* extinfo) { if (name == NULL) { return somain; } //WARNING: 从已经在加载的表中进行加载so soinfo* si = find_loaded_library_by_name(name); // Library might still be loaded, the accurate detection // of this fact is done by load_library if (si == NULL) { TRACE("[ '%s' has not been found by name. Trying harder...]", name); // 如果这个so没有加载过,则重新对so进行加载. si = load_library(name, dlflags, extinfo); } if (si != NULL && (si->flags & FLAG_LINKED) == 0) { DL_ERR("recursive link to \"%s\"", si->name); return NULL; } return si; }
我们先来看findloadedlibrarybyname函数的实现,等研究查找so的功能,然后在对loadlibrary函数进行加载.
static soinfo *find_loaded_library_by_name(const char* name) { const char* search_name = SEARCH_NAME(name); // solist 是一个全局的soinfo的链表 for (soinfo* si = solist; si != NULL; si = si->next) { if (!strcmp(search_name, si->name)) { return si; } } return NULL; }
从上面的代码中可以看到,findloadedlibrarybyname通过比对so的名称在solist表中进行查找.这个so的名称是怎么来得到呢?
#if defined(__LP64__) #define SEARCH_NAME(x) x #else // Nvidia drivers are relying on the bug: // http://code.google.com/p/android/issues/detail?id=6670 // so we continue to use base-name lookup for lp32 static const char* get_base_name(const char* name) { const char* bname = strrchr(name, '/'); return bname ? bname + 1 : name; } #define SEARCH_NAME(x) get_base_name(x) #endif
在非64位机器的条件下,SEARCHNAME宏定义的实现都是getbasename函数.该函数主要获取的是so名称的basename,也就是之后so的名称,不包含so的路径.findloadedlibrarybyname的实现在不同的Android版本中是不同的.在Android6.0的版本中该函数就改了个名称,并且具体的实现也不同了.
static bool find_loaded_library_by_soname(const char* name, soinfo** candidate) { *candidate = nullptr; // Ignore filename with path. if (strchr(name, '/') != nullptr) { return false; } uint32_t target_sdk_version = get_application_target_sdk_version(); for (soinfo* si = solist; si != nullptr; si = si->next) { const char* soname = si->get_soname(); if (soname != nullptr && (strcmp(name, soname) == 0)) { // If the library was opened under different target sdk version // skip this step and try to reopen it. The exceptions are // "libdl.so" and global group. There is no point in skipping // them because relocation process is going to use them // in any case. bool is_libdl = si == solist; if (is_libdl || (si->get_dt_flags_1() & DF_1_GLOBAL) != 0 || !si->is_linked() || si->get_target_sdk_version() == target_sdk_version) { *candidate = si; return true; } else if (*candidate == nullptr) { // for the different sdk version - remember the first library. *candidate = si; } } } return false; }
从上面的代码中可以看出,在Android6.0的版本中,查找so的名称和以前的版本中是不同的,并不是仅仅查找so的名称,也要加上so的路径.总结一下:
- 在android6.0以下的版本中(32位系统),使用so的名称进行查找.
- 在android6.0及以上的版本中,使用so的路径进行查找.
接下来继续看loadlibrary
static soinfo* load_library(const char* name, int dlflags, const android_dlextinfo* extinfo) { int fd = -1; ScopedFd file_guard(-1); if (extinfo != NULL && (extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD) != 0) { fd = extinfo->library_fd; } else { // Open the file. fd = open_library(name); if (fd == -1) { DL_ERR("library \"%s\" not found", name); return NULL; } file_guard.reset(fd); } ElfReader elf_reader(name, fd); struct stat file_stat; if (TEMP_FAILURE_RETRY(fstat(fd, &file_stat)) != 0) { DL_ERR("unable to stat file for the library %s: %s", name, strerror(errno)); return NULL; } // Check for symlink and other situations where // file can have different names. for (soinfo* si = solist; si != NULL; si = si->next) { if (si->get_st_dev() != 0 && si->get_st_ino() != 0 && si->get_st_dev() == file_stat.st_dev && si->get_st_ino() == file_stat.st_ino) { TRACE("library \"%s\" is already loaded under different name/path \"%s\" - will return existing soinfo", name, si->name); return si; } } if ((dlflags & RTLD_NOLOAD) != 0) { return NULL; } // Read the ELF header and load the segments. if (!elf_reader.Load(extinfo)) { return NULL; } soinfo* si = soinfo_alloc(SEARCH_NAME(name), &file_stat); if (si == NULL) { return NULL; } si->base = elf_reader.load_start(); si->size = elf_reader.load_size(); si->load_bias = elf_reader.load_bias(); si->phnum = elf_reader.phdr_count(); si->phdr = elf_reader.loaded_phdr(); // At this point we know that whatever is loaded @ base is a valid ELF // shared library whose segments are properly mapped in. TRACE("[ load_library base=%p size=%zu name='%s' ]", reinterpret_cast<void*>(si->base), si->size, si->name); if (!soinfo_link_image(si, extinfo)) { soinfo_free(si); return NULL; } return si; }
从上面的代码中可以看到,loadlibrary函数的实现比较简单,直接打开需要加载的so文件,然生成新的soinfo的结构体.至此系统已经完成了对soinfo结构体的构造.接下来继续看so的构造函数的调用.
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 %zd-entry DT_PREINIT_ARRAY in shared library!", name, preinit_array_count); } get_children().for_each([] (soinfo* si) { si->CallConstructors(); }); TRACE("\"%s\": calling constructors", name); // DT_INIT should be called before DT_INIT_ARRAY if both are present. // 调用so中的init函数 CallFunction("DT_INIT", init_func); // 调用so中的initarray中的函数 CallArray("DT_INIT_ARRAY", init_array, init_array_count, false); }
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). protect_data(PROT_READ | PROT_WRITE); }
在以上的代码中function是已经复制好的函数.具体的初始化过程在soinfolinkimage函数进行实现.CallArray的实现如下:
void soinfo::CallArray(const char* array_name __unused, linker_function_t* functions, size_t count, bool reverse) { if (functions == NULL) { return; } TRACE("[ Calling %s (size %zd) @ %p for '%s' ]", array_name, count, functions, name); int begin = reverse ? (count - 1) : 0; int end = reverse ? -1 : count; int step = reverse ? -1 : 1; for (int i = begin; i != end; i += step) { TRACE("[ %s[%d] == %p ]", array_name, i, functions[i]); CallFunction("function", functions[i]); } TRACE("[ Done calling %s for '%s' ]", array_name, name); }
CallArray实际上就是对initarray中的函数逐个的进行调用.调用完成之后,就基本上完成了对so的加载(初始化工作).