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

  1. 题目自拟,内容围绕对Linux内核如何装载和启动一个可执行程序

  2. 可以结合实验截图、ELF可执行文件格式、用户态的相关代码等

  3. 博客内容中需要仔细分析新可执行程序的执行起点及对应的堆栈状态等

  4. 总结部分需要阐明自己对“Linux内核装载和启动一个可执行程序”的理解

实验报告:

  1. 理解编译链接的过程和ELF可执行文件格式;

    1. C代码——预处理——汇编代码——目标代码——可执行文件
    2. gcc -E hello.c -o hello.i

      gcc –S hello.i –o hello.s
       gcc –c hello.s –o hello.o

    1. ELF可执行文件格式
    2. 预处理负责把include的文件包含进来及宏替换工作。 hello和hello.o都是ELF格式的文件
    3. ELF是一个可重定位(relocatable)文件保存着代码和适当的数据,用来和其他的object文件一起来创建一个可执行文件或者是一个共享文件
  2. 编程使用exec*库函数加载一个可执行文件,动态链接分为可执行程序装载时动态链接和运行时动态链接,编程练习动态链接库的这两种使用方式;

    1. 静态链接的ELF可执行文件和进程的地址空间
      程序从0x804800开始
    2. 动态链接加载前准备的工作

      (1)命令行参数和shell环境,一般我们执行一个程序的Shell环境,我们的实验直接使用execve系统调用。

      • $ ls -l /usr/bin 列出/usr/bin下的目录信息
      • Shell本身不限制命令行参数的个数,命令行参数的个数受限于命令自身
        • 例如,int main(int argc, char *argv[])
        • 又如, int main(int argc, char *argv[], char *envp[])
      • Shell会调用execve将命令行参数和环境参数传递给可执行程序的main函数
        • int execve(const char * filename,char * const argv[ ],char * const envp[ ]);
        • 库函数exec*都是execve的封装例程

      (2)命令行参数和环境变量是如何传递和保存的

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

    3. 装载时动态链接和运行时动态链接应用举例

      动态链接分为可执行程序装载时动态链接和运行时动态链接,如下代码演示了这两种动态链接。

      • 准备.so文件

      shlibexample.h (1.3 KB) - Interface of Shared Lib Example 
      shlibexample.c (1.2 KB) - Implement of Shared Lib Example

      编译成libshlibexample.so文件

      1
      $ gcc -shared shlibexample.c -o libshlibexample.so -m32

      dllibexample.h (1.3 KB) - Interface of Dynamical Loading Lib Example 
      dllibexample.c (1.3 KB) - Implement of Dynamical Loading Lib Example

      编译成libdllibexample.so文件

      1
      $ gcc -shared dllibexample.c -o libdllibexample.so -m32 #编译方式和上面一样
      • 分别以共享库和动态加载共享库的方式使用libshlibexample.so文件和libdllibexample.so文件

      main.c (1.9 KB) - Main program 
      编译main,注意这里只提供shlibexample的-L(库对应的接口头文件所在目录)和-l(库名,如libshlibexample.so去掉lib和.so的部分),并没有提供dllibexample的相关信息,只是指明了-ldl

      1
      2
      3
      4
      5
      6
      7
      8
      $ gcc main.c -o main -L/path/to/your/dir -lshlibexample -ldl -m32
      $ export LD_LIBRARY_PATH=$PWD #将当前目录加入默认路径,否则main找不到依赖的库文件,当然也可以将库文件copy到默认路径下。
      $ ./main
      This is a Main program!
      Calling SharedLibApi() function of libshlibexample.so!
      This is a shared libary!
      Calling DynamicalLoadingLibApi() function of libdllibexample.so!
      This is a Dynamical Loading libary!
  3. 使用gdb跟踪分析一个execve系统调用内核处理函数sys_execve ,验证您对Linux系统加载可执行程序所需处理过程的理解
    1.首先将exec函数写入test.c 函数

    2.重新make rootfs 发现已经成功写进一个exec 函数

    3.运行一下exec 

    4.正式来调试了设置断点

    5.单步进入第一个断点

    6.单步进入第二个断点

    7.单步进入第三个断点

    8.查看new_ip 地址

    9.查看hello文件信息 ==》 发现elf文件的hello入口地址就是new_ip的地址

  4. 特别关注新的可执行程序是从哪里开始执行的?为什么execve系统调用返回后新的可执行程序能顺利执行?对于静态链接的可执行程序和动态链接的可执行程序execve系统调用返回时会有什么不同?

    新的可执行程序通过修改内核堆栈eip作为新程序的起点,
    从new_ip开始执行后start_thread把返回到用户态的位置从int 0x80的下一条指令变成新加载的可执行文件的入口位置。

    当执行到execve系统调用时,进入内核态,用execve()加载的可执行文件覆盖当前进程的可执行程序,
    当execve系统调用返回时,返回新的可执行程序的执行起点(main函数),所以execve系统调用返回后新的可执行程序能顺利执行。

    execve系统调用返回时,如果是静态链接,elf_entry指向可执行文件规定的头部(main函数对应的位置0x8048***);如果需要依赖动态链接库,elf_entry指向动态链接器的起点。动态链接主要是由动态链接器ld来完成的。

  5. 总结

    装载和启动一个可执行程序依次调用以下函数:

    sys_execve() -> do_execve() -> do_execve_common() -> exec_binprm() -> search_binary_handler() -> load_elf_binary() -> start_thread()

    可执行程序的装载与庄生梦蝶的故事

    可执行文件开始执行的起点在哪里?如何才能让execve系统调用返回到用户态时执行新程序?

    • 庄生梦蝶 —— 醒来迷惑是庄周梦见了蝴蝶还是蝴蝶梦见了庄周?

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

    • 修改int 0x80压入内核堆栈的EIP

    • load_elf_binary -> start_thread

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

    (1)可以关注ELF格式中的interp和dynamic。

    (2)动态链接库的装载过程是一个图的遍历。

    (3)装载和连接之后ld将CPU的控制权交给可执行程序。

