一、可执行文件的创建

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

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. 进程保护--CrossThreadFlags标志位

    原理: 1. 将进程的所有线程的线程CrossThreadFlags标志位设置成Terminated或者System. 效果:任务管理器,WSYSCheck,ICESWORD无法结束进程.. 但PCH ...

  2. 《转》python学习--基础下

    转自http://www.cnblogs.com/BeginMan/archive/2013/04/12/3016323.html 一.数字 在看<Python 核心编程>的时候,我就有点 ...

  3. JZOJ.5335【NOIP2017模拟8.24】早苗

    Description

  4. 9.Node.js 包管理器npm

    npm 是 Node.js  官方提供的包管理工具, 用于 Node.js包的发布.传播.依赖控制 安装 express ==> 流行的基于Node.js的Web开发框架,可以快速地搭建一个完整 ...

  5. 二、微信小游戏开发 多线程Worker

    微信多线程Worker教程 微信多线程Worker API 一.创建Worker,并和当前线程通讯 多线程worker只能创建1个.能和当前线程互传数据. 创建worker 在微信开发者工具中,在当前 ...

  6. linux的ls命令输出结果的逐条解释

    转自:http://blog.csdn.net/god123209/article/details/7193485 ls 命令的含义是list显示当前目录中的文件名字.注意不加参数它显示除隐藏文件外的 ...

  7. JS判断当前是否是IE浏览器,并返回时IE几?

    原文参考: https://www.cnblogs.com/liuyanxia/p/5855760.html 具体代码示例: 这里返回的是:如果不是IE浏览器返回 -1 ,返回 7/8/9/10/11 ...

  8. SpringBoot系列教程起步

    本篇学习目标 Spring Boot是什么? 构建Spring Boot应用程序 三分钟开发SpringBoot应用程序 本章源码下载 Spring Boot是什么? spring Boot是由Piv ...

  9. 170529、springMVC 的工作原理和机制

    工作原理上面的是springMVC的工作原理图: 1.客户端发出一个http请求给web服务器,web服务器对http请求进行解析,如果匹配DispatcherServlet的请求映射路径(在web. ...

  10. MVC之AJAX异步提交表单

    第一种用法: 在MVC中,依然可以使用ajax校验,跟在WebForm中的使用时一样的,唯一的区别就是将以前的URL路劲改为访问控制器下的行为 前台 <html> <head> ...