原创作品转载请注明出处 + 《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. legoblock秀上限

    很久没有做题了,前天做了一道题结果弱的一逼...搜了解题报告不说...还尼玛秀了上限 题意: 给出宽和高为n和m的一堵墙,手上有长为1,2,3,4高均为1的砖,问形成一个坚固的墙有多少种做法. 坚固的 ...

  2. sql server2008 搭建链接服务器成功后查询时报Cannot obtain the schema rowset "DBSCHEMA_TABLES_INFO" for OLE DB provider "SQLNCLI10" for linked server "XXXXX". 的解决方法

    这是由于链接的数据库服务器的版本与本地数据库服务器不一致,有人说要升到sp3,sp4,然后在执行什么语句之类的 我觉得太繁琐了,通过网上查询之后看到可以这么做: USE master GRANT EX ...

  3. 你应该知道的8个Java牛人

    简单介绍一下8个Java牛人,他们为Java社区,创建了框架(framework),产品或者是写书,影响甚至改变了Java开发的方法 8.Tomcat创始人 James Duncan Davidson ...

  4. Thinking in C++: Pointers to members 指向成员的指针

    通常来说,一个指针(pointer)是一个存储地址的变量,你能在运行时去改变它,并且指针可以指向数据或函数. 但在C++中,指向成员的指针(pointer-to-member)指向的是class或st ...

  5. SQL Server不区分大小写的问题

    SQL Server不区分大小写的问题   默认情况下,SQL Server不区分大小写,如果数据表TEST的TNAME列中有数据“abcd”和“Abcd”, 如果使用查询语句:select * fr ...

  6. 批量更新数据小心SQL触发器的陷阱

    批量更新数据时候,Inserted和Deleted临时表也是批量的,但触发器只会调用执行一次!两个概念千万不要弄混淆! 错误的理解:例如:创建在A表上创建了一个Update触发器,里面写的是Updat ...

  7. Json序列化、反序列化互换

    // 序列化 using (MemoryStream stream = new MemoryStream()) { serializer.WriteObject(stream, hdm); jsonT ...

  8. Scriptcase演示程序

    为了方便大家对Scriptcase有一个非常直观的印象,现在将Scriptcase的示例程序进行了适当汉化,并且放在互联网上,您可以直接点击这个地址访问. 需要说明的是,示例程序中提供的范例,基本上都 ...

  9. [Javascript] Log Levels and Semantic Methods

    Go beyond console.log by learning about log levels, filtering log output and structuring your output ...

  10. 如何在macos下创建文件或者文件夹的快捷方式

    用的时间久了就发现一次次的打开finder的次数多了,每次打开每次都要一层层的去点开一个个文件夹,太复杂了,然而右键也没有windows中发送到快捷方式到桌面的选项 于是Google一下,按住comm ...