Centos下C++无法访问汇编声明的全局变量问题排查
环境与文件信息
系统环境:CentOS
C++文件:src/node_main.cc
#include <iostream> using namespace std; extern "C" { extern char __attribute__((weak)) global_variable; } // extern "C" int main() { printf("global_variable:%p", reinterpret_cast<uintptr_t>(&global_variable)); return 0; }
汇编文件:src/global_variable.S
.global global_variable global_variable:
GYP文件:node.gyp
{ 'targets': [ { 'target_name': 'global_variable', 'type': 'none', 'conditions': [ [ 'OS in "linux freebsd solaris"', { 'type': 'static_library', 'sources': [ 'src/global_variable.S' ] }], ] }, { 'target_name': 'myexperiment', 'type': 'executable', 'sources': [ 'src/node_main.cc' ], 'dependencies': [ 'global_variable' ], 'conditions': [ [ 'OS in "linux freebsd"', { 'dependencies': [ 'global_variable' ], # 'ldflags+': [ # '<(PRODUCT_DIR)/obj.target/global_variable/src/global_variable.o' # ] }] ] } ] }
构建命令
python3 main.py make
问题现象
注释掉ldflags+配置后,运行./out/Default/myexperiment会打印global_variable:(nil),无法访问global_variable;
启用ldflags配置时,程序正常打印global_variable:0x4006e6,能正确访问变量。
通过make -n查看编译命令,发现两种情况的唯一差异是是否直接添加global_variable.o目标文件:
- 启用
ldflags+时的命令:
g++ -o out/Default/myexperiment out/Default/obj.target/global_variable/src/global_variable.o -Wl,--start-group out/Default/obj.target/myexperiment/src/node_main.o out/Default/obj.target/libglobal_variable.a -Wl,--end-group
- 未启用时的命令:
g++ -o out/Default/myexperiment -Wl,--start-group out/Default/obj.target/myexperiment/src/node_main.o out/Default/obj.target/libglobal_variable.a -Wl,--end-group
明明两种情况都引入了libglobal_variable.a静态库,但只有直接加目标文件时才能访问变量,对此存在困惑。
原因与解决方案
核心原因
GCC链接器处理静态库时,只会把被当前已链接目标引用到的符号对应的目标文件从静态库中提取出来链接。这里的global_variable被标记为__attribute__((weak)),意味着如果链接器找不到这个符号的定义,会自动将其设为NULL,而不会报错。
在未启用ldflags+的情况下,链接器先处理node_main.o,里面对global_variable的引用是弱引用,链接器此时认为就算找不到定义也没关系,后续处理libglobal_variable.a时,因为没有强引用需要该符号,所以不会把global_variable.o从静态库中提取出来链接,最终程序里的global_variable就是NULL。
而直接添加global_variable.o时,该目标文件会被强制链接,里面的global_variable定义会被纳入程序,弱引用就能找到对应的符号地址。
解决方案
有几种方式可以解决这个问题:
- 去掉弱引用标记:把
extern char __attribute__((weak)) global_variable;中的__attribute__((weak))删掉,这样链接器会强制要求找到global_variable的定义,处理静态库时就会自动提取对应的global_variable.o。 - 强制链接静态库中的所有目标文件:在链接选项中添加
-Wl,--whole-archive libglobal_variable.a -Wl,--no-whole-archive,告诉链接器把静态库中的所有目标文件都链接进来,不管有没有被引用。 - 保持当前的
ldflags+配置:直接显式添加目标文件,强制链接该定义。
内容的提问来源于stack exchange,提问作者jasonjifly

