首页
社区
课程
招聘
[原创]android linker 解读2 ---- find_library
发表于: 2020-12-31 13:34 11228

[原创]android linker 解读2 ---- find_library

2020-12-31 13:34
11228

我们跟踪创建Loadtask过程。

image-20201118111340146

该函数调用LoadTask的重载函数。

该函数完成各个成员的赋值,简单看下哪些成员被赋值,name,needed_by ,elf_readermap , start_from.

start_with

追溯needed_by 来历, needed_by传入时实参为start_with , 继续往上跟,发现caller_addr 通过__builtin_return_address来赋值。

查看__builtin_return_address() 的说明 , 返回调用函数的地址,g++的内建函数 ,可以获取到调用函数时的ret地址。就可以获取到调用android_dlopen_exit的函数地址。

image-20201117212757895

在该函数将其转换为soinfo 。

image-20201118095629433

find_containing_library 根据地址的偏移计算来返回调用函数所在模块的soinfo 。

我们跟踪创建Loadtask过程。

image-20201118111340146

该函数调用LoadTask的重载函数 , 同时分配空间。

该函数完成各个成员的赋值,简单看下哪些成员被赋值,名称,needed_by ,elf_readermap , start_from 。

readers_map 就有意思了 , 在调用load过程中可以用来解析elf文件。

该过程在find_library中被生成 。

该步骤给列表中增加so。

筛选要载入的load_list 并解析elf文件的so

这个elf_reader 就是对应so用来解析elf文件的。

Dynamic entry

预连接所有DT_NEEDED 库, 逻辑简单,没有被连接过,则调用prelink_image()。

[x] prelink_image。 依次看全太肝了 , 后期在这里查字段。

涉及到重定位的镜像连接。

在Step3中找这rel_ 和pltrel 的赋值,分别为DT_JMPREL 和 DT_REL 对应.rel.plt 和.rel.dyn段 。

参数中有 plain_reloc_iterator ,传入rel的迭代器 。

Elf32_Rel的结构体。

对于DT_REL,该函数获取到rel.dyn 段, 到这里重定位的内容就很简单了。

start: =

addr : = 0

重定位的类型:

重定位运算方法:

验证重定位过程。

以类型为R_ARM_RELATIVE 为例。

偏移为1a610 类型为R_ARM_RELATIVE ,该偏移的值为19758 , 则修正 base+1a610 的地址的值为 19758 + base。

image-20201210153913863

image-20201210153926488

重定位结果。

但是重定位算法解决了,但是对于不同的类型含义是什么?? 可以确定R_ARM_RELATIVE 对应内部符号重定位 , R_ARM_GLOB_DAT,R_ARM_JMP_SLOT对应外部符号 ,其中R_ARM_GLOB_DAT和R_ARM_JMP_SLOT的差别是??

img
https://bbs.pediy.com/upload/attach/202012/790193_GT99SKKNDDFX66S.png
连接过程结束后,在依次返回到上层函数,就可以明白一个so的加载过程为 分配空间-> 解析elf -> 重定位空间 -> 调用init -> 存在则调用Jni_Onload .

https://docs.oracle.com/cd/E19120-01/open.solaris/819-0690/chapter6-26/index.html 重定位类型

http://nicephil.blinkenshell.org/my_book/ch07s04.html ELF 相关知识

// Step 0: prepare.
LoadTaskList load_tasks;
 
for (size_t i = 0; i < library_names_count; ++i) {
  const char* name = library_names[i];
  LD_LOG(kLogDlopen,"[linker.cpp] step 1 ,so_name",name);
 
  load_tasks.push_back(LoadTask::create(name, start_with, ns, &readers_map));
}
 
// If soinfos array is null allocate one on stack.
// The array is needed in case of failure; for example
// when library_names[] = {libone.so, libtwo.so} and libone.so
// is loaded correctly but libtwo.so failed for some reason.
// In this case libone.so should be unloaded on return.
// See also implementation of failure_guard below.
 
if (soinfos == nullptr) {
  size_t soinfos_size = sizeof(soinfo*)*library_names_count;
  soinfos = reinterpret_cast<soinfo**>(alloca(soinfos_size));
  memset(soinfos, 0, soinfos_size);
}
 
