程序的加载链接和库-可执行文件的加载和进程


程序的加载方式 ,覆盖载入,页映射,这两种方式都是利用了 局部性原理

覆盖载入


左边的程序是main调用A和B,A和B之间没有依赖关系,于是可以在加载完A之后用B覆盖A,这样就节省了内存。mian上的是overlay manager,也就是覆盖载入管理程序

右边是一个复杂的调用图,main->A ->C/D ,mian->B->E/F,从图中可以看出他们的依赖关系


页映射是将数据和指令分成4K-4M,一般是4K大小的页,其控制粒度比覆盖方式更细


程序不用一次性将全部的页都加载到内存,根据需要加载/替换页,页也跟虚拟内存有很大关系




从操作系统角度看创建一个进程需要做三件事
1.创建一个独立的虚拟地址空间
2.读取可执行文件头,并建立虚拟空间与可执行文件的映射关系
3.将CPU的指令寄存器设置成可执行文件的入口地址,启动运行

第二步中的映射关系如下图



页错误 page fault

当CPU准备执行这段指定时发现对应的页是空的,于是产生一个页错误,产生中断并将控制权交给操作系统,操作系统根据已经分配好的数据结构,去加载对应的页,之后返回中断,cpu重新执行一遍指令,结果就正确了





section 到 segment的映射

如果一个 text的section占两个页,init占一个页,如果能将他们合并,最后就只占两个页,节省了空间


一个程序它的section如下:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000400238  00000238
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.ABI-tag     NOTE             0000000000400254  00000254
       0000000000000020  0000000000000000   A       0     0     4
  [ 3] .note.gnu.build-i NOTE             0000000000400274  00000274
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         0000000000400298  00000298
       000000000000001c  0000000000000000   A       5     0     8
  [ 5] .dynsym           DYNSYM           00000000004002b8  000002b8
       00000000000000f0  0000000000000018   A       6     1     8
  [ 6] .dynstr           STRTAB           00000000004003a8  000003a8
       0000000000000072  0000000000000000   A       0     0     1
  [ 7] .gnu.version      VERSYM           000000000040041a  0000041a
       0000000000000014  0000000000000002   A       5     0     2
  [ 8] .gnu.version_r    VERNEED          0000000000400430  00000430
       0000000000000020  0000000000000000   A       6     1     8
  [ 9] .rela.dyn         RELA             0000000000400450  00000450
       0000000000000018  0000000000000018   A       5     0     8
  [10] .rela.plt         RELA             0000000000400468  00000468
       00000000000000d8  0000000000000018  AI       5    12     8
  [11] .init             PROGBITS         0000000000400540  00000540
       000000000000001a  0000000000000000  AX       0     0     4
  [12] .plt              PROGBITS         0000000000400560  00000560
       00000000000000a0  0000000000000010  AX       0     0     16
  [13] .text             PROGBITS         0000000000400600  00000600
       0000000000000354  0000000000000000  AX       0     0     16
  [14] .fini             PROGBITS         0000000000400954  00000954
       0000000000000009  0000000000000000  AX       0     0     4
  [15] .rodata           PROGBITS         0000000000400960  00000960
       0000000000000078  0000000000000000   A       0     0     8
  [16] .eh_frame_hdr     PROGBITS         00000000004009d8  000009d8
       0000000000000044  0000000000000000   A       0     0     4
  [17] .eh_frame         PROGBITS         0000000000400a20  00000a20
       0000000000000134  0000000000000000   A       0     0     8
  [18] .init_array       INIT_ARRAY       0000000000600e10  00000e10
       0000000000000008  0000000000000000  WA       0     0     8
  [19] .fini_array       FINI_ARRAY       0000000000600e18  00000e18
       0000000000000008  0000000000000000  WA       0     0     8
  [20] .jcr              PROGBITS         0000000000600e20  00000e20
       0000000000000008  0000000000000000  WA       0     0     8
  [21] .dynamic          DYNAMIC          0000000000600e28  00000e28
       00000000000001d0  0000000000000010  WA       6     0     8
  [22] .got              PROGBITS         0000000000600ff8  00000ff8
       0000000000000008  0000000000000008  WA       0     0     8
  [23] .got.plt          PROGBITS         0000000000601000  00001000
       0000000000000060  0000000000000008  WA       0     0     8
  [24] .data             PROGBITS         0000000000601060  00001060
       000000000000000c  0000000000000000  WA       0     0     4
  [25] .bss              NOBITS           0000000000601080  0000106c
       00000000000000d8  0000000000000000  WA       0     0     32
  [26] .comment          PROGBITS         0000000000000000  0000106c
       000000000000002d  0000000000000001  MS       0     0     1
  [27] .shstrtab         STRTAB           0000000000000000  00001099
       0000000000000108  0000000000000000           0     0     1
  [28] .symtab           SYMTAB           0000000000000000  000011a8
       0000000000000738  0000000000000018          29    50     8
  [29] .strtab           STRTAB           0000000000000000  000018e0
       00000000000002f4  0000000000000000           0     0     1

它对应到 segment如下:

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000400040 0x0000000000400040
                 0x00000000000001f8 0x00000000000001f8  R E    8
  INTERP         0x0000000000000238 0x0000000000400238 0x0000000000400238
                 0x000000000000001c 0x000000000000001c  R      1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x0000000000000b54 0x0000000000000b54  R E    200000
  LOAD           0x0000000000000e10 0x0000000000600e10 0x0000000000600e10
                 0x000000000000025c 0x0000000000000348  RW     200000
  DYNAMIC        0x0000000000000e28 0x0000000000600e28 0x0000000000600e28
                 0x00000000000001d0 0x00000000000001d0  RW     8
  NOTE           0x0000000000000254 0x0000000000400254 0x0000000000400254
                 0x0000000000000044 0x0000000000000044  R      4
  GNU_EH_FRAME   0x00000000000009d8 0x00000000004009d8 0x00000000004009d8
                 0x0000000000000044 0x0000000000000044  R      4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     10
  GNU_RELRO      0x0000000000000e10 0x0000000000600e10 0x0000000000600e10
                 0x00000000000001f0 0x00000000000001f0  R      1

 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 .text .fini .rodata .eh_frame_hdr .eh_frame 
   03     .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss 
   04     .dynamic 
   05     .note.ABI-tag .note.gnu.build-id 
   06     .eh_frame_hdr 
   07     
   08     .init_array .fini_array .jcr .dynamic .got 

从Section角度看ELF文件就是链接视图 Linking View, 从Segment角度看就是执行视图 Execution View

Segment的各个参数含义如下

参数 含义
Type Segment的类型,有LOAD,DYNAMIC,INTERP等
offset segment在文件中的偏移量
VirtAddr Segment的第一个字节在进程虚拟地址空间的起始位置
PhysAddr segment的物理装载地址
FileSiz segment在ELF文件中所站空间的长度
MemSiz segment在进程虚拟地址空间中所占的长度
Flags 权限属性,比如可读R,可写W,可执行X等
Align 对齐属性,实际对齐等于2的align次方

 


从操作系统角度看,各个section合并后成为segment的关系如下



启动一个python 内置的http server,cat /proc/[pid]/maps 结果如下

00400000-00401000 r-xp 00000000 fd:01 1051849                            /usr/bin/python2.7
00600000-00601000 r--p 00000000 fd:01 1051849                            /usr/bin/python2.7
00601000-00602000 rw-p 00001000 fd:01 1051849                            /usr/bin/python2.7
01561000-017b5000 rw-p 00000000 00:00 0                                  [heap]
7f676dd13000-7f676dd22000 r-xp 00000000 fd:01 1050073                    /usr/lib64/libbz2.so.1.0.6
7f676dd22000-7f676df21000 ---p 0000f000 fd:01 1050073                    /usr/lib64/libbz2.so.1.0.6
7f676df21000-7f676df22000 r--p 0000e000 fd:01 1050073                    /usr/lib64/libbz2.so.1.0.6
7f676df22000-7f676df23000 rw-p 0000f000 fd:01 1050073                    /usr/lib64/libbz2.so.1.0.6
7f676df23000-7f676df48000 r-xp 00000000 fd:01 1050000                    /usr/lib64/liblzma.so.5.2.2
7f676df48000-7f676e147000 ---p 00025000 fd:01 1050000                    /usr/lib64/liblzma.so.5.2.2
。。。。
。。。。
。。。。

7f677af34000-7f677af35000 r--p 00021000 fd:01 1049801                    /usr/lib64/ld-2.17.so
7f677af35000-7f677af36000 rw-p 00022000 fd:01 1049801                    /usr/lib64/ld-2.17.so
7f677af36000-7f677af37000 rw-p 00000000 00:00 0 
7ffe79477000-7ffe79498000 rw-p 00000000 00:00 0                          [stack]
7ffe79498000-7ffe7949a000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
各列的参数含义如下
1.VMA的地址范围
2.VMA的权限,r表示读 w为写 x执行 p是私有 s是共享
3.偏移量,表示VMA对应的Segment在映像文件中的偏移
4.表示映像文件所在设备的之主设备号和次设备号
5.映像文件的节点号
6.映像文件的路径

上图中用蓝色标注了一些特殊的VMA
Heap堆,Stack栈,vdso用于跟操作系统内核交互的模块

一个进程的VMA 大致有如下几种:

名称 含义
代码VMA 只读,可执行,有镜像文件
数据VMA 可读,可写,可执行,有镜像文件
堆VMA 可读写,可执行,无镜像文件,可向上扩展
栈VMA 可读写,不可执行,无镜像文件,可向下扩展

ELF与linux进程虚拟空间映射关系



linux进程初始化栈结构如下


esp指向初始化后的栈顶,也就是程序参数的个数,c的main函数中的argc,然后是两个参数,对应c的main函数就是argv。

之后是0表示结束,然后是两个系统参数,再后面跟0结为,最后是程序的内存布局



linux执行ELF程序的过程

调用fork创建一个子进程,再调用execve()函数执行指定的ELF文件

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main() {

   char buf[1024] = {0};
   printf("mini bash\n");
   scanf("%s", buf);
   pid_t pid = fork(); 
   while(1) {
       if(0 == pid) {
           if(execlp(buf,0) < 0) {
               printf("exec error\n");
           }  
       }
       else if(pid > 0) {
          int status;
          waitpid(pid,&status,0);
       }
       else {
          printf("fork error %d\n",pid);
       }
   }
   return 0;
}



参考

《程序员的自我修养-链接,装载与库》




 


猜你喜欢

转载自blog.csdn.net/hixiaoxiaoniao/article/details/80861315
今日推荐