我希望详细了解使用GDB main()
之前和之后的情况。仅仅用-g
重新编译glibc并与之链接就足够了吗?
答案 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);
}
我在该草图中遗漏了一些细节,但大纲应该是正确的。名为init
和fini
的函数指针的有趣业务主要是支持C ++程序中全局对象的构造函数和析构函数。对于普通C链接,这些指针将为NULL,并且不会产生任何影响。
答案 1 :(得分:3)
如果你想使用调试器,你可以这样使用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) 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 ()