// list of libraries to link - see step 2.
size_t soinfos_count = 0;
 
auto scope_guard = android::base::make_scope_guard([&]() {
  for (LoadTask* t : load_tasks) {
    LD_LOG(kLogDlopen,"[linker.cpp] before call deleter %s",t->get_name());
    LoadTask::deleter(t);
  }
});
 
auto failure_guard = android::base::make_scope_guard([&]() {
  // Housekeeping
  soinfo_unload(soinfos, soinfos_count);
});
 
ZipArchiveCache zip_archive_cache;
// Step 0: prepare.
LoadTaskList load_tasks;
 
for (size_t i = 0; i < library_names_count; ++i) {
  const char* name = library_names[i];
  LD_LOG(kLogDlopen,"[linker.cpp] step 1 ,so_name",name);
 
  load_tasks.push_back(LoadTask::create(name, start_with, ns, &readers_map));
}
 
// If soinfos array is null allocate one on stack.
// The array is needed in case of failure; for example
// when library_names[] = {libone.so, libtwo.so} and libone.so
// is loaded correctly but libtwo.so failed for some reason.
// In this case libone.so should be unloaded on return.
// See also implementation of failure_guard below.
 
if (soinfos == nullptr) {
  size_t soinfos_size = sizeof(soinfo*)*library_names_count;
  soinfos = reinterpret_cast<soinfo**>(alloca(soinfos_size));
  memset(soinfos, 0, soinfos_size);
}
 
// list of libraries to link - see step 2.
size_t soinfos_count = 0;
 
auto scope_guard = android::base::make_scope_guard([&]() {
  for (LoadTask* t : load_tasks) {
    LD_LOG(kLogDlopen,"[linker.cpp] before call deleter %s",t->get_name());
    LoadTask::deleter(t);
  }
});
 
auto failure_guard = android::base::make_scope_guard([&]() {
  // Housekeeping
  soinfo_unload(soinfos, soinfos_count);
});
 
ZipArchiveCache zip_archive_cache;
 
 
static LoadTask* create(const char* name,
                        soinfo* needed_by,
                        android_namespace_t* start_from,
                        std::unordered_map<const soinfo*, ElfReader>* readers_map) {
  LoadTask* ptr = TypeBasedAllocator<LoadTask>::alloc();
  return new (ptr) LoadTask(name, needed_by, start_from, readers_map);
}
static LoadTask* create(const char* name,
                        soinfo* needed_by,
                        android_namespace_t* start_from,
                        std::unordered_map<const soinfo*, ElfReader>* readers_map) {
  LoadTask* ptr = TypeBasedAllocator<LoadTask>::alloc();
  return new (ptr) LoadTask(name, needed_by, start_from, readers_map);
}
LoadTask(const char* name,
         soinfo* needed_by,
         android_namespace_t* start_from,
         std::unordered_map<const soinfo*, ElfReader>* readers_map)
  : name_(name), needed_by_(needed_by), si_(nullptr),
    fd_(-1), close_fd_(false), file_offset_(0), elf_readers_map_(readers_map),
    is_dt_needed_(false), start_from_(start_from) {}
LoadTask(const char* name,
         soinfo* needed_by,
         android_namespace_t* start_from,
         std::unordered_map<const soinfo*, ElfReader>* readers_map)
  : name_(name), needed_by_(needed_by), si_(nullptr),
    fd_(-1), close_fd_(false), file_offset_(0), elf_readers_map_(readers_map),
    is_dt_needed_(false), start_from_(start_from) {}
 
