it-swarm.cn

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

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

66
Jon Ball

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

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

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

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

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

有关更多信息,请查看 _ dwarf _ ,最初设计为与ELF(Linux和其他操作系统的二进制格式)配合使用的调试格式。

60
Todd Gamblin

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

8
Alexandra Franks

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

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

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

有关更多信息,请查看编译器文档:
但DevStudio的一个例子是:

  • 0xCDCDCDCD在堆中分配,但未初始化
  • 0xDDDDDDDD释放堆内存。
  • 0xFDFDFDFD“NoMansLand”围栏自动放置在堆内存的边界。永远不应该被覆盖。如果你覆盖了一个,你可能会走出数组的末尾。
  • 0xCCCCCCCC在堆栈上分配,但未初始化
8
Martin York

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

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

7
Dima

question 有一些重叠,它涵盖了另一方面的问题。

3
Rob Walker

正如您感兴趣的那样,您可以打开一个hexeditor并查看使用-g生成的可执行文件和一个没有的可执行文件。您可以看到添加的符号和内容。它也可能改变大会(-S),但我不确定。

3
Bernard

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

3
Anthony Giorgio