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

标记为extern的全局变量重复问题排查与解决咨询

问题背景

我在链接共享库时,该库声明了如下全局变量:

__attribute__((visibility("hidden"))) extern HookList<MallocHook::DeleteHook> delete_hooks_;

该变量的声明位于src/malloc_hook-inl.h:122,定义位于src/malloc_hook.cc:240。

尽管变量被声明为extern,却出现了重复实例。单次执行中,调试main.cpp第7、8行时,LLDB显示delete_hooks_来自不同地址空间:

栈追踪1(主二进制程序Example的地址空间)

this == 0x5574f1215968

base::internal::HookList<void (*)(const void *)>::Add(void (*)(const void *)) malloc_hook.cc:155
::MallocHook_AddDeleteHook(MallocHook_DeleteHook) malloc_hook.cc:265
MallocHook::AddDeleteHook(void (*)(const void *)) malloc_hook.h:115
::HeapProfilerStart(const char *) heap-profiler.cc:454
main main.cpp:7

栈追踪2(libtcmalloc.so的地址空间)

this == 0x7efe998baa28

base::internal::HookList<void (*)(const void *)>::empty() const malloc_hook-inl.h:88
[Inlined] free_fast_path(void *) tcmalloc.cc:1944
::tc_free(void *) tcmalloc.cc:1953
main main.cpp:8

我的问题
  1. 为什么该变量会重复?extern关键字不是应该防止这种情况吗?
  2. 如何避免这种重复?
  3. 我是否误用了该库?我认为已严格遵循官方文档指引。

相关环境与代码信息
  • 系统:Ubuntu 20.04,LLVM工具链19.0.0
  • CMake编译选项:-DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=/usr/bin/clang-19 -DCMAKE_CXX_COMPILER=/usr/bin/clang++-19
  • gperftools版本:2.15(源码来自gperftools仓库)

CMakeLists.txt

# CMakeLists.txt

cmake_minimum_required(VERSION 3.28) # likely compatible with an older version too

project(Example LANGUAGES CXX)

add_executable(Example main.cpp)

set(gperftools_build_benchmark OFF CACHE BOOL "Build gperftools benchmark" FORCE)
set(PATH_TO_GPERFTOOLS "${CMAKE_SOURCE_DIR}/gperftools")
add_subdirectory(${PATH_TO_GPERFTOOLS})
target_link_libraries(Example tcmalloc)
target_include_directories(Example PRIVATE ${PATH_TO_GPERFTOOLS}/src)

main.cpp

// main.cpp

#include <cstdlib>
#include "gperftools/heap-profiler.h"

int main() {
    HeapProfilerStart("/tmp/example");  // stacktrace #1
    std::free(nullptr);                 // stacktrace #2
    HeapProfilerStop();
    return 0;
}

解答

1. 变量重复的原因

核心问题是**visibility("hidden")属性加上编译链接方式导致的符号隔离**:

  • extern仅声明变量定义在别处,但__attribute__((visibility("hidden")))会让该符号仅在当前编译单元所在的二进制(主程序或共享库)内可见,无法跨二进制共享。
  • 你直接把gperftools源码作为子目录编译时,主程序Example包含malloc_hook-inl.h后,会因hidden可见性生成该变量的本地实例;而libtcmalloc.so编译时同样处理这个头文件,生成另一个独立实例。
  • 主程序调用HeapProfilerStart时操作的是自身的delete_hooks_实例,std::free调用tcmalloc代码时则操作共享库内的实例,因此出现两个不同地址。

2. 避免重复的方法

有两种可行思路:

方法一:使用系统预编译的gperftools库

通过包管理器安装gperftools(如sudo apt install libgoogle-perftools-dev),然后在CMake中链接系统库:

find_package(gperftools REQUIRED)
target_link_libraries(Example gperftools::tcmalloc)

这种方式下主程序只会链接预编译共享库,不会在本地生成符号实例。

方法二:修改编译配置,确保符号跨二进制可见

若必须从源码编译,需调整gperftools的可见性设置:

set(gperftools_build_benchmark OFF CACHE BOOL "Build gperftools benchmark" FORCE)
set(PATH_TO_GPERFTOOLS "${CMAKE_SOURCE_DIR}/gperftools")
# 全局设置符号可见性为default,覆盖hidden默认值
set(CMAKE_CXX_VISIBILITY_PRESET default)
set(CMAKE_C_VISIBILITY_PRESET default)
set(CMAKE_VISIBILITY_INLINES_HIDDEN OFF)
add_subdirectory(${PATH_TO_GPERFTOOLS})
target_link_libraries(Example tcmalloc)
target_include_directories(Example PRIVATE ${PATH_TO_GPERFTOOLS}/src)

这样delete_hooks_会变为全局可见,主程序和共享库将共享同一个实例。

3. 是否误用了库?

不算误用,但你的编译方式不符合gperftools的预期场景:

  • 官方文档默认用户链接预编译共享库,而非直接嵌入源码编译。直接嵌入源码会触发头文件内的hidden可见性规则,导致符号隔离问题。
  • 只要调整编译链接方式,按预期使用预编译库或修正可见性设置,即可正常使用。

内容的提问来源于stack exchange,提问作者Volodymyr Lashko

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 16:23:09