可执行程序的装载

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

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

可执行程序是怎么得来的?

首先,编译器预处理:1、将头文件加载进来;2、将宏替换

gcc -E -o hello.cpp hello.c (-m32)//c预处理成cpp文件

第二步,将cpp(预处理后的文件)编译成汇编代码

gcc -x cpp-output -S -o hello.s hello.cpp (-m32)//生成s汇编文件

第三步,将汇编代码编译成二进制目标文件

gcc -x assembler -c hello.s -o hello.o (-m32)//需要注意的是二进制文件不可读

第四步,链接成可执行文件

gcc -o hello.static hello.c (-m32) -static

什么是ELF格式文件?

Out是最古老的可执行文件,然后发展成COFF,然后就是PE和ELF。其中PE多用于WINDOWS,ELF多用于LINUX系统。

目标文件的三种形式:

1、可重定位文件//.o文件,用来和其他object文件一起创建下面两种文件

2、可执行文件//指出了应该从哪里开始执行

3、共享文件//主要是.so文件,用来被链接编辑器和动态链接器链接

ELF目标文件的格式:

ELF文件头格式:

左半边是ELF格式,右半边是执行时候的格式。

ELF头描述了该文件的组织情况,程序投标告诉系统如何创建一个进程的内存映像,section头表包含了描述文件sections的信息。

entry代表(刚加载过新的可执行文件之后的)程序的入口地址(头部之后是代码和数据,进程的地址空间是4G。

上面的1G是内核用,下面的3G是程序使用)

默认的ELF头加载地址是0x8048000,头部大概要到0x48100处或者0x483000处,也就是可执行文件加载到内存之后执行的第一条代码地址。

一般静态链接会将所有代码放在一个代码段;动态链接的进程会有多个代码段。

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

装载可执行程序之前的工作

可执行程序的执行环境

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

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

例如,int main(int argc, char *argv[])

又如, int main(int argc, char argv[], char envp[])//envp是shell的执行环境

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

int execve(const char * filename,char * const argv[ ],char * const envp[ ]);

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

fork子进程的时候完全复制了父进程;调用exec的时候,要加载的可执行程序把原来的进程环境覆盖掉,用户态堆栈也被清空

命令行参数和环境变量进入新程序的堆栈:把环境变量和命令行参数压栈(如上图),也就相当于main函数启动

shell程序-->execve-->sys_execve,然后在初始化新程序堆栈的时候拷贝进去先传递函数调用参数,再传递系统调用参数

装载时动态链接和运行时动态链接应用

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

共享库的动态链接:

动态加载库:

#ifdef __cplusplus

extern "C" {

#endif

/*

* Dynamical Loading Lib API Example

* input : none

* output : none

* return : SUCCESS(0)/FAILURE(-1)

*

*/

int DynamicalLoadingLibApi();

#ifdef __cplusplus

}

#endif

#endif /* _DL_LIB_EXAMPLE_H_ */

/*------------------------------------------------------*/

#include <stdio.h>

#include "dllibexample.h"

#define SUCCESS 0

#define FAILURE (-1)

/*

* Dynamical Loading Lib API Example

* input : none

* output : none

* return : SUCCESS(0)/FAILURE(-1)

*

*/

int DynamicalLoadingLibApi()

{

printf("This is a Dynamical Loading libary!\n");

return SUCCESS;

}

dlopen函数参考http://baike.baidu.com/link?url=05ftxNgbVsyrGNLqJbo3TpCyn27QeKOKaU7D70O-3bMu9ZsvruBZIHBZz-mhJgviNf4obS5HBNlpMPzcrbABZK的说明,

负责打开一个动态库并将其加载到内存;

dlsym函数与上面的dlopen函数配合使用,通过dlopen函数返回的动态库句柄(由dlopen打开动态链接库后返回的指针handle)以及对应的符号返回符号对应的指针。

三、可执行程序装载

可执行程序装载的关键问题

execve与fork是比较特殊的系统调用

execve用它加载的可执行文件把当前的进程覆盖掉,返回之后就不是原来的程序而是新的可执行程序起点;

fork函数的返回点ret_from_fork是用户态起点。

sys_execve内核处理过程

do_execve -> do_execve_common -> exec_binprm

最后,根据文件头部信息寻找对应的文件格式处理模块

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

庄周梦蝶——庄周(调用execve的可执行程序)入睡(调用execve陷入内核),醒来(系统调用execve返回用户态)发现自己是蝴蝶(被execve加载的可执行程序)

load_elf_binary调用start_thread函数

struct pt_regs *regs就是内核堆栈栈底的部分

发生中断的时候,esp和ip都进行压栈

通过修改内核堆栈中EIP的值(也就是把压入栈中的值用new_ip替换)作为新程序的起点

