C++ gcc-g:会发生什么

C++ gcc-g:会发生什么,c++,c,gcc,gdb,C++,C,Gcc,Gdb,这个问题是在一次采访中问我的 他们问我如何生成一个核心转储文件,我可以用它进行调试。 然后我说,在gcc中使用-g标志,我们可以做到这一点 然后他们问我,-g标志对编译器有什么作用 我说(可能是错误的答案)它将打开核心文件中所有可用于调试的符号 有人能告诉我它到底做什么吗?g标志告诉编译器生成调试信息。它对是否生成核心文件没有影响。在大多数类似unix的系统上,可以使用ulimit命令来设置它。这有点正确,但不完整-g请求编译器和链接器在可执行文件本身中生成并保留源代码级调试/符号信息 如果

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

他们问我如何生成一个核心转储文件,我可以用它进行调试。 然后我说,在
gcc
中使用
-g
标志,我们可以做到这一点

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

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


有人能告诉我它到底做什么吗?

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

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

如果

  • 该程序稍后会崩溃并生成一个核心文件(这表明实际代码中存在一些问题),或者
  • 故意的操作系统命令将其强制核心化(例如,
    kill-SIGQUIT
    pid),或
  • 程序调用转储核心的函数(例如)
…-所有这些都不是由于使用
-g
-导致的-然后调试器将知道如何从可执行文件中读取“
-g
”符号信息,并将其与内核交叉引用。这意味着您可以在堆栈框架中查看变量和函数的专有名称,获取行号,并在可执行文件中查看源代码

无论是从内核还是仅从可执行文件开始调试,该调试信息在调试时都非常有用。它甚至有助于从命令(如)生成更好的输出

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

您可能还想了解-一种常用的调试信息格式,用于编码可执行/库对象(例如,在UNIX和Linux上)中嵌入的调试/符号信息

根据Victor在评论中的请求进行更新

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

$ 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_num
    B
    的一个全局变量,隐式地将内存归零,而
  • NS::NS_my_num
    D
    ,因为可执行文件必须显式提供值
    2
    ,才能占用该内存

gcc-g标志告诉gcc生成并嵌入调试信息<代码>ulimit-c用于启用核心文件生成。您可以使用其中一个而不使用另一个。

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

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


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

为了支持核心生成,“ulimit”是一个实用工具,您可以使用它设置shell或其子进程的文件限制


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

如果不放置-g标志,则无法在gdb中调用list来列出源代码的外观。它将显示“未加载符号表。请使用“文件”命令。”


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

但我想知道调试信息到底是什么。我知道你已经知道了什么。我需要的东西远远不止这些。@wvwvvv除此之外,它很复杂-一个完整的描述需要几十页。您可以先看看DWARF()标准,它是嵌入在可执行文件中的调试符号的规范(在大多数*nixes上)-g指示编译器/链接器生成这些结构并将其嵌入到可执行文件中。下一步可能是了解调试器如何使用这些调试信息。简而言之,它添加了有关符号名称(函数、本地和全局变量等)的信息,以及有关类型、源文件名和行号的信息。它还可以通过自动定义的宏拉入不同的头文件(例如STL和assert宏的定义),以简化调试。它可能还会为内联函数生成特殊输出,以便可以单步进入内联函数/方法。注意:
strip
实用程序执行相反的操作,即它获取一个带有调试符号的库并将其剥离。符号信息是什么?你能不能帮我把这个答案再简单一点?@TonyD对此表示抱歉。它代表解释,就像我五岁一样。我还没学过编译器呢