欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android Native庫(kù)的加載及動(dòng)態(tài)鏈接的過程

 更新時(shí)間:2018年01月22日 10:16:47   作者:lbtrace  
這篇文章主要介紹了Android Native庫(kù)的加載及動(dòng)態(tài)鏈接的加載過程,需要的朋友可以參考下

Native庫(kù)的裝載過程

我們從一個(gè)簡(jiǎn)單的NDK Demo開始分析。

public class MainActivity extends AppCompatActivity {
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  // Example of a call to a native method
  final TextView tv = (TextView) findViewById(R.id.sample_text);
  tv.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View view) {
    tv.setText(stringFromJNI());
   }
  });
 }
 /**
  * A native method that is implemented by the 'native-lib' native library,
  * which is packaged with this application.
  */
 public native String stringFromJNI();
 // Used to load the 'native-lib' library on application startup.
 // 動(dòng)態(tài)庫(kù)的裝載及鏈接
 static {
  System.loadLibrary("native-lib");
 }
}

Android 鏈接器Linker之前的工作

 

下面從 System.loadLibrary() 開始分析。

public static void loadLibrary(String libname) {
  這里VMStack.getCallingClassLoader()返回應(yīng)用的類加載器
  Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), libname);
 }

下面看 loadLibrary0()

synchronized void loadLibrary0(ClassLoader loader, String libname) {
  if (libname.indexOf((int)File.separatorChar) != -1) {
   throw new UnsatisfiedLinkError(
 "Directory separator should not appear in library name: " + libname);
  }
  String libraryName = libname;
  if (loader != null) {
   // findLibrary()返回庫(kù)的全路徑名
   String filename = loader.findLibrary(libraryName);
   if (filename == null) {
    // It's not necessarily true that the ClassLoader used
    // System.mapLibraryName, but the default setup does, and it's
    // misleading to say we didn't find "libMyLibrary.so" when we
    // actually searched for "liblibMyLibrary.so.so".
    throw new UnsatisfiedLinkError(loader + " couldn't find \"" +
            System.mapLibraryName(libraryName) + "\"");
   }
   // 裝載動(dòng)態(tài)庫(kù)
   String error = doLoad(filename, loader);
   if (error != null) {
    throw new UnsatisfiedLinkError(error);
   }
   return;
  }
  ......
 }

參數(shù) loader 為Android的應(yīng)用類加載器,它是 PathClassLoader 類型的對(duì)象,繼承自 BaseDexClassLoader 對(duì)象,下面看 BaseDexClassLoader 的 findLibrary() 方法。

public String findLibrary(String name) {
  // 調(diào)用DexPathList的findLibrary方法
  return pathList.findLibrary(name);
 }

下面看 DexPathList 的 findLibrary() 方法

public String findLibrary(String libraryName) {
  // 產(chǎn)生平臺(tái)相關(guān)的庫(kù)名稱這里返回libxxx.so
  String fileName = System.mapLibraryName(libraryName);
  for (Element element : nativeLibraryPathElements) {
   // 查找動(dòng)態(tài)庫(kù)返回庫(kù)的全路徑名
   String path = element.findNativeLibrary(fileName);

   if (path != null) {
    return path;
   }
  }
  return null;
 }

回到 loadLibrary0() ,有了動(dòng)態(tài)庫(kù)的全路徑名就可以裝載庫(kù)了,下面看 doLoad() 。

private String doLoad(String name, ClassLoader loader) {
  ......
  // 獲取應(yīng)用類加載器的Native庫(kù)搜索路徑
  String librarySearchPath = null;
  if (loader != null && loader instanceof BaseDexClassLoader) {
   BaseDexClassLoader dexClassLoader = (BaseDexClassLoader) loader;
   librarySearchPath = dexClassLoader.getLdLibraryPath();
  }
  // nativeLoad should be synchronized so there's only one LD_LIBRARY_PATH in use regardless
  // of how many ClassLoaders are in the system, but dalvik doesn't support synchronized
  // internal natives.
  synchronized (this) {
   return nativeLoad(name, loader, librarySearchPath);
  }
 }

