实验作业: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_commonreturn 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_handlerretval = 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

转载于:https://www.cnblogs.com/paperfish/p/5361211.html

Linux内核实验作业七相关推荐

  1. linux内核提取ret2usr,Linux内核实验作业七

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

  2. Linux内核实验作业四

    实验作业:使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用 20135313吴子怡.北京电子科技学院 [第一部分]使用库函数API来获取用户标识号.库函数为getuid() 代码如下: ...

  3. Linux内核实验作业六

    实验作业:分析Linux内核创建一个新进程的过程 20135313吴子怡.北京电子科技学院 [第一部分]阅读理解task_struct数据结构 1.进程是计算机中已运行程序的实体.在面向线程设计的系统 ...

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

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

  5. linux内核态real cred,Linux内核实验报告——实验5.doc

    Linux内核实验报告 实验题目:新系统调用设计实验 实验目的: 系统调用是内核提供给用户应用程序使用的内核函数名,这些函数提供了内核为用户 应用程序所提供的系统服务功能.这些函数在用户应用程序中的书 ...

  6. 十天学Linux内核之第七天---电源开和关时都发生了什么

    十天学Linux内核之第七天---电源开和关时都发生了什么 原文:十天学Linux内核之第七天---电源开和关时都发生了什么 说实话感觉自己快写不下去了,其一是有些勉强跟不上来,其二是感觉自己越写越差 ...

  7. Linux内核学习(七):linux kernel内核启动(一):概述篇

    Linux内核学习(七):linux kernel内核启动(一):概述篇 这一篇让我们来大致的了解一下Linux内核的启动过程 这篇文章不涉及源码,重在让你知道这个linux内核的启动过程,源码详细的 ...

  8. linux内核分析期末,Linux内核分析作业

    Linux内核分析课程期末大作业 一.程序的主要设计思路及实现方式 在Linux内核中,存在一个全局变量:init_task.该变量即是Linux第一个启动的用户空间进程init对应的task_str ...

  9. Linux内核探讨-- 第七章

    本文是个人分析<Linux内核设计与实现>而写的总结,欢迎转载,请注明出处: http://blog.csdn.net/dlutbrucezhang/article/details/136 ...

最新文章

  1. oracle修改用户密码命令_oracle 11g dba用户秘密修改其他用户密码
  2. 腾讯云 Game-Tech 技术沙龙小游戏专场“空降”长沙
  3. 测试keepalived备备模式
  4. 第三步_安装jdk环境
  5. macOS搭建51单片机开发环境
  6. 深入机械制造业供应链关键节点,SCM供应链管理系统全面防控企业供应链风险
  7. 你在用FastReport.Net报表工具做报表没
  8. python语言turtle库画图代码示例_5分钟轻松搞定,Python开发之turtle库的基本操作...
  9. SNF快速开发平台2019-权限管理模型-平台服务(多平台\多组织\SAAS\多系统)
  10. Enhancing the Transferability of Adversarial Attacks through Variance Runing
  11. 生活照的尺寸是多少?如何将照片裁剪为生活照?
  12. 练习-Java类和对象之对象组合之求圆锥体表面积
  13. windows录屏_电脑是怎么录屏的呢?推荐三个录屏实用方法
  14. RT5350使用uboot从U盘启动linux成功
  15. 目前用于微型计算机系统的光盘有哪些,目前用于计算机系统的光盘,分为这三类...
  16. 低代码究竟能干啥?三位一体解决数字化管理的难点痛点
  17. axure8.1.0.3379 注册码破解码授权码license亲测可用
  18. soundtouch android,音频变时不变调处理(SoundTouch WSOLA)
  19. JavaSE第一阶段知识概括
  20. labview c语言定义变量,第一章:LabVIEW 的编程环境 如何使用 VI 的重入属性(Reentrant)...

热门文章

  1. 利用mysqldump 将一个表按条件导出数据
  2. 当程序员说“这代码写的可真烂”,他们的意思是“这烂代码不是我写的”。而当他们说这段代码有些“小问题”时,很可能这代码是他们自己写的...
  3. 操作系统1_进程控制块PCB
  4. CentOS 6.5下源码包安装配置JDK 7
  5. ios快速将显卡中数据读出压缩成视频--cocos2dx扩展
  6. NGRAIN在模型轻量化上的应用
  7. 鼠标悬停在li标签上时显示一个弹框_HTML标签——列表标签
  8. PostgreSQL 12系统表(3)pg_tablespace
  9. linux 1060显卡,Steam最受欢迎显卡型号出炉:GTX 1060显卡的占有率依然排名第一
  10. java贪吃蛇撞壁转弯代码_java贪吃蛇demo