CMake无法隐藏符号但Android.mk可行,求助解决-fvisibility=hidden失效问题
问题现象
在Android NDK环境中,使用CMake配置set(CMAKE_CXX_VISIBILITY_PRESET hidden),或给target添加-fvisibility=hidden编译选项,均无法实现符号隐藏。但-Wl,-Bsymbolic等其他编译选项可正常生效,编译宏也能正确传递。相同NDK环境下,用Android.mk配置-fvisibility=hidden却能成功隐藏符号。
相关配置与输出
CMakeLists.txt内容
cmake_minimum_required(VERSION 3.20) project(multp) message("generator: ${CMAKE_GENERATOR_PLATFORM}") message("CMAKE_SIZEOF_VOID_P is ${CMAKE_SIZEOF_VOID_P}") message("CMAKE_SYSTEM_NAME is ${CMAKE_SYSTEM_NAME}") enable_testing() list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") set(CMAKE_C_VISIBILITY_PRESET hidden) set(CMAKE_CXX_VISIBILITY_PRESET hidden) include(build/cmake/cmake-definition.cmake) include(build/cmake/Format.cmake) add_subdirectory(vendors/openssl/1.1.1s) add_subdirectory(vendors/rapidjson) add_subdirectory(vendors/gmssl) add_subdirectory(platform/multp)
target_link_libraries(multp PUBLIC gmssl rapidjson) target_compile_options(multp PRIVATE "-Wl,--exclude-libs,ALL" "-Wl,-Bsymbolic" "-fvisibility=hidden") target_link_options(multp PRIVATE "-Wl,--exclude-libs,ALL" "-Wl,-Bsymbolic" "-fvisibility=hidden")
编译命令行
cmake -DANDROID_ABI=arm64-v8a -DHDMW_CODEC_ENABLED=TRUE -DANDROID_PLATFORM=android-23 -DANDROID_STL=c++_shared -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=G:\Android\SDK\ndk\23.1.7779620\build\cmake\android.toolchain.cmake -DCMAKE_SYSTEM_NAME=Android -B ./cmake-android -S . -G Ninja
Android.mk内容
APP_STL := c++_shared APP_CFLAGS += -O3 -DANDROID \ -Werror=return-type \ -Werror=implicit-int \ -Werror=incompatible-pointer-types \ -Werror=shadow \ -Werror=return-stack-address \ -Werror=uninitialized \ -Werror=format \ -Werror=sign-compare \ -Werror=int-conversion \ -Werror=inconsistent-missing-override \ -Werror=implicit-function-declaration \ -fvisibility=hidden ifeq ($(NDK_DEBUG),1) APP_CFLAGS += -fsanitize=address \ -fno-omit-frame-pointer APP_LDFLAGS := -fsanitize=address endif ifdef BUILD_VERSION APP_CFLAGS += -DBUILD_VERSION=$(BUILD_VERSION) endif APP_CPPFLAGS := $(APP_CFLAGS) -fexceptions -std=c++14 APP_PLATFORM := android-21 #set as the ndk-build option, #like this: ndk-build NDK_DEBUG=0/1 #0:release, 1:debug #APP_OPTIM := release #the new ndk not support this setting. #NDK_TOOLCHAIN_VERSION := 4.9 APP_ABI := armeabi-v7a arm64-v8a
LOCAL_C_INCLUDES += \ $(LOCAL_PATH)/src \ $(LOCAL_PATH)/h \ $(LOCAL_PATH)/../../vendors/gmssl/include \ $(LOCAL_PATH)/../../vendors/openssl/1.1.1s/include LOCAL_CFLAGS := $(MY_COMMON_DEFS) LOCAL_CPPFLAGS += -frtti -fexceptions LOCAL_LDLIBS += -lz -ldl -llog LOCAL_STATIC_LIBRARIES := libgmssl \ libssl \ libcrypto
nm命令输出
work-dir> nm -CD libmultp.so U __cmsg_nxthdr U __cxa_allocate_exception U __cxa_atexit U __cxa_begin_catch U __cxa_end_catch U __cxa_finalize U __cxa_free_exception U __cxa_pure_virtual U __cxa_rethrow U __cxa_throw U __errno U __FD_ISSET_chk U __FD_SET_chk U __gxx_personality_v0 U __register_atfork U __sF 00000000001cfb9c T crc32_fast(void const*, unsigned long, unsigned int) 0000000000154b90 W void TrimString<CCmIsSpace>(CCmString&, CCmIsSpace) 000000000014bda0 T CalcCRC16_m(unsigned short&, CCmMessageBlock&, unsigned int) 00000000001d0224 T crc32_1byte(void const*, unsigned long, unsigned int) 00000000001403ac T init_config(char const*) 000000000015648c W void LTrimString<CCmIsSpace>(CCmString&, CCmIsSpace) 0000000000156534 W void RTrimString<CCmIsSpace>(CCmString&, CCmIsSpace) 000000000013fb10 T trim_string(char*) 00000000001d04bc T crc32_4bytes(void const*, unsigned long, unsigned int) 00000000001d05fc T crc32_8bytes(void const*, unsigned long, unsigned int) 0000000000141664 T get_local_ip() 00000000001b0e08 T ipv4_enabled() 00000000001b0db8 T ipv6_enabled() 0000000000141308 T resolve_2_ip(char*, char*) 00000000001d09ec T crc32_16bytes(void const*, unsigned long, unsigned int) 00000000001d00d4 T crc32_bitwise(void const*, unsigned long, unsigned int) 00000000001d0fc4 T crc32_combine(unsigned int, unsigned int, unsigned long)
排查与解决方法
检查
CMAKE_CXX_VISIBILITY_PRESET生效时机CMAKE_CXX_VISIBILITY_PRESET必须在add_subdirectory或add_library/add_executable之前设置才会对目标生效。当前配置中虽已提前设置,但需确认build/cmake/cmake-definition.cmake是否覆盖了该变量,若有覆盖会导致原配置失效。修正选项位置错误
target_compile_options中不应包含-Wl,--exclude-libs,ALL和-Wl,-Bsymbolic这类链接选项,需将其移至target_link_options,仅保留编译相关选项:target_compile_options(multp PRIVATE "-fvisibility=hidden") target_link_options(multp PRIVATE "-Wl,--exclude-libs,ALL" "-Wl,-Bsymbolic")补充C++符号隐藏辅助配置
对于C++代码,需额外启用内联函数符号隐藏:set(CMAKE_VISIBILITY_INLINES_HIDDEN ON)该配置确保内联函数的符号不会暴露到最终SO中。
验证编译选项是否实际传递
执行编译命令时添加VERBOSE=1,查看实际编译参数中是否包含-fvisibility=hidden:cmake --build ./cmake-android -- VERBOSE=1若未找到该选项,说明CMake配置被其他逻辑覆盖,需检查目标的编译选项优先级。
处理模板函数符号
nm输出中的TrimString<CCmIsSpace>等模板符号(标记为W),需添加额外编译选项处理:target_compile_options(multp PRIVATE "-fvisibility-inlines-hidden")或在模板声明前手动添加
__attribute__((visibility("hidden"))),强制隐藏模板实例化符号。排查第三方依赖符号传递
链接的gmssl、openssl等静态库若未启用符号隐藏,会将自身符号暴露到最终SO中。可尝试给第三方库也添加符号隐藏配置,或确认-Wl,--exclude-libs,ALL是否生效,彻底排除静态库的符号。
内容的提问来源于stack exchange,提问作者qian