nativeLoad() 最終調(diào)用 LoadNativeLibrary() ,下面直接分析 LoadNativeLibrary() 。

bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,
         const std::string& path,
         jobject class_loader,
         jstring library_path,
         std::string* error_msg) {
 ......
 SharedLibrary* library;
 Thread* self = Thread::Current();
 {
 // TODO: move the locking (and more of this logic) into Libraries.
 // 檢查動(dòng)態(tài)庫(kù)是否已經(jīng)裝載過,如果已經(jīng)裝載過(類加載器也匹配)直接返回。
 MutexLock mu(self, *Locks::jni_libraries_lock_);
 library = libraries_->Get(path);
 }
 ......
 // 沒有裝載過,裝載鏈接動(dòng)態(tài)庫(kù)
 // 參數(shù)patch_str傳遞的是動(dòng)態(tài)庫(kù)的全路徑,之所以還要傳遞搜索路徑,是因?yàn)榭赡馨囊蕾噹?kù)
 void* handle = android::OpenNativeLibrary(env,
           runtime_->GetTargetSdkVersion(),
           path_str,
           class_loader,
           library_path);
 ......
 // 查找動(dòng)態(tài)庫(kù)中的"JNI_OnLoad"函數(shù)
 sym = library->FindSymbol("JNI_OnLoad", nullptr);
 if (sym == nullptr) {
 VLOG(jni) << "[No JNI_OnLoad found in \"" << path << "\"]";
 was_successful = true;
 } else {
 // Call JNI_OnLoad. We have to override the current class
 // loader, which will always be "null" since the stuff at the
 // top of the stack is around Runtime.loadLibrary(). (See
 // the comments in the JNI FindClass function.)
 ScopedLocalRef<jobject> old_class_loader(env, env->NewLocalRef(self->GetClassLoaderOverride()));
 self->SetClassLoaderOverride(class_loader);

 VLOG(jni) << "[Calling JNI_OnLoad in \"" << path << "\"]";
 typedef int (*JNI_OnLoadFn)(JavaVM*, void*);
 JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
 // 調(diào)用庫(kù)的JNI_OnLoad函數(shù)注冊(cè)JNI, 本文暫不討論
 int version = (*jni_on_load)(this, nullptr);
 ......
 }
 ......
}

對(duì)于JNI注冊(cè),這里暫不討論,下面看 OpenNativeLibrary() 的實(shí)現(xiàn)。

void* OpenNativeLibrary(JNIEnv* env,
      int32_t target_sdk_version,
      const char* path,
      jobject class_loader,
      jstring library_path) {
#if defined(__ANDROID__)
 UNUSED(target_sdk_version);
 if (class_loader == nullptr) {
 return dlopen(path, RTLD_NOW);
 }
 std::lock_guard<std::mutex> guard(g_namespaces_mutex);
 // 找到類加載器映射的命名空間(Android應(yīng)用類加載器創(chuàng)建時(shí)創(chuàng)建)
 // 關(guān)于命名空間的動(dòng)態(tài)鏈接請(qǐng)參考http://jackwish.net/namespace-based-dynamic-linking-chn.html
 android_namespace_t* ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader);
 .......
 android_dlextinfo extinfo;
 // 在一個(gè)不同的命名空間中裝載
 extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
 extinfo.library_namespace = ns;
 // RILD_NOW表示重定位在dlopen返回前完成,不會(huì)延遲到第一次執(zhí)行(RTLD_LAZY)
 return android_dlopen_ext(path, RTLD_NOW, &extinfo);
 ......
}

下面看 android_dlopen_ext() 的實(shí)現(xiàn)

void* android_dlopen_ext(const char* filename, int flags, const android_dlextinfo* extinfo) {
 // __builtin_return_address是編譯器的內(nèi)建函數(shù),__builtin_return_address(0)表示當(dāng)前函數(shù)的返回地址
 void* caller_addr = __builtin_return_address(0);
 return dlopen_ext(filename, flags, extinfo, caller_addr);
}

