原创作品转载请注明出处 + 《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. [转] 关于C++中模板中的typename和class的区别比较

    C++箴言:理解typename的两个含义 转自http://blog.csdn.net/dick_china/article/details/4522253 问题:在下面的 template dec ...

  2. 从SG函数浅谈解决博弈问题的通法

    基于笔者之前对于几种二元零和博弈游戏的介绍,这里将其思想进行简单的提炼,并引出解决这类二元零和博弈游戏的强大工具——SG函数. 其实对于博弈游戏如Bash.Nim等基本类型,异或一些比较高级的棋类游戏 ...

  3. Huffman编码实现电文的转码与译码

    //first thing:thanks to my teacher---chenrong      Dalian Maritime university /* 构造Huffman Tree思路: ( ...

  4. 看小白如何解决ajax跨域问题

    由于此前很少写前端的代码(哈哈,不合格的程序员啊),最近项目中用到json作为系统间交互的手段,自然就伴随着众多ajax请求,随之而来的就是要解决ajax的跨域问题.本篇将讲述一个小白从遇到跨域不知道 ...

  5. halt和shutdown 的区别

    1.halt -h 标准情况下是关机 但是要手动关闭电源 .有些发行版增强了halt脚本 使其可以关闭电源 halt执行时﹐杀死应用进程﹐执行sync系统调用﹐文件系统写操作完成后就会停止内核. 2. ...

  6. 用htaccess进行访问控制(转)

    1. 文件访问控制 利用 httpd.conf 中的 Order.Files 及 FilesMatch 命令实现的访问控制可以满足大部分要求,但是当用户被拒绝时,他们看到的是硕大的“403 Forbi ...

  7. 安全框架Shiro和Spring Security比较

    Shiro 首先Shiro较之 Spring Security,Shiro在保持强大功能的同时,还在简单性和灵活性方面拥有巨大优势. Shiro是一个强大而灵活的开源安全框架,能够非常清晰的处理认证. ...

  8. [转] C++临时变量的生命周期

    http://www.cnblogs.com/catch/p/3251937.html C++中的临时变量指的是那些由编译器根据需要在栈上产生的,没有名字的变量. 主要的用途主要有两类: 1) 函数的 ...

  9. android ImageView scaleType属性(转)

    使用ImageView时经常会用到scaleType属性,如: 1 2 3 4 5 6 7 8 9 <ImageView   android:layout_width="50dp&qu ...

  10. ACM vim配置

    ACM现场赛时用的,比较简短,但是主要的功能都有了. 直接打开终端输入gedit ~/.vimrc 把下面的东西复制到里面就行了. filetype plugin indent on colo eve ...