实验七:Linux内核如何装载和启动一个可执行程序的更多相关文章

  1. 20135202闫佳歆--week 7 Linux内核如何装载和启动一个可执行程序--实验及总结

    week 7 实验:Linux内核如何装载和启动一个可执行程序 1.环境搭建: rm menu -rf git clone https://github.com/megnning/menu.git c ...

  2. 作业七:Linux内核如何装载和启动一个可执行程序

    作业七:Linux内核如何装载和启动一个可执行程序 一.编译链接的过程和ELF可执行文件格式 可执行文件的创建——预处理.编译和链接 在object文件中有三种主要的类型. 一个可重定位(reloca ...

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

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

  4. linux内核分析 第七周 Linux内核如何装载和启动一个可执行程序

    一.编译链接的过程和ELF可执行文件格式 vi hello.c gcc -E -o hello.cpp hello.c -m32 //预处理.c文件,预处理包括把include的文件包含进来以及宏替换 ...

  5. Linux内核设计第七周学习总结 Linux内核如何装载和启动一个可执行程序

    陈巧然原创作品 转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-100002900 实验目的 使用gdb跟踪s ...

  6. Linux内核如何装载和启动一个可执行程序(转)

    原文:http://www.cnblogs.com/petede/p/5351696.html 实验七:Linux内核如何装载和启动一个可执行程序 姓名:李冬辉 学号:20133201 注: 原创作品 ...

  7. 20135323符运锦----第七周:Linux内核如何装载和启动一个可执行程序

    可执行程序的装载 一.预处理.编译.链接和目标文件的格式 1.可执行程序是怎么得来的 ①编译器预处理 gcc -E -o XX.cpp XX.c (-m32)// 注:把include的文件包含进来, ...

  8. 第七周——Linux内核如何装载和启动一个可执行程序

    万子惠 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 part1 实验 ...

  9. linux内核分析第七周-Linux内核如何装载和启动一个可执行程序

    一.可执行文件的创建 可执行文件的创建就是三步:预处理.编译和链接. cd Code vi hello.c #写入最简单的helloworld的c程序 gcc -E -o hello.cpp hell ...

随机推荐

  1. Android MVC框架模式

    MCV  model view controller  模型-视图-控制写 M层:适合做一些业务逻辑处理,比如数据库存取操作,网络操作,复杂的算法,耗时的任务等都在model层处理. V层:应用层中处 ...

  2. Jquery中的事件绑定$(&quot;#btn&quot;).bind(&quot;click&quot;,function(){ })

    Jquery中的事件绑定:$("#btn").bind("click",function(){  }) 由于每次都这么调用太麻烦,所以jquery就用$(&qu ...

  3. 一起学android之怎样获取手机程序列表以及程序相关信息并启动指定程序 (26)

    效果图: 程序列表: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaGFpX3FpbmdfeHVfa29uZw==/font/5a6L5L2T/fonts ...

  4. [转] 剖析 epoll ET/LT 触发方式的性能差异误解(定性分析)

    http://blog.chinaunix.net/uid-17299695-id-3059078.html PS:Select和Poll都是水平触发,epoll默认也是水平触发 ET模式仅当状态发生 ...

  5. NSPredicate查询日期的问题

    查询日期的时候日期可以根据参数传进去,但不能在字符串中传参后在作为查询条件 简单比较以下两段代码 NSDate* date1=[NSDate date]; NSDate* date2=date1; r ...

  6. sed 批量替换多个文件里的某个字符/串

    提示: 国际惯例使用前先备份 sed -i "s/a/b/g" `grep 'a' -rl ./`

  7. RxJava RxAndroid【简介】

    资源 RxJava:https://github.com/ReactiveX/RxJava RxAndroid :https://github.com/ReactiveX/RxAndroid 官网:h ...

  8. codevs2492上帝造题的七分钟 2(线段树)

    /* 区间修改 区间查询 可以用线段树搞 但是一般的标记下放对这个题好像不合适 只能改叶子 然后更新父亲(虽然跑的有点慢) 小优化:如果某个点是1 就不用再开方了 所以搞一个f[i]标记 i 这个点还 ...

  9. C#中的IO流操作(FileStream)

    StreamReader和StreamWriter适用于对文本文件的操作,因为它是以字符为单位进行的操作 不用担心编码问题 using (Stream s = new FileStream(@&quo ...

  10. Jquery小东西收集

    1. $(document).ready(),$(function(){}),$(window).load(),window.onload的关系与区别 $(document).ready(functi ...