【问题标题】:gcc -g :what will happengcc -g : 会发生什么
【发布时间】:2011-07-07 22:38:58
【问题描述】:

这个问题是在一次采访中问我的。

他们问我如何生成可以调试的核心转储文件。 然后我说用gcc 中的-g 标志我们可以做到。

然后他们问我-g 标志对编译器有什么作用。

我说过(可能是一个错误的答案)它将打开核心文件中所有可用于调试的符号。

谁能告诉我它到底是做什么的?

【问题讨论】:

  • 可能值得注意的是,如果你想生成一个进程的核心转储,你可以将它的 PID 传递给 gcore 命令。

标签: c++ c gcc gdb


【解决方案1】:

-g 标志告诉编译器生成调试信息。它对是否生成核心文件没有影响。在大多数类 unix 系统上,可以使用 ulimit 命令进行设置。

【讨论】:

  • 但我想知道调试信息到底是什么。我知道你已经知道了。我需要一些超出此范围的东西。
  • @wvwvwv 除此之外,它很复杂——完整的描述需要几十页。您可以从查看 DWARF (dwarfstd.org/Download.php) 标准开始,它是嵌入在可执行文件中的调试符号的规范(在大多数 *nixes 上)。 -g 指示编译器/链接器生成这些结构并将其嵌入到可执行文件中。下一步可能是了解调试器如何使用这些调试信息。
  • 简而言之,它添加了有关符号名称(函数、局部和全局变量等)的信息以及有关类型、源文件名和行号的信息。它还可以通过自动定义的宏拉入不同的头文件(即,用于 STL,以及断言宏的定义)以简化调试。它可能还会为内联函数生成特殊输出,因此可以进入内联函数/方法。
【解决方案2】:

这有点对,但不完整。 -g 请求编译器和链接器在可执行文件本身中生成和保留源代码级调试/符号信息。

