tqsheng

go.....
随笔 - 366, 文章 - 18, 评论 - 101, 引用 - 0
数据加载中……

elf文件格式学习总结

    编译器将一个源文件生成目标文件时,会在目标文件中生成符号表和重定位表。

    

    符号表包含在文件中定义的全局符号以及在文件中引用的外部符号(外部函数或变量)。

    重定位表告诉链接器在哪些位置要进行重定位操作。

    

    编译器生成的目标文件在文件的开始处会有一个elf头,描绘了整个文件的组织结构。它还包括很多节(section)。这些节有的是系统定义好的,有些是用户在文件在通过.section命令自定义的,链接器会将多个输入目标文件中的相同的节合并。

    

    链接器对编译生成的目标文件进行链接时,A. 首先进行符号解析,找出外部符号在哪定义。如果外部符号在一个静态库中定义,则直接将对应的定义代码复制到最终生成的目标文件中。B. 接着链接器进行符号重定位。编译器在生成目标文件时,通常使用从零开始的相对地址,而在链接过程中,链接器从一个指定的地址开始,根据输入目标文件的顺序,以段(segment)为单位将它们拼装起来。其中每个段可以包括很多个节(section)。除了目标文件的拼装,重定位过程中还完成了下面两个任务:一是生成最终的符号表,二是对代码段(.text)中的某些位置进行修改,要修改的位置由编译器生成的重定位表指出。

    

    链接过程中还会生成两个表:got表和plt表。

    

    got表中每一项都是本运行模块要引用的全局变量或函数的地址,可以用got表来间接引用全局变量。函数也可以把got表的首地址作为一个基准,用相对该基准的偏移量来(直接)引用静态函数。由于动态链接器(ld-linux.so)不会把运行模块(*.so)加载到固定地址,在不同进程的地址空间中各运行模块的绝对地址、相对地址都不同。这种不同反映到got表上,就是每个进程的每个运行模块都有独立的got表,所以进程间不能共享got表(内容)。

    

    plt表中每一项都是一小段汇编代码,对应于本运行模块要引用的每一个全局函数。当链接器发现某个符号引用是位于其它共享目标文件(动态连接库*.so)中的一个全局函数时,就在文件的plt表里创建一个项目,以便将来重定位。

    

    链接生成的目标文件在文件开头也有一个elf头号,描绘了整个文件的组织结构,这个文件中会有多个段(segment),每个段都由相应的节(section)拼装而成。

    对由链接器链接生成的可执行目标文件进行加载运行时,内核首先读取elf头。根据头部数据指示分别读入各种数据结构,找出可加载的段并调用mmap()函数将其加载到内存。内核找到标记为PT_INTERP的段,这个段对应着动态链接器的名称,然后加载动态链接器(linux中通常是/lib/ld-linux.so.2)。接着内核将控制权交给动态链接器。动态链接器检查程序对外部文件(共享库)的依赖性,并在需要时对其进行加载。之后动态链接器开始对程序中的外部引用(即全局变量/函数)进行重定位,即定位出程序其引用的外部变量/函数的真实内存地址。R_386_GLOB_DAT类型的重定位项目涉及到got表。R_386_JMP_SLOT类型的重定位项目涉及到plt表。动态链接还有一个延迟(lazy)特性,即真正引用时才进行重定位(环境变量LD_BIND_NOW为空值NULL时)。接下来动态链接器执行elf文件中标记为.init节的代码,进行程序运行的初始化。最后动态链接器把控制权交给程序,从elf头中定义的入口处开始执行程序。

posted on 2012-12-07 22:43 tqsheng 阅读(309) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理