关于c

时间:2016-06-13 07:16:42

标签: c linker

目标文件中关于文本部分的每个机器代码都有地址,它将从0到数字。

当链接器链接所有目标文件时,有关指令的地址将发生变化。

我无法看到链接器是否会逐个读取有关文本部分的指令,以便更改每个指令地址。

Disassembly of section .text:

00000000 <_start>:

    0:  bf 00 00 00 00          mov    $0x0,%edi
    5:  8b 04 bd 00 00 00 00    mov    0x0(,%edi,4),%eax
    c:  89 c3                   mov    %eax,%ebx

链接

08048074 <_start>:

    8048074:    bf 00 00 00 00          mov    $0x0,%edi
    8048079:    8b 04 bd a0 90 04 08    mov    0x80490a0(,%edi,4),%eax
    8048080:    89 c3                   mov    %eax,%ebx

就像0→8048074等等。

1 个答案:

答案 0 :(得分:2)

好吧,所以我假设您正在使用一些基于unix的系统,因为这看起来像是objdump命令的输出,但我知道这对于ELF和PE文件都是相关的。

所以让我们开始吧,首先当你使用c时,你将一些模型编译成目标文件并最终将它们链接在一起,如前所述。 e.g:

  • m1.c - &gt; m1.o
  • m2.c - &gt; m2.o
  • main.c + m2.o + m1.o - &gt; main.exe

我们有一些名为m1.c / m2.c的c程序定义了一些由main.c调用的函数,最终所有函数都被链接并编译成一个完全可执行的main.exe。 / p> 现在,让我们潜入并了解引擎盖下发生的事情。首先,我想从一个非常重要的开始开始,在最终的可执行文件中,在我们的示例中(main.exe)所有地址是完全解析的虚拟地址(这不一定是真的,因为一些名为PIE / PIC的概念,但现在让我们不要进入它)

因此在你可执行的范围内,foo中的函数m1.o会有一些已解析的地址(例如0x400100),当你调用foo时你会在main.exe中看到反汇编诸如

之类的东西
call 0x400100

现在这就是概念上发生的事情,现在让我们进入实际发生的事情。 在获取指令时,例如jmpcall指令,某个地址作为操作数给出,然后处理器的指令寄存器被更改为以操作数给出的地址,所以你的问题很聪明,应该是链接器按指令进行指令,找到需要更改的内容并进行更改?好吧,链接器根本就不这样做,它比那更聪明。

首先,在编译时,编译器生成跳转并调用内部模块(例如jmp到相对于当前指令执行应该已经属于我们示例中的m1.o的某个地址)。那是什么意思? 让我们说我们有一些if语句,它将被编译为跳转到某些地址,编译器足够智能使用相对跳转操作数并在命令之间放置偏移量,因此当链接链接器时不会甚至必须改变它们,它与加载代码的地址无关,因为调用是相对于当前指令的,并且某些目标文件的命令之间的偏移在链接阶段保持静态。

现在,事情变得更加复杂,我们已经介绍了链接器如何避免更改m1.o内的地址,现在如果m2.o调用m1.o中定义的函数{1}}两者都是可执行文件,并且地球上没有办法让编译器可以假设它们之间的偏移,因为它们都不知道它们将链接多少其他模型,这是如何解决的?引入符号和重定位表。

  • 符号表 - 包含模型中所有符号的表格 - a 符号是其他模型可能需要通过名称识别的东西, 例如函数和全局变量。
  • 重定位表 - 包含所有&#34;出现次数的表格#34;的 某些模型中的符号。

您之前可能已经听说过这些,但现在我将向您解释这些问题。 在进入它之前,我需要警告我对ELF格式文件比较熟悉,但据我所知,概念上PE文件的工作方式相同。

让我们看看这个示例代码

#include <stdio.h>
/** file: m1.c **/

extern void goo();

void foo()
{
  printf("I am foo()!\n");
  goo();
}

#include <stdio.h>
/** file: m2.c **/

void goo()
{
  printf("I am goo()!\n");
}

在目标文件中编译m1.o时,会出现一些类似这样的表

符号:foo - &gt;在文件中的偏移量X,goo - &gt;未定义 重新定位:goo - &gt;在文件中的偏移量Y,

现在这意味着编译器会生成一个表,该表收集模型使用的所有函数并确定它们是否已定义 - 它给出了函数在字段中定义的偏移量,如果它不是定义它会陈述它,

它也会说在这个模型中goo被调用偏移X并且它需要被重新定位(我们已经达到了我的观点,它是你问题的答案!)

当链接到可执行文件时,链接器获取所有目标文件的所有符号,解析其中的一些地址,然后遍历每个目标文件的每个符号表,查看并确定哪些符号尚未定义,然后通过重定位表并查看对未定义的符号进行哪些调用,将其放在文件中的那个位置,然后简单地将调用的地址重新写入已解析的地址,这样如果我们在{{{ 1}}

m1.o

在符号解析之后,链接器可能在重定位表上有一些条目,说你需要重新定位第X行的goo地址,我们将导致

call 0x000000 ;undefined goo address

仅供参考,当有一个未定义的引用链接器错误时,它意味着你的符号表中有一些未定义的符号,并且链接器无法为它解析匹配的函数定义...如果我还没有弄清楚自己,这对全局变量和静态变量的作用完全相同,它们也被认为是符号