linux-elf装载过程

装载

装载到哪里?内存,进程上看,是映射到了虚拟进程空间.

装载谁? 程序运行的实体代码,数据,来自elf,共享库,OS等.

怎么装载?利用程序局部性原理: 运行某段程序时,很可能也要运行最近的1个代码块,于是先提前装载到内存.

内存肯定不够用,不过通过页映射,需要时(缺页异常),再从磁盘load到内存,做替换.

管理这个装载过程的,就是os的存储管理器(MMU).

具体过程:

1. 建立虚拟进程空间,
2. 读取elf文件头,建立好elf与虚拟空间映射关系,(为后续缺页装载准备).虚拟进程空间有个区域叫VMA, 对应映射的是elf的.text段
4. cpu pc跳转到入口,开始运行.
5.  发现空页,缺页异常,正式装载磁盘页到映射好的物理内存,虚拟进程空间也看的到

linux-elf装载过程

两个视图

链接的角度,elf的段按Section划分,(链接试图), 从装载的角度,elf按Segment(执行试图), 后者为了装载方便,不浪费内存,可能进行了段的合并优化等.

linux-elf装载过程

这就是elf中program header的由来,描述elf该如何被os装载到进程空间.

➜  compile-link-lib readelf -l ab

Elf file type is DYN (Shared object file)
Entry point 0x5a0
There are 9 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000000040 0x0000000000000040
                 0x00000000000001f8 0x00000000000001f8  R      0x8
  INTERP         0x0000000000000238 0x0000000000000238 0x0000000000000238
                 0x000000000000001c 0x000000000000001c  R      0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000950 0x0000000000000950  R E    0x200000
  LOAD           0x0000000000000db0 0x0000000000200db0 0x0000000000200db0
                 0x0000000000000264 0x0000000000000268  RW     0x200000
  DYNAMIC        0x0000000000000dc0 0x0000000000200dc0 0x0000000000200dc0
                 0x00000000000001f0 0x00000000000001f0  RW     0x8
  NOTE           0x0000000000000254 0x0000000000000254 0x0000000000000254
                 0x0000000000000044 0x0000000000000044  R      0x4
  GNU_EH_FRAME   0x00000000000007e0 0x00000000000007e0 0x00000000000007e0
                 0x0000000000000044 0x0000000000000044  R      0x4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10
  GNU_RELRO      0x0000000000000db0 0x0000000000200db0 0x0000000000200db0
                 0x0000000000000250 0x0000000000000250  R      0x1

 Section to Segment mapping:
  Segment Sections...
   00
   01     .interp
   02     .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame
   03     .init_array .fini_array .dynamic .got .data .bss
   04     .dynamic
   05     .note.ABI-tag .note.gnu.build-id
   06     .eh_frame_hdr
   07
   08     .init_array .fini_array .dynamic .got

Segment的内容:

segment类型有LOAD,DYNAMIC,INTERP,GNU_XXX等;
Offset是segment在文件里的偏移;
Virtual Addr就是映射到的虚拟进程空间.
Phy Addr是物理装载地址,一般与上面的一致.
Flag: R,W,X
Align: 对齐属性.(k bytes)
    - 对齐是为了页处理的方便,但是可能带来空间浪费.

其他VMA

VMA不止有映射代码的,还有映射堆,栈,数据,内核等.

linux-elf装载过程

程序实现的视图,还是来自这篇.

linux-elf装载过程

段地址对齐

1个例子,3个段的Segment情况:

linux-elf装载过程

合并前,映射了5个物理内存页:

linux-elf装载过程

合并后,仅映射3个物理内存页,(但是虚拟空间多了映射):

linux-elf装载过程