Android CMake库链接问题:避免重复符号与运行时错误
问题描述
我正在开发一个使用CMake构建原生libMain.so的Android项目。主项目的CMakeLists.txt中,main目标链接了boost等多个静态库:
// main CMakeLists.txt add_library(main SHARED <cpp files>) target_link_libraries(main boost png xml2) add_subdirectory(<path_to_boost_cmake> <path_to_boost_cmake>/build) add_subdirectory(<path_to_png_cmake> <path_to_png_cmake>/build) add_subdirectory(<path_to_xml2_cmake> <path_to_xml2_cmake>/build)
现在需要添加第三方提供的依赖库libXYZ,其CMakeLists.txt将libXYZ构建为共享库,同时链接boost和共享库openssl.so:
// libXYZ's CMakeLists.txt add_library(libXYZ SHARED <cpp files>) target_link_libraries(libXYZ boost libA) target_link_libraries(libXYZ $<$<STREQUAL:${MB_ARCH},arm64>:${CMAKE_CURRENT_SOURCE_DIR}/../../../remote/android_archive/arm64/lib/openssl.so>)
我将libXYZ链接到libMain.so,在主项目CMakeLists.txt中修改为:
target_link_libraries(main libXYZ boost png xml2)
遇到的问题:
- 原本预期因main和libXYZ都链接boost会出现重复符号错误,但实际没有;将libXYZ改为静态库构建后,却出现了boost的重复符号错误。为何共享库时无此错误?
- 移除主项目中的boost依赖后链接成功,但运行时提示找不到
openssl.so。
需求:在尽量少修改第三方libXYZ的CMake的前提下,实现无重复符号、无运行时错误的库链接方案。
问题分析与解决方案
一、共享库与静态库的符号差异解释
- 共享库(libXYZ.so)场景:当libXYZ作为共享库构建时,链接boost静态库只会将libXYZ实际用到的boost符号打包进自身,且Android平台默认会将这些符号设置为隐藏可见性。此时libMain.so再链接boost静态库,只会打包自己用到的boost符号,两者的符号在各自的共享库空间中独立,不会冲突。另外,链接器处理共享库依赖时,不会把共享库的依赖符号重复导入到主库,因此不会出现重复符号错误。
- 静态库(libXYZ.a)场景:当libXYZ作为静态库构建时,所有被libXYZ引用的boost符号会被打包进libXYZ.a。当libMain.so同时链接libXYZ.a和boost静态库时,链接器会把两部分中的boost符号都导入到libMain.so中,直接导致重复符号冲突。
二、无修改(或少修改)的解决方案
方案1:保持libXYZ为共享库,统一依赖并解决openssl加载问题
- 避免重复链接boost:在主项目中移除显式的boost依赖,让libMain.so通过libXYZ间接依赖boost(CMake会自动处理传递依赖,只要boost是通过
add_subdirectory引入的CMake目标)。修改主项目的target_link_libraries:target_link_libraries(main libXYZ png xml2) - 解决openssl.so运行时找不到的问题:
- 方法A:将
openssl.so复制到Android项目的jniLibs/${ABI}/目录下(比如jniLibs/arm64-v8a/openssl.so),CMake会自动将其打包到APK的对应ABI目录,运行时系统可直接找到。 - 方法B:若不想复制文件,可在主项目CMake中添加openssl的搜索路径,并通过自定义命令将其复制到输出目录:
if(MB_ARCH STREQUAL "arm64") # 添加openssl库的搜索路径 link_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../../remote/android_archive/arm64/lib) # 确保openssl.so被复制到APK的库目录 add_custom_command(TARGET main POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/../../../remote/android_archive/arm64/lib/openssl.so ${ANDROID_LIBRARY_OUTPUT_DIRECTORY}/arm64-v8a/openssl.so) endif()
- 方法A:将
方案2:将libXYZ改为静态库,解决重复符号问题
若必须将libXYZ改为静态库,可通过最小修改其CMake的方式避免重复符号:
- 修改libXYZ的CMake(仅少量改动):先将源码编译为对象库,再打包为静态库,同时通过
INTERFACE传递boost依赖,避免主项目重复链接:// libXYZ's CMakeLists.txt 修改部分 add_library(libXYZ_obj OBJECT <cpp files>) target_link_libraries(libXYZ_obj boost libA) add_library(libXYZ STATIC $<TARGET_OBJECTS:libXYZ_obj>) # 让boost依赖通过libXYZ传递给主项目 target_link_libraries(libXYZ INTERFACE boost) - 主项目中移除显式的boost依赖:
此时CMake会通过libXYZ的target_link_libraries(main libXYZ png xml2)INTERFACE依赖自动链接boost,且只会链接一次,避免重复符号。同时仍需按方案1中的方法解决openssl.so的运行时加载问题。
内容的提问来源于stack exchange,提问作者Vivek Mangal
相关产品推荐
相关产品推荐

