编写C / C ++代码时,为了调试二进制可执行文件,必须在编译器/链接器上启用调试选项。在GCC的情况下,选项是-g。启用调试选项后,如何影响二进制可执行文件?文件中存储了哪些允许调试器功能的附加数据呢?
答案 0 :(得分:60)
-g告诉编译器将符号表信息存储在可执行文件中。除其他外,这包括:
调试器使用此信息为符号输出有意义的名称,并将指令与源中的特定行相关联。
对于某些编译器,提供-g将禁用某些优化。例如,除非明确指出-O [123],否则icc会使用-g将默认优化级别设置为-O0。此外,即使您提供-O [123],仍会禁用阻止堆栈跟踪的优化(例如,从堆栈帧中删除帧指针。这对性能只有很小的影响)。
对于某些编译器,-g将禁用可能会混淆符号来源的优化(指令重新排序,循环展开,内联等)。如果您想通过优化进行调试,可以使用-g3和gcc来解决其中的一些问题。将包含有关可能已内联的宏,扩展和函数的额外调试信息。这可以允许调试器和性能工具将优化的代码映射到原始源,但这是最好的努力。一些优化确实会破坏代码。
有关详细信息,请查看DWARF,这是最初设计为与ELF(Linux和其他操作系统的二进制格式)配合使用的调试格式。
答案 1 :(得分:8)
将符号表添加到可执行文件中,该函数/变量名称映射到数据位置,以便调试器可以报告有意义的信息,而不仅仅是指针。这不会影响程序的速度,您可以使用'strip'命令删除符号表。
答案 2 :(得分:8)
除了调试和符号信息
谷歌DWARF(开发者在ELF上开玩笑)
默认情况下,启用调试时,大多数编译器优化都会关闭 所以代码是源代码到机器代码的纯粹翻译,而不是应用于发布二进制文件的许多高度专业化转换的结果。
但最重要的区别(在我看来)
调试版本中的内存通常初始化为某些编译器特定值,以便于调试。在发布版本中,除非应用程序代码明确地执行,否则不会初始化内存。
查看编译器文档以获取更多信息:
但DevStudio的一个例子是:
- 0xCDCDCDCD在堆中分配,但未初始化
- 0xDDDDDDDD释放堆内存。
- 0xFDFDFDFD“NoMansLand”围栏自动放置在堆内存的边界。永远不应该被覆盖。如果你覆盖了一个,你可能会走出数组的末尾。
- 0xCCCCCCCC在堆栈上分配,但未初始化
答案 3 :(得分:7)
-g在可执行文件中添加调试信息,例如变量名称,函数名称和行号。这允许调试器(例如gdb)逐行遍历代码,设置断点并检查变量的值。由于此附加信息,使用-g会增加可执行文件的大小。
此外,gcc允许将-g与-O标志一起使用,从而启用优化。调试优化的可执行文件可能非常棘手,因为可以优化变量,或者可以以不同的顺序执行指令。通常,在使用-g时关闭优化是个好主意,即使它会导致代码速度变慢。
答案 4 :(得分:3)
这个question有一些重叠,它涵盖了另一方的问题。
答案 5 :(得分:3)
正如您感兴趣的那样,您可以打开一个hexeditor并查看使用-g
生成的可执行文件和一个没有的可执行文件。您可以看到添加的符号和内容。它也可能会更改程序集(-S
),但我不确定。
答案 6 :(得分:3)
某些操作系统(如z/OS)会生成包含调试符号的“side file”。这有助于避免使用额外信息使可执行文件膨胀。