删除 DWARF-2 重复符号会耗尽内存

Removing DWARF-2 duplicate symbols exhausts memory

我正在处理一个被编译为共享对象的大型项目。使用 DWARF-2 符号 (-g -feliminate-unused-debug-types) 编译它会导致 .debug_info 大约为 700M。

如果我添加 -feliminate-dwarf2-dups,linker 死亡:

error adding symbols: Memory exhausted
ld returned 1 exit status

这是在具有 4G RAM 的系统上。由于这需要在广泛的系统上编译,消耗超过 4G RAM 是不可接受的。我尝试将 --no-keep-memory 传递给 ld,但它仍然失败。

ld normally optimizes for speed over memory usage by caching the symbol tables of input files in memory. This option tells ld to instead optimize for memory usage, by rereading the symbol tables as necessary. This may be required if ld runs out of memory space while linking a large executable.

我猜 ld 将所有符号加载到内存中,然后开始查找重复项,这需要 5 倍以上的内存才能将它们存储在磁盘上。

有没有一种简单的方法可以逐步做到这一点?喜欢:

  1. 从第一个 .o 文件加载符号
  2. 从下一个 .o 文件加载符号
  3. 合并它们,删除所有重复项
  4. 转到 2.

我可以 link 两个两个地归档到临时档案中,然后 link 两个两个地归档,等等,但我 真的 不想更改此项目的构建过程。也许我可以使用 objcopy 删除这些段,单独执行重复消除,然后将调试部分插入最终的 ELF?

是否有任何其他工具可以执行这些 DWARF 合并? dwarfdump 只读取文件。或者,我可以调用 gcc/ld 来执行此操作,而不是实际 linking 文件吗?

我知道有两种方法可以减小 DWARF 的大小。你想要哪一个取决于你的预期目的。

Fedora(也许还有其他发行版,我不知道)使用 dwz 工具来压缩 DWARF。这在事后有效:您 link 您的程序或共享库,然后 运行 dwz。它是一个 "semantic" 压缩器,这意味着它理解 DWARF 并将其重写为更小的形式。在 DWARF 术语中,它制作部分 CU 并以这种方式共享数据。 dwz 还有一种模式,它可以跨不同的可执行文件压缩数据以实现更多共享。

dwz 产生最佳压缩。主要的缺点是它不太适合开发人员的工作流程——它有点慢,使用大量内存等。不过它非常适合发行版,我认为它也适用于其他一些部署情况。

另一种压缩调试信息的好方法是对 gcc 使用 -fdebug-types-section 标志。这会更改 DWARF 输出以将大型类型放入它们自己的部分中。类型按其内容散列;然后 linker 会自动合并这些部分。

这种方法产生了不错的压缩效果,因为类型是 DWARF 的重要组成部分;具有不错的性能,因为在 linker 中以这种方式合并相同的部分很便宜。主要缺点是压缩效果不佳。

gdb 理解这两种压缩。对其他工具的支持参差不齐。

除了 Tom Tromey 的建议之外,您还可以做的另一件事是构建交叉编译器,例如x86_64 系统,针对您的各种系统。

这完全消除了 linker 内存的 4GB 限制,并且可能会更快(您的各种小型系统可能有弱 CPU,因此在它们上本地构建可能需要几个小时交叉编译时他们在强大的开发机器上只需要几分钟。

或者您可以使用 -fsplit-dwarf —— 无需 link 调试位到您的最终共享库中;你可以把它们分开。

更新:

We already do use crosscompilers. The problem is that not all x86_64 systems we use for build machines are powerful enough to optimize this.

在那种情况下,您没有 hard 4GB 要求,您声称这是不可接受的是假的。您可以获得 64GB 的 RAM,大约 500 美元,并加快一切速度。

developers may want to run the build on their primary system

如果您的开发人员的主系统小于 16GB,那您就是在浪费他们的时间。

Regarding -fsplit-dwarf, in cases where it is built remotely I'd still need to download the symbols as well, so there is little to gain

好处是您可以构建您的二进制文件。

好吧,另一件事是使用 "gold" linker,构建大型应用程序可能是 faster/better、link:https://en.wikipedia.org/wiki/Gold_(linker)

此外,gold linker 和拆分调试信息对我来说效果很好。

“Fission 在 GCC 4.7 中实现,需要最新版本的 objcopy 和 gold linker 的支持。

使用 -gsplit-dwarf 选项在编译时启用拆分 DWARF 的生成。该选项必须与-c一起使用;编译和linking在同一步骤时不能使用Fission

在 link 时间使用 gold linker 的 --gdb-index 选项(-Wl,--gdb-index 当 link 使用 gcc 或 g++ 时)创建.gdb_index 部分允许 GDB 在需要时定位和读取 .dwo 文件。"