Linux下Buck的link_whole未添加全部符号的原因及作用咨询
问题背景
测试库文件
头文件 ObjectA.h
#pragma once namespace sarora::testing { void testingObject(); void againTesting(); } // namespace sarora::testing
Cpp文件 ObjectA.cpp
#include "ObjectA.h" #include <iostream> namespace sarora::testing { void testingObject() { std::cout << "testingObject" << std::endl; } void againTesting() { std::cout << "againTesting" << std::endl; } } // namespace sarora::testing
Buck构建规则
库的BUCK规则
cpp_library( name = "object", srcs = ["ObjectA.cpp"], headers = ["ObjectA.h"], link_whole = True, )
主程序文件 main.cpp
#include "ObjectA.h" #include <iostream> using namespace std; int main(int argc, char* argv[]) { sarora::testing::testingObject(); }
主程序的BUCK规则
cpp_binary( name = "test", srcs = ["test.cpp"], deps = [ ":object", ], )
现象
主程序仅调用了testingObject(),执行nm main_executable_path | grep testingObject能查到对应符号,但nm main_executable_path | grep againTesting却找不到againTesting的符号。
解答
1. Buck中link_whole参数的作用
link_whole是Buck给C++库配置的链接控制选项,本质是传递给底层链接器(比如GCC的ld、Clang的ld.lld)一个指令:不要对这个库做未使用符号的剔除。
默认情况下,链接器会做“死代码消除”优化:只把被直接/间接引用到的符号(函数、全局变量等)打包进最终可执行文件,未被引用的符号会被丢弃,以此减小可执行文件体积。而当link_whole = True时,链接器会把该库的所有内容完整链接,不管其中的符号有没有被上层代码引用——但这个逻辑主要针对**静态库(.a文件)**生效。
2. 为什么会出现当前现象
你遇到的情况看起来有点反直觉,但核心原因在于目标文件的编译粒度:
- 你的
cpp_library只有一个源文件ObjectA.cpp,编译后生成的是单个目标文件(ObjectA.o),而不是由多个.o归档而成的静态库(.a)。 link_whole参数的设计初衷是解决静态库的链接问题:默认链接静态库时,链接器只会提取其中被引用的.o文件;而link_whole=True会强制链接器把静态库里所有的.o都包含进去。但对于单个.o文件,这个参数不起作用——链接器依然会进行符号级的死代码消除(比如通过-fdata-sections -ffunction-sections和-Wl,--gc-sections这类编译/链接选项)。- 因为
againTesting()没有被任何代码引用,所以链接器直接把这个函数的符号从最终可执行文件中剔除了,导致nm查不到它。
如果要验证这个逻辑,可以把ObjectA.cpp拆成两个源文件:比如ObjectA1.cpp只实现testingObject(),ObjectA2.cpp只实现againTesting(),然后修改cpp_library的srcs为这两个文件。此时object库会被打包成静态库,设置link_whole=True后,链接器会把两个.o都完整链接进去,这时用nm就能查到againTesting的符号了。
内容的提问来源于stack exchange,提问作者Shivanshu Arora
相关产品推荐
相关产品推荐