sys_execve内部处理过程

需要动态链接的可执行文件先加载连接器ld;否则直接把elf文件entry地址赋值给entry即可。

start_thread(regs, elf_entry, bprm->p)会将CPU控制权交给ld来加载依赖库并完成动态链接;对于静态链接的文件elf_entry是新程序执行的起点

使用gdb跟踪sys_execve内核函数的处理过程

首先还是更新menu内核,然后查看test.c,可以看到最新添加的exec系统调用。

查看Makefile,发现增加了gcc -o hello hello.c -m32 -static一句;

补充cp hello ../rootfs以及cp init ../rootfs

启动内核并验证execv函数

启动GDB调试

退出调试状态,输入redelf -h hello可以查看hello的EIF头部

浅析动态链接的可执行程序的装载

动态链接的过程中,内核做了什么

ldd test

可执行程序需要依赖动态链接库,而这个动态链接库可能会依赖其他的库,这样形成了一个关系图;

interpreter:需要依赖动态链接器进行加载这些库并进行解析(这就是一个图的遍历),装载所有需要的动态链接库;

之后ld将CPU的控制权交给可执行程序;

所以,动态链接的过程主要是动态链接器在起作用。

"Linux内核分析"第七周的更多相关文章

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  8. linux内核分析 第七周

    一.课堂相关 (一)预处理.编译.链接和目标文件的格式 1.可执行程序是怎么得来的 C代码--预处理--汇编代码--目标代码--可执行文件 预处理负责把include的文件包含进来及宏替换工作. he ...

  9. Linux内核分析——第七周学习笔记20135308

    第七周 可执行程序的装载 一.预处理.编译.链接和目标文件的格式 1.可执行程序是怎么来的 C代码—>预处理—>汇编代码—>目标代码—>可执行文件 .asm汇编代码 .o目标码 ...

随机推荐

  1. 浅谈tidb事务与MySQL事务之间的区别

    MySQL是我们日常生活中常见的数据库,他的innodb存储引擎尤为常见,在事务方面使用的是扁平事务,即要么都执行,要么都回滚.而tidb数据库则使用的是分布式事务.两者都能保证数据的高一致性,但是在 ...

  2. 如何开启win10的上帝模式

    用了这么久的电脑,小编才知道还有“上帝模式”这一说,原谅小编的孤陋寡闻.翻阅资料才知道,上帝模式简单来说就是一个全能的控制面板,如控制面板的功能.界面个性化.辅助功能选项等方方面面的控制设置,几乎包含 ...

  3. Linux 小知识翻译 - 「分区」

    安装Linux的时候,需要对硬盘进行分区.那么「分区」到底是什么呢? 「分区」在日语中有区分,分割的意思.计算机术语中有时会说「对一个磁盘进行分区」,整个意思就是指定如何分割磁盘的意思. 「对磁盘进行 ...

  4. Python3编写网络爬虫04-爬取猫眼电影排行实例

    利用requests库和正则表达式 抓取猫眼电影TOP100 (requests比urllib使用更方便,由于没有学习HTML系统解析库 选用re) 1.目标 抓取电影名称 时间 评分 图片等 url ...

  5. python之面向对象进阶2

    封装.property装饰器 封装分为3种情况:封装对象的属性.封装类的属性.封装方法. 封装对象的属性:(在属性名前加双下划线__) class Person: def __init__(self, ...

  6. Scout YYF I POJ - 3744(矩阵优化)

    题意:一条路上有n个地雷,给出地雷的位置.某人从起点(位置1)出发,走一步的概率是p,走两步的概率是(1-p),然后问有多少概率走过这个雷区. 思路: 只要走过最后一个地雷就代表走过雷区了. 而每到 ...

  7. 30个你 “ 不可能全部会做 ” 的javascript题目

    1,以下表达式的运行结果是: ["1","2","3"].map(parseInt) A.["1","2&qu ...

  8. 判断MS SQLSERVER临时表是否存在

    drop table  #tempcitys select * into #tempcitys from hy_citys 上面的语句第一次运行的时候就肯定出错了,但第二次就不会.因为select * ...

  9. [转]QGis2.9在windows下的编译以及二次开发包下载

    今天心血来潮,将QGis在github上的代码更新后,又编译了一下.留意到源代码包里面的INSTALL文件有更新,于是本次编译完全基于官方的编译说明.编译过程非常顺利,除了在CMake的第一次conf ...

  10. ESP32 ADC

    2个12位的ADC,共计18通道,ADC2比较特殊的一点就是:ADC2和wifi共用,wifi的优先级更高,所以ADC2只有在WIFI模块不用的情况下好使: 在读取ADC数据之前,必须先对ADC进行设 ...