设为首页 加入收藏

TOP

ELF文件的加载过程(load_elf_binary函数详解)--Linux进程的管理与调度(十三)(一)
2019-09-01 23:10:11 】 浏览:149
Tags:ELF 文件 加载 过程 load_elf_binary 函数 详解 --Linux 进程 管理 调度 十三

加载和动态链接

从编译/链接和运行的角度看,应用程序和库程序的连接有两种方式。

一种是固定的、静态的连接,就是把需要用到的库函数的目标代码(二进制)代码从程序库中抽取出来,链接进应用软件的目标映像中;

另一种是动态链接,是指库函数的代码并不进入应用软件的目标映像,应用软件在编译/链接阶段并不完成跟库函数的链接,而是把函数库的映像也交给用户,到启动应用软件目标映像运行时才把程序库的映像也装入用户空间(并加以定位),再完成应用软件与库函数的连接。

这样,就有了两种不同的ELF格式映像。

  • 一种是静态链接的,在装入/启动其运行时无需装入函数库映像、也无需进行动态连接。
  • 另一种是动态连接,需要在装入/启动其运行时同时装入函数库映像并进行动态链接。

Linux内核既支持静态链接的ELF映像,也支持动态链接的ELF映像,而且装入/启动ELF映像必需由内核完成,而动态连接的实现则既可以在内核中完成,也可在用户空间完成。

因此,GNU把对于动态链接ELF映像的支持作了分工:

把ELF映像的装入/启动入在Linux内核中;而把动态链接的实现放在用户空间(glibc),并为此提供一个称为”解释器”(ld-linux.so.2)的工具软件,而解释器的装入/启动也由内核负责,这在后面我们分析ELF文件的加载时就可以看到

这部分主要说明ELF文件在内核空间的加载过程,下一部分对用户空间符号的动态解析过程进行说明。

Linux可执行文件类型的注册机制

在说明ELF文件的加载过程以前,我们先回答一个问题,就是:

为什么Linux可以运行ELF文件?

内核对所支持的每种可执行的程序类型都有个struct linux_binfmt的数据结构,这个结构我们在前面的博文中我们已经提到, 但是没有详细讲. 其定义如下

/*
  * This structure defines the functions that are used to load the binary formats that
  * linux accepts.
  */
struct linux_binfmt {
    struct list_head lh;
    struct module *module;
    int (*load_binary)(struct linux_binprm *);
    int (*load_shlib)(struct file *);
    int (*core_dump)(struct coredump_params *cprm);
    unsigned long min_coredump;     /* minimal dump size */
 };

linux_binfmt定义在include/linux/binfmts.h中

linux支持其他不同格式的可执行程序, 在这种方式下, linux能运行其他操作系统所编译的程序, 如MS-DOS程序, 活BSD Unix的COFF可执行格式, 因此linux内核用struct linux_binfmt来描述各种可执行程序。

linux内核对所支持的每种可执行的程序类型都有个struct linux_binfmt的数据结构,

其提供了3种方法来加载和执行可执行程序

函数 描述
load_binary 通过读存放在可执行文件中的信息为当前进程建立一个新的执行环境
load_shlib 用于动态的把一个共享库捆绑到一个已经在运行的进程, 这是由uselib()系统调用激活的
core_dump 在名为core的文件中, 存放当前进程的执行上下文. 这个文件通常是在进程接收到一个缺省操作为”dump”的信号时被创建的, 其格式取决于被执行程序的可执行类型

所有的linux_binfmt对象都处于一个链表中, 第一个元素的地址存放在formats变量中, 可以通过调用register_binfmt()和unregister_binfmt()函数在链表中插入和删除元素, 在系统启动期间, 为每个编译进内核的可执行格式都执行registre_fmt()函数. 当实现了一个新的可执行格式的模块正被装载时, 也执行这个函数, 当模块被卸载时, 执行unregister_binfmt()函数.

当我们执行一个可执行程序的时候, 内核会list_for_each_entry遍历所有注册的linux_binfmt对象, 对其调用load_binrary方法来尝试加载, 直到加载成功为止.

其中的load_binary函数指针指向的就是一个可执行程序的处理函数。而我们研究的ELF文件格式的linux_binfmt结构对象elf_format, 定义如下, 在/fs/binfmt.c中

static struct linux_binfmt elf_format = {
    .module      = THIS_MODULE,
    .load_binary = load_elf_binary,
    .load_shlib      = load_elf_library,
    .core_dump       = elf_core_dump,
    .min_coredump    = ELF_EXEC_PAGESIZE,
    .hasvdso     = 1
};

要支持ELF文件的运行,则必须向内核登记注册elf_format这个linux_binfmt类型的数据结构,加入到内核支持的可执行程序的队列中。内核提供两个函数来完成这个功能,一个注册,一个注销,即:

int register_binfmt(struct linux_binfmt * fmt)
int unregister_binfmt(struct linux_binfmt * fmt)

当需要运行一个程序时,则扫描这个队列,依次调用各个数据结构所提供的load处理程序来进行加载工作,ELF中加载程序即为load_elf_binary,内核中已经注册的可运行文件结构linux_binfmt会让其所属的加载程序load_binary逐一前来认领需要运行的程序binary,如果某个格式的处理程序发现相符后,便执行该格式映像的装入和启动

内核空间的加载过程load_elf_binary

内核中实际执行execv()或execve()系统调用的程序是do_execve(),这个函数先打开目标映像文件,并从目标文件的头部(第一个字节开始)读入若干(当前Linux内核中是128)字节(实际上就是填充ELF文件头,下面的分析可以看到),然后调用另一个函数search_binary_handler(),在此函数里面,它会搜索我们上面提到的Linux支持的可执行文件类型队列,让各种可执行程序的处理程序前来认领和处理。如果类型匹配,则调用load_binary函数指针所指向的处理函数来处理目标映像文件。

在ELF文件格式中,处理函数是load_elf_binary函数,下面主要就是分析load_elf_binary函数的执行过程(说明:因为内核中实际的加载需要涉及到很多东西,这里只关注跟ELF文件的处理相关的代码)

其流程如下:

  1. 填充并且检查目标程序ELF头部
  2. load_elf_phdrs加载目标程序的程序头表
  3. 如果需要动态链接, 则寻找和处理解释器段
  4. 检查并读取解释器的程序表头
  5. 装入目标程序的段segment
  6. create_elf_tables填写目标文件的参数环境变量等必要信息
  7. start_kernel宏准备进入
首页 上一页 1 2 3 4 5 6 7 下一页 尾页 1/8/8
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Android音频系统 下一篇Linux内存描述之内存区域zone--Li..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目