您的位置:首页 > 其它

第七周:可执行程序的装载

2016-04-10 21:50 225 查看

范闻泽 原创作品转载请注明出处 《Linux内核分析》MOOC课程 http://mooc.study.163.com/course/USTC-1000029000

一、预处理,编译,链接和目标文件格式

1.可执行程序是怎么得来的



c代码->预处理->汇编代码->汇编器->目标代码->链接成可执行文件->加载到内核执行



2.目标文件的格式ELF

符号修饰标准、变量内层布局、函数调用方式等这些跟可执行代码二进制兼容性相关的内容称为ABI(Application Binary Interface)

常见的ABI格式:





3.静态链接的ELF可执行文件和进程的地址空间



一般静态链接将会把所有代码放在同一个代码段。



动态连接的进程会有多个代码段。



二、可执行程序、共享库和动态链接

1.可执行程序的执行环境

命令行参数和shell环境,一般我们执行一个程序的Shell环境,我们的实验直接使用execve系统调用。

Shell本身不限制命令行参数的个数, 命令行参数的个数受限于命令自身。

shell会调用execve将命令行参数和环境参数传递给可执行程序的main函数。

命令行参数和环境串都放在用户态堆栈中。


2.装载时动态链接和运行时动态链接应用举例

动态链接分为可执行程序装载时动态链接和运行时动态链接


三、可执行程序的装载

1. sys_execve内核处理过程

(1)新的可执行程序起点

一般是地址空间为0x8048000或0x8048300


(2)execve与fork

execve和fork都是特殊一点的系统调用:一般的都是陷入到内核态再返回到用户态。


fork两次返回,第一次返回到父进程继续向下执行,第二次是子进程返回到ret_from_fork然后正常返回到用户态。

execve执行的时候陷入到内核态,用execve中加载的程序把当前正在执行的程序覆盖掉,当系统调用返回的时候也就返回到新的可执行程序起点。

execve

- 执行到可执行程序 -> 陷入内核
- 构造新的可执行文件 -> 覆盖掉原可执行程序
- 返回到新的可执行程序,作为起点(也就是main函数)
- 需要构造其执行环境;


Shell会调用execve将命令行参数和环境参数传递给可执行程序的main函数,先函数调用参数传递,再系统调用参数传递。

(3)静态链接的可执行程序和动态链接的可执行程序execve系统调用返回时不同

静态链接:elf_entry指向可执行文件的头部,一般是main函数,是新程序执行的起点。

2.动态链接的可执行程序的装载

动态链接库的依赖关系会形成一个图。

load_elf_interp实际加载动态链接器,entry返回的是动态链接器的入口,根据需求加载动态链接库,根据库的需要再加载更多的库。

四、实验 Linux内核如何装载和启动一个可执行程序

1.环境搭建:

rm menu -rf
git clone https://github.com/megnning/menu.git cd menu
ls
mv test_exec.c test.c
vi test.c   // 可以看到增加了一个exec的程序,只比fork程序多了一个execlp
vi Makefile // 查看Makefile的更改,加入了hello
make rootfs




执行exec后,出现了hello world,这其实是加载了一个新的程序hello。

2.使用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

b sys_execve    //可以先停在sys_execve然后再设置其他断点
b load_elf_binary
b start_thread






以上两图为设置断点。
先设置sys_execve后,在menuOS里执行系统调用exec,会停在如图所示代码,找到第一个断点



找到了第二个断点



给新栈的栈底指针赋值



找到了第三个断点start_thread



查看附近的代码:给新栈的赋值。

执行到start_thread的时候有一个问题:
new ip到底是指向哪里的?
用po(print object)指令:

po new_ip
可以看到一个地址:0x80495ba




readelf -h hello
找到hello这个可执行程序的入口地址。
这是一个静态编译的可执行文件。




new ip是返回用户态的第一条指令的地址。

3.实验总结

1. execve()系统调用,实质是运行的sys_execve()函数。
2. sys_execve中有do_execve(),读取128个字节的文件头部。
3. 调用search_binary_handle()去搜索和匹配合适的可执行文件装载处理过程。
4. ELF被load_elf_binary()装载。
load_elf_binary函数中调用了start_thread函数,创建新进程的堆栈,有pt_regs栈底指针,更重要的是修改eip,即程序执行入口。
5. 函数返回至do_execve(),又返回至sys_execve()。
6. 新的程序开始执行,ELF可执行文件装载完成。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: