《Linux内核分析》第七周学习总结

《Linux内核分析》第七周学习总结

                        ——可执行程序的装载

姓名:王玮怡  学号:20135116

一、理论部分总结

(一)可执行程序的装载

1、预处理、编译、链接和目标文件的格式

  C代码经过编译器的预处理(.cpp),然后编译成汇编代码(.asm/.s),由汇编器成目标代码(.o,二进制文件),再链接成可执行文件,最后由操作系统加载到内存中执行。

  • 预处理:编译器将C源代码中包含的头文件编译进来和执行宏替换等工作

  gcc -E hello.c -o hello.i

  • 编译:gcc首先要检查代码后,把代码翻译成汇编语言

  gcc –S hello.i –o hello.s

  • 汇编:把编译阶段生成的.s文件转成二进制目标代码

  gcc –c hello.s –o hello.o

  • 链接:将编译输出.o文件链接成最终的可执行文件(hello也是一个二进制文件)

  gcc hello.o –o hello

2、目标文件的格式ELF

(1)目标文件格式分类

(2)ABI和目标文件

ABI:应用程序二进制接口,在目标文件中二进制兼容模式

(3)ELF中三种目标文件

  • 可重定位文件  .o文件
  • 可执行文件
  • 共享目标文件    .so文件

(4)ELF头

*当创建或增加一个进程映像时,系统在理论上将拷贝一个文件的段到一个虚拟的内存段

3、静态链接的ELF可执行文件与进程的地址空间

  • 可执行文件加载到内存中开始执行的第一行
  • 代码一般静态链接会将所有代码放在一个代码段
  • 动态链接的进程会有多个代码段

(二)可执行程序、共享库和动态进程

1、装载可执行程序之前的工作

(1)命令行参数和shell环境

  • 列出/usr/bin下的目录信息:

  $ ls -l /usr/bin

  • Shell本身不限制命令行参数的个数,命令行参数的个数受限于命令自身

  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)命令行参数和环境变量是如何保存和传递的

  • shell程序 —> execve —> sys_execve
  • 命令行参数和环境串都放在用户态堆栈中
  • 初始化新程序堆栈时拷贝进去

  • 先函数调用参数传递,再系统调用参数传递

2、装载时动态链接和运行时动态链接应用

  动态链接分为可执行程序装载时动态链接和运行时动态链接

(三)可执行程序的装载

1、execve系统调用的内核处理过程

(1)新的可执行程序起点——一般是地址空间为0x8048000或0x8048300

(2)execve和fork都是特殊的系统调用——一般的都是陷入到内核态再返回到用户态

  • fork两次返回,第一次返回到父进程继续向下执行,第二次是子进程返回到ret_from_fork然后正常返回到用户态。
  • execve执行的时候陷入到内核态,用execve中加载的程序把当前正在执行的程序覆盖掉,当系统调用返回的时候也就返回到新的可执行程序起点。
  • sys_execve内部会解析可执行文件格式

  do_execve —> do_execve_common —> exec_binprm

  search_binary_handler符合寻找文件格式对应的解析模块

  对于ELF格式的可执行文件fmt->load_binary(bprm);执行的应该是load_elf_binary其内部是和ELF文件格式解析的部分需要和ELF文件格式标准结合起来阅读

*load_elf_binary中,调用了start_thread()函数,通过修改内核堆栈中EIP的值作为新程序的起点

2、sys_execve的内部处理过程

  • 系统调用的入口:do_execve

  return do_execve(getname(filename), argv, envp);

  • 转到do _ execve _ common函数

  return do_execve_common(filename, argv, envp);

  file = do_ open_exec(filename);     //打开要加载的可执行文件,加载它的文件头部

  bprm->file = file;

  bprm->filename = bprm->interp = filename->name;    //创建了一个结构体bprm,把环境变量和命令行参数都copy到结构体中

  • exec_binprm:

  ret = search_binary_handler(bprm);  //寻找此可执行文件的处理函数 在其中关键的代码

  list_for_each_entry(fmt, &formats, lh);

  retval = fmt->load_binary(bprm); //在这个循环中寻找能够解析当前可执行文件的代码并加载出来,实际调用的是load_elf_binary函数

  • 文件解析相关模块:核心的工作就是把文件映射到进程的空间,对于ELF可执行文件会被默认映射到0x8048000。
  • 需要动态链接的可执行文件先加载链接器ld?(load _ elf _ interp 动态链接库动态链接文件),动态链接器的起点
  • 如果它是一个静态链接,可直接将文件地址入口进行赋值

3、可执行程序的装载与“庄生梦蝶”

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

4、动态链接的可执行程序的装载

  • 实际上动态链接库的依赖关系会形成一个“依赖树”
  • 动态链接库的装载过程一般是一个图的广度遍历
  • 动态链接是由动态链接器完成而不是内核

*静态链接:直接执行可执行程序的入口

*动态链接:装载和链接之后ld将CPU的控制权交给可执行程序

实验部分 ——Linux内核如何装载和启动一个可执行程序

(一)搭建环境

(查看代码时,可以使用shift+G直接跳到文件末尾)

(生成根文件系统时,将init hello放入rootfs地址中,这样在执行exec文件时,就自动加载hello文件)

(二)使用gdb跟踪sys_execve内核函数的处理过程

1、加载符号表,并连接到端口1234

2、设置断点

3、执行

输入c继续运行,进入到sys_execve系统调用:

