一般,共享目标文件在创建时,其基本虚拟地址是 0。
我们用“readelf -l”查看一个so文件的程序头表,可以查看权限(Flags)为类型为可加载(LOAD)且权限为可读可执行(R E)的段需要映射到进程虚拟地址空间中的位置(VirtAddr)是不是0。
[root@localhost 1]# readelf -l libtest1.soElf file type is DYN (Shared object file) Entry point 0x4f0 There are 6 program headers, starting at offset 64Program Headers:Type Offset VirtAddr PhysAddrFileSiz MemSiz Flags AlignLOAD 0x0000000000000000 0x0000000000000000 0x00000000000000000x00000000000006ec 0x00000000000006ec R E 200000LOAD 0x00000000000006f0 0x00000000002006f0 0x00000000002006f00x00000000000001f8 0x0000000000000208 RW 200000………………………………….
如果这个段需要映射到进程虚拟地址空间中的位置(VirtAddr)是0,则说明这个共享库加载到内存之后的位置是不固定的,这种共享库我们称之为“动态共享库”(Dynamic Shared Library),也就是我们常说的“动态库”。
如果这个段需要映射到进程虚拟地址空间中的位置(VirtAddr)不是0,而是一个确定的地址,这种共享库我们称之为“静态共享库”(Static Shared Library)。注意,与“静态库”有很明显的区别,从名字上就可以看出,多了“共享”。
静态共享库(Static Shared Library)有一个先天的缺陷:管理起来及其困难。你想啊,我编了一个静态共享库A,用了0x1000到0x2000的地址,别人的静态共享库如果也用了0x1000到0x2000的地址,那我俩的共享库就不能同时被加载。而且,假如我还写了另一个静态共享库B,用了0x2000到0x3000的地址,然后我发现我之前写的静态共享库A有问题,需要改一下,改完之后被改大了,要用到0x1000到0x3000的地址了。天啦噜,B的地址也要被往后改了!!!
“静态共享库”这种机制在现在看来真是让人无法理解,但是想到它是在“静态库”之后、动态共享库之前被设计出来的,放在它们俩中间的位置来看,我们就可以理解了:“静态共享库”解决了“静态库”一万个人要用printf就必须一万个人都自己加载一遍printf的定义那种丧心病狂的内存浪费。
可以这样想:“静态共享库”是对“静态库”的改进,解决了其对内存空间浪费的情况;“动态共享库”是对“静态共享库”的改进,解决了其可能造成地址冲突的情况。
其实现在还有一些“静态共享库”在被使用(因为不需要重定位,所以运行起来很快。目前有一些linux系统自己用的库是静态共享库,这些库一般都很成熟,不会有大的改动),比如Linux的“/lib64/libc.so.6”就是一个“静态共享库”,如果你用“readelf -l”看它的程序头表,可以发现这个so是被加载到了它指定的位置的。
[root@localhost 1]# readelf -l /lib64/libc.so.6 Elf file type is DYN (Shared object file) Entry point 0x383681ee30 There are 10 program headers, starting at offset 64Program Headers:Type Offset VirtAddr PhysAddrFileSiz MemSiz Flags AlignPHDR 0x0000000000000040 0x0000003836800040 0x00000038368000400x0000000000000230 0x0000000000000230 R E 8INTERP 0x000000000015a910 0x000000383695a910 0x000000383695a9100x000000000000001c 0x000000000000001c R 10[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]LOAD 0x0000000000000000 0x0000003836800000 0x00000038368000000x0000000000189e5c 0x0000000000189e5c R E 200000LOAD 0x000000000018a708 0x0000003836b8a708 0x0000003836b8a7080x0000000000004f50 0x0000000000009220 RW 200000…………………………………
可以看到,“/lib64/libc.so.6”会被装载到“0x3836400000”开始的地址。