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方法来完成链接。


原创粉丝点击