一、可执行文件的创建

可执行文件的创建就是三步:预处理、编译和链接。

cd Code
vi hello.c #写入最简单的helloworld的c程序
gcc -E -o hello.cpp hello.c -m32 #-E参数就是生成预处理后的文件,看到-o后面的是生成的文件hello.cpp,注意它并不是cplusplus,而是随意起的后缀名
vi hello.cpp #查看该文件,发现预处理做了把include的文件包含进来以及宏替换等工作。
gcc -x cpp-output -S -o hello.s hello.cpp -m32 #-x language filename作用是设定文件使用的语言,使后缀名无效。此处就是让刚才的cpp不要让编译器误会为cplusplus,而是当做cpp-output这种文件格式。-s是指生成汇编.s文件
vi hello.s
gcc -x assembler -c hello.s -o hello.o -m32 #-c指将.s转为.o文件
vi hello.o
gcc -o hello hello.o -m32 #-o指将.o文件链接为可执行的文件
vi hello
gcc -o hello.static hello.o -m32 -static #静态链接
ls -l #注意看结果中的各文件的大小,其中静态链接的很大,因为它把所需要的库一次包到进程(可执行文件)中

二、可执行文件的组成

可执行文件属于目标文件之一。目标文件的格式为ELF。ELF的格式以段来组织的二进制代码,所以我们知道:①它已经符合某种机器的ABI了;②为什么进程认为自己占了全部的空间,拥有一套完善的页表,也就是理解了线性地址是什么情况。那么多线程时候是怎么组织的呢?暂时我还不知道。
以ELF为格式的主要有三种文件:①可重定位文件:保持着代码和适当的数据,用来和其他的object文件一起来创建一个可执行文件或者一个共享文件。例如.o文件。
②可执行文件:可以运行的文件。该文件指出了exec(BA_OS)如何来创建进程映象。再来联想下程序和进程的区别。到底这种可执行文件是进程还是程序?我们发现它的段中只含.text和.data一类的段,而不含有堆栈段。所以可以确定它只是程序。当它被操作系统调入内存开始执行时才会真正的成为进程。例如.out文件。
③共享object文件:保存着代码和数据,被两个链接器链接。一个是连接编辑器,可以和其他可重定位和共享object文件来创建其他的object。第二个是动态链接器,联合一个可执行文件和其他共享object文件来创建一个进程映像。
ELF文件的头部:使用命令查看hello文件的头:shiyanlou:Code/ $ readelf -h hello

ELF的头保存的是元数据,也就是路线图,描述了文件的组织情况。比如程序头表(program header table)告诉系统如何来创建一个进程的内存映像。section头表(section header table)包含描述文件sections的信息。每个section在这个表中有一个入口;每个入口给出了该section的名字,大小等等信息。
ELF的剩余部分是sections,包括代码段,数据段。这些在程序变成进程映像时加载到内存的虚拟地址空间中,其中代码段的起始地址就是0x8048000,但它加载的不是纯代码段的内容,而是从ELF头开始加载,所以从图中可以看出真正的代码从0x8048320开始,这才是真正的程序入口。静态链接时程序所需要的代码全部在代码段中,而动态链接就不一样了,它会在运行时候去找内存中间部分加载的库函数。

三、可执行程序的加载

(1)装载:可执行程序的执行环境,shell
shell就是用户键入命令,加载并执行可执行程序的控制台
shell的本质是什么呢?就是提供图形化的界面,将用户写入的字符串解析成真正执行的命令或者说可执行程序。有两个问题:①真正执行程序的是什么程序或指令?答案是execve系统调用(库函数exec*都是execve的封装例程)。②如何给该系统调用传参?也就是传参的默认格式是什么?答:shell会传入execve的参数有两种,一种是程序本身参数,也就是main的参数argc,argv;第二种是shell环境变量的参数,envp字符串数组中。来看下execve的参数类型:
int execve(const char * filename,char * const argv[ ],char * const envp[ ]);
当然,有些程序的main函数是不处理环境变量参数的,例如常见的int main(int argc, char *argv[])。但是有时候也支持,例如int main(int argc, char *argv[], char *envp[]),所以这个时候传入的环境变量参数才会被解析使用。
新问题:execve是如何从内核态将参数传给进程(假设该进程是用户态)的用户态堆栈的?
先看两张图:

发现,仍是从用户态的数据段中复制到用户态堆栈中的。那么跟内核什么关系呢?执行execve的进程就是当前的shell,所以参数首先会被压在当前shell进程的内核堆栈中。关键在传入内核的参数是指针,所以内核(sys_execve)要做的就是把指针的值复制回新进程的代码段,再复制到进程的用户堆栈段。初始化后的进程内存地址空间就是第二张图那样。也就解释了sys_execve加载进程并初始化的作用、结果。shell每次都fork一个shell去执行命令,所以,当新进程起来后,启动它的shell结束,曾经保存的参数就不要了。
(2)链接
静态链接就是经过静态链接方式编译的可执行程序,正如上述描述的加载部分,编译后直接加载进程所需信息,而且以后不需要其他信息了。
动态链接不同,它分为装载时的动态链接和运行时的动态链接。
在linux下动态链接文件格式为.so(windows下为.dll)。关于这点的理解,mooc课程中有个例子。写在一篇中篇幅太长,不分析了。

四、分析execve系统调用内核处理函数sys_execve

execve和前面博文分析的fork系统一样,是一种特殊的系统调用。fork的特殊在于系统调用后两次返回,生成了新进程,而不单单是在原来程序的系统调用的下一条语句。而execve的特殊在于它返回之后,执行的是一个新的程序了(例如返回程序的main入口,修改的是elf_entry),而不是以前调用execve的进程shell了。
内核处理函数sys_execve内部会解析可执行文件格式,它的内部执行流程是do_execve -> do_execve_common -> exec_binprm。
gdb断点设置:b sys_execve ;停到该位置,继续设置断点 b load_elf_binary; b start_thread。
其中的一些函数解释:
1)search_binary_handler符合寻找文件格式对应的解析模块,如下:

