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

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

How Does The Debugging Option -g Change the Binary Executable?

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


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

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

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

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

对于某些编译器,-g将禁用可能混淆符号来源的优化(指令重新排序,循环展开,内联等)。如果要通过优化进行调试,则可以在gcc中使用-g3来解决一些问题。有关宏,扩展和可能内联的功能的其他调试信息将包括在内。这可以使调试器和性能工具将优化的代码映射到原始源,但这是最大的努力。一些优化确实使代码混乱。

有关更多信息,请查看DWARF,它最初是与ELF一起使用的调试格式(Linux和其他OS的二进制格式)。


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


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

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

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

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

  • 0xCDCDCDCD Allocated in heap, but not initialized
  • 0xDDDDDDDD Released heap memory.
  • 0xFDFDFDFD "NoMansLand" fences automatically placed at boundary of heap memory. Should never be overwritten. If you do overwrite one, you're probably walking off the end of an array.
  • 0xCCCCCCCC Allocated on stack, but not initialized

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

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


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


只是出于兴趣,您可以打开一个hexeditor并查看使用-g生成的可执行文件和不使用-g生成的可执行文件。您可以看到符号和添加的内容。它也可能会更改程序集(-S),但是我不


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


推荐阅读