oneself是什么意思 elf是什么意思( 三 )


小结至此 , ELF文件中相关的字段已经介绍完毕 , 主要组成也就是Section Header Table和Program Header Table两部分 , 整体框架相当简洁 。而ELF中体现拓展性的地方则是在Section和Segment的类型上(s_type和p_type) , 这两个字段的类型都是 ElfN_Word  , 在32位系统下大小为4字节 , 也就是说最多可以支持高达 2^32 - 1 种不同的类型!除了上面介绍的常见类型 , 不同操作系统或者厂商还能定义自己的类型去实现更多复杂的功能 。
程序加载在新版的ELF标准文档中 , 将ELF的介绍分成了三部分 , 之一部分介绍ELF文件本身的结构 , 第二部分是处理器相关的内容 , 第三部分是操作系统相关的内容 。ELF的加载实际上是与操作系统相关的 , 不过大部分情况下我们都是在GNU/Linux环境中运行 , 因此就以此为例介绍程序的加载流程 。
Linux中分为用户态和内核态 , 执行ELF文件在用户态的表现就是执行 execve 系统调用 , 随后陷入内核进行处理 。
内核空间内核空间对execve的处理其实可以单独用一篇文章去介绍 , 其中涉及到进程的创建、文件资源的处理以及进程权限的设置等等 。我们这里主要关注其中ELF处理相关的部分即可 , 实际上内核可以识别多种类型的可执行文件 , ELF的处理代码主要在 fs/binfmt_elf.c 中的 load_elf_binary 函数中 。
对于ELF而言 , Linux内核所关心的只有Program Header部分 , 甚至大部分情况下只关心三种类型的Header , 即 PT_LOAD 、 PT_INTERP 和 PT_GNU_STACK。以3.18内核为例 , load_elf_binary主要有下面操作:
对ELF文件做一些基本检查 , 保证 e_phentsize = sizeof(struct elf_phdr) 并且 e_phnum 的个数在一定范围内;循环查看每一项program header , 如果有PT_INTERP则使用 open_exec 加载进来 , 并替换原程序的 bprm->buf ;根据 PT_GNU_STACK 段中的flag设置栈是否可执行;使用 flush_old_exec 来更新当前可执行文件的所有引用;使用 setup_new_exec 设置新的可执行文件在内核中的状态;setup_arg_pages 在栈上设置程序调用参数的内存页;循环每一项 PT_LOAD 类型的段 ,  elf_map 映射到对应内存页中 , 初始化BSS;如果存在interpreter , 将入口(elf_entry)设置为interpreter的函数入口 , 否则设置为原ELF的入口地址;install_exec_creds(bprm) 设置进程权限等信息;create_elf_tables 添加需要的信息到程序的栈中 , 比如 ELF auxiliary vector ;设置 current->mm 对应的字段;从内核的处理流程上来看 , 如果是静态链接的程序 , 实际上内核返回用户空间执行的就是该程序的入口地址代码;如果是动态链接的程序 , 内核返回用户空间执行的则是interpreter的代码 , 并由其加载实际的ELF程序去执行 。
为什么要这么做呢?如果把动态链接相关的代码也放到内核中 , 就会导致内核执行功能过多 , 内核的理念一直是能不在内核中执行的就不在内核中处理 , 以避免出现问题时难以更新而且影响系统整体的稳定性 。事实上内核中对ELF文件结构的支持是相当有限的 , 只能读取并理解部分的字段 。
用户空间内核返回用户空间后 , 对于静态链接的程序是直接执行 , 没什么好说的 。而对于动态链接的程序 , 实际是执行interpreter的代码 。ELF的interpreter作为一个段 , 自然是编译链接的时候加进去的 , 因此和编译使用的工具链有关 。对于Linux系统而言 , 使用的一般是GCC工具链 , 而interpreter的实现 , 代码就在glibc的 elf/rtld.c 中 。
interpreter又称为dynamic linker , 以glibc2.27为例 , 它的大致功能如下:
将实际要执行的ELF程序中的内存段加载到当前进程空间中;将动态库的内存段加载到当前进程空间中;对ELF程序和动态库进行重定向操作(relocation);调用动态库的初始化函数(如 .preinit_array, .init, .init_array );将控制流传递给目标ELF程序 , 让其看起来自己是直接启动的;其中参与动态加载和重定向所需要的重要部分就是Program Header Table中 PT_DYNAMIC 类型的Segment 。前面我们提到在Section Header中也有一部分参与动态链接的section , 即 .dynamic。我在自己解析动态链接文件的时候发现 , 实际上 .dynamic section中的数据 , 和 PT_DYNAMIC 中的数据指向的是文件中的 同一个地方  , 即这两个entry的s_offset和p_offset是相同 。每个元素的类型如下:

推荐阅读