如果...

  • 程序碰巧后来崩溃并生成一个核心文件(这表明实际代码中存在一些问题),或者
  • 故意的操作系统命令强制它进入核心(例如kill -SIGQUITpid),或者
  • 程序调用转储核心的函数(例如abort

...- 实际上这些都不是由使用-g 引起的-然后调试器将知道如何从可执行文件中读取“-g”符号信息并与内核交叉引用它。这意味着您可以在堆栈帧中查看变量和函数的正确名称,获取行号并在您在可执行文件中四处走动时查看源代码。

调试信息在调试时很有用 - 无论您是从内核开始还是仅从可执行文件开始。它甚至有助于从pstack 等命令产生更好的输出。

请注意,您的环境可能有其他设置来控制是否生成内核(它们可能很大,并且没有一般的方法可以知道它们是否/何时可以删除,因此并不总是需要它们)。例如,在 UNIX/LINUX shell 上,它通常是 ulimit -c

您可能也有兴趣阅读 DWARF Wikipedia - 一种常用的调试信息格式,用于对可执行/库对象中的嵌入式调试/符号信息进行编码(例如,在 UNIX 和 Linux 上)。

根据 Victor 在 cmets 中的请求更新...

符号信息列出了源代码中的标识符(通常仅在需要任何 name mangling 之后)、它们将在进程内存中加载的(虚拟)内存地址/偏移量、类型(例如数据与代码)。比如……

$ cat ok.cc
int g_my_num;
namespace NS { int ns_my_num = 2; }
int f() { return g_my_num + NS::ns_my_num; }
int main() { return f(); }

$ g++ -g ok.cc -o ok    # compile ok executable with symbol info

$ nm ok    # show mangled identifiers
00000000004017c8 d _DYNAMIC
0000000000401960 d _GLOBAL_OFFSET_TABLE_
0000000000400478 R _IO_stdin_used
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
                 w _Jv_RegisterClasses
000000000040037c T _Z1fv                     # this is f()
0000000000401798 D _ZN2NS9ns_my_numE         # this is NS::ns_my_num
00000000004017a8 d __CTOR_END__
00000000004017a0 d __CTOR_LIST__
00000000004017b8 d __DTOR_END__
00000000004017b0 d __DTOR_LIST__
0000000000400540 r __FRAME_END__
00000000004017c0 d __JCR_END__
00000000004017c0 d __JCR_LIST__
00000000004017c8 d __TMC_END__
00000000004017c8 d __TMC_LIST__
0000000000401980 A __bss_start
0000000000401788 D __data_start
0000000000400440 t __do_global_ctors_aux
00000000004002e0 t __do_global_dtors_aux
0000000000401790 d __dso_handle
0000000000000000 a __fini_array_end
0000000000000000 a __fini_array_start
                 w __gmon_start__
0000000000000000 a __init_array_end
0000000000000000 a __init_array_start
00000000004003a0 T __libc_csu_fini
00000000004003b0 T __libc_csu_init
                 U __libc_start_main
0000000000000000 a __preinit_array_end
0000000000000000 a __preinit_array_start
0000000000401980 A _edata
0000000000401994 A _end
0000000000400494 T _fini
000000000040047c T _init
0000000000400220 T _start
000000000040024c t call_gmon_start
0000000000401980 b completed.6118
0000000000401788 W data_start
0000000000400270 t deregister_tm_clones
0000000000401988 b dtor_idx.6120
0000000000401994 A end
0000000000400350 t frame_dummy
0000000000401990 B g_my_num                   # our global g_my_num
0000000000400390 T main                       # the int main() function
00000000004002a0 t register_tm_clones

$ nm ok | c++filt            # c++filt "unmangles" identifiers...
00000000004017c8 d _DYNAMIC
0000000000401960 d _GLOBAL_OFFSET_TABLE_
0000000000400478 R _IO_stdin_used
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
                 w _Jv_RegisterClasses
000000000040037c T f()
0000000000401798 D NS::ns_my_num
00000000004017a8 d __CTOR_END__
00000000004017a0 d __CTOR_LIST__
00000000004017b8 d __DTOR_END__
00000000004017b0 d __DTOR_LIST__
0000000000400540 r __FRAME_END__
00000000004017c0 d __JCR_END__
00000000004017c0 d __JCR_LIST__
00000000004017c8 d __TMC_END__
00000000004017c8 d __TMC_LIST__
0000000000401980 A __bss_start
0000000000401788 D __data_start
0000000000400440 t __do_global_ctors_aux
00000000004002e0 t __do_global_dtors_aux
0000000000401790 d __dso_handle
0000000000000000 a __fini_array_end
0000000000000000 a __fini_array_start
                 w __gmon_start__
0000000000000000 a __init_array_end
0000000000000000 a __init_array_start
00000000004003a0 T __libc_csu_fini
00000000004003b0 T __libc_csu_init
                 U __libc_start_main
0000000000000000 a __preinit_array_end
0000000000000000 a __preinit_array_start
0000000000401980 A _edata
0000000000401994 A _end
0000000000400494 T _fini
000000000040047c T _init
0000000000400220 T _start
000000000040024c t call_gmon_start
0000000000401980 b completed.6118
0000000000401788 W data_start
0000000000400270 t deregister_tm_clones
0000000000401988 b dtor_idx.6120
0000000000401994 A end
0000000000400350 t frame_dummy
0000000000401990 B g_my_num
0000000000400390 T main
00000000004002a0 t register_tm_clones

注意事项:

  • 我们的函数f()main()T 类型(代表“TEXT” - 用于只读非零内存内容,无论它实际上是文本还是其他数据或可执行代码),
  • g_my_numB 是具有隐式归零内存的全局变量,而
  • NS::ns_my_numD,因为可执行文件必须显式提供值 2 才能占用该内存。

man/info-page for nm 进一步记录了这些事情......

【讨论】:

  • 注意:strip 实用程序执行相反的操作,即它使用带有调试符号的库并将它们剥离。
  • 什么是符号信息?你能帮我把这个答案更ELIF吗?
  • @TonyD 抱歉。它代表像我五岁一样解释。我还没有上过编译器的课程,所以我没有那个词汇。
  • @VictorLin:这里涉及的太多了,无法在那个层面上解释它——它会迅速解释为地址与偏移量、“虚拟”内存、操作系统加载程序等,但我'已经给出了一些基本的细节和一个例子——希望它有所帮助。
【解决方案3】:

gcc -g 标志告诉 gcc 生成和嵌入调试信息。 ulimit -c 用于启用核心文件生成。你可以拥有其中一个而没有另一个。

【讨论】:

    【解决方案4】:

    核心文件是在分段错误或此类异常时生成的。 gdb source.cc core 是查看核心文件的一种方法。回溯和调查每一帧是研究核心的开始。 -g 在二进制文件中添加调试符号。

    【讨论】:

      【解决方案5】:

      -g 将调试信息(变量名、行号等)添加到可执行文件中。这是理解核心文件所需要做的一部分。

      http://gcc.gnu.org/onlinedocs/gcc/Debugging-Options.html#Debugging-Options

      【讨论】:

        【解决方案6】:

        核心转储是进程的默认操作之一,当该进程接收到信号时,例如在标准信号“SIGQUIT”、“SIGILL”、“SIGABRT”、“SIGFPE”、“SIGSEGV”中。但是,大多数 shell 都禁止创建核心文件,这仅仅是因为核心文件往往很大,并且可能需要一些时间或很多时间。

        为了启用核心生成,“ulimit”是可用于设置 shell 或其子进程的文件限制的实用程序。

        编译器标志“-g”或仅与编译器有关的任何内容。从逻辑上讲,它与核心转储无关。

        【讨论】:

          【解决方案7】:

          如果你不放 -g 标志,不能在 gdb 中调用 list 来列出源代码的样子。它将显示“未加载符号表。使用“文件”命令。”

          此外,如果您在 gdb 中键入 info func 或 info frame, info locals,没有 -g 将不会显示返回数据类型及其参数,基本上不会将指令转换为变量(从符号表映射)。

          【讨论】:

            猜你喜欢
            • 2011-09-30
            • 1970-01-01
            • 2010-10-14
            • 2011-08-16
            • 2017-07-16
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多