输入s进行跟踪:

new_ip是返回到用户态的第一条指令的地址:

时间: 04-06

《Linux内核分析》第七周学习总结的相关文章

LINUX内核分析第七周学习总结——可执行程序的装载

LINUX内核分析第六周学习总结——进程的描述和进程的创建 张忻(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.知识概要 (一)预处理.编译.链接和目标文件的格式 1.可执行程序是怎么得来的 2.目标文件的格式ELF 3.静态链接的ELF可执行文件和进程的地址空间 (二)可执行程序.共享库和动态加载 1.装载可执行程序之前的工作 2.装载时动态链接和运行时动态链接应用举例 (三)

Linux内核分析——第七周学习笔记20135308

第七周 可执行程序的装载 一.预处理.编译.链接和目标文件的格式 1.可执行程序是怎么来的 C代码—>预处理—>汇编代码—>目标代码—>可执行文件 .asm汇编代码 .o目标码 a.out可执行文件 预处理负责把include的文件包含进来及宏替换工作. 2.目标文件的格式ELF (1)常见的ELF格式文件: (2)ABI——应用程序二进制接口 在目标文件中,他已经是二进制兼容,即适应二进制指令. (3)ELF中三种目标文件: 一个可重定位(relocatable)文件保存着代码和

LINUX内核分析第七周学习总结:可执行程序的装载

一.得到一个可执行程序 1. 预处理.编译.链接 gcc hello.c -o hello.exegcc编译源代码生成最终可执行的二进制程序,GCC后台隐含执行了四个阶段步骤.预处理 => 编译 => 汇编 => 链接预处理:编译器将C源代码中包含的头文件编译进来和执行宏替换等工作.gcc -E hello.c -o hello.i编译:gcc首先要检查代码的规范性.是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc把代码翻译成汇编语言.gcc –S hello.i –o

LINUX内核分析第六周学习总结——进程的描述和进程的创建

LINUX内核分析第六周学习总结——进程的描述和进程的创建 张忻(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.知识概要 进程的描述 进程描述符task_struct数据结构(一) 进程描述符task_struct数据结构(二) 进程的创建 进程的创建概览及fork一个进程的用户态代码 理解进程创建过程复杂代码的方法 浏览进程创建过程相关的关键代码 创建的新进程是从哪里开始执行的

LINUX内核分析第三周学习总结——构造一个简单的Linux系统MenuOS

LINUX内核分析第三周学习总结——构造一个简单的Linux系统MenuOS 黄韧(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 回顾: [计算机三个法宝] 1)存储程序计算机 2)函数调用堆栈 3)中断 [操作系统两把宝剑] 1)中断上下文的切换:保存现场和恢复现场 2)进程上下文的切换 一.使用gdb跟踪调试内核从start_kernel到init进程启动 使用实验楼的虚拟机打开

LINUX内核分析第八周学习总结——进程的切换和系统的一般执行过程

LINUX内核分析第八周学习总结——进程的切换和系统的一般执行过程 黄韧(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.知识概要 Linux中进程调度的基本概念与相关知识 schedule函数如何实现进程调度 Linux进程的执行过程(一般情况与特殊情况) 宏观描述Linux系统执行 二.学习笔记 (一)进程切换的关键代码switch_to分析 进程进度与进程调度的时机分析 1.

《Linux内核分析》第一周学习小结 计算机是如何工作的?

<Linux内核分析>第一周.计算机是如何工作的? 20135204 郝智宇  一.存储程序计算机工作模型 1.      冯诺依曼体系结构: 数字计算机的数制采用二进制:计算机应该按照程序顺序执行. 内存保存指令和数据,CPU负责解释和执行这些指令. 2.    API: 程序员与计算机的接口界面. 3.    EIP: CALL  RET  IMP   二.X86汇编基础 1.      X86CPU的寄存器 E开头32位,R开头64位.   堆栈是计算机中非常基础性的东西. 2.    

20135327郭皓--Linux内核分析第七周 可执行程序的装载

第七周 可执行程序的装载 郭皓 原创作品转载请注明出处 <Linux内核分析>MOOC课程 http://mooc.study.163.com/course/USTC-1000029000 一.预处理,编译,链接和目标文件格式 1.可执行程序是怎么得来的 c代码->预处理->汇编代码->汇编器->目标代码->链接成可执行文件->加载到内核执行 2.目标文件的格式ELF 符号修饰标准.变量内层布局.函数调用方式等这些跟可执行代码二进制兼容性相关的内容称为ABI

&quot;Linux内核分析&quot;第七周

可执行程序的装载 张文俊+原创作品转载请注明出处+<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.预处理.编译.链接和目标文件的格式 可执行程序是怎么得来的? 首先,编译器预处理:1.将头文件加载进来:2.将宏替换 gcc -E -o hello.cpp hello.c (-m32)//c预处理成cpp文件 第二步,将cpp(预处理后的文件)编译成汇编代码 gcc -x cpp-output -S -o h

Linux内核分析第七周作业

Linux内核如何装载和启动一个可执行程序 有了上次的教训,这次直接用vmware完成 (- ̄3 ̄)- 先观察MenuOS新增的函数 1 int Exec(int argc, char *argv[]) 2 { 3 int pid; 4 /* fork another process */ 5 pid = fork(); 6 if (pid < 0) 7 { 8 /* error occurred */ 9 fprintf(stderr,"Fork Failed!"); 10 e