void* android_dlopen_ext(const char* filename, int flag, const android_dlextinfo* extinfo) {
  const void* caller_addr = __builtin_return_address(0);
  return __loader_android_dlopen_ext(filename, flag, extinfo, caller_addr);
}
void* android_dlopen_ext(const char* filename, int flag, const android_dlextinfo* extinfo) {
  const void* caller_addr = __builtin_return_address(0);
  return __loader_android_dlopen_ext(filename, flag, extinfo, caller_addr);
}
 
 
 
 
soinfo* find_containing_library(const void* p) {
  ElfW(Addr) address = reinterpret_cast<ElfW(Addr)>(p);
  for (soinfo* si = solist_get_head(); si != nullptr; si = si->next) {
    if (address >= si->base && address - si->base < si->size) {
      return si;
    }
  }
  return nullptr;
}
soinfo* find_containing_library(const void* p) {
  ElfW(Addr) address = reinterpret_cast<ElfW(Addr)>(p);
  for (soinfo* si = solist_get_head(); si != nullptr; si = si->next) {
    if (address >= si->base && address - si->base < si->size) {
      return si;
    }
  }
  return nullptr;
}
 
 
static LoadTask* create(const char* name,
                        soinfo* needed_by,
                        android_namespace_t* start_from,
                        std::unordered_map<const soinfo*, ElfReader>* readers_map) {
  LoadTask* ptr = TypeBasedAllocator<LoadTask>::alloc();
  return new (ptr) LoadTask(name, needed_by, start_from, readers_map);
}
static LoadTask* create(const char* name,
                        soinfo* needed_by,
                        android_namespace_t* start_from,
                        std::unordered_map<const soinfo*, ElfReader>* readers_map) {
  LoadTask* ptr = TypeBasedAllocator<LoadTask>::alloc();
  return new (ptr) LoadTask(name, needed_by, start_from, readers_map);
}
LoadTask(const char* name,
         soinfo* needed_by,
         android_namespace_t* start_from,
         std::unordered_map<const soinfo*, ElfReader>* readers_map)
  : name_(name), needed_by_(needed_by), si_(nullptr),
    fd_(-1), close_fd_(false), file_offset_(0), elf_readers_map_(readers_map),
    is_dt_needed_(false), start_from_(start_from) {}
LoadTask(const char* name,
         soinfo* needed_by,
         android_namespace_t* start_from,
         std::unordered_map<const soinfo*, ElfReader>* readers_map)
  : name_(name), needed_by_(needed_by), si_(nullptr),
    fd_(-1), close_fd_(false), file_offset_(0), elf_readers_map_(readers_map),
    is_dt_needed_(false), start_from_(start_from) {}
 
// readers_map is shared across recursive calls to find_libraries.
std::unordered_map<const soinfo*, ElfReader> readers_map;
// readers_map is shared across recursive calls to find_libraries.
std::unordered_map<const soinfo*, ElfReader> readers_map;
// Step 1: expand the list of load_tasks to include
  // all DT_NEEDED libraries (do not load them just yet)
  for (size_t i = 0; i<load_tasks.size(); ++i) {
    LoadTask* task = load_tasks[i];
    soinfo* needed_by = task->get_needed_by();
    bool is_dt_needed = needed_by != nullptr && (needed_by != start_with || add_as_children);
    task->set_extinfo(is_dt_needed ? nullptr : extinfo);
    task->set_dt_needed(is_dt_needed);
 
/*
try to find the load.Note: start from the namespace that is stored in the LoadTask. This namespace is different from the current namespace when the LoadTask is for a transitive dependency and the lib that created the LoadTask is not found in the current namespace but in one of the linked namespace.
*/
    if (!find_library_internal(const_cast<android_namespace_t*>(task->get_start_from()),
                               task,
                               &zip_archive_cache,
                               &load_tasks,
                               rtld_flags,
                               search_linked_namespaces || is_dt_needed)) {
      return false;
    }
 
    soinfo* si = task->get_soinfo();
 
    if (is_dt_needed) {
      needed_by->add_child(si);
 
      if (si->is_linked()) {
        si->increment_ref_count();
      }
    }
 
    // When ld_preloads is not null, the first
    // ld_preloads_count libs are in fact ld_preloads.
    if (ld_preloads != nullptr && soinfos_count < ld_preloads_count) {
      ld_preloads->push_back(si);
    }
 
    if (soinfos_count < library_names_count) {
      soinfos[soinfos_count++] = si;
    }
  }
