Linux内核实验作业七
实验作业:Linux内核如何装载和启动一个可执行程序
20135313吴子怡.北京电子科技学院
【第一部分】理解编译链接的过程和ELF可执行文件格式
1.编译链接的过程

2.ELF可执行文件格式
一个可重定位(relocatable)文件保存着代码和适当的数据,用来和其他的object文件一起来创建一个可执行文件或者是一个共享文件。
一个可执行(executable)文件保存着一个用来执行的程序;该文件指出了exec(BA_OS)如何来创建程序进程映象。
一个共享object文件保存着代码和合适的数据,用来被不同的两个链接器链接。
3.流程图:(execve–> do——execve –> search_binary_handle –> load_binary)

4.堆栈变化图(参数块和环境块如何被传到新进程):

【第二部分】编程使用exec*库函数加载一个可执行文件,动态链接分为可执行程序装载时动态链接和运行时动态链接
这个部分的实践我是迷茫的。但是根据我查找的资料和学习的内容。可以借他山之石来填充我的学习记录。我整理如下:
两种动态库实现文件比较起来没有过多不同。差异关键在调用端:main函数
①比较库的实现

发现:没有功能差异,右侧仅仅多个额外的宏定义。
②比较库头文件

分析如上。
③验证
- 编译库
gcc -shared shlibexample.c -o libshlibexample.so -m32
gcc -shared dllibexample.c -o libdllibexample.so -m32
分析:和常规的可执行文件比较,这里注意的就是要加上 -shared标志
- 编译Main
gcc main.c -o main -L/media/sda_m/SharedLibDynamicLink -lshlibexample -ldl -m32
额外需要注意的就是这里library路径需要指明当前的路径。直接-L是不可行的。
- 出现典型的找不到动态库的错误
#错误情形
/usr/bin/ld: cannot find -lshlibexample
collect2: ld returned 1 exit status
- 设置动态库查找的位置
export LD_LIBRARY_PATH=$PWD
如果不设置,将会报找不到动态库
#错误情形
./main: error while loading shared libraries: libshlibexample.so: cannot open shared object file: No such file or directory
- 成功调用动态库和运行时动态库的函数

【第三部分】使用gdb跟踪分析execve系统调用内核处理函数sys_execve ,验证对Linux系统加载可执行程序所需处理过程的理解
①设置以下断点:


②在MenuOS执行exec后,中断情况如下:



③进入search_binary_handler后可以查看一些变量情况(fmt,bprm)


④进入start_thread后使用po命令,可以看到new_ip:

⑤在改变regs前后查看regs


⑥继续跟踪可以看到在执行do_notify_resume后,进入0x08048d0a处,即之前的new_ip处(hello的入口地址):