list_for_each_entry(fmt, &formats, lh) {
if (!try_module_get(fmt->module))
continue;
read_unlock(&binfmt_lock);
bprm->recursion_depth++;
retval = fmt->load_binary(bprm);
read_lock(&binfmt_lock);

对于ELF格式的可执行文件fmt->load_binary(bprm);执行的应该是load_elf_binary

2)Linux内核是如何支持多种不同的可执行文件格式的?

static struct linux_binfmt elf_format = {
.module = THIS_MODULE,
.load_binary = load_elf_binary,
.load_shlib = load_elf_library,
.core_dump = elf_core_dump,
.min_coredump = ELF_EXEC_PAGESIZE,
};
static int __init init_elf_binfmt(void)
{
register_binfmt(&elf_format);
return 0;
}
elf_format 和 init_elf_binfmt,就是观察者模式中的观察者。

3)可执行文件开始执行的起点在哪里?如何才能让execve系统调用返回到用户态时执行新程序?
load_elf_binary -> start_thread中通过修改内核堆栈中的EIP的值作为新程序的起点。即修改一开始int 0x80压入内核堆栈的EIP。start_thread中的new_ip是返回到用户态第一条指令的地址,与可执行程序的头中的入口地址相同。

linux内核分析第七周-Linux内核如何装载和启动一个可执行程序的更多相关文章

  1. linux内核分析 第七周 Linux内核如何装载和启动一个可执行程序

    一.编译链接的过程和ELF可执行文件格式 vi hello.c gcc -E -o hello.cpp hello.c -m32 //预处理.c文件,预处理包括把include的文件包含进来以及宏替换 ...

  2. LINUX内核分析第七周学习总结:可执行程序的装载

    LINUX内核分析第七周学习总结:可执行程序的装载 韩玉琪 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/cours ...

  3. Linux内核分析 第七周 可执行程序的装载

    张嘉琪 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 Linux内核分析 第七 ...

  4. 20135327郭皓--Linux内核分析第七周 可执行程序的装载

    第七周 可执行程序的装载 郭皓 原创作品转载请注明出处 <Linux内核分析>MOOC课程 http://mooc.study.163.com/course/USTC-1000029000 ...

  5. LINUX内核分析第七周学习总结

    LINUX内核分析第七周学习总结 标签(空格分隔): 20135328陈都 陈都 原创作品转载请注明出处 <Linux内核分析>MOOC课程 http://mooc.study.163.c ...

  6. Linux内核分析第七周———可执行程序的装载

    Linux内核分析第七周---可执行程序的装载 李雪琦+原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/US ...

  7. Linux内核分析第七周学习笔记——Linux内核如何装载和启动一个可执行程序

    Linux内核分析第七周学习笔记--Linux内核如何装载和启动一个可执行程序 zl + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study. ...

  8. LINUX内核分析第七周学习总结——可执行程序的装载

    LINUX内核分析第六周学习总结——进程的描述和进程的创建 张忻(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/cours ...

  9. Linux内核分析第七周总结

    第七章 可执行程序的装载 可执行程序的生成 可执行程序的生成: c语言代码--->经过编译器的预处理--->编译成汇编代码--->由汇编器编译成目标代码--->链接成可执行文件 ...

随机推荐

  1. Objective-c为什么要有属性

    属性:为什么要有属性 . 首先是因为实例变量的安全性和继承能力,如果我们允许被继承,子类要动这些实例变量,我们需要能够参与进来,如果子类设置了某个值,我们需要检查范围,保证不会破坏父类,保证不会破坏父 ...

  2. 1.node.js下载

    1.下载node.js http://nodejs.cn/ 2.下载git https://git-scm.com/download/win 3.安装npm npm install npm -g 使用 ...

  3. 【Android】Android内存溢出问题---用自行开辟的空间进行对内存管理

    public static Bitmap readBitmap(String path) { BitmapFactory.Options options = new BitmapFactory.Opt ...

  4. for update 和 t.rowid的区别

    select * from table_name for update; 和 select t.*, t.rowid from table_name t 的区别 前者会对你查询出来的结果加上锁,而后者 ...

  5. 最小树形图(hdu4009)

    Transfer water Time Limit: 5000/3000 MS (Java/Others)    Memory Limit: 65768/65768 K (Java/Others) T ...

  6. 混合欧拉回路的判断(Dinic)

    POJ1637 Sightseeing tour Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 7483   Accepte ...

  7. jenkins之升级

    首先查看系统war包放置的位置 rpm -ql jenkins 下载一个war包 下载地址 https://mirrors.tuna.tsinghua.edu.cn/jenkins/war/2.61/ ...

  8. C# WebBrowser的8个方法、13个属性和事件

    1.方法 说明 GoBack 相当于IE的“后退”按钮,使你在当前历史列表中后退一项 GoForward 相当于IE的“前进”按钮,使你在当前历史列表中前进一项  GoHome 相当于IE的“主页”按 ...

  9. Python 百度语音识别与合成REST API及ffmpeg使用

    操作系统:Windows Python:3.5 欢迎加入学习交流QQ群:657341423 百度语音识别官方文档 百度语音合成官方文档 注意事项:接口支持 POST 和 GET两种方式,个人支持用po ...

  10. rbac - 界面、权限

    一.模板继承 知识点: users.html / roles.html 继承自 base.html 滑动时,固定 position: fixed;top:60px;bottom:0;left:0;wi ...