接下來就Android鏈接器linker的工作了。

Android 鏈接器Linker的裝載工作

 

下面從 do_dlopen() 開始分析。

void* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo,
     void* caller_addr) {
 // caller_addr在libnativeloader.so中
 // 查找地址所在的動(dòng)態(tài)庫(kù)(采用遍歷查找,可以優(yōu)化查找)
 soinfo* const caller = find_containing_library(caller_addr);
 // ns為調(diào)用庫(kù)所在命名空間
 android_namespace_t* ns = get_caller_namespace(caller);
 ......
 if (extinfo != nullptr) {
 ......
 // extinfo->flags為ANDROID_DLEXT_USE_NAMESPACE
 if ((extinfo->flags & ANDROID_DLEXT_USE_NAMESPACE) != 0) {
  if (extinfo->library_namespace == nullptr) {
  DL_ERR("ANDROID_DLEXT_USE_NAMESPACE is set but extinfo->library_namespace is null");
  return nullptr;
  }
  // 命名空間使用應(yīng)用自身類加載器-命名空間
  ns = extinfo->library_namespace;
 }
 }
 ......
 // 在命名空間ns中裝載庫(kù)
 soinfo* si = find_library(ns, translated_name, flags, extinfo, caller);
 ......
}

find_library() 當(dāng)參數(shù)translated_name不為空時(shí),直接調(diào)用 find_libraries() ,這是裝載鏈接的關(guān)鍵函數(shù),下面看它的實(shí)現(xiàn)。

static bool find_libraries(android_namespace_t* ns,
       soinfo* start_with,
       const char* const library_names[],
       size_t library_names_count, soinfo* soinfos[],
       std::vector<soinfo*>* ld_preloads,
       size_t ld_preloads_count, int rtld_flags,
       const android_dlextinfo* extinfo,
       bool add_as_children) {
 // ns為應(yīng)用類加載器-命名空間
 // 這里start_with為libnativeloader.so的soinfo
 // library_names為需要裝載的動(dòng)態(tài)庫(kù),不包含依賴庫(kù)
 // library_names_count需要裝載的動(dòng)態(tài)庫(kù)的數(shù)量,這里為1
 // ld_preloads為nullptr
 // add_as_children為false
 ......
 // 為需要裝載的動(dòng)態(tài)庫(kù)創(chuàng)建LoadTask添加到load_tasks
 // LoadTask用于管理動(dòng)態(tài)庫(kù)的裝載
 for (size_t i = 0; i < library_names_count; ++i) {
 const char* name = library_names[i];
 load_tasks.push_back(LoadTask::create(name, start_with, &readers_map));
 }
 // Construct global_group.
 // 收集命名空間ns中設(shè)置了DF_1_GLOBAL(RTLD_GLOBAL:共享庫(kù)中的符號(hào)可被后續(xù)裝載的庫(kù)重定位)標(biāo)志的動(dòng)態(tài)庫(kù)
 soinfo_list_t global_group = make_global_group(ns);
 ......
 // Step 1: expand the list of load_tasks to include
 // all DT_NEEDED libraries (do not load them just yet)
 // load_tasks以廣度優(yōu)先遍歷的順序存儲(chǔ)動(dòng)態(tài)庫(kù)依賴樹
 // 例如依賴樹: 1
 //   / \
 //   2 3
 //   /  \
 //  4  5
 // load_tasks: 1->2->3->4->5
 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);
 // 收集動(dòng)態(tài)庫(kù)的信息以及它的依賴庫(kù)
 if(!find_library_internal(ns, task, &zip_archive_cache, &load_tasks, rtld_flags)) {
  return false;
 }
 soinfo* si = task->get_soinfo();
 if (is_dt_needed) {
  // si添加到needed_by的依賴中
  needed_by->add_child(si);
 }
 if (si->is_linked()) {
  // 已經(jīng)鏈接過的庫(kù)增加引用計(jì)數(shù)
  si->increment_ref_count();
 }
 ......
 if (soinfos_count < library_names_count) {
  soinfos[soinfos_count++] = si;
 }
 }
 // Step 2: Load libraries in random order (see b/24047022)
 LoadTaskList load_list;
 // 需要裝載的庫(kù)放到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);
 }
 }
 // 隨機(jī)化load_list中庫(kù)的順序
 shuffle(&load_list);
 for (auto&& task : load_list) {
 // 裝載動(dòng)態(tài)庫(kù)
 if (!task->load()) {
  return false;
 }
 }
 // Step 3: pre-link all DT_NEEDED libraries in breadth first order.
 // 預(yù)鏈接load_tasks中沒有鏈接過的庫(kù),見下文
 for (auto&& task : load_tasks) {
 soinfo* si = task->get_soinfo();
 if (!si->is_linked() && !si->prelink_image()) {
  return false;
 }
 }
 // Step 4: Add LD_PRELOADed libraries to the global group for
 // future runs. There is no need to explicitly add them to
 // the global group for this run because they are going to
 // appear in the local group in the correct order.
 if (ld_preloads != nullptr) {
 for (auto&& si : *ld_preloads) {
  si->set_dt_flags_1(si->get_dt_flags_1() | DF_1_GLOBAL);
 }
 }
 // Step 5: link libraries.
 // 鏈接過程,見下文
 soinfo_list_t local_group;
 // 廣度優(yōu)先遍歷添加動(dòng)態(tài)庫(kù)依賴圖到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) {
 local_group.push_back(si);
 return true;
 });
 // We need to increment ref_count in case
 // the root of the local group was not linked.
 bool was_local_group_root_linked = local_group.front()->is_linked();
 bool linked = local_group.visit([&](soinfo* si) {
 if (!si->is_linked()) {
  if (!si->link_image(global_group, local_group, extinfo)) {
  return false;
  }
 }
 return true;
 });
 if (linked) {
 // 設(shè)置鏈接標(biāo)志
 local_group.for_each([](soinfo* si) {
  if (!si->is_linked()) {
  si->set_linked();
  }
 });
 failure_guard.disable();
 }
 ......
}

