首先,我从技术角度问这个问题,而不是图书馆代码用户的角度。区别的一个例子是共享对象包含程序头,而普通目标文件则不包含。还有什么不同之处?
至于我的问题的目的,我试图弄清楚需要从共享对象文件中删除哪些内容,以使链接器将其视为普通对象文件并尝试重新定位并将其静态链接到生成的可执行文件,而不是将其标识为共享库并生成DT_NEEDED
引用。这反过来是将共享库原始“转换”为可以静态链接的东西的第一步(然而,可能需要进一步努力使重定位可满足。)
答案 0 :(得分:11)
您将发现的一个主要差异是,在最后的链接阶段,许多C库组件静态链接到库中,形成INIT和FINI符号等。这些在程序头中用DT_INIT
和DT_FINI
条目指定;您需要将这些转换为静态构造函数/析构函数条目。 DT_NEEDED条目将在转换为.o时丢失;你需要手动重新添加它们。
在最终链接阶段生成的PLT需要与最终输出文件合并,或者转换回普通的重定位;这是非平凡的,因为PLT只是代码。 GOT也是一个问题;它位于.text段的固定相对偏移处,并包含指向数据成员的指针。但是,它还包含一个指向_DYNAMIC结构的指针,每个库或可执行文件只能有一个。并且您无法更改GOT中的偏移量,因为它们直接从代码中引用。
因此很难将.so转换为真正的.o;在转换为PLT / GOT时丢失了信息。更好的方法可能是更改C库中的动态链接器,以支持将已在内存中映射的共享库链接为静态图像。也就是说,只需将.so转换为页面对齐的只读部分,就可以将.so转换为.o;然后将其传递给动态链接器以使用适当的权限重新映射并执行正常的共享库初始化。然后添加一个静态构造函数来调用C库来初始化共享库。最后,添加适当的导出符号以对应共享库的.text段中的动态符号。
但是,这种方法的一个问题是静态构造函数可能在初始化伪solib的静态构造函数之前运行。在这种情况下,它们不能尝试从solib调用函数,否则你可能会崩溃,因为solib尚未初始化。通过使导出的符号指向一个蹦床函数可以避免这种情况,该函数可以确保首先初始化solib(尽管数据符号不是那么容易!)您可能还会发现this previous question对您有用。