Android下如何通过dlopen返回的不透明句柄获取动态库文件名
Android下如何通过dlopen返回的不透明句柄获取动态库文件名
我来给你梳理几个可行的方案,完美适配你提到的Android环境限制:
方案一:利用动态库默认符号获取地址(最简便)
每个正常编译的动态库,链接器都会自动生成_init(初始化函数)和_fini(终止函数)这两个符号——不管库有没有主动导出其他符号,这俩肯定存在。你可以借助它们拿到库内的有效地址,再用dladdr反查文件名:
#include <dlfcn.h> const char* get_lib_filename(void* handle) { // 获取库的初始化函数地址 void* init_addr = dlsym(handle, "_init"); if (init_addr == NULL) { // 极端情况:库被特殊编译选项剥离了_init,换_fini试试 init_addr = dlsym(handle, "_fini"); if (init_addr == NULL) { return NULL; } } // 通过dladdr获取地址所属的库信息 Dl_info info; if (dladdr(init_addr, &info) != 0) { return info.dli_fname; } return NULL; }
这个方法简单高效,几乎能覆盖99%的场景,除非你碰到那种刻意去掉初始化函数的极端库。
方案二:用dl_iterate_phdr遍历匹配(兜底方案)
如果方案一失效,还可以通过dl_iterate_phdr遍历系统中所有已加载的共享库,然后逐个验证是否是你要找的那个:
#include <dlfcn.h> #include <link.h> typedef struct { void* target_handle; const char* found_name; } MatchData; static int phdr_match_callback(struct dl_phdr_info* info, size_t size, void* arg) { MatchData* data = (MatchData*)arg; // 跳过匿名映射(比如主程序本身、匿名内存段) if (info->dlpi_name[0] == '\0') { return 0; } // 用RTLD_NOLOAD打开库,不会重新加载,只获取已加载的句柄 void* temp_handle = dlopen(info->dlpi_name, RTLD_NOLOAD | RTLD_LAZY); if (temp_handle == data->target_handle) { // 找到目标库,记录文件名并终止遍历 data->found_name = info->dlpi_name; return 1; } // 记得关闭临时句柄(如果成功打开的话) if (temp_handle != NULL) { dlclose(temp_handle); } return 0; } const char* get_lib_filename_fallback(void* handle) { MatchData data = {.target_handle = handle, .found_name = NULL}; dl_iterate_phdr(phdr_match_callback, &data); return data.found_name; }
这里用RTLD_NOLOAD参数很关键,它不会重新加载库,只是获取已加载实例的句柄,既安全又不会影响现有程序状态。而且Android的bionic库中,同一个已加载库的句柄是唯一的,直接比较句柄就能确认匹配。
注意事项
- 方案一中的
_init/_fini符号:只有用-nostartfiles这类极端编译选项的库才会缺失,普通第三方库和系统库都不会有这个问题。 - 句柄比较的可靠性:Android官方虽然说dlopen返回的句柄是不透明的,但实际实现中,同一个库的多次dlopen(带RTLD_NOLOAD)会返回同一个句柄,所以直接比较是安全的。
备注:内容来源于stack exchange,提问作者HJP
相关产品推荐
相关产品推荐

