Android源码学习——linker(2)
来源:互联网 发布:西班牙无敌舰队知乎 编辑:程序博客网 时间:2024/05/20 12:24
本文学习的源码参考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方法来完成链接。
- Android源码学习——linker(2)
- Android源码学习——linker(3)
- Android源码学习——linker(1)
- Android源码学习——linker(4)
- Android Linker学习笔记
- Android Linker学习笔记
- Android Linker学习笔记
- Android Linker学习笔记
- ELF Linker学习篇(四)——初始化
- K60学习笔记3——Linker
- 《Android系统学习》第六章:Android4.1 HAL段错误问题—linker与prelink
- 《Android系统学习》第六章:Android4.1 HAL段错误问题—linker与prelink
- 《Android系统学习》第六章:Android4.1 HAL段错误问题—linker与prelink
- Android源码学习——ClassLoader(2)
- android-jamendo源码学习(一)——SplashscreenActivity
- Android源码学习——ClassLoader(1)
- Android源码学习——ClasLoader(3)
- Android学习——MediaScanner源码分析
- Lucene 的核心索引类
- dedecms任意页面调用栏目内容标签{dede:field.content/}的方法
- 如何使QGraphicsItem不随QGraphicsView放大缩小而改变大小
- ZooKeeper 学习 (五) 开源ZkClient操作ZooKeeper
- Android学习笔记(一)
- Android源码学习——linker(2)
- dedecms中无法直接在arctype数据表中直接调取栏目链接
- PAT --- 1002. 写出这个数 (20)
- java关于异常处理只使用try-finally而不使用catch
- Local variables and pointer
- atitit 碎片化学习.docx attilax 总结
- JavaScript的eval用法
- 关于MFC List Control 中删除 选中的n行 和 单行问题
- atom设置国内镜像