调试选项 -g 如何更改二进制可执行文件?

Posted

技术标签:

【中文标题】调试选项 -g 如何更改二进制可执行文件?【英文标题】:How Does The Debugging Option -g Change the Binary Executable? 【发布时间】:2010-09-10 12:23:03 【问题描述】:

在编写 C/C++ 代码时,为了调试二进制可执行文件,必须在编译器/链接器上启用调试选项。对于 GCC,选项是 -g。启用调试选项后,对二进制可执行文件有何影响?文件中存储了哪些额外数据以允许调试器正常运行?

【问题讨论】:

【参考方案1】:

-g 告诉编译器将符号表信息存储在可执行文件中。其中包括:

符号名称 输入符号信息 符号来自的文件和行号

调试器使用此信息为符号输出有意义的名称并将指令与源代码中的特定行相关联。

对于某些编译器,提供 -g 将禁用某些优化。例如,icc 使用 -g 将默认优化级别设置为 -O0,除非您明确指定 -O[123]。此外,即使您提供 -O[123],阻止堆栈跟踪的优化仍将被禁用(例如,从堆栈帧中剥离帧指针。这对性能只有很小的影响)。

对于某些编译器,-g 将禁用可能混淆符号来源的优化(指令重新排序、循环展开、内联等)。如果您想通过优化进行调试,可以使用 -g3 和 gcc 来解决其中的一些问题。将包含有关可能已内联的宏、扩展和函数的额外调试信息。这可以允许调试器和性能工具将优化的代码映射到原始源,但这是最好的努力。一些优化确实会破坏代码。

有关更多信息,请查看 DWARF,这是最初设计用于与 ELF(Linux 和其他操作系统的二进制格式)一起使用的调试格式。

【讨论】:

只是补充一点,它还可以减慢可执行文件的速度。我正在使用 Sun Studio 编译器测试一些 OpenMP 代码,并且使用调试信息,代码运行速度要慢得多。只是要记住的事情。 除非 Sun 编译器中的 -g 标志禁用某些优化,否则调试信息不​​应降低您的代码速度。 这是 OpenMP 代码,它确实减慢了速度。我在玩分形,并致力于使用 OpenMP 编译器扩展。单线程上的代码比单线程上的非 OpenMP 代码运行得慢。我禁用了调试并且速度均衡了。 注明。这其实有点意思。也许它在那里添加了额外的东西来告诉调试器并行区域......他们在这里(docs.sun.com/source/819-3683/OpenMP.html)说你可以将主线程映射回源而不是从属线程,这似乎也很奇怪。 我认为是这样,当然不会影响 GCC,当单线程代码从 11 秒变为 22 秒时,当然给我一个惊喜。:/ 禁用调试和 4 个线程(我有一个Q6600)它下降到大约 3 秒。【参考方案2】:

将符号表添加到将函数/变量名称映射到数据位置的可执行文件中,以便调试器可以报告有意义的信息,而不仅仅是指针。这不会影响程序的速度,您可以使用“strip”命令删除符号表。

【讨论】:

【参考方案3】:

除了调试和符号信息 Google DWARF(ELF 上的开发者笑话)

默认情况下,启用调试时,大多数编译器优化都会关闭。 因此,代码是源代码到机器代码的纯粹翻译,而不是许多应用于发布二进制文件的高度专业化转换的结果。

但最重要的区别(在我看来) Debug 版本中的内存通常初始化为一些编译器特定的值以方便调试。在发布版本中,除非应用程序代码明确这样做,否则不会初始化内存。

查看您的编译器文档以获取更多信息: 但是 DevStudio 的一个例子是:

0xCDCDCDCD 在堆中分配,但未初始化 0xDDDDDDDD 已释放堆内存。 0xFDFDFDFD “NoMansLand”栅栏自动放置在堆内存的边界。永远不应该被覆盖。如果您确实覆盖了一个,您可能会离开数组的末尾。 0xCCCCCCCC 在堆栈上分配,但未初始化

【讨论】:

【参考方案4】:

-g 在可执行文件中添加调试信息,例如变量名、函数名和行号。这允许调试器(例如 gdb)逐行执行代码、设置断点并检查变量的值。由于这些附加信息,使用 -g 会增加可执行文件的大小。

此外,gcc 允许将 -g 与 -O 标志一起使用,从而开启优化。调试优化的可执行文件可能非常棘手,因为变量可能会被优化掉,或者指令可能会以不同的顺序执行。通常,使用 -g 时最好关闭优​​化,即使它会导致代码慢得多。

【讨论】:

【参考方案5】:

出于兴趣,您可以打开十六进制编辑器并查看使用-g 生成的可执行文件,而没有生成的可执行文件。您可以看到添加的符号和内容。它也可能会更改程序集 (-S),但我不确定。

【讨论】:

【参考方案6】:

这个question 有一些重叠,它从另一端涵盖了这个问题。

【讨论】:

【参考方案7】:

某些操作系统(如z/OS)会生成一个包含调试符号的“边文件”。这有助于避免使用额外信息使可执行文件变得臃肿。

【讨论】:

以上是关于调试选项 -g 如何更改二进制可执行文件?的主要内容,如果未能解决你的问题,请参考以下文章

使用无控制台和无调试选项时,可执行文件上的 Pyinstaller 编解码器错误

如何指定输出可执行文件的名称?

创建独立的 Lua 可执行文件

OSX gobjdump 损坏可执行文件

如何将参数传递给 nodemon(或 node-supervisor)中的可执行文件?

使用 gdb 修改二进制文件