"Linux内核分析"第七周
可执行程序的装载
张文俊+原创作品转载请注明出处+《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内核分析"第七周的更多相关文章
- LINUX内核分析第七周学习总结:可执行程序的装载
LINUX内核分析第七周学习总结:可执行程序的装载 韩玉琪 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/cours ...
- Linux内核分析 第七周 可执行程序的装载
张嘉琪 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 Linux内核分析 第七 ...
- LINUX内核分析第七周学习总结
LINUX内核分析第七周学习总结 标签(空格分隔): 20135328陈都 陈都 原创作品转载请注明出处 <Linux内核分析>MOOC课程 http://mooc.study.163.c ...
- Linux内核分析第七周———可执行程序的装载
Linux内核分析第七周---可执行程序的装载 李雪琦+原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/US ...
- Linux内核分析第七周学习笔记——Linux内核如何装载和启动一个可执行程序
Linux内核分析第七周学习笔记--Linux内核如何装载和启动一个可执行程序 zl + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study. ...
- LINUX内核分析第七周学习总结——可执行程序的装载
LINUX内核分析第六周学习总结——进程的描述和进程的创建 张忻(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/cours ...
- 20135327郭皓--Linux内核分析第七周 可执行程序的装载
第七周 可执行程序的装载 郭皓 原创作品转载请注明出处 <Linux内核分析>MOOC课程 http://mooc.study.163.com/course/USTC-1000029000 ...
- linux内核分析 第七周
一.课堂相关 (一)预处理.编译.链接和目标文件的格式 1.可执行程序是怎么得来的 C代码--预处理--汇编代码--目标代码--可执行文件 预处理负责把include的文件包含进来及宏替换工作. he ...
- Linux内核分析——第七周学习笔记20135308
第七周 可执行程序的装载 一.预处理.编译.链接和目标文件的格式 1.可执行程序是怎么来的 C代码—>预处理—>汇编代码—>目标代码—>可执行文件 .asm汇编代码 .o目标码 ...
随机推荐
- Linux结构目录
linux结构目录 Linux中有一句话叫做:一切皆文件. 下面来了解一下这些文件. 首先看一下Linux根目录下结构: bin:存放二进制可执行文件,一般常用命令都存放在这里. boot:存放系统启 ...
- 为PHP7安装Windows Server 2012 R2过程记录
因为要安装php-7.0.6-Win32-VC14-x64,需要先安装vcredist2015_x64_14.0.23026.0. 之前安装了Windows Server 2012 R2后,一直无法成 ...
- February 8th, 2018 Week 6th Thursday
When you fall in love, friends, let yourself fall. 当你坠入爱河,我的朋友,你就放手去爱吧. To love someone is like movi ...
- 借鉴bootstrap的方法,快速实现响应式开发
响应式开发 注意:任何框架都是一个辅助手段,只需借鉴其中的核心思想,把其中核心的东西提炼出来即可.不要因为,提到响应式就想到只能够用bootstrap来实现,框架现有的东西是可以提高我们的效率,但是其 ...
- 深入浅出RxJava(二:操作符)
看完这篇blog,我相信你肯定想立即在你的项目中使用RxJava了,这篇blog将介绍许多RxJava中的操作符,RxJava的强大性就来自于它所定义的操作符. 首先先看一个例子: 准备工作 假设我有 ...
- eclipse 右键发现没有 build-path
1)确认下是否有.project和.classPath文件 2)点击右上角按钮先切换到java下,默认方式是javaEE 然后就能出现build path了 这是build path 子项为灰色,依然 ...
- zabbix4.0安装和配置
以下为master端安装,开始 内核$ cat /etc/centos-release CentOS Linux release 7.4.1708 (Core) $ uname -r 3.10.0-6 ...
- MySQL(九)插入、更新和删除
常用的SQL语句,除了select用于查询,还有insert.update.delete等. 一.insert insert:用来插入(或添加)行到数据库中,常见方式有以下几种: ①插入完整的行: ② ...
- SourceInsight工具增强——AStyle(代码格式化)、PC-Lint(静态检查)
Artistic Style(AStyle) AStyle是一款开源.高效.精简的代码格式化工具,适用于C.C++.C#.Java等.官方地址在:http://astyle.sourceforge.n ...
- TerraGate软件安装后,不能启动的解决办法
在服务端安装Skyline的TerraGate软件的时候,大家可能会遇到过这样的问题,“TerraGate软件安装后,不能启动”,很多时候,这个问题是因为TerraGate设 置的端口号已经被占用造成 ...