静态 动态编译链接 基础(linux x86)

relocatable object file: 

编译(不链接)生成relocatable object file. 为了之后能够正确的链接,.o文件需要包含以下信息:

(1)执行代码.text section

(2)数据,此时只处理全局数据(局部变量位于stack上),分为已经初始化的.data section和未初始化的.bss section以及只读.rodata section。

(3)定义符号和引用的符号的信息,符号表.symtab section。symbol的name的字符串保存在其他section即.strtab中。

(4)帮助linker把若干个relocatable object file(以及库文件)组织成一个可以被加载的image的relocation information,即.rel.text, .rel.data

(5).o文件中还会内嵌平台相关信息给linker, 因为编译器非常了解目标平台而linker没有那么知道target的信息。

elf文件格式:

elf header

-- section header table

---section

---section

--- ...

-- program header table

---segment

---segment

---... 

section header table中的每一项描述了一个section,并给出了该section在elf中的偏移。

执行gcc -c 只是编译而不链接,得到 .o文件。因为它尚未链接不会被加载执行,因此entry point address是没有用的,因此等于0.同理此处也不存在program header。section header table位于文件的856偏移处,其中有13个section,每个占用64byte。

readelf -S dynlib.o得到每个section的信息。共13个section,第一个section是inactive的,后面包括.text(代码段),.rela.text(重定位信息), .data(已初始化的数据段), .bss(未初始化的数据段), .rodata 等。

address描述运行地址,因为此时不具备运行条件,所有section的address都是0. 

PROGBITS说明该section包含了程序定义的信息,是属于program的bits.

EntSize, 即entry size,有些section由一个个固定size的item组成,如.rela.text和.symtab。

flag中的A标记表示程序执行的时候该section占据memory,如.text, .data, .rodata等,而.rela.text, .systab(符号表), .strtab(字符串表)和程序执行无关,主要是向linker提供链接所需要的信息。

用readelf -s可以查看symtab即符号表中的信息。

使用readelf -r 来display relocations. offset表示当需要relocation的时候,linker要找的实际位置信息(相对.text section的偏移)。

 

 

 

静态编译、动态编译和符号表:

对于静态编译的程序,.o文件中的符号表作用(1)对外宣称自己定义了哪些符号(2)向外宣布自己引用了哪些符号,需要其他模块来支持。这些信息在link的时候由static linker用来整合各个relocatable object file中的资源。而加载执行时这些符号表已经没有用了(stripped vs. not stripped)。

而动态链接的程序中有两个符号表,一是.symtab section(符号表),另一个是.dynsym section(动态符号表)。这两个符号表都有自己对应的string table,分别为.strtab 和 .dynstr section. 

readelf -s 得到dynsym及 symtab的信息。其中的binding属性主要被static linker用来进行.o之间的符号解析,一个符号可以有global, local, weak三种binding property.  

Vis一项对应symbol visibility,即符号的可见性。

动态文件的加载:

shared object elf文件的加载是根据program header进行的。readelf -l 可以得到program headers的信息。带有LOAD标记的那些program header entry会被map到进程地址空间上去。第一项是code segment,由于动态库的代码是PIC的,因此其VirtAddr和PhysAddr都是0,表示可以运行在任意地址上。第二项是data segment,在实际中,动态库的code和data segment都是连续加载的,因此,如果code segment的run time地址是0的话,那么data segment的地址应该是0x7d0,不过由于code segment是0x200000对齐的,因此data segment的地址被设定为0x2007d0。当然,如果实际该动态库被加载到了进程的X虚拟地址上的话,data segment的runtime地址应该是X + 0x7d0。对于动态库而言,其code segment可以被多个进程共享,也就是说,虽然code segment被加载到不同的进程的不同的虚拟地址空间,但是其物理地址是一样的,只不过各个进程设定自己的page table就OK了。对于data segment,各个进程都有自己的副本,不可能共享的。

与静态链接的可执行程序相比,dynamic那个entry是动态库特有的,其中包含了需要给dynamic linker用于做动态链接的信息。

DYNAMIC segment只包含了.dynamic一个section,需要注意的是.dynamic section也是data segment的一部分被加载到了进程的地址空间中。readelf -d 可以得到dynamic section中的信息。

 

动态库中如何访问全局变量及函数

以printf为例, 其重定位信息如下:

在符号表中可以查到got的位置:

 

 ...

 

 

参考链接:

http://www.wowotech.net/basic_subject/pic.html

http://blog.csdn.net/ifloveelse/article/details/32172541

posted on 2016-01-09 17:36  CarrieSmile  阅读(471)  评论(0)    收藏  举报