Linux ELF 32位加载

时间:2012-08-27 02:04:48

标签: c++ linux compiler-construction interpreter elf

我正在编写x86二进制解释器。

目前我正在处理加载可执行文件和共享对象的问题。但是我有些疑惑:

1)动态链接器/加载器是否会连接主可执行文件和共享库的.ini部分,以便为进程映像生成一个.ini部分?对于.fini部分?

2)它是否连接了许多符号和字符串表?

3)我对重新安置感到失望。它们应该在二进制加载时或者在调用过程时发生?我想我不明白动态链接器/加载器如何管理重定位。

4)为什么存在.hash和.gnu.hash部分?为什么我需要'哈希'一个符号?

欢迎链接,评论和明显的答案。

1 个答案:

答案 0 :(得分:5)

就装载机而言,部分并不重要 - 它们被忽略了。加载程序仅查看段,并且可执行文件的每个可加载段都加载到指定的地址。然后,加载器将触发动态链接器(如果在可执行文件中调用)以处理共享对象。通常,符号和字符串表不在可加载段中,因此加载器会忽略它们。

依次回答你的问题:

1)加载程序忽略.init和.fini部分。它们通常是某些可加载段的一部分,exectuable中的初始代码将运行.init部分中的代码。动态链接器将加载共享对象的段,并调用每个入口点,类似地调用某个加载段中的.init代码

2)字符串/符号表仅对链接有意义,而不是加载。因此,动态链接器将查看它们以解析任何重定位并构建跳转表

3)重定位主要用于(静态)链接 - 可执行文件应该永远不会有它们,并且它们在共享对象中很少见(它们通常与位置无关,因此不需要它们)。一些动态链接器根本不能处理重定位(不确定正常的linux动态链接器),所以它们无法加载仍然具有重定位的共享对象

4).hash部分只是加速符号查找的优化 - 而不是通过符号表对特定符号进行线性搜索,.hash部分将直接带您到它。如果您愿意,可以放心地忽略它们并慢慢进行符号查找。

修改

关于ELF加载器的作用的简短模糊描述:

  1. 读取ELF文件的程序头

  2. 将文件的所有LOAD段加载到内存中。

  3. 如果程序头中有INTERP条目,则递归加载该二进制文件

  4. 调用程序的入口点。

  5. 这几乎就是它(关于设置堆栈还有一些额外的瑕疵,但是在加载器运行之前,这并不是加载器的一部分,而不是进程设置的一部分。)

    对于静态链接的可执行文件,没有INTERP条目,所以就是这样。对于动态链接的可执行文件,INTERP部分将类似于“/lib/ld-linux.so.2”(字符串),因此对加载器的递归调用将读取该二进制文件,加载所有LOAD部分,通知没有INTERP部分(所以没有进一步的递归调用),调用入口点,然后返回(此时基本可执行文件的加载器将调用基本可执行文件的入口点)。

    现在,动态链接器是第二个可加载的可执行文件(/lib/ld-linux.so.2)。它的作用是去阅读原始二进制文件的.dynamic部分。这将告诉它要加载的共享对象列表,以及表(.plt部分 - 程序加载表)以填充这些共享对象中特定符号的地址。因此,它将加载这些共享对象,查找其中的符号,并将其地址粘贴到该表中。每个共享对象都有自己的.dynamic节,它将由动态链接器递归处理。符号查找查看到目前为止加载的所有对象中的所有符号,因此主程序中的符号可以“覆盖”其他共享对象中的符号,并使其地址卡在共享对象的.plt中。在加载每个共享对象及其所有依赖项之后,将调用共享对象的入口点。如果两个共享对象相互依赖(这是合法的),它们都将被加载并解析它们的.plt,然后将调用两个入口点,但不会以任何特别定义的顺序。

    请注意,在上述所有情况中,重定位从未进入过。重定位可能发生的地方是共享对象无法在共享对象中指定的(虚拟)地址加载(因为在该地址已经加载了其他东西)。发生这种情况时,需要重新定位共享对象以在不同的地址加载,这涉及查看对象中的所有重定位条目,以查找需要修补以处理重定位地址的内容。

    最后,符号引用只在包含引用的对象的符号表中有一个偏移量 - 链接器需要在已加载到的所有其他对象的符号表中查找符号名称(字符串)弄清楚它的含义。每个对象都有自己的符号表,并且这些表不是逻辑上组合的。符号查找遍历到目前为止已加载的所有对象,依次在每个符号表中查找该符号,查找定义符号的条目。