android链接器命名空间源码分析
二代壳加壳apk在实现函数抽取的时候需要选择合适的时机进行抽取代码的回填,一般选择在LoadMethod函数中即函数被加载前。所以需要通过inline hook libart.so的LoadMethod函数,前提是需要得到libart.so的模块基地址并得到LoadMethod函数的地址。如果直接在native中通过dlopen获取libart.so的handle是会报错的,而如果获取libc.so的handle是会成功的。获取libart.so错误的原因就是因为libart.so是系统私有库,而且没有被共享。通过设置 adb shell setprop debug.ld.all dlerror,dlopen
打开linker链接器调试日志查看报错信息。
链接器命名空间源码分析#
这里apk是通过System.loadLibrary加载了一个so库libnamespacetest.so,并在so库中调用dlopen获取libc.so和libart.so的handle,无论是dlopen还是android_dlopen_ext最后都是调用do_dlopen函数,所以从do_dlopen开始分析。
do_dlopen#
- 调用find_containing_library根据solist获取dlopen调用地址对应的so文件的soinfo指针,这里就是libnamespacetest.so的soinfo指针。
- 调用get_caller_namespace获取调用者对应的命名空间,调用者so文件对应的soinfo的primary_namespace_成员就是其对应的命名空间。根据之前的分析libnamespacetest.so是由apk通过System.loadLibrary加载的,所以其对应的命名空间就是apk默认类加载器映射的命名空间。
- 调用find_library寻找待加载so文件的soinfo指针,如果找到了就获取其对应的handle信息,否则就返回失败。这里还可以得到一个信息,android早期版本中dlopen加载so文件返回的handle就是so文件对应的soinfo结构体指针,现在获取的handle并不是简单的soinfo结构体指针
//bionic/linker/linker.cpp
void* do_dlopen(const char* name, int flags,
const android_dlextinfo* extinfo,
const void* caller_addr) {
std::string trace_prefix = std::string("dlopen: ") + (name == nullptr ? "(nullptr)" : name);
ScopedTrace trace(trace_prefix.c_str());
ScopedTrace loading_trace((trace_prefix + " - loading and linking").c_str());
//获取caller_addr调用地址对应的so文件的soinfo指针
soinfo* const caller = find_containing_library(caller_addr);
//获取caller调用者的命名空间
android_namespace_t* ns = get_caller_namespace(caller);
LD_LOG(kLogDlopen,
"dlopen(name=\"%s\", flags=0x%x, extinfo=%s, caller=\"%s\", caller_ns=%s@%p, targetSdkVersion=%i) ...",
name,
flags,
android_dlextinfo_to_string(extinfo).c_str(),
caller == nullptr ? "(null)" : caller->get_realpath(),
ns == nullptr ? "(null)" : ns->get_name(),
ns,
get_application_target_sdk_version());
.....
//得到此so文件对应的soinfo指针
soinfo* si = find_library(ns, translated_name, flags, extinfo, caller);
loading_trace.End();
if (si != nullptr) {
//通过soinfo指针得到对应的handle
void* handle = si->to_handle();
LD_LOG(kLogDlopen,
"... dlopen calling constructors: realpath=\"%s\", soname=\"%s\", handle=%p",
si->get_realpath(), si->get_soname(), handle);
si->call_constructors();
failure_guard.Disable();
LD_LOG(kLogDlopen,
"... dlopen successful: realpath=\"%s\", soname=\"%s\", handle=%p",
si->get_realpath(), si->get_soname(), handle);
return handle;
}
return nullptr;
}
find_library_internal#
find_library-->find_libraries-->find_library_internal,直接分析find_library_internal函数。
- 调用find_loaded_library_by_soname()在caller命名空间和链接的命名空间中查找库是否已经加载。
- 如果上一步未找到就调用load_library()尝试在caller命名空间中加载库。
- 如果前两步都没成功就尝试调用find_library_in_linked_namespace()在caller命名空间的所有链接命名空间中搜索库,并尝试调用load_library在链接命名空间中加载库。
static bool find_library_internal(android_namespace_t* ns,
LoadTask* task,
ZipArchiveCache* zip_archive_cache,
LoadTaskList* load_tasks,
int rtld_flags) {
//在caller命名空间和链接的命名空间中查找库是否已经加载
soinfo* candidate;
if (find_loaded_library_by_soname(ns, task->get_name(), true /* search_linked_namespaces */,
&candidate)) {
LD_LOG(kLogDlopen,
"find_library_internal(ns=%s, task=%s): Already loaded (by soname): %s",
ns->get_name(), task->get_name(), candidate->get_realpath());
task->set_soinfo(candidate);
return true;
}
//如果上一步未找到就调用load_library尝试在caller命名空间中加载库
if (load_library(ns, task, zip_archive_cache, load_tasks, rtld_flags,
true /* search_linked_namespaces */)) {
return true;
}
//如果前两步都没成功就尝试在caller命名空间的所有链接命名空间中搜索库并尝试在链接命名空间中加载
// if a library was not found - look into linked namespaces
// preserve current dlerror in the case it fails.
DlErrorRestorer dlerror_restorer;
LD_LOG(kLogDlopen, "find_library_internal(ns=%s, task=%s): Trying %zu linked namespaces",
ns->get_name(), task->get_name(), ns->linked_namespaces().size());
for (auto& linked_namespace : ns->linked_namespaces()) {
if (find_library_in_linked_namespace(linked_namespace, task)) {
// Library is already loaded.
if (task->get_soinfo() != nullptr) {
return true;
}
if (load_library(linked_namespace.linked_namespace(), task, zip_archive_cache, load_tasks,
rtld_flags, false /* search_linked_namespaces */)) {
LD_LOG(kLogDlopen, "find_library_internal(ns=%s, task=%s): Found in linked namespace %s",
ns->get_name(), task->get_name(), linked_namespace.linked_namespace()->get_name());
return true;
}
}
}
return false;
}
find_loaded_library_by_soname#
- 首先会判断加载的库名称(也就是dlopen传入的so名称)是否为绝对路径,如果是直接返回false。
- 如果不是绝对路径就调用find_loaded_library_by_soname()在caller命名空间已经加载的so库列表中找是否有待加载的so库。
- 如果上一步没找到就遍历caller命名空间所有链接的命名空间,并调用 android_namespace_link_t::is_accessible()进行权限判断。权限判断失败直接返回false,权限判断成功就调用find_loaded_library_by_soname在链接命名空间已经加载的so库列表中找是否有待加载的so库。
/ Returns true if library was found and false otherwise
static bool find_loaded_library_by_soname(android_namespace_t* ns,
const char* name,
bool search_linked_namespaces,
soinfo** candidate) {
*candidate = nullptr;
//如果是绝对路径就直接返回false
if (strchr(name, '/') != nullptr) {
return false;
}
//在caller命名空间已经加载的so库列表中找是否有待加载的so库
bool found = find_loaded_library_by_soname(ns, name, candidate);
//如果没找到就遍历caller命名空间所有链接命名空间
if (!found && search_linked_namespaces) {
// if a library was not found - look into linked namespaces
for (auto& link : ns->linked_namespaces()) {
//首先判断权限
if (!link.is_accessible(name)) {
continue;
}
android_namespace_t* linked_ns = link.linked_namespace();
//权限判断通过就在链接命名空间中已经加载的so库列表中找是否有待加载的so库
if (find_loaded_library_by_soname(linked_ns, name, candidate)) {
return true;
}
}
}
return found;
}
android_namespace_link_t::is_accessible()进行权限判断。
- 先判断此链接的allow_all_shared_libs_ 是否为true,如果为true说明当前命名空间中无法加载的so库都可以在链接命名空间中搜索加载。
- 如果allow_all_shared_libs_为false就需要进一步判断待加载的so库是否在此链接命名空间的shared_lib_sonames_列表中。
bool is_accessible(const char* soname) const {
return allow_all_shared_libs_ || shared_lib_sonames_.find(soname) != shared_lib_sonames_.end();
}
load_library#
load_library先判断extinfo是否为空,只有java层调用System.loadLibrary时extinfo才不为空。对于在native层中直接调用dlopen和android_dlopen_ext而言extinfo都为空。
- extinfo不为空,直接调用重载的load_library
- extinfo为空,需要先调用open_library将so文件从磁盘加载到内存中,在调用重载的load_library
static bool load_library(android_namespace_t* ns,
LoadTask* task,
ZipArchiveCache* zip_archive_cache,
LoadTaskList* load_tasks,
int rtld_flags,
bool search_linked_namespaces) {
const char* name = task->get_name();
soinfo* needed_by = task->get_needed_by();
const android_dlextinfo* extinfo = task->get_extinfo();
off64_t file_offset;
std::string realpath;
//判断extinfo是否为空,只有从java层调用System.loadLibrary来到者才extinfo才不为空
if (extinfo != nullptr && (extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD) != 0) {
file_offset = 0;
if ((extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET) != 0) {
file_offset = extinfo->library_fd_offset;
}
if (!realpath_fd(extinfo->library_fd, &realpath)) {
if (!is_first_stage_init()) {
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);
return load_library(ns, task, load_tasks, rtld_flags, realpath, search_linked_namespaces);
}
//对于直接native调用dlopen而言先调用open_library把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);
return false;
}
task->set_fd(fd, true);
task->set_file_offset(file_offset);
//调用重载的load_library加载so文件获取soinfo结构
return load_library(ns, task, load_tasks, rtld_flags, realpath, search_linked_namespaces);
}
open_library会尝试从磁盘中加载so文件
- 判断是否为绝对路径,是的话直接打开。
- 尝试从LD_LIBRARY_PATH设置的路径中打开文件(ld_library_paths)
- 尝试从default_library_paths中加载
static int open_library(android_namespace_t* ns,
ZipArchiveCache* zip_archive_cache,
const char* name, soinfo *needed_by,
off64_t* file_offset, std::string* realpath) {
TRACE("[ opening %s from namespace %s ]", name, ns->get_name());
//如果是绝对路径直接打开文件
if (strchr(name, '/') != nullptr) {
int fd = -1;
if (strstr(name, kZipFileSeparator) != nullptr) {
fd = open_library_in_zipfile(zip_archive_cache, name, file_offset, realpath);
}
if (fd == -1) {
fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY | O_CLOEXEC));
if (fd != -1) {
*file_offset = 0;
if (!realpath_fd(fd, realpath)) {
if (!is_first_stage_init()) {
PRINT("warning: unable to get realpath for the library \"%s\". Will use given path.",
name);
}
*realpath = name;
}
}
}
return fd;
}
//尝试从LD_LIBRARY_PATH设置的路径中打开(ld_library_paths)
// Otherwise we try LD_LIBRARY_PATH first, and fall back to the default library path
int fd = open_library_on_paths(zip_archive_cache, name, file_offset, ns->get_ld_library_paths(), realpath);
if (fd == -1 && needed_by != nullptr) {
fd = open_library_on_paths(zip_archive_cache, name, file_offset, needed_by->get_dt_runpath(), realpath);
// Check if the library is accessible
if (fd != -1 && !ns->is_accessible(*realpath)) {
close(fd);
fd = -1;
}
}
......
//尝试从default_library_paths中加载
if (fd == -1) {
fd = open_library_on_paths(zip_archive_cache, name, file_offset, ns->get_default_library_paths(), realpath);
}
return fd;
}
open_library如果成功加载文件后就调用重载的load_library。
- 调用is_accessible进行权限检查,检查通过直接调用soinfo_alloc为so文件申请soinfo结构体。
- is_accessible权限检查未通过调用is_greylisted判断是否在灰名单中,如果在就调用soinfo_alloc为so文件申请soinfo结构体。
- is_accessible权限检查未通过,也不在灰名单中则此caller命名空间无权限加载此so文件,打印错误日志。(测试apk就是在这出错的)
static bool load_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();
const char* name = task->get_name();
const android_dlextinfo* extinfo = task->get_extinfo();
LD_LOG(kLogDlopen, "load_library(ns=%s, task=%s, flags=0x%x, realpath=%s)", ns->get_name(), name,
rtld_flags, realpath.c_str());
...
//文件不在临时文件系统,is_accessible权限检查没有通过
if ((fs_stat.f_type != TMPFS_MAGIC) && (!ns->is_accessible(realpath))) {
// TODO(dimitry) before O release: add a namespace attribute to have this enabled
// only for classloader-namespaces
const soinfo* needed_by = task->is_dt_needed() ? task->get_needed_by() : nullptr;
//是否在灰名单中
if (is_greylisted(ns, name, needed_by)) {
// print warning only if needed by non-system library
if (needed_by == nullptr || !is_system_library(needed_by->get_realpath())) {
const soinfo* needed_or_dlopened_by = task->get_needed_by();
const char* sopath = needed_or_dlopened_by == nullptr ? "(unknown)" :
needed_or_dlopened_by->get_realpath();
DL_WARN_documented_change(__ANDROID_API_N__,
"private-api-enforced-for-api-level-24",
"library \"%s\" (\"%s\") needed or dlopened by \"%s\" "
"is not accessible by namespace \"%s\"",
name, realpath.c_str(), sopath, ns->get_name());
add_dlwarning(sopath, "unauthorized access to", name);
}
} else {
//如果权限检查未通过,也不在灰名单中则证明当前caller命名空间无权限加载此库文件。(app报错就是在此报错的)
// do not load libraries if they are not accessible for the specified namespace.
const char* needed_or_dlopened_by = task->get_needed_by() == nullptr ?
"(unknown)" :
task->get_needed_by()->get_realpath();
DL_ERR("library \"%s\" needed or dlopened by \"%s\" is not accessible for the namespace \"%s\"",
name, needed_or_dlopened_by, ns->get_name());
// do not print this if a library is in the list of shared libraries for linked namespaces
if (!maybe_accessible_via_namespace_links(ns, name)) {
PRINT("library \"%s\" (\"%s\") needed or dlopened by \"%s\" is not accessible for the"
" namespace: [name=\"%s\", ld_library_paths=\"%s\", default_library_paths=\"%s\","
" permitted_paths=\"%s\"]",
name, realpath.c_str(),
needed_or_dlopened_by,
ns->get_name(),
android::base::Join(ns->get_ld_library_paths(), ':').c_str(),
android::base::Join(ns->get_default_library_paths(), ':').c_str(),
android::base::Join(ns->get_permitted_paths(), ':').c_str());
}
return false;
}
}
//如果上述检查都通过就为加载的so文件申请一个soinfo结构
soinfo* si = soinfo_alloc(ns, realpath.c_str(), &file_stat, file_offset, rtld_flags);
if (si == nullptr) {
return false;
}
task->set_soinfo(si);
......
return true;
}
is_accessible进行权限检查,依次进行如下检查,有一项检查通过则直接返回true。
- 判断命名空间的is_isolated_是否为true,如果不是则证明不是严格隔离,权限检查通过。(这就是通常说的如果命名空间不是严格隔离的则可以加载任意绝对路径的库文件的原理)
- 判断是否在白名单中
- 判断是否在ld_library_paths中(LD_LIBRARY_PATH设置)
- 判断是否在default_library_paths中
- 判断是否在特权路径permitted_paths中
//bionic/linker/linker_namespaces.cpp
bool android_namespace_t::is_accessible(const std::string& file) {
//判断命名空间的is_isolated_,即是否为严格隔离,如果不是则权限检查通过。
if (!is_isolated_) {
return true;
}
//判断是否在白名单中
if (!whitelisted_libs_.empty()) {
const char *lib_name = basename(file.c_str());
if (std::find(whitelisted_libs_.begin(), whitelisted_libs_.end(),
lib_name) == whitelisted_libs_.end()) {
return false;
}
}
//判断是否在ld_library_paths中(LD_LIBRARY_PATH设置)
for (const auto& dir : ld_library_paths_) {
if (file_is_in_dir(file, dir)) {
return true;
}
}
//判断是否在default_library_paths中
for (const auto& dir : default_library_paths_) {
if (file_is_in_dir(file, dir)) {
return true;
}
}
//判断是否在特权路径permitted_paths中
for (const auto& dir : permitted_paths_) {
if (file_is_under_dir(file, dir)) {
return true;
}
}
return false;
}
find_library_in_linked_namespace#
如果第一步和第二步都失败了就遍历caller命名空间的链接命名空间并调用find_library_in_linked_namespace。
- 调用find_loaded_library_by_soname在链接命名空间中已经加载的so库列表中查找是否存在待加载so文件,如果是绝对路径直接返回false。(这次传入false所以不会调用链接命名空间的链接命名空间,这也是链接命名空间不具有传递性的原理)
- 无论找没找到都调用is_accessible权限检查(检查 链接命名空间的allow_all_shared_libs_是否为True或者so文件是否在链接命名空间的共享库列表shared_lib_sonames_中。)
static bool find_library_in_linked_namespace(const android_namespace_link_t& namespace_link,
LoadTask* task) {
android_namespace_t* ns = namespace_link.linked_namespace();
soinfo* candidate;
bool loaded = false;
std::string soname;
//检查so库在链接命名空间中是否加载,传入false不会去检查链接命名空间的链接命名空间(这也是链接命名空间不具有传递性的原理)
if (find_loaded_library_by_soname(ns, task->get_name(), false, &candidate)) {
loaded = true;
soname = candidate->get_soname();
} else {
soname = resolve_soname(task->get_name());
}
//进行权限检查
if (!namespace_link.is_accessible(soname.c_str())) {
// the library is not accessible via namespace_link
LD_LOG(kLogDlopen,
"find_library_in_linked_namespace(ns=%s, task=%s): Not accessible (soname=%s)",
ns->get_name(), task->get_name(), soname.c_str());
return false;
}
//如果已经加载就返回
// if library is already loaded - return it
if (loaded) {
LD_LOG(kLogDlopen, "find_library_in_linked_namespace(ns=%s, task=%s): Already loaded",
ns->get_name(), task->get_name());
task->set_soinfo(candidate);
return true;
}
// returning true with empty soinfo means that the library is okay to be
// loaded in the namespace but has not yet been loaded there before.
LD_LOG(kLogDlopen, "find_library_in_linked_namespace(ns=%s, task=%s): Ok to load", ns->get_name(),
task->get_name());
task->set_soinfo(nullptr);
return true;
}
总结#
再次查看apk加载libc.so和libart.so的日志信息。
-
dlopen打开libc.so会成功,调用流程为dlopen->find_libraries->find_library_internal,然后第一步调用find_loaded_library_by_soname可以通过权限检查(libc.so在public.libraries.txt中声明共享)并在caller命名空间链接的defult命名空间中找到libc.so,返回找到的soinfo结构指针并返回对应的handle。
-
dlopen打开/apex/com.android.runtime/lib/libart.so会失败,调用流程为dlopen->find_libraries->find_library_internal,然后第一步调用find_loaded_library_by_soname因为是绝对路径直接返回false。第二步调用load_library->open_library,因为是绝对路径所以直接打开文件,然后判断权限没通过又不在灰名单中所以无权限打印错误日志。第三步调用find_library_in_linked_namespace->find_loaded_library_by_soname,因为是绝对路径所以直接返回并调用is_accessible进行权限检查失败打印日志。(这里因为find_library_in_linked_namespace->find_loaded_library_by_soname找没找到so文件都会返回并进行权限检查,所以即便传入的是libart.so可以在caller命名空间链接的runtime命名空间加载的so列表中找到libart.so文件也不行,因为caller命名空间链接到runtime空间并没有设置libart.so为共享库,所以权限还是通过不了)
14:50:53.713 D dlopen(name="libc.so", flags=0x0, extinfo=(null), caller="/data/app/com.reverccqin.namespacetest-pJvDUzHPZs4h76jUcSOgZA==/lib/arm/libnamespacetest.so", caller_ns=classloader-namespace@0xed7da330, targetSdkVersion=32) ...
14:50:53.714 D find_libraries(ns=classloader-namespace): task=libc.so, is_dt_needed=0
14:50:53.714 D find_library_internal(ns=classloader-namespace, task=libc.so): Already loaded (by soname): /apex/com.android.runtime/lib/bionic/libc.so
14:50:53.714 D ... dlopen calling constructors: realpath="/apex/com.android.runtime/lib/bionic/libc.so", soname="libc.so", handle=0x22165f21
14:50:53.714 D ... dlopen successful: realpath="/apex/com.android.runtime/lib/bionic/libc.so", soname="libc.so", handle=0x22165f21
14:51:09.223 D dlopen(name="/apex/com.android.runtime/lib/libart.so", flags=0x0, extinfo=(null), caller="/data/app/com.reverccqin.namespacetest-pJvDUzHPZs4h76jUcSOgZA==/lib/arm/libnamespacetest.so", caller_ns=classloader-namespace@0xed7da330, targetSdkVersion=32) ...
14:51:09.223 D find_libraries(ns=classloader-namespace): task=/apex/com.android.runtime/lib/libart.so, is_dt_needed=0
14:51:09.224 D load_library(ns=classloader-namespace, task=/apex/com.android.runtime/lib/libart.so, flags=0x0, realpath=/apex/com.android.runtime/lib/libart.so)
14:51:09.224 E library "/apex/com.android.runtime/lib/libart.so" ("/apex/com.android.runtime/lib/libart.so") needed or dlopened by "/data/app/com.reverccqin.namespacetest-pJvDUzHPZs4h76jUcSOgZA==/lib/arm/libnamespacetest.so" is not accessible for the namespace: [name="classloader-namespace", ld_library_paths="", default_library_paths="/data/app/com.reverccqin.namespacetest-pJvDUzHPZs4h76jUcSOgZA==/lib/arm:/data/app/com.reverccqin.namespacetest-pJvDUzHPZs4h76jUcSOgZA==/base.apk!/lib/armeabi-v7a", permitted_paths="/data:/mnt/expand:/data/data/com.reverccqin.namespacetest"]
14:51:09.224 D find_library_internal(ns=classloader-namespace, task=/apex/com.android.runtime/lib/libart.so): Trying 3 linked namespaces
14:51:09.224 D find_library_in_linked_namespace(ns=(default), task=/apex/com.android.runtime/lib/libart.so): Not accessible (soname=libart.so)
14:51:09.224 D find_library_in_linked_namespace(ns=runtime, task=/apex/com.android.runtime/lib/libart.so): Not accessible (soname=libart.so)
14:51:09.224 D find_library_in_linked_namespace(ns=sphal, task=/apex/com.android.runtime/lib/libart.so): Not accessible (soname=libart.so)
14:51:09.224 D ... dlopen failed: library "/apex/com.android.runtime/lib/libart.so" needed or dlopened by "/data/app/com.reverccqin.namespacetest-pJvDUzHPZs4h76jUcSOgZA==/lib/arm/libnamespacetest.so" is not accessible for the namespace "classloader-namespace"
14:51:09.224 D dlerror set to "dlopen failed: library "/apex/com.android.runtime/lib/libart.so" needed or dlopened by "/data/app/com.reverccqin.namespacetest-pJvDUzHPZs4h76jUcSOgZA==/lib/arm/libnamespacetest.so" is not accessible for the namespace "classloader-namespace""
find_library_internal函数整个调用链判断流程如下
①. find_loaded_library_by_soname() 在caller命名空间和链接的命名空间中查找库是否已经加载
判断路径,全路径直接返回false。
先在caller的命名空间对应的so文件列表中找是否已经加载了此so文件是否已经加载,找到了就返回对应soinfo指针
没找到再继续在所有链接的命名空间中查找此so文件是否加载,需要先调用is_accessible进行权限检查(检查 链接命名空间的allow_all_shared_libs_是否为True或者so文件是否在链接命名空间的共享库列表shared_lib_sonames_中。
如果上一步的is_accessible权限检查通过就在链接命名空间对应的so文件列表中找此so文件是否已经加载,如果找到了就返回对应的soinfo指针。
②. load_library 如果没有在caller命名空间和链接的命名空间已经加载的so库列表中没有找到so文件就继续调用load_library,传入的命名空间是caller的,最后一个参数为true表示搜索链接命名空间。
if(extinfo != NULL) load_library 如果是从java层调用的则extinfo不为空,此so文件已经被加载到内存了直接调用重载的load_library
if(extinfo == NULL) open_library
如果是绝对路径直接打开文件。(所以再直接使用dlopen传入绝对路径是可以过掉第一层检测的)
否则尝试从ld_library_path和default library_path路径中查找
如果都没找到就会返回失败
if(extinfo == NULL) load_library 如果是直接在native层调用的dlopen或者android_dlopen就需要先调用open_library加载此文件成功后,再调用重载的load_library
先调用android_namespace_t::is_accessible判断此so文件是否在caller命名空间的访问路径中。(先判断此命名空间的统一访问控制权限is_isolated(可直接通过),再判断是否在ld_library, default_library, permitted_path中
android_namespace_t::is_accessible权限判断通过就调用soinfo_alloc申请soinfo结构体,并返回。
android_namespace_t::is_accessible权限判断失败就继续判断是否再greylist灰名单也会调用soinfo_alloc申请soinfo结构体。(现在已经失效)
android_namespace_t::is_accessible权限判断失败也不在greylist灰名单中就返回失败 无权限(我们就是在这错误的)
③. 前两步都失败就去遍历caller命名空间所有的链接命名空间,然后以链接命名空间作为参数调用 find_library_in_linked_namespace()
find_loaded_library_by_soname在链接的命名空间中查找库是否已经加载,绝对路径直接返回。这次不会去查找链接命名空间自己的链接命名空间。
找没找到都调用is_accessible权限检查(检查 链接命名空间的allow_all_shared_libs_是否为True或者so文件是否在链接命名空间的共享库列表shared_lib_sonames_中。(我们在此也错误了)
如果find_library_in_linked_namespace成功但是so文件还没加载,调用load_library进行加载。
源码基于:android 10.0.0_r7
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】