find_libraries() 中動(dòng)態(tài)庫(kù)的裝載可以分為兩部分

  • 收集動(dòng)態(tài)庫(kù)的信息及其依賴庫(kù)
  • 裝載動(dòng)態(tài)庫(kù)及依賴庫(kù)
  • 收集動(dòng)態(tài)庫(kù)的信息及依賴庫(kù)

下面從 find_library_internal() 開始分析。

static bool find_library_internal(android_namespace_t* ns,
         LoadTask* task,
         ZipArchiveCache* zip_archive_cache,
         LoadTaskList* load_tasks,
         int rtld_flags) {
 soinfo* candidate;
 // 在應(yīng)用類加載器-命名空間中查找動(dòng)態(tài)庫(kù)是否已經(jīng)裝載過
 if (find_loaded_library_by_soname(ns, task->get_name(), &candidate)) {
 task->set_soinfo(candidate);
 return true;
 }
 // 在默認(rèn)命名空間中查找動(dòng)態(tài)庫(kù)是否已經(jīng)裝載過
 if (ns != &g_default_namespace) {
 // check public namespace
 candidate = g_public_namespace.find_if([&](soinfo* si) {
  return strcmp(task->get_name(), si->get_soname()) == 0;
 });
 ......
 }
 ......
 // 裝載庫(kù)
 if (load_library(ns, task, zip_archive_cache, load_tasks, rtld_flags)) {
 return true;
 } else {
 // In case we were unable to load the library but there
 // is a candidate loaded under the same soname but different
 // sdk level - return it anyways.
 if (candidate != nullptr) {
  task->set_soinfo(candidate);
  return true;
 }
 }
 return false;
}

下面分析 load_library()

static bool load_library(android_namespace_t* ns,
       LoadTask* task,
       ZipArchiveCache* zip_archive_cache,
       LoadTaskList* load_tasks,
       int rtld_flags) {
 ......
 // 打開庫(kù)文件返回文件描述符
 int fd = open_library(ns, zip_archive_cache, name, needed_by, &file_offset, &realpath);
 if (fd == -1) {
 DL_ERR("library \"%s\" not found", name);
 return false;
 }
 task->set_fd(fd, true);
 task->set_file_offset(file_offset);
 // 裝載庫(kù)
 return load_library(ns, task, load_tasks, rtld_flags, realpath);
}

下面看另一個(gè) load_library() 的實(shí)現(xiàn)

static bool load_library(android_namespace_t* ns,
       LoadTask* task,
       LoadTaskList* load_tasks,
       int rtld_flags,
       const std::string& realpath) {
 off64_t file_offset = task->get_file_offset();
 const char* name = task->get_name();
 const android_dlextinfo* extinfo = task->get_extinfo();
 ......
 // 為動(dòng)態(tài)庫(kù)創(chuàng)建soinfo,用于記錄動(dòng)態(tài)鏈接信息等
 soinfo* si = soinfo_alloc(ns, realpath.c_str(), &file_stat, file_offset, rtld_flags);
 if (si == nullptr) {
 return false;
 }
 task->set_soinfo(si);
 // Read the ELF header and some of the segments.
 // 讀取ELF文件頭以及一些段信息
 if (!task->read(realpath.c_str(), file_stat.st_size)) {
 soinfo_free(si);
 task->set_soinfo(nullptr);
 return false;
 }
 ......
 // 查找依賴庫(kù),創(chuàng)建LoadTask添加到load_tasks
 for_each_dt_needed(task->get_elf_reader(), [&](const char* name) {
 load_tasks->push_back(LoadTask::create(name, si, task->get_readers_map()));
 });
 return true;
}

下面分析ELF文件頭以及段信息的讀取過程,也就是LoadTask的 read() ,它直接調(diào)用ElfReader的 Read() 方法。

bool ElfReader::Read(const char* name, int fd, off64_t file_offset, off64_t file_size) {
 CHECK(!did_read_);
 CHECK(!did_load_);
 name_ = name;
 fd_ = fd;
 file_offset_ = file_offset;
 file_size_ = file_size;
 if (ReadElfHeader() &&
  VerifyElfHeader() &&
  ReadProgramHeaders() &&
  ReadSectionHeaders() &&
  ReadDynamicSection()) {
 did_read_ = true;
 }
 return did_read_;
}
ReadElfHeader() : 讀取ELF文件頭信息
VerifyElfHeader() : 校驗(yàn)ELF(文件類型等)
ReadProgramHeaders() : 根據(jù)ELF文件頭信息獲取程序頭表
ReadSectionHeaders() : 根據(jù)ELF文件頭信息獲取段頭表
ReadDynamicSection() : 獲取Dynamic Section的信息
裝載動(dòng)態(tài)庫(kù)
動(dòng)態(tài)庫(kù)的裝載在LoadTask的 load() 中實(shí)現(xiàn)。
bool load() {
 ElfReader& elf_reader = get_elf_reader();
 // 映射動(dòng)態(tài)庫(kù)的可加載Segment到進(jìn)程的虛擬地址空間中
 if (!elf_reader.Load(extinfo_)) {
  return false;
 }
 // 保存裝載信息
 // 動(dòng)態(tài)庫(kù)裝載的起始地址
 si_->base = elf_reader.load_start();
 // 可裝載的Segment大小之和
 si_->size = elf_reader.load_size();
 si_->set_mapped_by_caller(elf_reader.is_mapped_by_caller());
 // 動(dòng)態(tài)庫(kù)裝載的期望起始地址,通常si_->load_bias = si_->base
 si_->load_bias = elf_reader.load_bias();
 // 動(dòng)態(tài)庫(kù)程序頭表項(xiàng)數(shù)
 si_->phnum = elf_reader.phdr_count();
 // 動(dòng)態(tài)庫(kù)程序頭表的地址
 si_->phdr = elf_reader.loaded_phdr();
 return true;
 }

在實(shí)際的地址計(jì)算中,使用si_->load_bias,不使用si_->base。

