Linux 内核如何装载和启动一个可执行程
编译链接的过程和 ELF 可执行文件格式
询问ChatGPT
启动MenuOS
首先进入LinuxKernel 文件夹,删除menu目录,然后git clone克隆一个新版本的menu,进入menu之后,运行make rootfs脚本自动编译生成根目录系统。
cd LinuxKernel
rm -rf menu
git clone https://github.com/mengning/menu.git
cd menu
mv test_exec.c test.c
make rootfs
可以看到test.c中增加了exec函数并执行exec指令。
调试MenuOS
通过增加-s -S启动参数打开调试模式,并打开gdb进行调试。
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S
gdb
file linux-3.18.6/vmlinux
target remote:1234
水平分割一个窗口,启动gdb加载内核,连接到target 1234,并在sys_execve和load_elf_binary和start_thread三处设置断点。连续按3个c执行之后,出现menuos界面。
在MenuOS里面输入exec,遇到了第一个断点sys_execve,按s进行跟踪,会看到do_execve,按c继续执行,到第二个断点load_elf_binary处停下来,按c继续执行,在第三个断点start_thread处停下来,使用po new_ip查看new_ip的指向,其实new_ip是返回到用户态的第一条指令的地址,可以看到修改了内核堆栈的位置,这样再返回用户态时就有了新的执行环境。
总结
静态链接的可执行程序和动态链接的可执行程序在执行 execve 系统调用返回时有一些不同之处。
-
静态链接的可执行程序:在静态链接时,所有程序运行所需的库函数和其他依赖项都被编译进可执行文件中。因此,当执行 execve 系统调用时,操作系统加载整个可执行文件到内存中,并开始执行程序。这意味着可执行文件本身包含了所有运行所需的代码和数据,不需要额外的库文件来支持程序的执行。
-
动态链接的可执行程序:在动态链接时,程序的依赖项(如库函数)并不被编译进可执行文件中,而是作为独立的共享库文件存在。因此,当执行 execve 系统调用时,操作系统加载可执行文件到内存中,但并不立即执行程序。相反,操作系统会解析可执行文件中的动态链接信息,并加载所需的共享库文件。然后,操作系统将程序的控制权转移到入口点(entry point),并开始执行程序。
因此,主要的不同点在于静态链接的可执行程序包含了所有运行所需的代码和数据,而动态链接的可执行程序则依赖于独立的共享库文件。动态链接的可执行程序具有以下优势:
-
节省内存:多个程序可以共享相同的共享库,这样就不需要为每个程序都加载和占用内存。
-
灵活性:如果共享库更新或修复了某些问题,所有使用该库的程序都可以受益,而无需重新编译和部署每个程序。
-
可执行文件较小:由于共享库被独立存储,可执行文件的大小相对较小。