电子说
1.1. 地址无关代码
需要被 Prelink 的 ELF 文件,无论是共享库还是可执行文件,编译时必须加 -fpic/-fPIC 参数,生成目标无关地址代码。对于可执行文件,不能使用 -fpie/-fPIE 加 –pie 生成地址无关可执行文件,否则无法被 prelink。
这个结论是根据上述测试程序得出的,其中的详细机理有待进一步研究。
1.2. 检查Prelink 状态
可以使用 readelf 和 objdump 工具来检查一个 ELF 文件是否已经被 prelink。例如:
注意观察到 6~14 行,对比没有被 prelink 之前的状态,INIT、FINI、STRTAB、SYMTAB 等 section 的地址已经修改为运行时进程空间的虚拟内存地址。第 30 行,RELACOUNT 表示已经预先进行重定位的符号的数量;第 31 行是 prelink 根据 ELF 所直接依赖的共享库计算的 MD5 值,该值用于判断该 ELF 所以来的共享库是否被修改过;从第 32 行可以看出该 ELF 已被加上 PRELINKED 标记和时间戳。
但是,并非所有被成功 prelink 的 ELF 文件都会加上 PRELINKED 的标记和时间戳。在用 prelink 处理完我们的SDK的后,发现 target_bin 所有的依赖项都有 PRELINKED 标记,target_bin 自身并没有此标记。但是通过测试其启动速度,确有巨大的提升,证明 prelink 在 target_bin 上确实发挥了作用。
至于为什么没有这个标记,暂时还没有调查清楚,仍待进一步研究。
对于上述情况,通过 objdump 等工具查看ELF文件的 section header,我们仍然可以发现 prelink 处理后留下的蛛丝马迹。
Prelink 之前,查看 target_bin 的节头:
Prelink 之后,再次查看节头:
对比 prelink 前后的节头信息,我们发现 prelink 后每个节的地址都有了调整,增加了.gnu.liblist , .gnu.conflict 和 .gnu.prelink_undo 这三个节。同时 .dynstr 节的 size 由 0xa3cb1 增加到了 0xa3e8c。这些都是 prelink 之后 ELF 的 size 有所增大的原因。
1.3. 查看ELF依赖树
Prelink 的处理过程是从目标 ELF 文件开始,检查其依赖树。从叶子节点开始处理,自底向上,直至根节点。若中间任何节点处理异常,则目标文件都无法被 prelink。同理,如果已经被 prelink 处理的 ELF 文件,如果其依赖树的中任何节点对应的 ELF 文件有更改,则需要从根开始重新 prelink。如果被更改的 ELF 所处的层级较低,被很多可执行文件依赖,则可能整个系统的 ELF 都需要重新进行 Prelink 处理。
可以使用 lddtree 查看 ELF 文件的依赖树。但是这个工具比较鸡肋,只适用于处理本机的 ELF 文件,无法像 prelink 一样可以在运行时指定 sysroot 和 LD_LIBRARY_PATH。
1.4. 不必要的依赖项
如果 prelink 在处理某个 ELF 文件(记为 A)的过程中,发现 A 并没有使用其直接依赖的另一个 ELF 文件(记为 B),而 A 又通过 C 间接依赖到 B,并且 B 已经被 prelink 处理。此时,A 将无法被 prelink。
1.5. 动态加载的共享库
Prelink 对于通过 dlopen 方式打开的共享库没有效果。
全部0条评论
快来发表一下你的评论吧 !