You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++ Linux/Android获取已加载so库句柄与导出函数地址方案

问题根因

这个现象是Android Linker的命名空间隔离机制导致的,和库有没有实际加载到内存没有直接关系:

  1. 你在/proc/self/maps里看到的已加载库,是内核视角的进程内存映射,不代表这些库对当前调用上下文的linker可见
  2. libSwordsman.so和它依赖的libfmod.so是被非默认linker命名空间加载的(常见于游戏加固、系统私有组件、应用沙箱隔离场景),你当前代码运行在默认命名空间下,该命名空间的库搜索路径里没有libfmod.so,哪怕内存中已经存在同名库,跨命名空间的已加载库默认不会被dlopen识别,因此直接dlopen会报依赖不存在,带RTLD_NOLOAD标志也会判定库未加载返回空。
  3. dladdr能正常返回信息是因为这个函数是直接遍历当前进程所有已映射的ELF内存段做匹配,不经过linker命名空间校验,所以哪怕跨命名空间也能识别。
可行解决方案

按适配成本从低到高排序:

  • 优先尝试:补全依赖后走标准dlopen流程
    先通过/proc/self/maps拿到libfmod.so的绝对加载路径,先调用dlopen(libfmod_absolute_path, RTLD_LAZY)把这个库注册到当前默认命名空间的已加载列表里——因为库已经在内存中,这个调用不会触发实际的文件加载,只会在当前命名空间补登记,不会有额外性能开销。登记完成后再用libSwordsman.so的绝对路径调用dlopen,就不会再报依赖找不到的错误,能正常拿到可用句柄给dlsym调用。
  • 适配多版本的通用方案:遍历linker已加载库列表拿soinfo指针
    dlopen返回的句柄本质是linker内部存储库元数据的soinfo结构指针,你可以用系统公开的dl_iterate_phdr接口遍历所有已加载的ELF对象,匹配到libSwordsman.so对应的路径条目后,顺着结构偏移拿到对应的soinfo指针,这个指针可以直接传给dlsym使用,完全不需要走dlopen流程,也不受命名空间限制。

    注意:不同Android版本的soinfo结构偏移有差异,需要针对目标适配的Android版本,从对应版本的linker二进制中提取偏移量,不要硬编码单个版本的偏移避免崩溃。

  • 保底无依赖方案:手动解析ELF导出表
    如果不想依赖linker内部结构,也不需要硬算所有函数偏移:你已经拿到了库的加载基址,只需要按照ELF文件格式解析头部,找到动态符号表(.dynsym)、动态字符串表(.dynstr),遍历导出符号匹配你需要的函数名,拿到符号的相对偏移后加上库基址就是函数的实际内存地址,整个逻辑只需要读写进程内存,不需要调用任何非公开API,适配所有Android版本,代码量在100行以内,比逐个硬编码偏移的维护成本低很多。
避坑说明
  • 不要直接把/proc/self/maps里拿到的库基址强转为句柄传给dlsym,dlsym会校验传入指针是否为合法的soinfo结构,非法传入会直接触发进程崩溃。
  • dladdr1确实在Android NDK中未暴露,不要尝试强行链接该接口,在部分系统版本上会直接加载失败。

内容的提问来源于stack exchange,提问作者Yang Jim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 15:09:18