本文学习的源码参考AndroidXRef,版本为Lollipop 5.1.0_r1。
上一部分讲到do_dlopen里面,调用了find_library(name, flags, extinfo)
去加载so库,最终返回一个soinfo的指针,我们看下这个过程里面做了什么:
static soinfo* find_library(const char* name, int dlflags, const android_dlextinfo* extinfo) {
if (name == nullptr) {
somain->ref_count++;
return somain;
}
soinfo* si;
if (!find_libraries(&name, 1, &si, nullptr, 0, dlflags, extinfo)) {
return nullptr;
}
return si;
}
调了find_libraries,继续看这个函数:
static bool find_libraries(const char* const library_names[], size_t library_names_size, soinfo* soinfos[],
soinfo* ld_preloads[], size_t ld_preloads_size, int dlflags, const android_dlextinfo* extinfo) {
// Step 0: prepare.
LoadTaskList load_tasks;
for (size_t i = 0; i < library_names_size; ++i) {
const char* name = library_names[i];
load_tasks.push_back(LoadTask::create(name, nullptr));
}
// Libraries added to this list in reverse order so that we can
// start linking from bottom-up - see step 2.
SoinfoLinkedList found_libs;
size_t soinfos_size = 0;
auto failure_guard = make_scope_guard([&]() {
// Housekeeping
load_tasks.for_each([] (LoadTask* t) {
LoadTask::deleter(t);
});
for (size_t i = 0; i<soinfos_size; ++i) {
soinfo_unload(soinfos[i]);
}
});
// Step 1: load and pre-link all DT_NEEDED libraries in breadth first order.
for (LoadTask::unique_ptr task(load_tasks.pop_front()); task.get() != nullptr; task.reset(load_tasks.pop_front())) {
soinfo* si = find_library_internal(load_tasks, task->get_name(), dlflags, extinfo);
if (si == nullptr) {
return false;
}
soinfo* needed_by = task->get_needed_by();
if (is_recursive(si, needed_by)) {
return false;
}
si->ref_count++;
if (needed_by != nullptr) {
needed_by->add_child(si);
}
found_libs.push_front(si);
// When ld_preloads is not null first
// ld_preloads_size libs are in fact ld_preloads.
if (ld_preloads != nullptr && soinfos_size < ld_preloads_size) {
ld_preloads[soinfos_size] = si;
}
if (soinfos_size<library_names_size) {
soinfos[soinfos_size++] = si;
}
}
// Step 2: link libraries.
soinfo* si;
while ((si = found_libs.pop_front()) != nullptr) {
if ((si->flags & FLAG_LINKED) == 0) {
if (!si->LinkImage(extinfo)) {
return false;
}
si->flags |= FLAG_LINKED;
}
}
// All is well - found_libs and load_tasks are empty at this point
// and all libs are successfully linked.
failure_guard.disable();
return true;
}
也是分了好几步来完成的:
Step 0:预处理工作。主要是为了加载多个so,建立一个load_tasks,需要注意的是,so的加载顺序要符合依赖关系;
Step 1:加载so并且预链接依赖库。对于load_tasks里面的每一个对象,会去调用find_library_internal(load_tasks, task->get_name(), dlflags, extinfo)
加载so,然后去检查依赖so,并压栈到found_libs里面(这里不太懂,好像跟我之前看的4.4版本的不太一样);
Step 2:so的链接。调用si->LinkImage(extinfo)
完成对so的链接。
首先find_library_internal这个函数吧:
static soinfo* find_library_internal(LoadTaskList& load_tasks, const char* name, int dlflags, const android_dlextinfo* extinfo) {
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 == nullptr) {
TRACE("[ '%s' has not been found by name. Trying harder...]", name);
si = load_library(load_tasks, name, dlflags, extinfo);
}
return si;
}
先去find_loaded_library_by_name(name)
查找so库是否被加载,如果已经被加载那么直接返回soinfo指针就可以了;否则会去调load_library(load_tasks, name, dlflags, extinfo)
加载so。
看一下so加载的具体过程:
static soinfo* load_library(LoadTaskList& load_tasks, const char* name, int dlflags, const android_dlextinfo* extinfo) {
int fd = -1;
off64_t file_offset = 0;
ScopedFd file_guard(-1);
if (extinfo != nullptr && (extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD) != 0) {
fd = extinfo->library_fd;
if ((extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET) != 0) {
file_offset = extinfo->library_fd_offset;
}
} else {
// Open the file.
fd = open_library(name);
if (fd == -1) {
DL_ERR("library \"%s\" not found", name);
return nullptr;
}
file_guard.reset(fd);
}
if ((file_offset % PAGE_SIZE) != 0) {
DL_ERR("file offset for the library \"%s\" is not page-aligned: %" PRId64, name, file_offset);
return nullptr;
}
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 nullptr;
}
// Check for symlink and other situations where
// file can have different names.
for (soinfo* si = solist; si != nullptr; 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 &&
si->get_file_offset() == file_offset) {
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) {
DL_ERR("library \"%s\" wasn't loaded and RTLD_NOLOAD prevented it", name);
return nullptr;
}
// Read the ELF header and load the segments.
ElfReader elf_reader(name, fd, file_offset);
if (!elf_reader.Load(extinfo)) {
return nullptr;
}
soinfo* si = soinfo_alloc(SEARCH_NAME(name), &file_stat, file_offset);
if (si == nullptr) {
return nullptr;
}
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();
if (!si->PrelinkImage()) {
soinfo_free(si);
return nullptr;
}
for_each_dt_needed(si, [&] (const char* name) {
load_tasks.push_back(LoadTask::create(name, si));
});
return si;
}
稍微有点长,但是逻辑还算清晰,还是分步来讲:
首先,open_library(name)
打开so文件,返回一个文件句柄;
然后,创建一个ElfReader对象,调用这个对象的Load方法去加载so里面的段;
接下来,调用soinfo_alloc为soinfo分配结构,并且基于装载结果为si的成员变量赋值;
最后,怎么多了一个si->PrelinkImage()
???(暂且不管,慢慢看吧)
大致过程清楚了,find_library包含了so的加载和链接过程,其中,又通过ElfReader对象的Load方法去完成加载,加载完成后会构造一个soinfo的结构体指针,然后调用这个结构体的PrelinkImage和LinkImage方法来完成链接。