从 Shell 加载到执行 “Hello World” 的完整流程
引言
本文将详细探讨从 Shell 输入命令到执行一个简单的 “Hello World” 程序的整个过程,帮助开发者深入了解底层机制。
Hello World 源码
首先,我们来看一下这个简单的 C 程序:
// hello.c
#include <stdio.h>
int main() {
printf("Hello, World!\n");
return 0;
}
这段代码非常直观:它定义了一个 main() 函数,该函数调用 printf 输出字符串 "Hello, World!\n" 并返回 0 表示成功退出。
编译与反汇编
使用 GCC 编译器将此代码编译为 AArch64 架构的目标文件:
aarch64-linux-gnu-gcc -o hello hello.c
为了分析其内部工作原理,我们可以使用 objdump 反汇编生成的二进制文件:
aarch64-linux-gnu-objdump -d hello > hello_disasm.txt
用户交互与 Shell 解析
当用户在 Shell 中键入 ./hello 并按下回车时,Shell 解析命令行参数,并准备启动指定的应用程序。这标志着程序执行的第一步。
创建新进程
- Fork 函数:Shell 使用
fork()函数,调用clone()系统调用来创建一个新的子进程。此时,子进程是父进程(通常是 Shell)的一个副本,包括内存空间、文件描述符等。 - Exec 系列函数:在子进程中,Shell 调用
execve()或其他exec系列函数之一来加载并执行指定的 ELF 文件。这一步会替换当前进程的用户空间镜像为新程序的镜像。
加载器解析 ELF 文件
接下来,操作系统的加载器读取 ELF 文件头和程序头表,了解程序的入口点、段信息等,并将程序的各个段(如代码段、数据段)映射到内存中适当的位置。如果程序依赖于共享库(如 glibc),加载器还会解析动态链接信息,并加载必要的共享库到内存中。
初始化运行时环境
在这个阶段,初始化栈和其他寄存器,设置堆管理器(malloc/free),初始化线程系统(如果程序是多线程的),以及完成动态链接器的初始化,确保所有符号都能正确解析。
_start 入口点
控制权传递给程序的入口点 _start。这是由编译器提供的启动代码的一部分,负责设置初始的栈帧和其他必要的环境变量,并准备参数来调用 _libc_start_main。
0000000000401000 <_start>:
401000: a9be7bfd stp x29, x30, [sp,#-16]!
401004: 910003fd mov x29, sp
...
401020: 90000013 bl 4011b0 <__libc_start_main@plt>
401024: d65f03c0 ret
最后,通过 bl 指令跳转到 _libc_start_main。
__libc_start_main 初始化
libc_start_main 是 glibc 内部函数,负责进一步初始化运行时环境,包括调用__libc_csu_init初始化全局构造函数和静态构造函数,即执行所有带有__attribute((constructor))属性的函数以及C++中的静态对象构造函数。
最终调用 main()。
main() 函数执行
控制权交给用户定义的 main() 函数,在这个例子中,它调用了 puts 来打印 "Hello, World!\n",然后返回 0 表示成功退出。
0000000000401158 <main>:
401158: a9be7bfd stp x29, x30, [sp,#-16]!
40115c: 910003fd mov x29, sp
...
401168: 90000010 bl 4010e0 <puts@plt>
...
401170: 52800000 mov w0, #0x0 // #0
401174: a8c17bfd ldp x29, x30, [sp],#16
401178: d65f03c0 ret
程序结束
main() 执行完毕后,任何全局或静态对象的析构函数会被调用,以清理资源。最后,调用 _exit() 系统调用来终止进程,返回 main() 的返回值作为整个程序的退出状态码。
总结
从用户在 Shell 中输入命令开始,经过一系列复杂的初始化和加载过程,最终到达 main() 函数的执行,每个阶段都有其特定的任务,以确保程序能够在正确的环境中开始执行。这一过程中涉及多个关键步骤,包括创建新进程、加载和解析 ELF 文件、初始化运行时环境以及调用 _libc_start_main 和用户定义的 main() 函数。

411

被折叠的 条评论
为什么被折叠?