// Step 1: expand the list of load_tasks to include
  // all DT_NEEDED libraries (do not load them just yet)
  for (size_t i = 0; i<load_tasks.size(); ++i) {
    LoadTask* task = load_tasks[i];
    soinfo* needed_by = task->get_needed_by();
    bool is_dt_needed = needed_by != nullptr && (needed_by != start_with || add_as_children);
    task->set_extinfo(is_dt_needed ? nullptr : extinfo);
    task->set_dt_needed(is_dt_needed);
 
/*
try to find the load.Note: start from the namespace that is stored in the LoadTask. This namespace is different from the current namespace when the LoadTask is for a transitive dependency and the lib that created the LoadTask is not found in the current namespace but in one of the linked namespace.
*/
    if (!find_library_internal(const_cast<android_namespace_t*>(task->get_start_from()),
                               task,
                               &zip_archive_cache,
                               &load_tasks,
                               rtld_flags,
                               search_linked_namespaces || is_dt_needed)) {
      return false;
    }
 
    soinfo* si = task->get_soinfo();
 
    if (is_dt_needed) {
      needed_by->add_child(si);
 
      if (si->is_linked()) {
        si->increment_ref_count();
      }
    }
 
    // When ld_preloads is not null, the first
    // ld_preloads_count libs are in fact ld_preloads.
    if (ld_preloads != nullptr && soinfos_count < ld_preloads_count) {
      ld_preloads->push_back(si);
    }
 
    if (soinfos_count < library_names_count) {
      soinfos[soinfos_count++] = si;
    }
  }
// Step 2: Load libraries in random order (see b/24047022)
LoadTaskList load_list;
for (auto&& task : load_tasks) {
  soinfo* si = task->get_soinfo();
  auto pred = [&](const LoadTask* t) {
    return t->get_soinfo() == si;
  };
 
  if (!si->is_linked() &&
      std::find_if(load_list.begin(), load_list.end(), pred) == load_list.end() ) {
    load_list.push_back(task);
  }
}
shuffle(&load_list);
 
for (auto&& task : load_list) {
    //*** 看看这个loda函数
  if (!task->load()) {
    return false;
  }
}
// Step 2: Load libraries in random order (see b/24047022)
LoadTaskList load_list;
for (auto&& task : load_tasks) {
  soinfo* si = task->get_soinfo();
  auto pred = [&](const LoadTask* t) {
    return t->get_soinfo() == si;
  };
 
  if (!si->is_linked() &&
      std::find_if(load_list.begin(), load_list.end(), pred) == load_list.end() ) {
    load_list.push_back(task);
  }
}
shuffle(&load_list);
 
