调试C运行时

时间:2011-11-02 06:08:05

标签: c debugging gdb runtime glibc

我希望详细了解使用GDB main()之前和之后的情况。仅仅用-g重新编译glibc并与之链接就足够了吗?

2 个答案:

答案 0 :(得分:4)

您无需在调试器中启动。

当OS加载您的可执行文件时,它会将控制权传递给其入口点,该入口点不是名为main()的函数。在GCC和glibc中,真正的入口点通常被命名为_start,但您的里程可能因您的平台而异。当然,如果您没有使用glibc,或者正在使用不同的C编译器,那么它可能会有更多变化。

_start处代码的关键任务是初始化所需的所有内容,以创建main()期望的条件。请注意,这对于C ++而言很多更复杂,并且由于GCC支持这两种语言,因此真正的启动代码将具有额外的功能,其唯一目的是支持C ++的要求。

_start的源代码几乎总是用汇编语言编写,并且是高度特定于平台的。对于32位x86平台,可以在sysdeps/i386/elf/start.S下的glibc源代码树中找到一个样本。

虽然您可能永远不需要在桌面操作系统上调试普通代码,但在处理小型嵌入式系统时,通常需要很好地理解运行时环境的初始化方式。特别是,许多嵌入式系统直接从系统重置启动到此启动代码的版本。在这样的系统上,必须打开将要使用的内存或正确配置CPU的主时钟源并将第一个堆栈指针设置为合理的东西,然后才能担心更高级别的概念,这一点并不罕见。 .text.data.bss段。

链接的start.S版本假定它是在unix或linux的某种风格下启动的(我看起来不太仔细)。因此,它假设已创建该进程,并且代码和数据段已加载并可以使用。它将命令行参数从操作系统提供的格式转换为调用arvc所需的熟悉的argv[]main(),但它通过glibc源中的其他位置提供的包装器进行调用在csu/libc-start.c中找到了__libc_start_main()

由于支持各种功能的条件补充指令的丰富性,该函数的来源变得非常复杂。但实际上,对于一个常见的案例,它可归结为以下内容:

STATIC int
__libc_start_main(int (*main) (int, char **, char **),
                 int argc, char **av,
                 int (*init)(int, char **, char **),
                 void (*fini) (void),
                 void (*rtld_fini) (void), void *__unbounded stack_end)
{

    int result;
    /* some basic initializations goes here, then... */
    /* initialize some core parts of the library */
    __libc_init_first (argc, argv, __environ);
    /* arrange to call finalizers at exit if any */
    if (fini)
        __cxa_atexit ((void (*) (void *)) fini, NULL, NULL);
    /* call initializers, if any */
    if (init)
        init(argc, argv, __environ);
    /* call user's actual main, which might not return */
    result = main (argc, argv, __environ);
    /* if main did return, exit appropriately */
    exit (result);
}

我在该草图中遗漏了一些细节,但大纲应该是正确的。名为initfini的函数指针的有趣业务主要是支持C ++程序中全局对象的构造函数和析构函数。对于普通C链接,这些指针将为NULL,并且不会产生任何影响。

答案 1 :(得分:3)

如果你想使用调试器,你可以这样使用GDB:

  • 安装`glibc`包的调试信息(here是用Fedora做的方法,我不知道其他的发行版)
  • 或将GDB指向一致的调试文件目录:
(gdb) show debug-file-directory
The directory where separate debug symbols are searched for is "/usr/lib/debug".
(gdb) set debug-file-directory ...

(我系统中的/usr/lib/debug/lib64/libc-2.14.so.debug

  • 告诉GDB在你的`main`:
  • 之前显示回溯
(gdb) show backtrace past-entry
Whether backtraces should continue past the entry point of a program is off.
(gdb) set backtrace past-entry on
  • 那么你应该看看你在寻找什么,然后浏览它:
(gdb) where
#0  main () at test.c:4
#1  __libc_start_main (main=0x40050f <main>, argc=1,...) at libc-start.c:226
#2  _start ()