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);
}
这个方法主要有两个作用:
- 找到lib的全称
- 调用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; }
0 条评论