__libc_start_main与__libc_start_call_main的区别是什么?
问题描述
我之前了解到__libc_start_main()函数,原本以为它会像相关描述里说的那样直接调用main()函数,但在测试程序的main()处设置断点后,查看栈指针$rsp发现它指向的是__libc_start_call_main()的地址。找不到两者差异的资料,想知道这两个函数的区别是什么?
测试源代码
#include <stdio.h> int main(void) { puts("Sunghyeon Lee"); }
GDB调试输出
──(kali㉿kali)-[~] └─$ gdb test GNU gdb (Debian 12.1-3) 12.1 Copyright (C) 2022 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: <https://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type "help". Type "apropos word" to search for commands related to "word"... Reading symbols from test... (No debugging symbols found in test) (gdb) b *main Breakpoint 1 at 0x1139 (gdb) r Starting program: /home/kali/test [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Breakpoint 1, 0x0000555555555139 in main () (gdb) x/a $rsp 0x7fffffffdec8: 0x7ffff7dd920a <__libc_start_call_main+122>
回答
这两个函数都是GNU libc程序启动流程的核心组成部分,属于内部实现的模块化拆分,具体区别如下:
__libc_start_main():是对外暴露的标准启动入口(由动态链接器加载程序后调用),主要负责启动流程的前期准备工作:包括初始化进程运行环境、设置栈保护机制、处理命令行参数与环境变量、初始化线程相关结构、注册进程退出处理函数等。完成所有准备后,它不会直接调用main(),而是跳转至__libc_start_call_main()。__libc_start_call_main():是libc内部的专用辅助函数,专门负责调用main()及后续收尾逻辑:它会整理好main()所需的argc、argv、envp参数,执行调用main()的指令;在main()返回后,还会处理返回值并调用exit()完成进程的资源清理与退出流程。
你在GDB中看到$rsp指向__libc_start_call_main()的地址,是因为__libc_start_call_main()执行了调用main()的指令,当main()被断点暂停时,栈顶保存的正是__libc_start_call_main()中调用main()之后的返回地址。
这种拆分是GNU libc后续版本的实现调整,早期版本中__libc_start_main()确实会直接调用main(),但为了提升代码的模块化程度与可维护性,官方将调用main()的逻辑单独抽离为__libc_start_call_main()。
内容的提问来源于stack exchange,提问作者Sunghyeon Lee