for (auto&& task : load_list) {
    //*** 看看这个loda函数
  if (!task->load()) {
    return false;
  }
}
bool load() {
  ElfReader& elf_reader = get_elf_reader();
    //在elf_read.Load 下完成elf文件空间的分配和解析 ,还不能算载入了so。
  if (!elf_reader.Load(extinfo_)) {
    return false;
  }
  si_->base = elf_reader.load_start();
  si_->size = elf_reader.load_size();
  si_->set_mapped_by_caller(elf_reader.is_mapped_by_caller());
  si_->load_bias = elf_reader.load_bias();
  LD_LOG(kLogDlopen,"[linker.cpp] Step2 load_bias %p ",si_->load_bias);
  si_->phnum = elf_reader.phdr_count();
  si_->phdr = elf_reader.loaded_phdr();
  return true;
}
bool load() {
  ElfReader& elf_reader = get_elf_reader();
    //在elf_read.Load 下完成elf文件空间的分配和解析 ,还不能算载入了so。
  if (!elf_reader.Load(extinfo_)) {
    return false;
  }
  si_->base = elf_reader.load_start();
  si_->size = elf_reader.load_size();
  si_->set_mapped_by_caller(elf_reader.is_mapped_by_caller());
  si_->load_bias = elf_reader.load_bias();
  LD_LOG(kLogDlopen,"[linker.cpp] Step2 load_bias %p ",si_->load_bias);
  si_->phnum = elf_reader.phdr_count();
  si_->phdr = elf_reader.loaded_phdr();
  return true;
}
typedef struct
{
  Elf64_Sxword    d_tag;            /* Dynamic entry type */
  union
    {
      Elf64_Xword d_val;        /* Integer value */
      Elf64_Addr d_ptr;            /* Address value */
    } d_un;
} Elf64_Dyn;
typedef struct
{
  Elf64_Sxword    d_tag;            /* Dynamic entry type */
  union
    {
      Elf64_Xword d_val;        /* Integer value */
      Elf64_Addr d_ptr;            /* Address value */
    } d_un;
} Elf64_Dyn;
// Step 3: pre-link all DT_NEEDED libraries in breadth first order.
for (auto&& task : load_tasks) {
  soinfo* si = task->get_soinfo();
  if (!si->is_linked() && !si->prelink_image()) {
    return false;
  }
}
// Step 3: pre-link all DT_NEEDED libraries in breadth first order.
for (auto&& task : load_tasks) {
  soinfo* si = task->get_soinfo();
  if (!si->is_linked() && !si->prelink_image()) {
    return false;
  }
}
// Step 5: link libraries that are not destined to this namespace.
// Do this by recursively calling find_libraries on the namespace where the lib
// was found during Step 1.
for (auto&& task : load_tasks) {
  soinfo* si = task->get_soinfo();
  if (si->get_primary_namespace() != ns) {
    const char* name = task->get_name();
    if (find_libraries(si->get_primary_namespace(), task->get_needed_by(), &name, 1,
                       nullptr /* soinfos */, nullptr /* ld_preloads */, 0 /* ld_preload_count */,
                       rtld_flags, nullptr /* extinfo */, false /* add_as_children */,
                       false /* search_linked_namespaces */, readers_map, namespaces)) {
      // If this lib is directly needed by one of the libs in this namespace,
      // then increment the count
      soinfo* needed_by = task->get_needed_by();
      if (needed_by != nullptr && needed_by->get_primary_namespace() == ns && si->is_linked()) {
        si->increment_ref_count();
      }
    } else {
      return false;
    }
  }
}
  //连接这个命名空间的库文件
// Step 6: link libraries in this namespace
soinfo_list_t local_group;
walk_dependencies_tree(
    (start_with != nullptr && add_as_children) ? &start_with : soinfos,
    (start_with != nullptr && add_as_children) ? 1 : soinfos_count,
    [&] (soinfo* si) {
  if (ns->is_accessible(si)) {
    local_group.push_back(si);
    return kWalkContinue;
  } else {
    return kWalkSkip;
  }
});
soinfo_list_t global_group = ns->get_global_group();
bool linked = local_group.visit([&](soinfo* si) {
  if (!si->is_linked()) {
    LD_LOG(kLogDlopen,"so %s is not linked , now try to link ",si->get_soname());
    if (!si->link_image(global_group, local_group, extinfo) ||
        !get_cfi_shadow()->AfterLoad(si, solist_get_head())) {
      return false;
    }
  }
 
  return true;
});
if (linked) {
  local_group.for_each([](soinfo* si) {
    LD_LOG(kLogDlopen,"travser local_group list %s" , si->get_soname());
    if (!si->is_linked()) {
      si->set_linked();
    }
  });
 
  failure_guard.Disable();
}
 
return linked;
// Step 5: link libraries that are not destined to this namespace.
// Do this by recursively calling find_libraries on the namespace where the lib
// was found during Step 1.
for (auto&& task : load_tasks) {
  soinfo* si = task->get_soinfo();
  if (si->get_primary_namespace() != ns) {
    const char* name = task->get_name();
    if (find_libraries(si->get_primary_namespace(), task->get_needed_by(), &name, 1,
                       nullptr /* soinfos */, nullptr /* ld_preloads */, 0 /* ld_preload_count */,
                       rtld_flags, nullptr /* extinfo */, false /* add_as_children */,
                       false /* search_linked_namespaces */, readers_map, namespaces)) {
      // If this lib is directly needed by one of the libs in this namespace,
      // then increment the count
      soinfo* needed_by = task->get_needed_by();
      if (needed_by != nullptr && needed_by->get_primary_namespace() == ns && si->is_linked()) {
        si->increment_ref_count();
      }
    } else {
      return false;
    }
  }
}
  //连接这个命名空间的库文件
