关于共享库
上一篇 /
下一篇 2008-01-22 11:24:11
/ 个人分类:Linux下C语言
x7Ul_U0共享库的工作方式分为三步:Linux宝库"c;BGD Q3L
_8NH8xtF @Y$`jy0
| link时,linker搜索整个库以找到用于解决那些未定义的外部符号模块 |
| linker标记模块来自的库名,在可执行体中放一个库的列表 |
| load时,启动代码找到这些库,并在程序开始前把它们映射到程序地址空间 |
Linux宝库t:?+F?;a[
]`
TLinux宝库z?!z^&s2w启动代码可以放在:1、OS中 2、可执行体中 3、动态链接器中
1E{m%b*@K K$Z0Linux宝库Cu}+?.Ic6s在第二步中,库列表的建立如下表所示:
4S$|3^!_/d#V(^ e'p#@_0Linux宝库g nz4k9uY
s| COFF | linker在该文件中创建了一个以库名命名的段 |
| Linux宝库'i aV9v:B)m$\"~'O LINUX m N6ok0F3d j0Linux宝库LV;w:|7S`LINKER hs^k \3_1AK mO0 | q
AX'| N3L9Y1I2TC0 通过设置向量(它向一个全局符号) a)S4^,^6[
N
``v0x'\/S \5{0?3~.O)q+w0每个共享库定义一个设置向量符号(一般由库名、版本、加载地址组成)Linux宝库(eM%@-CUm~;g3q/rH bk1ySK#]V C0linker创建一个指向每个这种数据结构指针的数组让启动代码用Linux宝库$y$vDn'Q'kmG!P)M |
| BSD/OS | 使用shell脚本完成 |
Linux宝库Q\-os^Linux宝库2{2|+m`$W6|"yI而在第三步的映射中,每个系统的处理也不同:Linux宝库^i}n9o*Al&I
Linux宝库(}Z)mT9k"}| LINUX | 增加了一个uselib()系统调用(它完成获取一个库的名字和地址,并将其映射到程序地址空间)。而启动代码(例程)搜索库列表,并对每一项执行uselib() |
| BSD/OS | 使用mmap()系统调用将一个文件的多个页映射进地址空间。启动例程会遍历库列表,打开每个对应文件,并将文件的第一页映射到加载地址,然后再调用各自的自举例程。自举例程映射余下的文本段、数据段、bss段。 |
!s'bnz&j5]O7},}0j(sC:Cra|c0共享库的结构一般如下:
\9{e1oa6dt0Linux宝库PNev }$|jE$L| 文件头:a.out/coff/elf头 |
| (初始化代码,不总是存在) |
| 调转表 |
| 代码 |
| 全局数据 |
| 私有数据 |
Linux宝库5uxB:GMP[6xP+]Linux宝库7JNj?S,u跳转指令表:作为一个中间层,它搜集了库中所有例程的地址,便于管理。
9kkq^9W*y.Y0Linux宝库P)]kLSz&S这样在符号替换成地址的时候,将调转表的地址作为例程的相应地址,而不是例程的实际地址。因为调转指令大小相同,调转表的地址也就容易计算。
(\2jWO#X&x0Linux宝库F j'YB Jf库代码的升级一般只修改代码段,而数据段一般不修改,这样的中间层就够就能保证原有代码不用改动,之用修改这个表即可。
+Tn^@E0C&{&^Jg f5aKZ)q0针对库例程代码有这么一个中间层,但针对数据就没有,所能做的是把固定数据长度,结构的数据搜集到数据段的开头(匿名数据前面),比如 FILE数据结构、errno、tzname。
c7D n1f4M:_9F[6[0Linux宝库g,]N!nuP?qba5]8s注:在ELF头有一个nterp段,其中包含一个运行该文件时所需使用的解释器程序名字。比如BSD使用C库作为解释器,那么程序启动前内核会将共享C库先映射进来Linux宝库+^~L5hz(X7e
,^r7v*_]$E0空占位库:绑定到应用程序,用于库之间的引用???
*mv(ap6o"g+b"m(@0- 它从共享库中提取需要的符号,针对输入库的符号调整这些符号
- 库中的每一例程<--->空占位库中包含一个同时定义了输出和输入的全局符号的对应项
- 空占位库只包含符号的定义(绝对数而非相对数),即:
| 跳转表中每一项地址 | 每个文本全局变量 |
| 共享库中的实际地址 | 每个数据或bss全局变量 |
| 未定义 | 没有定义的全局变量 |
)llZ.B}2e0那么启动一个共享库一般分为三个步骤:
Z9f/?Q1Bf-Y&am0- 加载可执行代码
- 映射库
- 执行库特定初始化代码
导入论坛
收藏
分享给好友
管理
举报
TAG: