Android 版本:android-8.0.0_r4

目的

  • elf的加载过程
  • 在so加载过程中确定依赖库先被调用construct 还是后调用。
  • elf 重定位过程做了什么?

过程

System.loadLibrary源码位于:./libcore/ojluni/src/main/java/java/lang/System.java

public static void loadLibrary(String libname) {
  Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), libname);
}

可以看到然后又调用了Runtime.java的loadLibrary0方法:./libcore/ojluni/src/main/java/java/lang/Runtime.java

    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) {
           String filename = loader.findLibrary(libraryName);
           if (filename == null) {
               throw new UnsatisfiedLinkError(loader + " couldn't find \"" +
                                              System.mapLibraryName(libraryName) + "\"");
          }
           String error = doLoad(filename, loader);
           if (error != null) {
               throw new UnsatisfiedLinkError(error);
          }
           return;
      }

       String filename = System.mapLibraryName(libraryName);
       List<String> candidates = new ArrayList<String>();
       String lastError = null;
       for (String directory : getLibPaths()) {
           String candidate = directory + filename;
           candidates.add(candidate);

           if (IoUtils.canOpenReadOnly(candidate)) {
               String error = doLoad(candidate, loader);
               if (error == null) {
                   return; // We successfully loaded the library. Job done.
              }
               lastError = error;
          }
      }

       if (lastError != null) {
           throw new UnsatisfiedLinkError(lastError);
      }
       throw new UnsatisfiedLinkError("Library " + libraryName + " not found; tried " + candidates);
  }

这个方法主要有两个作用:

  1. 找到lib的全称
  2. 调用doLoad加载lib库

首先会判断loader 不为空来执行上面两步,如果为空,则换个方法继续执行上面两步。

    private String doLoad(String name, ClassLoader loader) {
   
       String librarySearchPath = null;
       if (loader != null && loader instanceof BaseDexClassLoader) {
           BaseDexClassLoader dexClassLoader = (BaseDexClassLoader) loader;
           librarySearchPath = dexClassLoader.getLdLibraryPath();
      }

       synchronized (this) {
           return nativeLoad(name, loader, librarySearchPath);
      }
  }

拿到库的搜索路径,调用了native 的nativeLoad方法。

private static native String nativeLoad(String filename, ClassLoader loader,String ibrarySearchPath);

native函数在libcore/ojluni/src/main/native/register.cpp,统一注册,

其中我们需要的nativeLoad函数在libcore/ojluni/src/main/native/Runtime.c 文件下

JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env,
                                jstring javaFilename,
                                jobject javaLoader,
                                jstring javaLibrarySearchPath) {
 ScopedUtfChars filename(env, javaFilename);
 if (filename.c_str() == NULL) {
   return NULL;
}

 std::string error_msg;
{
   art::JavaVMExt* vm = art::Runtime::Current()->GetJavaVM();
   bool success = vm->LoadNativeLibrary(env,
                                        filename.c_str(),
                                        javaLoader,
                                        javaLibrarySearchPath,
                                        &error_msg);
   if (success) {
     return nullptr;
  }
}

 // Don't let a pending exception from JNI_OnLoad cause a CheckJNI issue with NewStringUTF.
 env->ExceptionClear();
 return env->NewStringUTF(error_msg.c_str());
}

继续跟:

bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,
                                 const std::string& path,
                                 jobject class_loader,
                                 jstring library_path,
                                 std::string* error_msg) {
 error_msg->clear();

 // See if we've already loaded this library. If we have, and the class loader matches, return successfully without doing anything.
。。。。。 , 已经加载了so
 if (library != nullptr) {
   if (library->GetClassLoaderAllocator() != class_loader_allocator) {

     StringAppendF(error_msg, "Shared library \"%s\" already opened by "
         "ClassLoader %p; can't open in ClassLoader %p",
         path.c_str(), library->GetClassLoader(), class_loader);
     LOG(WARNING) << error_msg;
     return false;
  }
   VLOG(jni) << "[Shared library \"" << path << "\" already loaded in "
             << " ClassLoader " << class_loader << "]";
   if (!library->CheckOnLoadResult()) {
     StringAppendF(error_msg, "JNI_OnLoad failed on a previous attempt "
         "to load \"%s\"", path.c_str());
     return false;
  }
   return true;
}
.........
//没有加载过so , 就调用OpenNativeLibrary来加载so。
 Locks::mutator_lock_->AssertNotHeld(self);
 const char* path_str = path.empty() ? nullptr : path.c_str();
 bool needs_native_bridge = false;
 void* handle = android::OpenNativeLibrary(env,
                                           runtime_->GetTargetSdkVersion(),
                                           path_str,
                                           class_loader,
                                           library_path,
                                           &needs_native_bridge,
                                           error_msg);

 VLOG(jni) << "[Call to dlopen(\"" << path << "\", RTLD_NOW) returned " << handle << "]";

 if (handle == nullptr) {
   VLOG(jni) << "dlopen(\"" << path << "\", RTLD_NOW) failed: " << *error_msg;
   return false;
}

.........
 //要是so加载成功后,还要看有没有JNI_OnLoad函数,来判断native函数是静态注册还是Jni动态注册。
 bool was_successful = false;
 void* 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);
   int version = (*jni_on_load)(this, nullptr);
  .......
}

继续跟android::OpenNativeLibrary , system/core/libnativeloader/native_loader.cpp

void* OpenNativeLibrary(JNIEnv* env,
                       int32_t target_sdk_version,
                       const char* path,
                       jobject class_loader,
                       jstring library_path,
                       bool* needs_native_bridge,
                       std::string* error_msg) {
#if defined(__ANDROID__)
 UNUSED(target_sdk_version);
 if (class_loader == nullptr) {
   *needs_native_bridge = false;
   return dlopen(path, RTLD_NOW);
}

 std::lock_guard<std::mutex> guard(g_namespaces_mutex);
 NativeLoaderNamespace ns;

 if (!g_namespaces->FindNamespaceByClassLoader(env, class_loader, &ns)) {
   if (!g_namespaces->Create(env,
                             target_sdk_version,
                             class_loader,
                             false /* is_shared */,
                             false /* is_for_vendor */,
                             library_path,
                             nullptr,
                             &ns,
                             error_msg)) {
     return nullptr;
  }
}

 if (ns.is_android_namespace()) {
   android_dlextinfo extinfo;
   extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
   extinfo.library_namespace = ns.get_android_ns();

   void* handle = android_dlopen_ext(path, RTLD_NOW, &extinfo);
   if (handle == nullptr) {
     *error_msg = dlerror();
  }
   *needs_native_bridge = false;
   return handle;
} else {
   void* handle = NativeBridgeLoadLibraryExt(path, RTLD_NOW, ns.get_native_bridge_ns());
   if (handle == nullptr) {
     *error_msg = NativeBridgeGetError();
  }
   *needs_native_bridge = true;
   return handle;
}
#else
 UNUSED(env, target_sdk_version, class_loader, library_path);
 *needs_native_bridge = false;
 void* handle = dlopen(path, RTLD_NOW);
 if (handle == nullptr) {
   if (NativeBridgeIsSupported(path)) {
     *needs_native_bridge = true;
     handle = NativeBridgeLoadLibrary(path, RTLD_NOW);
     if (handle == nullptr) {
       *error_msg = NativeBridgeGetError();
    }
  } else {
     *needs_native_bridge = false;
     *error_msg = dlerror();
  }
}
 return handle;
#endif
}

有两个点,class_loader == NULL , 和非空的情况。这里猜测对应两种情况(未加验证)class_loader为空时,对应so中函数主动调用打开so,但是so间引用直接使用dlopen应该不走这个过程 。不晓得这两种是什么情况。。。。大神可以指教下。第一个过程直接走dlopen。第二个过程,走android_dlopen_ext , 两个函数最终都会走到do_dlopen 来打开so 。

void* do_dlopen(const char* name, int flags,
               const android_dlextinfo* extinfo,
               const void* caller_addr) {

// 在此之前都是针对,flag 和extinfo flag的处理。
 const char* translated_name = name;
 if (g_is_asan && translated_name != nullptr && translated_name[0] == '/') {
   char original_path[PATH_MAX];
   if (realpath(name, original_path) != nullptr) {
     asan_name_holder = std::string(kAsanLibDirPrefix) + original_path;
     LD_LOG(kLogDlopen,"... pareto's here realpath =\"%s\"",name);
     if (file_exists(asan_name_holder.c_str())) {
       soinfo* si = nullptr;
       if (find_loaded_library_by_realpath(ns, original_path, true, &si)) {
         PRINT("linker_asan dlopen NOT translating \"%s\" -> \"%s\": library already loaded", name,
               asan_name_holder.c_str());
      } else {
         PRINT("linker_asan dlopen translating \"%s\" -> \"%s\"", name, translated_name);
         translated_name = asan_name_holder.c_str();
      }
    }
  }
}

 ProtectedDataGuard guard;
   //关注点 find_library
 soinfo* si = find_library(ns, translated_name, flags, extinfo, caller);
 loading_trace.End();

 if (si != nullptr) {
   void* handle = si->to_handle();
//调用init
   si->call_constructors();
 
   return handle;
}

 return nullptr;
}

find_library内部调用重载函数find_library ,内部的重载函数就是本次分析的重点。

static soinfo* find_library(android_namespace_t* ns,
                            const char* name, int rtld_flags,
                            const android_dlextinfo* extinfo,
                            soinfo* needed_by) {
  soinfo* si;

  // readers_map is shared across recursive calls to find_libraries.
  // However, the map is not shared across different threads.
  std::unordered_map<const soinfo*, ElfReader> readers_map;
  if (name == nullptr) {
    si = solist_get_somain();
  } else if (!find_libraries(ns,
                             needed_by,
                             &name,
                             1,
                             &si,
                             nullptr,
                             0,
                             rtld_flags,
                             extinfo,
                             false /* add_as_children */,
                             true /* search_linked_namespaces */,
                             readers_map)) {
    return nullptr;
  }
	//  增加so的索引计数
  si->increment_ref_count();

  return si;
}

关键函数到位了

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,
                    bool search_linked_namespaces,
                    std::unordered_map<const soinfo*, ElfReader>& readers_map,
                    std::vector<android_namespace_t*>* namespaces) {
  // 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 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];
    LD_LOG(kLogDlopen,"[linker.cpp] traverse load_tasks %s",task->get_name());
    soinfo* needed_by = task->get_needed_by();
    LD_LOG(kLogDlopen,"[linker.cpp] print needed_by so_name %s ,add_as_children  %d ",needed_by->get_soname(),add_as_children);
    bool is_dt_needed = needed_by != nullptr && (needed_by != start_with || add_as_children);
    LD_LOG(kLogDlopen,"[linker.cpp] value of is_dt_needed %d",is_dt_needed);
    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) {
    LD_LOG(kLogDlopen,"[linker.cpp] Step2 list all task %s ",task->get_name());
    if (!task->load()) {
      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 4: Construct the global group. Note: DF_1_GLOBAL bit of a library is
  // determined at step 3.

  // Step 4-1: DF_1_GLOBAL bit is force set for LD_PRELOADed libs because they
  // must be added to the global group
  if (ld_preloads != nullptr) {
    for (auto&& si : *ld_preloads) {
      si->set_dt_flags_1(si->get_dt_flags_1() | DF_1_GLOBAL);
    }
  }

  // Step 4-2: Gather all DF_1_GLOBAL libs which were newly loaded during this
  // run. These will be the new member of the global group
  soinfo_list_t new_global_group_members;
  for (auto&& task : load_tasks) {
    soinfo* si = task->get_soinfo();
    if (!si->is_linked() && (si->get_dt_flags_1() & DF_1_GLOBAL) != 0) {
      new_global_group_members.push_back(si);
    }
  }

  // Step 4-3: Add the new global group members to all the linked namespaces
  for (auto si : new_global_group_members) {
    for (auto linked_ns : *namespaces) {
      if (si->get_primary_namespace() != linked_ns) {
        linked_ns->add_soinfo(si);
        si->add_secondary_namespace(linked_ns);
      }
    }
  }

  // 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();
        //这里可以看出先递归needed_by。
      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;
    }
  });
  LD_LOG(kLogDlopen,"[linker.cpp] link libraries in this namespace");
  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;
  });
  LD_LOG(kLogDlopen,"[linker.cpp] all link libraries in this namespace has linker");
  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;
}
分类: 安卓

pareto

未来什么方向不管,先做自己喜欢做的事情。

0 条评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注