本文记录一次Android ROM移植过程中遇到的dlopen failed: cannot locate symbol问题的解决过程,希望能对遇到类似问题的Android ROM移植爱好者有所启示。
1. 问题描述
在移植小米13 LineageOS 22.1 (Android 15)时,遇到了这个这个报错Abort message: 'could not dlopen c2.dolby.hevc.dec.so: dlopen failed: cannot locate symbol "_ZN7android8hardware5media2c24V1_05utils6objcpyEPNS3_10WorkBundleERKNSt3__14listINS7_10unique_ptrI6C2WorkNS7_14default_deleteISA_EEEENS7_9allocatorISD_EEEEPNS4_16BufferPoolSenderE" referenced by "/vendor/lib64/c2.dolby.client.so"...'
.
报错信息表明c2.dolby.client.so依赖的c2.dolby.hevc.dec.so依赖不存在的符号ZN7android***。
2. 符号提供者查找
首先,我们在使用objdump在原stock rom中查找一下该符号的提供者,可以使用如下命令:
for f in {system,system_ext,vendor,odm,product}/**/*.so; do aarch64-linux-gnu-objdump -T "$f" 2>/dev/null |grep _ZN7android8hardware5media2c24V1_05utils6objcpyEPNS3_10WorkBundleERKNSt3__14listINS7_10unique_ptrI6C2WorkNS7_14default_deleteISA_EEEENS7_9allocatorISD_EEEEPNS4_16BufferPoolSenderE && echo "$f"; done
结果如下:
0000000000000000 DF *UND* 0000000000000000 Base _ZN7android8hardware5media2c24V1_05utils6objcpyEPNS3_10WorkBundleERKNSt3__14listINS7_10unique_ptrI6C2WorkNS7_14default_deleteISA_EEEENS7_9allocatorISD_EEEEPNS4_16BufferPoolSenderE vendor/lib64/c2.dolby.client.so 0000000000050920 g DF .text 0000000000000008 Base _ZN7android8hardware5media2c24V1_05utils6objcpyEPNS3_10WorkBundleERKNSt3__14listINS7_10unique_ptrI6C2WorkNS7_14default_deleteISA_EEEENS7_9allocatorISD_EEEEPNS4_16BufferPoolSenderE vendor/lib64/libcodec2_hidl@1.0.so 0000000000000000 DF *UND* 0000000000000000 Base _ZN7android8hardware5media2c24V1_05utils6objcpyEPNS3_10WorkBundleERKNSt3__14listINS7_10unique_ptrI6C2WorkNS7_14default_deleteISA_EEEENS7_9allocatorISD_EEEEPNS4_16BufferPoolSenderE vendor/lib64/libcodec2_hidl@1.1.so 0000000000000000 DF *UND* 0000000000000000 Base _ZN7android8hardware5media2c24V1_05utils6objcpyEPNS3_10WorkBundleERKNSt3__14listINS7_10unique_ptrI6C2WorkNS7_14default_deleteISA_EEEENS7_9allocatorISD_EEEEPNS4_16BufferPoolSenderE vendor/lib64/libcodec2_hidl@1.2.so 0003da0c g DF .text 00000004 Base _ZN7android8hardware5media2c24V1_05utils6objcpyEPNS3_10WorkBundleERKNSt3__14listINS7_10unique_ptrI6C2WorkNS7_14default_deleteISA_EEEENS7_9allocatorISD_EEEEPNS4_16BufferPoolSenderE vendor/lib/libcodec2_hidl@1.0.so
从中可以发现该符号提供者是vendor/lib64/libcodec2hidl@1.0.so. 根据该so文件命名猜出其是应该是由AOSP源码编译出来的,而不是无源码的vendor blob. 接下来,可以通过分析一下Android源码看看为什么会缺少该符号,以及该符号是否由其它替代品。
3. Android源码分析
首先,先用c++filt
对该符号demangle一下,可以得到如下结果:
android::hardware::media::c2::V1_0::utils::objcpy(android::hardware::media::c2::V1_0::WorkBundle*, std::__1::list<std::__1::unique_ptr<C2Work, std::__1::default_delete<C2Work> >, std::__1::allocator<std::__1::unique_ptr<C2Work, std::__1::default_delete<C2Work> > > > const&, android::hardware::media::c2::V1_0::utils::BufferPoolSender*)
简化一下可以得:
android::hardware::media::c2::V1_0::utils::objcpy(android::hardware::media::c2::V1_0::WorkBundle*, list<unique_ptr<C2Work>> const&, android::hardware::media::c2::V1_0::utils::BufferPoolSender*)
然后,在Android Code Search 中搜索一下objcpy, 可以发现objcpy相关源码位于frameworks/av/media/codec2/hal/hidl/1.0/utils/include/codec2/hidl/1.0/types.h和其实现文件types.cpp, 如下:
typedef ::android::BufferPoolSender<BufferPoolTypes> BufferPoolSender; bool objcpy(WorkBundle* d, const std::list<std::unique_ptr<C2Work>>& s, BufferPoolSender* bpSender = nullptr);
从中可以看出,在Android 15中,BufferPoolSender不再为android::hardware::media::c2::V1_0::utils::BufferPoolSender
。
查看git提交历史,可以是发现这个ab22b476668b449f2a7e48819387d4cbdfd6e750 commit将BufferPoolSender改为成了BufferPoolSender template, 并将其从types.h移动到了BufferPoolSender.h, 如下:
// Android 14 android::hardware::media::c2::V1_0::utils::BufferPoolSender struct BufferPoolSender { typedef ::android::hardware::media::bufferpool::V2_0::ResultStatus ResultStatus; typedef ::android::hardware::media::bufferpool::V2_0::BufferStatusMessage BufferStatusMessage; typedef ::android::hardware::media::bufferpool:: BufferPoolData BufferPoolData; virtual ResultStatus send(const std::shared_ptr<BufferPoolData>& bpData, BufferStatusMessage* bpMessage) = 0; virtual ~BufferPoolSender() = default; }; // Android 15 android::BufferPoolSender template <typename BufferPoolTypes> struct BufferPoolSender { typedef typename BufferPoolTypes::BufferPoolData BufferPoolData; typedef typename BufferPoolTypes::ResultStatus ResultStatus; typedef typename BufferPoolTypes::BufferPoolStatus BufferPoolStatus; typedef typename BufferPoolTypes::BufferStatusMessage BufferStatusMessage; virtual BufferPoolStatus send(const std::shared_ptr<BufferPoolData>& bpData, BufferStatusMessage* bpMessage) = 0; virtual ~BufferPoolSender() = default; }; struct BufferPoolTypes { typedef bufferpool::BufferPoolData BufferPoolData; typedef bufferpool::V2_0::ResultStatus BufferPoolStatus; typedef bufferpool::V2_0::ResultStatus ResultStatus; typedef bufferpool::V2_0::BufferStatusMessage BufferStatusMessage; };
两者都没有数据成员,且具有两个签名一致的virtual函数,因此两者对象的内存布局应该完全一致,也就是说可以将Android 14 BufferPoolSender对象强转为Android 15 BufferPoolSender对象.
因此,问题中缺少的ZN7android***符号可以根据Android 15 objcpy函数来实现,下一小节将讨论具体做法。
4. 缺失符号ZN7android***实现方法
由于可以Android 14 BufferPoolSender对象强转为Android 15 BufferPoolSender对象,因此缺失符号ZN7android***可通过透传调用Android 15 objcpy来实现,如下:
#include <codec2/hidl/1.0/types.h> using ::android::hardware::media::c2::V1_0::utils::BufferPoolSender; using ::android::hardware::media::c2::V1_0::WorkBundle; // android::hardware::media::c2::V1_0::utils::objcpy extern "C" { bool _ZN7android8hardware5media2c24V1_05utils6objcpyEPNS3_10WorkBundleERKNSt3__14listINS7_10unique_ptrI6C2WorkNS7_14default_deleteISA_EEEENS7_9allocatorISD_EEEEPNS_16BufferPoolSenderINS4_15BufferPoolTypesEEE(WorkBundle* d, const std::list<std::unique_ptr<C2Work>>& s, BufferPoolSender* bpSender); bool _ZN7android8hardware5media2c24V1_05utils6objcpyEPNS3_10WorkBundleERKNSt3__14listINS7_10unique_ptrI6C2WorkNS7_14default_deleteISA_EEEENS7_9allocatorISD_EEEEPNS4_16BufferPoolSenderE(WorkBundle* d, const std::list<std::unique_ptr<C2Work>>& s, BufferPoolSender* bpSender) { return _ZN7android8hardware5media2c24V1_05utils6objcpyEPNS3_10WorkBundleERKNSt3__14listINS7_10unique_ptrI6C2WorkNS7_14default_deleteISA_EEEENS7_9allocatorISD_EEEEPNS_16BufferPoolSenderINS4_15BufferPoolTypesEEE(d, s, bpSender); } }
之所以用extern "C", 是因为Android 15目前已没有真正的BufferPoolSender定义,进而导致C++ name mangle将不能生成目标符号。
由于动态链接时linker不会校验参数类型(最多校验一下参数内存占用大小,参数校验是compiler的责任),因此上述代码可以被简化为:
// android::hardware::media::c2::V1_0::utils::objcpy extern "C" { bool _ZN7android8hardware5media2c24V1_05utils6objcpyEPNS3_10WorkBundleERKNSt3__14listINS7_10unique_ptrI6C2WorkNS7_14default_deleteISA_EEEENS7_9allocatorISD_EEEEPNS_16BufferPoolSenderINS4_15BufferPoolTypesEEE(void* d, const void* s, void* bpSender); bool _ZN7android8hardware5media2c24V1_05utils6objcpyEPNS3_10WorkBundleERKNSt3__14listINS7_10unique_ptrI6C2WorkNS7_14default_deleteISA_EEEENS7_9allocatorISD_EEEEPNS4_16BufferPoolSenderE(void* d, const void* s, void* bpSender) { return _ZN7android8hardware5media2c24V1_05utils6objcpyEPNS3_10WorkBundleERKNSt3__14listINS7_10unique_ptrI6C2WorkNS7_14default_deleteISA_EEEENS7_9allocatorISD_EEEEPNS_16BufferPoolSenderINS4_15BufferPoolTypesEEE(d, s, bpSender); } }
上述代码将被用于生成libcodec2hidl@1.0shim.so, 其将作为c2.dolby.hevc.dec.so的依赖so以提供缺失的ZN7android***符号。 下面将介绍如何将上述代码融入Android构建系统以生成该so.
5. Android构建系统融入
现代Android一般使用Soong来构建简单新模块,因此本文也将如此。 libcodec2hidl@1.0shim模块的构建规则文件Android.bp如下:
cc_library_shared { name: "libcodec2_hidl@1.0_shim", include_dirs: [ "frameworks/av/media/codec2/hal/hidl/1.0/utils/include", ], shared_libs: [ "libcodec2_hidl@1.0", ], srcs: [ "libcodec2/libcodec2_hidl@1.0.cpp", ], vendor: true, }
其中libcodec2/libcodec2hidl@1.0.cpp内容为上一节中的C++代码,vendor: true
表明libcodec2hidl@1.0shim.so将和c2.dolby.hevc.dec.so一样被安装到vendor分区。
为了让Android构建系统能够编译本模块,需要将其加入PRODUCTPACKAGES中:PRODUCT_PACKAGES += libcodec2_hidl@1.0_shim
.
为了让extract util能够自动给c2.dolby.hevc.dec.so添加libcodec2hidl@1.0shim.so依赖,需要在extract-files.py中加入如下代码:
blob_fixups: blob_fixups_user_type = { 'vendor/lib64/c2.dolby.hevc.dec.so': blob_fixup() .add_needed("libcodec2_hidl@1.0_shim.so"), } module = ExtractUtilsModule( 'sm8550-common', 'xiaomi', blob_fixups=blob_fixups, lib_fixups=lib_fixups, namespace_imports=namespace_imports, )
其本质上相当于patchelf --add-needed libcodec2_hidl@1.0_shim.so c2.dolby.hevc.dec.so
.
6. 总结
Android新版本可能因采用新实现方法而会缺少某些vendor blob依赖的老版本符号,而这些老版本符号一般可以通过透传调用新版本符号来实现。 文中讨论了vendor blob依赖缺少的老版本符号问题的一种解决过程,希望其能帮助到遇到类似问题的Android ROM移植爱好者。