为什么我用 -LTO 构建的 C++ 二进制文件如此之大?

Yan*_* TM 6 c++ macos compilation lto

我正在 Mac 上编译一些二进制文件,但是使用更新的编译器编译后的大小变得很大(从之前的 ~5MB 增加到 ~20MB)。我认为这与之前未激活的 LTO(链接时间优化)有关。我没有在 linux 上观察到这个文件膨胀。

在玩弄之后strip(实际上没有减少大小,尽管尝试基于 Xcode 的标志-S -x并且也没有标志,以及 GNU libtools strip 由 homebrew binutils 配方提供的标志-s,所有这些似乎都具有相同的效果)我找到了这个工具:https ://github.com/google/bloaty Bloaty McBloated,在我的二进制文件上运行时,它会产生以下输出:

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  53.9%  9.72Mi  53.8%  9.72Mi    __GNU_LTO,__wrapper_sects
  32.5%  5.86Mi  32.4%  5.86Mi    __GNU_DWARF_LTO,__debug_info
   6.2%  1.11Mi   6.2%  1.11Mi    __TEXT,__text
   2.2%   403Ki   2.2%   403Ki    __TEXT,__eh_frame
   1.6%   298Ki   1.6%   298Ki    __GNU_LTO,__wrapper_names
   1.0%   177Ki   1.0%   177Ki    Export Info
   0.7%   131Ki   0.7%   131Ki    Weak Binding Info
   0.4%  77.0Ki   0.4%  77.0Ki    __GNU_DWARF_LTO,__debug_str
   0.4%  75.8Ki   0.4%  75.8Ki    __DATA,__gcc_except_tab
   0.2%  44.6Ki   0.2%  44.6Ki    __GNU_LTO,__wrapper_index
   0.2%  39.4Ki   0.2%  39.4Ki    __DATA_CONST,__const
   0.2%  33.1Ki   0.2%  33.1Ki    __GNU_DWARF_LTO,__debug_abbrev
   0.1%  26.4Ki   0.1%  26.4Ki    __GNU_DWARF_LTO,__debug_line
   0.1%  21.7Ki   0.1%  23.6Ki    [20 Others]
   0.1%  19.0Ki   0.1%  19.0Ki    __TEXT,__text_cold
   0.1%  18.1Ki   0.1%  18.1Ki    __TEXT,__const
   0.0%  8.82Ki   0.0%  8.82Ki    __TEXT,__text_startup
   0.0%  8.60Ki   0.0%  8.60Ki    __TEXT,__cstring
   0.0%       0   0.0%  7.18Ki    __DATA,__pu_bss5
   0.0%       0   0.0%  6.88Ki    __DATA,__bss5
   0.0%  5.87Ki   0.0%  5.87Ki    __DATA,__la_symbol_ptr
 100.0%  18.1Mi 100.0%  18.1Mi    TOTAL
Run Code Online (Sandbox Code Playgroud)

那么谁能告诉我这些巨大的*_LTO部分是做什么用的,以及如何通过后处理或向我的构建链添加编译标志来摆脱它们。

操作系统是 MacOS,我使用的是 g++ 10,完整的跟踪在这里:https : //github.com/yanntm/testGithbuActions/runs/1778387086?check_suite_focus=true

我正在尝试尽可能多地编译静态以获得更好的可移植性。然而,二进制文件仍然动态链接到 /usr/lib/libSystem.B.dylib(我显然不能用 libtool 静态链接这个)。

我不想要任何调试符号,因为这是面向最终用户的生产二进制文件。

Sam*_*hik 7

您将在 gcc 的文档中找到答案:

\n
\n

链接时间优化是作为 GCC 前端实现的,用于 GIMPLE 的字节码表示,该表示在 .o 文件的特殊部分中发出。

\n

[...]

\n

由于 GIMPLE 字节码与最终目标代码一起保存,因此使用 LTO 支持生成的目标文件比常规目标文件更大。

\n

[...]

\n

当前的实现仅生成 \xe2\x80\x9cfat\xe2\x80\x9d 对象,有效地\n使编译时间加倍,并将文件大小增加到原始大小的 5 倍。

\n
\n

但是等等,还有更多。您仅使用-flto. 你是否也用过-ffat-lto-objects,那么,如 gcc 的信息页面中所述:

\n
\n

\'-ffat-lto-objects\'

\n

胖 LTO 对象是包含中间语言和目标代码的目标文件。这使得它们可用于 LTO\n链接和正常链接。此选项仅在使用 \'-flto\' 进行编译时才有效,并在链接时被忽略。

\n
\n

尝试使用strip将是徒劳的。strip仅删除调试数据。这不是调试数据,而是基本上半编译的 C++ 代码,最终编译作为链接周期的一部分发生。如果您想“摆脱它们”,请不要使用 LTO。

\n

编辑:某些 gcc/binutils 配置可能会在目标二进制文件中留下 LTO 部分。我研究了 Fedora 的默认 rpmbuild 配置,它默认使用 LTO 构建,但不会遭受相同的可执行文件膨胀。

\n

事实证明,Fedora 的 rpmbuild 执行了一个brp-strip-lto脚本可归结为:

\n
sh -c "$STRIP -p -R .gnu.lto_* -R .gnu.debuglto_* -N __gnu_lto_v1 \\"\\$@\\"" ARG0\n
Run Code Online (Sandbox Code Playgroud)\n

关键选项是两个-R选项,不清楚符号__gnu_lto_v1是什么,被删除-N

\n