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

Gcc 调试选项-g如何更改二进制可执行文件?,gcc,gdb,debugging,Gcc,Gdb,Debugging,编写C/C++代码时,为了调试二进制可执行文件,必须在编译器/链接器上启用调试选项。对于GCC,选项为-g。启用调试选项后,如何影响二进制可执行文件?文件中存储了哪些额外的数据以允许调试器执行其功能?-g告诉编译器将符号表信息存储在可执行文件中。其中包括: 符号名 符号的类型信息 符号来源的文件和行号 调试器使用此信息为符号输出有意义的名称,并将指令与源中的特定行关联 对于某些编译器,提供-g将禁用某些优化。例如,icc使用-g将默认优化级别设置为-O0,除非您明确指示-O[123]。此外,

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

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

  • 符号名
  • 符号的类型信息
  • 符号来源的文件和行号
调试器使用此信息为符号输出有意义的名称,并将指令与源中的特定行关联

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

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


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

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

这与另一方面的问题有一些重叠。

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

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


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

除了调试和符号信息之外
谷歌矮人(关于ELF的开发者笑话)

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

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

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

  • 0xCDCDCD已在堆中分配,但未初始化
  • 0xDDDD释放了堆内存
  • 0xFDFD“NoMansLand”围栏自动放置在堆内存的边界处。不应该被覆盖。如果您确实覆盖了一个,那么可能是从数组的末尾走出来的
  • 0xCCCC已在堆栈上分配,但未初始化

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

除此之外,它还可以降低可执行文件的速度。我用Sun Studio编译器测试了一些OpenMP代码,通过调试信息,代码运行速度要慢得多。只是要记住一点。除非Sun编译器中的-g标志禁用某些优化,否则调试信息不应降低代码的速度。这是OpenMP代码,它确实降低了代码的速度。我在玩分形,并在使用OpenMP编译器扩展。单线程上的代码运行速度比单线程上的非OpenMP代码慢。我禁用了调试,速度均衡。注意。这其实很有趣。也许它在那里放了额外的东西来告诉调试器关于并行区域的信息。。。他们在这里说()可以将主线程映射回源代码,但不能映射到从属线程,这似乎也很奇怪。我认为是这样的,当然不会影响GCC,当单线程代码从11秒变为22秒时,确实让我感到惊讶。:/在禁用调试和4个线程(我有一个Q6600)的情况下,它下降到大约3秒。