// Step 6: link libraries in this namespace
soinfo_list_t local_group;
walk_dependencies_tree(
    (start_with != nullptr && add_as_children) ? &start_with : soinfos,
    (start_with != nullptr && add_as_children) ? 1 : soinfos_count,
    [&] (soinfo* si) {
  if (ns->is_accessible(si)) {
    local_group.push_back(si);
    return kWalkContinue;
  } else {
    return kWalkSkip;
  }
});
soinfo_list_t global_group = ns->get_global_group();
bool linked = local_group.visit([&](soinfo* si) {
  if (!si->is_linked()) {
    LD_LOG(kLogDlopen,"so %s is not linked , now try to link ",si->get_soname());
    if (!si->link_image(global_group, local_group, extinfo) ||
        !get_cfi_shadow()->AfterLoad(si, solist_get_head())) {
      return false;
    }
  }
 
  return true;
});
if (linked) {
  local_group.for_each([](soinfo* si) {
    LD_LOG(kLogDlopen,"travser local_group list %s" , si->get_soname());
    if (!si->is_linked()) {
      si->set_linked();
    }
  });
 
  failure_guard.Disable();
}
 
return linked;
bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& local_group,
                        const android_dlextinfo* extinfo) {
 
 
// 这里仅保留了大家比较熟悉的类型。常规都是rel。
 
#if defined(USE_RELA)
  if (rela_ != nullptr) {
    DEBUG("[ relocating %s ]", get_realpath());
    if (!relocate(version_tracker,
            plain_reloc_iterator(rela_, rela_count_), global_group, local_group)) {
      return false;
    }
  }
  if (plt_rela_ != nullptr) {
    DEBUG("[ relocating %s plt ]", get_realpath());
    if (!relocate(version_tracker,
            plain_reloc_iterator(plt_rela_, plt_rela_count_), global_group, local_group)) {
      return false;
    }
  }
#else
    //把重心放在这里
 
  if ( != nullptr) {
    DEBUG("[ relocating %s ]", get_realpath());
    if (!relocate(version_tracker,
            plain_reloc_iterator(rel_, rel_count_), global_group, local_group)) {
      return false;
    }
  }
  if (plt_rel_ != nullptr) {
    LD_LOG(kLogDlopen,"[ relocating %s plt ]", get_realpath());
    if (!relocate(version_tracker,
            plain_reloc_iterator(plt_rel_, plt_rel_count_), global_group, local_group)) {
      return false;
    }
  }
#endif
bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& local_group,
                        const android_dlextinfo* extinfo) {
 
 
// 这里仅保留了大家比较熟悉的类型。常规都是rel。
 
#if defined(USE_RELA)
  if (rela_ != nullptr) {
    DEBUG("[ relocating %s ]", get_realpath());
    if (!relocate(version_tracker,
            plain_reloc_iterator(rela_, rela_count_), global_group, local_group)) {
      return false;
    }
  }
  if (plt_rela_ != nullptr) {
    DEBUG("[ relocating %s plt ]", get_realpath());
    if (!relocate(version_tracker,
            plain_reloc_iterator(plt_rela_, plt_rela_count_), global_group, local_group)) {
      return false;

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2020-12-31 16:51 被pareto编辑 ,原因:
上传的附件:
收藏
免费 3
支持
分享
最新回复 (4)
雪    币: 14855
活跃值: (6083)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
调用init -> 存在则调用Jni_Onload
这部分怎么没有解读?调试so 入口的关键代码
2020-12-31 14:38
0
雪    币: 463
活跃值: (2706)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
3
可能我俩关注的重点不太一样的吧 , 这个部分我目前只关注调用先后和多个needed_by so的加载顺序。
2020-12-31 14:45
0
雪    币: 14855
活跃值: (6083)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4

如果你只关注调用先后和多个needed_by so的加载顺序,为何so重定位又花那么多篇幅?

最后于 2020-12-31 16:22 被tDasm编辑 ,原因:
2020-12-31 16:21
0
雪    币: 463
活跃值: (2706)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
5
你可以看下上篇的目的
2020-12-31 16:33
0
游客
登录 | 注册 方可回帖
返回
//