下面看ElfReader的 Load() 方法

bool ElfReader::Load(const android_dlextinfo* extinfo) {
 CHECK(did_read_);
 CHECK(!did_load_);
 if (ReserveAddressSpace(extinfo) &&
  LoadSegments() &&
  FindPhdr()) {
 did_load_ = true;
 }

 return did_load_;
}

ReserveAddressSpace(): 保留虛擬地址空間為動(dòng)態(tài)庫(kù)(裝載地址隨機(jī)化)
LoadSegments() : 裝載ELF文件中可裝載的Segments
FindPhdr() : 確保程序頭表包含在一個(gè)可加載的Segment中

動(dòng)態(tài)庫(kù)的裝載已經(jīng)完成,下面看鏈接過程。

Native庫(kù)動(dòng)態(tài)鏈接的過程

 

預(yù)鏈接

下面看 prelink_image()

bool soinfo::prelink_image() {
 /* Extract dynamic section */
 ElfW(Word) dynamic_flags = 0;
 // 根據(jù)程序頭表的地址計(jì)算dynamic section的地址
 phdr_table_get_dynamic_section(phdr, phnum, load_bias, &dynamic, &dynamic_flags);
 ......
 uint32_t needed_count = 0;
 // 解析dynamic section獲取動(dòng)態(tài)鏈接信息
 for (ElfW(Dyn)* d = dynamic; d->d_tag != DT_NULL; ++d) {
 DEBUG("d = %p, d[0](tag) = %p d[1](val) = %p",
   d, reinterpret_cast<void*>(d->d_tag), reinterpret_cast<void*>(d->d_un.d_val));
 switch (d->d_tag) {
  ......
  case DT_STRTAB:
  // 動(dòng)態(tài)字符串表的地址
  strtab_ = reinterpret_cast<const char*>(load_bias + d->d_un.d_ptr);
  break;
  case DT_STRSZ:
  strtab_size_ = d->d_un.d_val;
  break;
  case DT_SYMTAB:
  // 動(dòng)態(tài)符號(hào)表的地址
  symtab_ = reinterpret_cast<ElfW(Sym)*>(load_bias + d->d_un.d_ptr);
  break;
  ......
  case DT_JMPREL:
  // 需重定位的函數(shù)表(.rela.plt)的地址
#if defined(USE_RELA)
  plt_rela_ = reinterpret_cast<ElfW(Rela)*>(load_bias + d->d_un.d_ptr);
#else
  plt_rel_ = reinterpret_cast<ElfW(Rel)*>(load_bias + d->d_un.d_ptr);
#endif
  break;
  ......
  case DT_RELA:
  // 需重定位的數(shù)據(jù)表(.rela.dyn)的地址
  rela_ = reinterpret_cast<ElfW(Rela)*>(load_bias + d->d_un.d_ptr);
  break;
  ......
  case DT_NEEDED:
  // 依賴的動(dòng)態(tài)庫(kù)
  ++needed_count;
  break;
 }
 }
 ......
 // Sanity checks.
 // 檢查動(dòng)態(tài)鏈接信息
 if (relocating_linker && needed_count != 0) {
 DL_ERR("linker cannot have DT_NEEDED dependencies on other libraries");
 return false;
 }
 if (nbucket_ == 0 && gnu_nbucket_ == 0) {
 DL_ERR("empty/missing DT_HASH/DT_GNU_HASH in \"%s\" "
  "(new hash type from the future?)", get_realpath());
 return false;
 }
 if (strtab_ == 0) {
 DL_ERR("empty/missing DT_STRTAB in \"%s\"", get_realpath());
 return false;
 }
 if (symtab_ == 0) {
 DL_ERR("empty/missing DT_SYMTAB in \"%s\"", get_realpath());
 return false;
 }
 ......
}

鏈接

鏈接主要完成符號(hào)重定位工作,下面從 link_image() 開始分析

bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& local_group,
      const android_dlextinfo* extinfo) {
 ......
#if defined(USE_RELA)
 // rela_為重定位數(shù)據(jù)表的地址
 if (rela_ != nullptr) {
 DEBUG("[ relocating %s ]", get_realpath());
 // 數(shù)據(jù)引用重定位
 if (!relocate(version_tracker,
   plain_reloc_iterator(rela_, rela_count_), global_group, local_group)) {
  return false;
 }
 }
 // plt_rela_為重定位函數(shù)表的地址
 if (plt_rela_ != nullptr) {
 DEBUG("[ relocating %s plt ]", get_realpath());
 // 函數(shù)引用重定位
 if (!relocate(version_tracker,
   plain_reloc_iterator(plt_rela_, plt_rela_count_), global_group, local_group)) {
  return false;
 }
 }
#else
 ......
}

下面以函數(shù)引用重定位為例分析 relocate() 方法

template<typename ElfRelIteratorT>
bool soinfo::relocate(const VersionTracker& version_tracker, ElfRelIteratorT&& rel_iterator,
      const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
 for (size_t idx = 0; rel_iterator.has_next(); ++idx) {
 const auto rel = rel_iterator.next();
 if (rel == nullptr) {
  return false;
 }
 // rel->r_info的低32位
 ElfW(Word) type = ELFW(R_TYPE)(rel->r_info);
 // rel->r_info的高32位
 ElfW(Word) sym = ELFW(R_SYM)(rel->r_info);
 // 重定位地址的存儲(chǔ)位置
 ElfW(Addr) reloc = static_cast<ElfW(Addr)>(rel->r_offset + load_bias);
 ElfW(Addr) sym_addr = 0;
 const char* sym_name = nullptr;
 ElfW(Addr) addend = get_addend(rel, reloc);
 ......
 if (sym != 0) {
  // sym為動(dòng)態(tài)符號(hào)表項(xiàng)的索引
  // symtab_[sym].st_name為符號(hào)在動(dòng)態(tài)字符串表的索引
  // sysm_name為需重定位的符號(hào)名
  sym_name = get_string(symtab_[sym].st_name);
  const version_info* vi = nullptr;
  if (!lookup_version_info(version_tracker, sym, sym_name, &vi)) {
  return false;
  }
  // 查找符號(hào)返回符號(hào)表項(xiàng)的地址
  if (!soinfo_do_lookup(this, sym_name, vi, &lsi, global_group, local_group, &s)) {
  return false;
  }
  if (s == nullptr) {
  ......
  } else {
  ......
  // 根據(jù)符號(hào)表項(xiàng)計(jì)算符號(hào)地址
  sym_addr = lsi->resolve_symbol_address(s);
  ......
  }
  ......
 }
 switch (type) {
  // ELF64中R_GENERIC_JUMP_SLOT = R_AARCH64_JUMP_SLOT
  case R_GENERIC_JUMP_SLOT:
  count_relocation(kRelocAbsolute);
  MARK(rel->r_offset);
  TRACE_TYPE(RELO, "RELO JMP_SLOT %16p <- %16p %s\n",
     reinterpret_cast<void*>(reloc),
     reinterpret_cast<void*>(sym_addr + addend), sym_name);
  // 符號(hào)地址更新到reloc(GOT表)中
  *reinterpret_cast<ElfW(Addr)*>(reloc) = (sym_addr + addend);
  break;
  ......
 }
 }
 return true;
}

參考

Tool Interface Standard (TIS) Executable and Linking Format (ELF) Specification

總結(jié)

以上所述是小編給大家介紹的Android Native庫(kù)的加載及動(dòng)態(tài)鏈接的過程,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!

