150 likes | 369 Views
库包装符号重定位. 张洪娟 2010-12-13. 背景. 在库包装模块中, x86 程序依赖的 x86 库被包装成 loongson 本地库,导致符号重定位出现一些特殊问题。 先看一下符号重定位和解析机制 符号重定位 全局符号介入. 符号重定位. 为什么需要重定位 当用 gcc 编译程序,过程包含预处理、编译、汇编和链接过程 目标文件中用到的符号被定义在其他目标文件中,在程序链接或者装载之前,并不能得到其他模块的符号值,所以需要进行符号重定位。 如何重定位?( x86 )
E N D
库包装符号重定位 张洪娟 2010-12-13
背景 • 在库包装模块中,x86程序依赖的x86库被包装成loongson本地库,导致符号重定位出现一些特殊问题。 • 先看一下符号重定位和解析机制 • 符号重定位 • 全局符号介入
符号重定位 • 为什么需要重定位 • 当用gcc编译程序,过程包含预处理、编译、汇编和链接过程 • 目标文件中用到的符号被定义在其他目标文件中,在程序链接或者装载之前,并不能得到其他模块的符号值,所以需要进行符号重定位。 • 如何重定位?(x86) • 每个文件都由重定位表来记录重定位相关的信息,在适当的时刻(链接、装载或者运行时),链接器会根据重定位项对符号进行重定位,具体来说就是根据重定位项中的重定位类型,将符号值填充到相应的重定位入口 • 重定位入口:每个需要被重定位的地方
同名全局量处理 • 问题提出 • 程序的动态链接中,主文件依赖一系列so文件(ldd命令可以看到),这里面定义了一些同名的全局变量,这些同名全局变量的逻辑语义是一样的,那么符号的优先级是如何规定的, loader是如何实现的呢? • 符号优先级 • 全局符号介入:共享对象里面的全局符号被另一个共享对象的全局符号覆盖的现象 • Linux 动态链接器实现 • 每当文件被装载时,会将其符号表并入到全局符号表 • 定义规则:当一个符号需要被加入全局符号表时,如果相同的全局符号名已经存在,则后加入的符号被忽略 • 主文件声明但没有定义的全局变量 • x86主文件在自己bss段为声明的全局变量分配内存,也会有重定位项(重定位类型为R_386_COPY),所以处理方式与库类似,只需根据重定位项进行处理.
符号处理机制 • 在正常的程序链接、装载中,loader会根据上述重定位规则实现符号解析工作。即, • X86 loader会实现x86程序的上述功能 • Loongson本地的loader也会实现本地文件之间的上述功能 mips主程序 符号重定位 X86主程序 符号重定位 mips so1 X86 so1 mips loader mips so2 x86 loader X86 so2 . . . . . . mips soN X86 soN
问题提出 • 在库包装模块中,x86程序依赖loongson本地库 • 当x86文件中使用了loongson库中定义的全局量,谁来为它重定位? • 当x86文件和loongson库中定义了同名的全局变量,谁来决定符号优先级进而进行符号解析? • 这是本文要解决的问题 • 在介绍具体实现之前,先介绍一些实现中用到的相关背景知识 add on firefox dbt flashplayer wrapper lib
相关函数-dlopen(1) • Dlopen和dlsym • void *dlopen(const char *filename, int flag) • void *dlsym(void *handle, const char *symbol) • The function dlopen() loads the dynamic library file named by the null-terminated string filename and returns an opaque "handle" for the dynamic library • The function dlsym() takes a "handle" of a dynamic library returned by dlopen() and the null-terminated symbol name, returning the address where that symbol is loaded into memory • 如果文件名为NULL,那么dlopen返回的是主程序的句柄,也就是说我们可以在运行时找到全局符号表里面的任意一个符号,并且可以执行它。全局符号表包括了程序的可执行文件本身、被动态链接器加载到进程中的所有供想问模块以及在运行时通过dlopen打开的并且使用了RTLD_GLOBAL方式的模块中的所有符号( RTLD_GLOBAL 表示将被加载模块的符号合并到全局符号表中)
相关函数-dlopen(2) • 使用dlopen filename为0特性 • 根据dlopen功能,当filename为0时,我们可以得到loongson本地全局符号表,包括所有loongson本地主文件及其依赖的本地库和使用dlopen(设置flag RTLD_GLOBAL)打开因库包装而装载的本地库中的全局符号
相关定义 • 几个定义 • X86全局符号表:模块中一个记录x86所有符号的符号表,并且当该值被本地库中的符号重定位时,将该符号从该表中删除。也就是说,该表记录: x86所有全局符号-被本地库重定位的符号 • X86重定位表:记录所有x86库文件对本地符号的使用,这些符号需要本地库中定义的符号去重定位 • 符号处理模块:该模块对遍历到的每个x86符号进行解析,判断应该用x86符号重定位loongson本地符号,还是用loongson本地符号去重定位x86相应符号 • 重定位:本文中提到的重定位符号,都是指对需要重定位的符号进行重定位,这是具体实现中的一个细节
相关模块 • 库包装相关功能 • 在被包装的x86本地库装载时,其相应的本地库不会被装载,只有在用到的时候才会被装载,也就是说在x86 loader链接装载x86相关文件时,不会有loongson本地库被装载 • 我们不妨把x86 load过程作为一个整体,本模块所有工作都是在该模块之后进行。 • 模块调用点 • X86loader 刚结束时 • X86 loader结束之后每次dlopen装载x86文件(需要自己截获x86 dlopen) • X86 loader结束之后dbt每次dlopen打开包装库 X86 loader X86 dlopen Dbt dlopen 调用点
调用点-X86 loader结束 • 实现步骤 • 在x86loader结束之后,根据x86装载序遍历x86主文件和共享目标文件的符号表,记录符号信息,形成x86全局符号表 • 调用符号处理模块
符号处理模块 • 对x86全局符号表的每一个符号,使用dlopen\dlsym上述功能来查找,如果该符号不为空 • 如果该符号在被包装的库里, • 则用该符号值去重定位所有当前及以后装载的x86库文件中的该符号(x86主程序中的符号不需重定位) • 并将该符号从x86全局符号表中删除 • 如果该符号不在被包装的库里,说明不是插件依赖的本地库,不需要处理。 • 如果该符号不存在,说明现在已装载的本地库中没有定义该符号 • 如果x86符号值不为0,则用该x86符号的值去重定位所有在此之后装载的被包装的本地库 • 如果为0,则将该符号添加到x86重定位表中,并在以后装载的本地包装库中搜索符号来重定位 该处以及x86dlopen调用点处理 本地dlopen调用点处理
调用点-x86 dlopen装载文件 • 每当x86文件被dlopen装载,检查其flag是否包含RTLD_GLOBAL,如果有,将其符号信息添加到x86全局符号表,并将为0的符号添加到x86重定位表。 • 对该x86库中每一个需要重定位的符号,判断x86全局符号表中是否存在该符号 • 如果有,应该用x86中符号值来重定位该符号,则x86 loader会进行定位,不予处理; • 否则,调用dlopen\dlsym查找本地是否装载了该符号, • 如果装载了并且在包装库中,则用本地值来重定位该x86符号,并将该符号从x86全局符号表中删除。 • 否则,会用该值去重定位本地库,这在本地库被dlopen时会检测, 这里不需处理。
实现-本地dlopen打开包装库 • Dbt 库包装模块使用dlopen(设flag含RTLD_GLOBAL)打开包装的本地库时, • 对本地库中每一个需要重定位的符号 • 如果x86全局符号表存在并且不为0,则用x86符号值来重定位该符号值 • 如果x86全局符号表中不存在该值,那应该用本地库中的符号定位该值,本地loader会帮助处理,我们不予处理。 • 对x86重定位表的每一个符号,使用dlsym检查该库中有没有这个符号定义, • 如果有,则用本地符号值来重定位该值,并将该符号从x86重定位表删除 • 如果没有,还会在以后装载本地库时搜索,现在不需处理。