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