相關(guān)文章

  • 自定義Android六邊形進(jìn)度條(附源碼)

    自定義Android六邊形進(jìn)度條(附源碼)

    這篇文章主要介紹了自定義Android六邊形進(jìn)度條,本文設(shè)計(jì)的進(jìn)度條是六邊形的,對(duì)進(jìn)度條感興趣的小伙伴們可以參考一下
    2016-02-02
  • kotlin使用Dagger2的過程全紀(jì)錄

    kotlin使用Dagger2的過程全紀(jì)錄

    Dagger2是一款基于Java注解,在編譯階段完成依賴注入的開源庫(kù),主要用于模塊間解耦,方便進(jìn)行測(cè)試。下面這篇文章主要給大家介紹了關(guān)于kotlin使用Dagger2的過程的相關(guān)資料,需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-03-03
  • Android使用View Animation實(shí)現(xiàn)動(dòng)畫加載界面

    Android使用View Animation實(shí)現(xiàn)動(dòng)畫加載界面

    這篇文章主要為大家詳細(xì)介紹了Android使用View Animation實(shí)現(xiàn)動(dòng)畫加載界面的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-04-04
  • 淺談Android app開發(fā)中Fragment的Transaction操作

    淺談Android app開發(fā)中Fragment的Transaction操作

    這篇文章主要介紹了Android app開發(fā)中Fragment的Transaction操作,包括Transaction和Fragment的生命周期的聯(lián)系等內(nèi)容,需要的朋友可以參考下
    2016-02-02
  • Android使用相機(jī)實(shí)現(xiàn)拍照存儲(chǔ)及展示功能詳解

    Android使用相機(jī)實(shí)現(xiàn)拍照存儲(chǔ)及展示功能詳解

    這篇文章主要介紹了Android使用相機(jī)實(shí)現(xiàn)拍照存儲(chǔ)及展示功能,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2023-01-01
  • Android 從底部彈出Dialog(橫向滿屏)的實(shí)例代碼

    Android 從底部彈出Dialog(橫向滿屏)的實(shí)例代碼

    在android開發(fā)中經(jīng)常會(huì)遇到底部彈出框的功能,今天小編抽時(shí)間給大家整理一個(gè)底部彈出橫向滿屏的dialog,需要的朋友參考下
    2016-11-11
  • Android使用ContentProvider初始化SDK庫(kù)方案小結(jié)

    Android使用ContentProvider初始化SDK庫(kù)方案小結(jié)

    這篇文章主要介紹了Android使用ContentProvider初始化SDK庫(kù)方案總結(jié),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-04-04
  • Android如何通過手機(jī)獲取驗(yàn)證碼來完成注冊(cè)功能

    Android如何通過手機(jī)獲取驗(yàn)證碼來完成注冊(cè)功能

    注冊(cè)app或者網(wǎng)絡(luò)帳號(hào)的時(shí)候,經(jīng)常需要手機(jī)獲取驗(yàn)證碼,來完成注冊(cè)功能,那么android如何通過手機(jī)獲取驗(yàn)證碼來完成注冊(cè)功能,代碼是怎么實(shí)現(xiàn)的呢?下面看看小編給大家?guī)Я说囊欢未a分析吧
    2015-11-11
  • Android多種支付方式的實(shí)現(xiàn)示例

    Android多種支付方式的實(shí)現(xiàn)示例

    App的支付流程,添加多種支付方式,不同的支付方式,對(duì)應(yīng)的操作不一樣,有的會(huì)跳轉(zhuǎn)到一個(gè)新的webview,有的會(huì)調(diào)用系統(tǒng)瀏覽器,有的會(huì)進(jìn)去一個(gè)新的表單頁(yè)面,等等,本文就給大家詳細(xì)介紹一下Android 多種支付方式的優(yōu)雅實(shí)現(xiàn),需要的朋友可以參考下
    2023-09-09
  • Android使用ViewFlipper和GestrueDetector共同實(shí)現(xiàn)滑屏效果實(shí)例

    Android使用ViewFlipper和GestrueDetector共同實(shí)現(xiàn)滑屏效果實(shí)例

    這篇文章主要介紹了Android使用ViewFlipper和GestrueDetector共同實(shí)現(xiàn)滑屏效果,結(jié)合完整實(shí)例形式分析了ViewFlipper和GestrueDetector控件實(shí)現(xiàn)滑屏功能的布局與相關(guān)操作技巧,需要的朋友可以參考下
    2017-02-02

最新評(píng)論