81

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

4

7 回答 7

79

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

  • 符号名称
  • 符号的类型信息
  • 符号来自的文件和行号

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

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

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

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

于 2008-09-18T02:57:56.847 回答
10

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

于 2008-09-18T03:00:41.897 回答
8

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

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

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

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

  • 0xCDCDCDCD 在堆中分配,但未初始化
  • 0xDDDDDDDD 释放堆内存。
  • 0xFDFDFDFD "NoMansLand" 栅栏自动放置在堆内存的边界。永远不应该被覆盖。如果您确实覆盖了一个,那么您可能会离开数组的末尾。
  • 0xCCCCCCCC 在堆栈上分配,但未初始化
于 2008-09-18T03:12:16.733 回答
7

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

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

于 2008-09-18T03:10:55.427 回答
4

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

于 2008-09-18T03:08:24.577 回答
3

这个问题有一些重叠,涵盖了另一方的问题。

于 2008-09-18T03:02:50.890 回答
3

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

于 2008-09-18T03:23:21.607 回答