22/8/16 深入理解计算机系统第七章笔记
7.11 从应用程序中加载和链接共享库
共享库的作用例子:
- 分发软件,可以用户下载共享库来更新软件。
- 构建高性能的web服务器,不用fork和execve子进程,而是可以在运行时更新已经存在的函数,以及添加新函数。
#inlcude<dlfc.h>
void *dlopen(const char *filename, int flag)
void *dlsym(void *handle,char* symbol)
运行时加载和链接共享库函数
dlopen()以指定模式打开指定的动态连接库文件,并返回一个指向句柄的指针,错误返回null,dlclose来卸载打开的库。 flag的值:
- RTLD_LAZY 暂缓决定,等有需要时再解出符号
- RTLD_NOW 立即决定,立即解析对外部符号的引用。
- RTLD_GLOBAL此共享对象定义的符号将可用于后续加载的共享对象的符号解析。
链接选项 -rdynamic 用来通知链接器将所有符号添加到动态符号表中。
dlsym函数输入是一个指向前面已经打开的共享库的句柄和一个symbol名字,如果符号存在,返回符号地址,否则返回null。
dlerror函数返回一个字符串,它描述上面三个函数的最近的错误。
JAVA的JNI可以调用“本地的”C和C++函数,先将C函数编译为共享库,再动态链接和调用。
7.12位置无关代码
可以加载而无需重定位的代码被称为位置无关代码(PIC)用户对GCC使用-fpic选项指示GNU编译系统生成PIC代码,共享库编译必须总是使用该选项。
全局偏移量表(GOT),包含指令和数据段变量的相对距离,在加载时,动态链接器重定位GOT的每个条目。
PIC函数调用:
使用延迟绑定,将过程地址的绑定推迟到第一次调用时,可以减少函数在加载时的重定位。第一次调用时开销大,但以后的调用只需要花一条指令。
延迟绑定通过GOT和过程链接表(PLT)来实现。
过程链接表(PLT):PLT是个数组,每个元素16字节,PLT[0]是一个特殊的条目,跳转到动态链接器中,PLT调用系统启动函数,PLT[2]开始的条目调用用户代码调用的函数。
全局偏移量表(GOT):GOT[2]是动态链接器在.so模块的入口点,其他条目对应一个被调用的函数。
addvec为要执行的函数
第一次执行共享库的函数
- 第一步不直接调用函数,而是进入PLT[2](对应的PLT条目)
- 通过GOT[4]进行间接跳转,只是简单的把控制传送回PLT[2]的下一条指令
- 把addvec的ID压入栈中,PLT[2]跳转到PLT[0]
- PLT[2]通过GOT[1]把动态链接器的一个参数压入栈中,然后通过GOT[2]间接跳转到动态链接器中。然后动态链接器根据两个被压入栈的条目来确定addvec的运行时位置,用这个地址重写GOT[4]。然后把控制传递给addvec。
之后的执行步骤:
- 控制传递给PLT[2]
- 通过GOT[4]间接跳转会将控制直接转移给addvec
浙公网安备 33010602011771号