Linux内核分析——可执行程序的装载

链接的过程

  • 首先运行C预处理器cpp,将C的源程序(a.c)翻译成ASCII码的中间文件(a.i)
  • 接着C编译器ccl,将a.i翻译成ASCII汇编语言文件a.s
  • 接着运行汇编器as,将a.s翻译成可重定位的目标文件a.o
  • 最终完全链接成可执行文件a.out

目标文件

  • 目标文件有三种形式:

    • 可重定位的目标文件
    • 可执行目标文件
    • 共享目标文件
  • ELF格式的可重定位目标文件的结构如下:
  • .text:已编译程序的机器代码
  • .rodata:只读数据
  • .data:已初始化的全局C变量
  • .bss:未初始化的全局C变量.在目标文件中这个节不占实际空间,仅是一个占位符.
  • .sysmtab:一个符号表,存放在程序中被定义和引用的函数和全局变量的信息.
  • .rel.text:当链接器把这个目标文件和其他文件结合时,.text节中的许多位置都需要修改.一般而言,任何调用外部函数或者引用全局变量的指令都要修改.另一个方面,调用本地函数的指令则不需要修改.
  • .rel//.data:被模块定义或引用的任何全局变量的信息.
  • .debug:一个调试符号表
  • .line:原始C源程序中的行号和.text节中机器指令之间的映射.
  • .strtab:一个字符串表,其中内容包括.symtab和.debug节中的符号表,以及节头部中的节名字.
  • 可以通过readelf -h process查看的ELF文件的头部信息

如何将新程序的数据保存下来

  • 通过shell程序调用execve将命令行参数和环境参数传递给可执行程序的main函数中。而后execve在创建新的用户态堆栈时,则将main函数中参数压入堆栈中。最终执行sys_execve来真正实现在系统下参数的传递。
  • 当新的可执行文件被调用的时候,则旧的可执行文件所占有的空间会被新的可执行文件所占用,从而execve返回时,返回的并非为旧的可执行文件所产生的数据,而是新加载进来的可执行文件的返回数据,从而使新的可执行文件可以被执行。

可执行文件的相关点

  • start_thread通过修改内核堆栈中EIP的值作为新程序的起点
  • 根据静态链接的可执行文件elf_entry就是可执行文件头中的起点entry,多为main函数对应的位置
  • 若需要依赖动态链接库的话,则elf_entry则指向动态链接器的起点,即将CPU控制权交给ld来加载依赖库并完成动态链接
  • 新的可执行程序被调用前,需要通过修改int 0x80压入内核堆栈的EIP
  • elf可执行文件会被默认映射到0x8048000这个地址上

execve在内核中的执行过程

  • execve运行可执行程序的主要步骤:

    • 删除已存在的用户区域:删除当前可执行文件所占有的用户部分中的堆栈空间
    • 隐藏私有区域:为新程序的文本、数据和堆栈创建新的他空间,而这些空间是新的可执行文件所私有的,并且是写时拷贝的。
    • 映射共享区域:如果ELF文件与共享目标连接,就需要动态链接,并映射至用户虚拟地址空间中的共享区域。
    • 设置程序计数器:设置EIP,使其指向新的可执行文件的入口地址
    • 如下图:

execve函数在内核中执行流程

  • 在用户态中调用execve(),引发系统中断,在内核态中执行对应的函数sys_execve
  • sys_execve函数调用do_execve函数,该函数会读入可执行文件。
  • 接下来系统会调用search_binary_handler,根据可执行文件的类型查找到相应的处理函数。根据每种文件创建一个struct linux_binfmt的结构体,并将其连接到一个链表智商,执行时候系统就会遍历这个链表,从而找到相应的结构。
    从而调用对应的load_binary函数开始加载可执行文件。
  • 系统是通过load_elf_binary来加载elf类型的可执行文件。该函数会先读入ELF文件的头部,根据ELF文件的头部信息读入各种数据。
  • 如果存在动态链接库,则需要将动态链接映射到共享区域之中。此时就需要使用load_elf_interp来加载映像,并把返回的入口地址设置为load_elf_interp的动态链接器的入口
  • 如下图所示:

实验部分

实验目的