<分析sys_execve>
**当sys_execve被调用后,涉及的主要函数为:do_execve -> do_execve_common -> exec_binprm
①syscall
SYSCALL_DEFINE3(execve,
const char __user *, filename,
const char __user *const __user *, argv,
const char __user *const __user *, envp)
{ //真正执行程序的功能exec.c文件中的do_execve函数中实现
return do_execve(getname(filename), argv, envp); }
②do_execve
int do_execve(struct filename *filename,
const char __user *const __user *__argv,
const char __user *const __user *__envp)
{
struct user_arg_ptr argv = { .ptr.native = __argv };
struct user_arg_ptr envp = { .ptr.native = __envp };
//调用do_execve_common
return do_execve_common(filename, argv, envp);
}
③do_execve_common
static int do_execve_common(struct filename *filename,
struct user_arg_ptr argv,
struct user_arg_ptr envp)
{
struct linux_binprm *bprm;
struct file *file;
struct files_struct *displaced;
int retval; .. //打开要执行的文件,并检查其有效性
file = do_open_exec(filename);
retval = PTR_ERR(file);
if (IS_ERR(file))
goto out_unmark; sched_exec();
// 填充linux_binprm结构
bprm->file = file;
bprm->filename = bprm->interp = filename->name; ... //将文件名、环境变量和命令行参数拷贝到新分配的页面中
retval = copy_strings_kernel(1, &bprm->filename, bprm);
if (retval < 0)
goto out;
bprm->exec = bprm->p;
retval = copy_strings(bprm->envc, envp, bprm);
if (retval < 0)
goto out;
retval = copy_strings(bprm->argc, argv, bprm);
if (retval < 0)
goto out;
//调用exec_binprm,保存当前的pid并且调用 search_binary_handler
retval = exec_binprm(bprm);
if (retval < 0)
goto out;
/* execve succeeded */
current->fs->in_exec = 0;
current->in_execve = 0;
acct_update_integrals(current);
task_numa_free(current);
free_bprm(bprm);
putname(filename);
if (displaced)
put_files_struct(displaced);
return retval; }
<分析>关于linux_binprm保存要执行的文件相关的参数,包括argc,envc,filename,interp
exec_binprm在保存了bprm后调用该函数来进一步操作,这个函数除了保存pid以外,还执行了ret = search_binary_handler(bprm);来查询能够处理相应可执行文件格式的处理器,并调用相应的load_binary方法以启动进程。
④search_binary_handler
int search_binary_handler(struct linux_binprm *bprm)
{
...
//循环binary formats handler,直到找到
retry:
read_lock(&binfmt_lock);
list_for_each_entry(fmt, &formats, lh) {
if (!try_module_get(fmt->module))
continue;
read_unlock(&binfmt_lock);
bprm->recursion_depth++;
//解析elf格式执行的位置
retval = fmt->load_binary(bprm);
read_lock(&binfmt_lock); ...
}
<分析>
- 这里的
fmt是linux_binfmt格式,该结构用来load the binary formats。 - 经由search_binary_handler函数呼叫load_elf_binary函数。
- ELF格式的二进制映像的认领、装入和启动是由load_elf_binary()完成的。
在/fs/binfmt_elf.c中,定义了如下结构:
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,
};
⑤load_elf_binary
static int load_elf_binary(struct linux_binprm *bprm)
{
... //获取头
loc->elf_ex = *((struct elfhdr *)bprm->buf); //读取头信息
if (loc->elf_ex.e_phentsize != sizeof(struct elf_phdr))
goto out;
if (loc->elf_ex.e_phnum < 1 ||
loc->elf_ex.e_phnum > 65536U / sizeof(struct elf_phdr))
goto out;
size = loc->elf_ex.e_phnum * sizeof(struct elf_phdr);
retval = -ENOMEM;
elf_phdata = kmalloc(size, GFP_KERNEL);
if (!elf_phdata)
goto out;
... //读取可执行文件的解析器
for (i = 0; i < loc->elf_ex.e_phnum; i++) {
if (elf_ppnt->p_type == PT_INTERP) {
...
} ... //如果需要装入解释器,并且解释器的映像是ELF格式的,就通过load_elf_interp()装入其映像,并把将来进入用户空间时的入口地址设置成load_elf_interp()的返回值,那显然是解释器的程序入口。而若不装入解释器,那么这个地址就是目标映像本身的程序入口。
if (elf_interpreter) {
unsigned long interp_map_addr = 0; elf_entry = load_elf_interp(&loc->interp_elf_ex,
interpreter,
&interp_map_addr,
load_bias);
if (!IS_ERR((void *)elf_entry)) { interp_load_addr = elf_entry;
elf_entry += loc->interp_elf_ex.e_entry;
}
if (BAD_ADDR(elf_entry)) {
retval = IS_ERR((void *)elf_entry) ?
(int)elf_entry : -EINVAL;
goto out_free_dentry;
}
reloc_func_desc = interp_load_addr; allow_write_access(interpreter);
fput(interpreter);
kfree(elf_interpreter);
} else {
elf_entry = loc->elf_ex.e_entry;
if (BAD_ADDR(elf_entry)) {
retval = -EINVAL;
goto out_free_dentry;
}
}
<分析>当load_elf_binary()执行完毕,返回至do_execve()在返回至sys_execve()时,系统调用的返回地址已经被改写成了被装载的ELF程序的入口地址了。
***新的可执行程序是从哪里开始执行的?
当sys_execve()系统调用从内核态返回到用户态时,EIP寄存器直接跳转到ELF程序的入口地址。
【第四部分】总结
“Linux内核装载和启动一个可执行程序”
linux通过sys_execve()系统调用从文件系统中读取、识别并加载elf。
调用sys_execve后,执行过程:
do_execve -> do_execve_common -> exec_binprm->load_elf_binary()->sys_close
根据elf的库类型,elf_entry是不一样的。load_elf_binary通过解析器将不同的入口地址写入。
【第五部分】附录
作者:吴子怡
学号:20135313
原创作品转载请注明出处
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
Linux内核实验作业七的更多相关文章
- Linux内核实验作业四
实验作业:使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用 20135313吴子怡.北京电子科技学院 [第一部分]使用库函数API来获取用户标识号.库函数为getuid() 代码如下: ...
- Linux内核实验作业六
实验作业:分析Linux内核创建一个新进程的过程 20135313吴子怡.北京电子科技学院 [第一部分]阅读理解task_struct数据结构 1.进程是计算机中已运行程序的实体.在面向线程设计的系统 ...
- Linux内核设计第七周 ——可执行程序的装载
Linux内核设计第七周 ——可执行程序的装载 第一部分 知识点总结 一.预处理.编译.链接和目标文件的格式 1.可执行程序是怎么得来的 编译链接的过程 预处理阶段 gcc -E -o XX.cpp ...
- 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内核分析(七) 这两天家里的事好多,我们今天继续接着上一次的内容学习,上次我们完善了字符设备控制方法,并深入分析了系统调用的实质,今天我们主 ...
随机推荐
- SDN2017 期末作业验收
GIT仓库:GITHUB 负载均衡程序 拓扑如图 目的 实现一个负载均衡的北向程序: 服务器host 2 ,host 3,host 4上各自有不同的服务,host 1是客户端 当host 2,host ...
- [Android自动化] 在 pip-9.0.1 版本情况下安装 uiautomator2 报错的解决办法
1.在命令窗口中使用命令: pip install uiautomator2 时报 pip 版本过低,需要先升级 pip 版本,理论上会按照提示进行升级 pip 操作,但执行升级命令时到最后却还是报错 ...
- 【剑指offer】数值的整数次方
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/mmc_maodun/article/details/25506085 转载请注明出处:http:// ...
- Git——新手入门与上传项目到远程仓库GitHub(转)
Git概述 什么是Git? 刚开始对这个东西也感到挺迷茫,并且问了好多已经学习android一段时间的同学也是一头雾水,直到了解并使用之后,才体会到Git的好处以及重要意义. Git:是目前世界上最先 ...
- nginx中server的匹配顺序
在开始处理一个http请求时,nginx会取出header头中的host,与nginx.conf中每个server的server_name进行匹配,以此决定到底由哪一个server块来处理这个请求. ...
- 前端性能优化:Add Expires headers
前端性能优化:Add Expires headers Expires headers 是什么? Expires headers:直接翻译是过期头.Expires headers 告诉浏览器是否应该从服 ...
- php实现远程网络文件下载到服务器指定目录(方法二)
<?php // maximum execution time in seconds set_time_limit (24 * 60 * 60); //if (!isset($_POST['su ...
- LED驱动电源
LED驱动电源 LED驱动电源,你了解多少? LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电).低压直 ...
- Eclipse-快捷键大全(转载)
快速展开类:ctrl + shift +*(小键盘) 快速关闭类:ctrl+ shift + /(小键盘) Ctrl+1 快速修复(最经典的快捷键,就不用多说了)Ctrl+D: 删除当前行 Ctrl ...
- 将jar文件加到Maven的local repository中
对于Maven项目来说,日常使用的多数第三方java库文件都可以从Maven的Central Repository中自动下载,但是如果我们需要的jar文件不在Central Repository中,那 ...