ELF文件与库的原理
库的制作和原理
1 ELF文件
ELF文件主要包含以下4种:
可重定位文件(xxx.o)
可执行文件
共享目标文件(xxx.so)
内核转储
ELF的文件构成:
1.ELF header:用于定位文件的其他部分
2.Program header table:列举了所有有效段及其属性(包括每个段的起始位置和位移,长度)
3.Section header table:包含对节(section)的描述
4.Section:ELF文件的各种信息和数据都存储在不同的节中(如代码节存储了可执行代码,数据节存储了全局变量和静态数据等)
其中,节头表section header table用于链接时链接器将section合并成segment(优化内存管理和方便权限访问控制)
程序头表program header table用于加载时告诉操作系统如何加载文件,完成进程内存的初始化
readelf
基本语法; readelf <选项> <elf文件>
-h显示ELF文件头(架构,字节序,入口点地址等)
-S显示节区表(如.text,.data的详细位置和大小)
[16].text(代码段)存储机器指令的地方,其中glags是AX,A代表Alloc(运行时分配内存),X代表Execute(可执行)
[18].rodata(只读数据段)它的flags只有A,没有W(write)说明这块内存运行时是被保护的
[25].data(已初始化数据段)
[26].bss(未初始化数据段)其它节区大都type为PROGBITS,而.bss是NOBITS,这意味着它在磁盘上的ELF文件中不占用实际的物理空间(只记录需要多少字节),直至程序被加载到内存运行时,操作系统才会给它分配空间
-l显示程序头表,展示系统如何将文件加载进内存
这里就清楚展示了链接器会把具有相同访问权限的section打包为一个segment交给OS载入
-s显示符号表,包含程序中定义和引用的所有函数和全局变量
-d显示动态节区,可以查看程序依赖哪些共享库
。。。。。。
ELF加载与进程地址空间
1 ELF文件在没被加载到内存之前就有地址(ELF对自己的代码和数据进行统一编址(起始地址+偏移量,其中起始地址为0)
进程mm_struct,vm_area_struct在创建时的初始化数据来自ELF文件的segment
所以虚拟地址需要OS和编译器;
2 动态库加载
动态链接实际上是在程序加载时才会进行链接的整个过程
动态库的调用,是在进程地址空间从代码区跳转到共享区,而访问库中的方法,只需要库的起始虚拟地址+方法的偏移量
但是,程序每次运行,把库加载并映射的时候,库的起始虚拟地址都会改变,所以调用处的地址也会因变化而进行修改,为了维护代码区的只读,动态链接采用的是GOT(全局偏移量表)数据区),
相当于是又包装了一层,以表的一一对应的特性实现了不变与变的统一;自然的,每个进程就有自己独立的GOT表(因为不同进程对库的虚拟编址不同),而地址也就变成了got表地址+表中偏移量
。。。。。。
-延迟绑定PLT(程序开始—>第一次调用)降低开销
反汇编指令objdump -d <文件>
(当然,还可以搭配**|**管道符等使用)