std::string error_msg; { art::JavaVMExt* vm = art::Runtime::Current()->GetJavaVM(); // here bool success = vm->LoadNativeLibrary(env, filename.c_str(), javaLoader, caller, &error_msg); if (success) { returnnullptr; } }
// Don't let a pending exception from JNI_OnLoad cause a CheckJNI issue with NewStringUTF. env->ExceptionClear(); return env->NewStringUTF(error_msg.c_str()); }
// 1. 判斷so是否已加載 // See if we've already loaded this library. If we have, and the class loader // matches, return successfully without doing anything. // TODO: for better results we should canonicalize the pathname (or even compare // inodes). This implementation is fine if everybody is using System.loadLibrary. // some code here... // Open the shared library. Because we're using a full path, the system // doesn't have to search through LD_LIBRARY_PATH. (It may do so to // resolve this library's dependencies though.)
// Failures here are expected when java.library.path has several entries // and we have to hunt for the lib.
// Below we dlopen but there is no paired dlclose, this would be necessary if we supported // class unloading. Libraries will only be unloaded when the reference count (incremented by // dlopen) becomes zero from dlclose.
// Retrieve the library path from the classloader, if necessary. ScopedLocalRef<jstring> library_path(env, GetLibrarySearchPath(env, class_loader));
if (env->ExceptionCheck() == JNI_TRUE) { LOG(ERROR) << "Unexpected exception:"; env->ExceptionDescribe(); env->ExceptionClear(); } // Create a new entry. // TODO: move the locking (and more of this logic) into Libraries. bool created_library = false; { // Create SharedLibrary ahead of taking the libraries lock to maintain lock ordering. std::unique_ptr<SharedLibrary> new_library( new SharedLibrary(env, self, path, handle, needs_native_bridge, class_loader, class_loader_allocator));
MutexLock mu(self, *Locks::jni_libraries_lock_); library = libraries_->Get(path); if (library == nullptr) { // We won race to get libraries_lock. library = new_library.release(); libraries_->Put(path, library); created_library = true; } } if (!created_library) { LOG(INFO) << "WOW: we lost a race to add shared library: " << "\"" << path << "\" ClassLoader=" << class_loader; return library->CheckOnLoadResult(); } VLOG(jni) << "[Added shared library \"" << path << "\" for ClassLoader " << class_loader << "]";
bool was_successful = false; // 3. 若JNI_OnLoad存在, 則調用 void* sym = library->FindSymbol("JNI_OnLoad", nullptr, android::kJNICallTypeRegular); 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 << "\"]"; using JNI_OnLoadFn = int(*)(JavaVM*, void*); JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym); int version = (*jni_on_load)(this, nullptr);
if (!g_namespaces->FindNamespaceByClassLoader(env, class_loader, &ns)) { // This is the case where the classloader was not created by ApplicationLoaders // In this case we create an isolated not-shared namespace for it. if (!g_namespaces->Create(env, target_sdk_version, class_loader, false/* is_shared */, false/* is_for_vendor */, library_path, nullptr, &ns, error_msg)) { returnnullptr; } }
voidsoinfo::call_constructors(){ if (constructors_called) { return; }
//.... // DT_INIT should be called before DT_INIT_ARRAY if both are present. // 1. 調用.init call_function("DT_INIT", init_func_, get_realpath()); // 2. 調用.init_array call_array("DT_INIT_ARRAY", init_array_, init_array_count_, false, get_realpath());
// 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(); } elseif (!find_libraries(ns, needed_by, &name, 1, &si, nullptr, 0, rtld_flags, extinfo, false/* add_as_children */, true/* search_linked_namespaces */, readers_map)) { returnnullptr; }
// add_as_children - add first-level loaded libraries (i.e. library_names[], but // not their transitive dependencies) as children of the start_with library. // This is false when find_libraries is called for dlopen(), when newly loaded // libraries must form a disjoint tree. boolfind_libraries(android_namespace_t* ns, soinfo* start_with, constchar* 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);
for (size_t i = 0; i < library_names_count; ++i) { constchar* name = library_names[i]; 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.
// 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();
// 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)) { returnfalse; }
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; };
for (auto&& task : load_list) { if (!task->load()) { returnfalse; } }
Step 3:按BFS( 廣度優先 )來預鏈接所有共享庫
調用prelink_image來實現預鏈接
1 2 3 4 5 6 7 8 9 10
// bionic/linker/linker.cpp
// 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()) { returnfalse; } }
/* We can't log anything until the linker is relocated */ bool relocating_linker = (flags_ & FLAG_LINKER) != 0; if (!relocating_linker) { INFO("[ Linking \"%s\" ]", get_realpath()); DEBUG("si->base = %p si->flags = 0x%08x", reinterpret_cast<void*>(base), flags_); }
if (dynamic == nullptr) { if (!relocating_linker) { DL_ERR("missing PT_DYNAMIC in \"%s\"", get_realpath()); } returnfalse; } else { if (!relocating_linker) { DEBUG("dynamic = %p", dynamic); } } // 1. 解析.dynamic節 // Extract useful information from dynamic section. // Note that: "Except for the DT_NULL element at the end of the array, // and the relative order of DT_NEEDED elements, entries may appear in any order." // // source: http://www.sco.com/developers/gabi/1998-04-29/ch5.dynamic.html uint32_t needed_count = 0; 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_SONAME: // this is parsed after we have strtab initialized (see below). break;
if (!powerof2(gnu_maskwords_)) { DL_ERR("invalid maskwords for gnu_hash = 0x%x, in \"%s\" expecting power to two", gnu_maskwords_, get_realpath()); returnfalse; } --gnu_maskwords_;
flags_ |= FLAG_GNU_HASH; break;
case DT_STRTAB: strtab_ = reinterpret_cast<constchar*>(load_bias + d->d_un.d_ptr); break;
case DT_STRSZ: strtab_size_ = d->d_un.d_val; break;
case DT_SYMTAB: symtab_ = reinterpret_cast<ElfW(Sym)*>(load_bias + d->d_un.d_ptr); break;
case DT_SYMENT: if (d->d_un.d_val != sizeof(ElfW(Sym))) { DL_ERR("invalid DT_SYMENT: %zd in \"%s\"", static_cast<size_t>(d->d_un.d_val), get_realpath()); returnfalse; } break; // ... } }
// 完整性檢測 if (relocating_linker && needed_count != 0) { DL_ERR("linker cannot have DT_NEEDED dependencies on other libraries"); returnfalse; } 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()); returnfalse; } if (strtab_ == 0) { DL_ERR("empty/missing DT_STRTAB in \"%s\"", get_realpath()); returnfalse; } if (symtab_ == 0) { DL_ERR("empty/missing DT_SYMTAB in \"%s\"", get_realpath()); returnfalse; } // 2. 根據上述解析好的strtab表來解析DT_SONAME、DT_RUNPATH // second pass - parse entries relying on strtab for (ElfW(Dyn)* d = dynamic; d->d_tag != DT_NULL; ++d) { switch (d->d_tag) { case DT_SONAME: set_soname(get_string(d->d_un.d_val)); break; case DT_RUNPATH: set_dt_runpath(get_string(d->d_un.d_val)); break; } }
// ... returntrue; }
Step 4:構建global group
global group是一組具有DF_1_GLOBAL標誌的共享庫,這些庫在加載過程中被確定為全局可見的。
// 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) { constchar* 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 { returnfalse; } } }
// Library might still be loaded, the accurate detection // of this fact is done by load_library. TRACE("[ \"%s\" find_loaded_library_by_soname failed (*candidate=%s@%p). Trying harder...]", task->get_name(), candidate == nullptr ? "n/a" : candidate->get_realpath(), candidate); // 關鍵點here if (load_library(ns, task, zip_archive_cache, load_tasks, rtld_flags, search_linked_namespaces)) { returntrue; } // 當指定so沒有找到時, 會嘗試在linked namespaces裡查找 if (search_linked_namespaces) { // if a library was not found - look into linked namespaces for (auto& linked_namespace : ns->linked_namespaces()) { if (find_library_in_linked_namespace(linked_namespace, task)) { if (task->get_soinfo() == nullptr) { // try to load the library - once namespace boundary is crossed // we need to load a library within separate load_group // to avoid using symbols from foreign namespace while. // // However, actual linking is deferred until when the global group // is fully identified and is applied to all namespaces. // Otherwise, the libs in the linked namespace won't get symbols from // the global group. if (load_library(linked_namespace.linked_namespace(), task, zip_archive_cache, load_tasks, rtld_flags, false)) { returntrue; } // lib was not found in the namespace. Try next linked namespace. } else { // lib is already loaded returntrue; } } } }
if (!realpath_fd(extinfo->library_fd, &realpath)) { PRINT("warning: unable to get realpath for the library \"%s\" by extinfo->library_fd. " "Will use given name.", name); realpath = name; }
task->set_fd(extinfo->library_fd, false); task->set_file_offset(file_offset); returnload_library(ns, task, load_tasks, rtld_flags, realpath, search_linked_namespaces); } // 2. 正式打開指定so文件 // Open the file. int fd = open_library(ns, zip_archive_cache, name, needed_by, &file_offset, &realpath); if (fd == -1) { DL_ERR("library \"%s\" not found", name); returnfalse; }
// If the name contains a slash, we should attempt to open it directly and not search the paths. // 1. 判斷name是否包含'/', 是則進入該if分支 if (strchr(name, '/') != nullptr) { int fd = -1; // kZipFileSeparator == "!/", 顯然name中沒有kZipFileSeparator, 因此不會走這條分支 if (strstr(name, kZipFileSeparator) != nullptr) { fd = open_library_in_zipfile(zip_archive_cache, name, file_offset, realpath); }
if (fd == -1) { // 2. 正式調用open打開文件 fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY | O_CLOEXEC)); if (fd != -1) { *file_offset = 0; if (!realpath_fd(fd, realpath)) { PRINT("warning: unable to get realpath for the library \"%s\". Will use given path.", name); *realpath = name; } } } // 3. 返回文件描述符 return fd; } // ... }
staticboolload_library(android_namespace_t* ns, LoadTask* task, LoadTaskList* load_tasks, int rtld_flags, const std::string& realpath, bool search_linked_namespaces){ off64_t file_offset = task->get_file_offset(); constchar* name = task->get_name(); const android_dlextinfo* extinfo = task->get_extinfo(); // 1. 校驗so文件的各種屬性是否符合規範 //... // Check for symlink and other situations where // file can have different names, unless ANDROID_DLEXT_FORCE_LOAD is set if (extinfo == nullptr || (extinfo->flags & ANDROID_DLEXT_FORCE_LOAD) == 0) { soinfo* si = nullptr; // 2. 在給定的命名空間中查找已加載的庫,並根據不同的條件判斷是否可以直接返回已加載的庫。 if (find_loaded_library_by_inode(ns, file_stat, file_offset, search_linked_namespaces, &si)) { TRACE("library \"%s\" is already loaded under different name/path \"%s\" - " "will return existing soinfo", name, si->get_realpath()); task->set_soinfo(si); returntrue; } }
//...
soinfo* si = soinfo_alloc(ns, realpath.c_str(), &file_stat, file_offset, rtld_flags); if (si == nullptr) { returnfalse; }
task->set_soinfo(si); // 3. 正式開始讀取so文件 // Read the ELF header and some of the segments. if (!task->read(realpath.c_str(), file_stat.st_size)) { soinfo_free(si); task->set_soinfo(nullptr); returnfalse; }
// find and set DT_RUNPATH and dt_soname // Note that these field values are temporary and are // going to be overwritten on soinfo::prelink_image // with values from PT_LOAD segments. const ElfReader& elf_reader = task->get_elf_reader(); for (constElfW(Dyn)* d = elf_reader.dynamic(); d->d_tag != DT_NULL; ++d) { // 在.dynamic節找當前so的運行路徑和文件名 if (d->d_tag == DT_RUNPATH) { si->set_dt_runpath(elf_reader.get_string(d->d_un.d_val)); } if (d->d_tag == DT_SONAME) { si->set_soname(elf_reader.get_string(d->d_un.d_val)); } } // 在.dynamic節找所有的依據庫 for_each_dt_needed(task->get_elf_reader(), [&](constchar* name) { load_tasks->push_back(LoadTask::create(name, si, ns, task->get_readers_map())); });