踩坑实录:GCC 7.3.0 开启PGO编译优化竟然缺gcov符号?最终解决方案在这里!

151 阅读2分钟

一、背景

在 C/C++ 性能优化实践中,PGO(Profile-Guided Optimization) 是一项非常有效的编译优化技术。其基本流程是分为两个阶段:

  1. 生成阶段(profile-generate) 编译器在代码中插入额外的计数逻辑(例如函数调用次数、分支命中次数等)。

  2. 使用阶段(profile-use) 将运行时采集到的 profile 数据反馈给编译器,指导优化决策(函数内联、分支预测、代码布局等)。

理论上,这个流程对开发者应当是透明的,但在 麒麟 V10 SP2(gcc 7.3.0 默认编译器) 上,PGO 可能遇到 缺少 gcov 符号 的报错,导致无法完成编译/链接。

二、问题现象

在 profile-generate 阶段编译成功,但在运行阶段时,出现类似报错:

undefined reference to `__gcov_indirect_call_counter'

即便显式指定了-lgcov,依然无法解决问题。使用 nm libgcov.a 可以确认 __gcov_indirect_call_counter 符号确实存在,但最终生成的可执行文件中仍然符号状态是 U(undefined)。

三、问题原因

GCC 7.x 系列在 PGO/-fprofile-generate 的实现中存在一定问题:

  • 在 -fprofile-generate 下,编译器会插入间接调用统计(indirect call profiling)相关的符号,例如 __gcov_indirect_call_counter。按照预期,这些符号定义应来自 libgcov.a,由链接器自动拉入。但在 GCC 7.3.0 的实现中,部分对象文件未被正确解析进最终链接结果,即便显式写上 -lgcov 也不生效。

这导致了 “符号存在 → 链接器没拉进来 → 最终二进制缺失” 的情况。

四、解决方法:强制整个 libgcov 链入

关键点在于:不能只依赖链接器按需解析,而是要 把 libgcov.a 整个库强制打进最终可执行文件。
在 GNU ld 或 gold 链接器下,可以使用 --whole-archive 参数:

g++ -o myprog myprog.o
-Wl,--whole-archive /usr/lib/gcc/x86_64-linux-gnu/7.3.0/libgcov.a -Wl,--no-whole-archive

这样,libgcov.a 中所有对象文件都会被整体打包进最终二进制,从而确保 __gcov_indirect_call_counter 等符号得到正确定义。

五、总结

  • 现象:PGO 阶段缺少 __gcov_indirect_call_counter 符号,即使指定 -lgcov 仍无效。
  • 原因:GCC 7.3.0 在 profile-generate 下插入的 gcov 符号,并未被正常解析进最终可执行文件。
  • 解决:使用 --whole-archive 参数强制整个 libgcov.a 链入,确保符号完整性。

在 GCC 8.x 及之后版本,这一问题已不再出现。建议及时关注操作系统发行版的软件更新,若后续提供了 GCC 的修订或升级包,建议更新至发行版支持的最新稳定版本,以获得更完善的 PGO 支持。

📬 欢迎关注公众号“Hankin-Liu的技术研究室”,收徒传道。持续分享信创、软件性能测试、调优、编程技巧、软件调试技巧相关内容,输出有价值、有沉淀的技术干货。