使用gdb跟踪sys_execve内核函数的处理过程,分析exec*函数对应的系统调用处理过程,理解Linux内核如何装载和启动一个可执行程序。

实验过程

  • 执行MenuOS,其中装载了execve
  • 设置断点
  • 装载和运行一个可执行文件的顺序为:

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

总结

    • 当linux内核或程序(例如shell)用fork函数创建子进程后,子进程往往要调用一种exec函数以执行另一个程序。
    • 当进程调用一种exec函数时,该进程执行的程序完全替换为新程序,而新程序则从其main函数开始
时间: 2024-12-14 18:42:06

Linux内核分析——可执行程序的装载的相关文章

20135239 益西拉姆 linux内核分析 可执行程序的装载

益西拉姆 + 原创作品请勿转载 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ” week 7 可执行程序的装载 1.预处理.编译.链接和目标文件的格式 从c语言到可执行程序的由来过程 可执行文件的创建——预处理.编译和链接 以helloworld为例 -s assembler 汇编 gcc -o hello hello.o -m32 是把hello.o链接成可执行文件. ELF格式的文件是怎么回事? v

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

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

《linux 内核分析》 第7周 可执行程序的装载

王一+<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-100002900 一.理解编译链接的过程和ELF可执行文件格式: 1.编译链接过程 编译器预处理成cpp文件 gcc -E -o q.cpp q.c -m32 汇编器编译成汇编代码  -x是将编译cpp输出文件,输出汇编文件 gcc -x cpp-output -S -o hello.s hello.cpp -m32 汇编代码编译成二进制目标文件  -x将汇编编译为object

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

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

20135201李辰希 《Linux内核分析》第七周 可执行程序的装载

李辰希  原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.预处理.编译.链接和目标文件的格式 1.可执行程序是怎么得来的 编译链接的过程 1.预处理阶段 gcc -E -o XX.cpp XX.c -m32 XX.cpp是预处理文件 2.编译器生成汇编代码阶段 gcc -x cpp-output -S -o hello.s hello.cpp -m32 XX.s是汇编代码 3.汇编器

Linux内核分析之可执行程序的装载和启动

一.内容分析 1.可执行文件的创建 (1)预处理阶段 预处理过程读入源代码,检查包含预处理指令的语句和宏定义,并对源代码进行相应的转换,预处理过程还会删除程序中的注释和多余的空白字符.其中预处理指令主要包括以下四个方面: 宏定义指令——预处理过程会把源代码中出现的宏标识符替换成宏定义时的值,常用的两种宏定义: //声明一个标识符,全部用大写字母来定义宏 #define MAX_NUM 10 //带参数的#define指令(宏函数) #define Cube(x) ((x)*(x)) int i,

Linux内核分析:实验七--Linux内核如何装载和启动一个可执行程序

刘畅 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 写在前面 本次实验着力分析Linux内核装载和启动一个可执行程序的过程,其中包括可执行文件格式的分析.可执行文件的装载和链接的过程,并通过GDB跟踪execve系统调用来梳理Linux系统加载可执行程序的过程. 可执行文件的格式分析 相对于其它文件类型,可执行文件可能是一个操作系统中最重要的文件类型,因为它们是完成操作的真正执行者.可

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

姓名:何伟钦 学号:20135223 ( *原创作品转载请注明出处*) ( 学习课程:<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-100002900 学习内容:Linux内核如何装载和启动一个可执行程序 理解编译链接的过程和ELF可执行文件格式: 编程使用exec*库函数加载一个可执行文件,动态链接分为可执行程序装载时动态链接和运行时动态链接,编程练习动态链接库的这两种使用方式: 使用gdb跟踪分析一个execve系统调用内核处

Linux内核分析课程总结

blog链接: 计算机是如何工作的 操作系统是如何工作的 构造一个简单的linux系统MenuOS 系统调用的工作机制(上) 系统调用的工作机制(下) 进程的描述与创建 可执行程序的装载 进程的切换与系统的一般执行过程 学习总结: 收获: 通过这门课程,我系统性地.深入地学习了操作系统内核的相关结构和设计原理,与之前不同,<linux内核分析>带领我们从linux内核源代码着手,真正地从根本上去了解linux的工作机制.在这个过程中我尝试了自己去构造一个简单的linux系统并编写和执行了一些简