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

有帮助吗?

解决方案

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

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

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

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

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

欲了解更多信息,请查看 矮人, ,最初设计用于与 ELF(Linux 和其他操作系统的二进制格式)配合使用的调试格式。

其他提示

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

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

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

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

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

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

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

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

与此有一些重叠 问题 这从另一方面涵盖了这个问题。

出于兴趣,您可以打开十六进制编辑器并查看使用 -g 和一个没有。您可以看到添加的符号和内容。它可能会改变装配(-S)也是,但我不确定。

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top