当我们在linux系统的bash下输入一个命令执行某个elf程序时, linux系统是怎么装载这个elf文件并执行它的呢? 我们以helloworld为例子 a.out
首先,bash 进程会调用 fork() 系统调用创建一个新的进程,假设叫做hello进程
新的进程(hello进程)调用 execve() 系统调用执行 a.out
2.1 execve 有三个参数:程序文件名 执行参数 环境变量 其中, Glibc 对 execve 系统调用进程了包装,提供了 execl execp execle execv execvp这五种APOI 它们只是调用参数有区别,最终都会调用 execve.
2.2 进入 execve linux内核才开始真正的装载工作。
2.3 execve -> sys_execve -> do_execve do_execve 读取可执行文件 a.out 的前128字节判断文件格式 因为前128字节标识了文件类型:elf java windows的pe 等可执行文件格式 每个可执行文件的前4字节很特殊,称为魔数字表明格式和类型,如ELF的是 0x7f e l f java的可执行文件前4字节是: c a f e,shell python脚本前面是 #!/bin/sh等
2.4 do_execve -> search_binary_handle 这个搜索和匹配合适的可执行文件装载处理过程。
2.5 然后调用 load_elf_binary 它装载可执行文件 a.out ,具体包括以下步骤: 第一,检查可执行文件格式的有效性 第二,寻找动态链接的 ”.interp" 段,设置动态链接器路径 第三,根据elf文件的程序头标描述,对elf文件进行映射 第四,初始化elf进程环境 第五,将系统调用的返回地址修改成elf 可执行文件的入口点,这里就是 a.out 的 main
2.6 ELF a.out 开始执行,elf 可执行文件装载完成
原先的bash进程继续返回等待这个进程结束,然后继续等待用户输入命令
PE 文件的所有段的起始地址都是页的倍数 这样映射就简单多了 没有所谓的 segment 了,但是会浪费一点内存磁盘和内存空间
RAV 相对虚拟地址: 就是地址偏移 基地址: 文件的装载目标地址就是基地址