7

I have written a simple Hello World program.

   #include <stdio.h>
    int main() {
    printf("Hello World");
    return 0;
    }

I wanted to understand how the relocatable object file and executable file look like. The object file corresponding to the main function is

0000000000000000 <main>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   bf 00 00 00 00          mov    $0x0,%edi
   9:   b8 00 00 00 00          mov    $0x0,%eax
   e:   e8 00 00 00 00          callq  13 <main+0x13>
  13:   b8 00 00 00 00          mov    $0x0,%eax
  18:   c9                      leaveq 
  19:   c3                      retq 

Here the function call for printf is callq 13. One thing i don't understand is why is it 13. That means call the function at adresss 13, right??. 13 has the next instruction, right?? Please explain me what does this mean??

The executable code corresponding to main is

00000000004004cc <main>:
  4004cc:       55                      push   %rbp
  4004cd:       48 89 e5                mov    %rsp,%rbp
  4004d0:       bf dc 05 40 00          mov    $0x4005dc,%edi
  4004d5:       b8 00 00 00 00          mov    $0x0,%eax
  4004da:       e8 e1 fe ff ff          callq  4003c0 <printf@plt>
  4004df:       b8 00 00 00 00          mov    $0x0,%eax
  4004e4:       c9                      leaveq 
  4004e5:       c3                      retq 

Here it is callq 4003c0. But the binary instruction is e8 e1 fe ff ff. There is nothing that corresponds to 4003c0. What is that i am getting wrong?

Thanks. Bala

4

3 回答 3

7

E8指令 ( )中的调用目标call指定为与当前指令指针 (IP) 值的相对偏移量。

在您的第一个代码示例中,偏移量显然是0x00000000. 它基本上说

call +0

的实际地址printf尚不清楚,因此编译器只是将 32 位值0x00000000作为占位符放在那里。

这种零偏移的不完整调用自然会被解释为对当前IP值的调用。在您的平台上,IP 是预先递增的,这意味着当执行某些指令时,IP 包含下一条指令的地址。即,当0xE执行地址处的指令时,IP 包含 value 0x13。而 thecall +0自然被解释为对指令的调用0x13。这就是为什么你会0x13在不完整代码的反汇编中看到这一点。

代码完成后,占位符0x00000000偏移量将替换为printf代码中函数的实际偏移量。偏移量可以是正数(向前)或负数(向后)。在您的情况下,调用时的 IP 是0x4004DF,而printf函数的地址是0x4003C0。因此,机器指令将包含一个等于 的 32 位偏移值0x4003C0 - 0x4004DF,即负值-287。所以你在代码中看到的其实是

call -287

-2870xFFFFFEE1二进制的。这正是您在机器代码中看到的内容。只是您使用的工具将其向后显示。

于 2010-05-06T23:01:48.920 回答
7

在第一种情况下,看一下指令编码——函数地址所在的地方都是零。那是因为对象还没有被链接,所以外部符号的地址还没有被连接。当您最终链接到可执行格式时,系统会在其中粘贴另一个占位符,然后动态链接器最终会printf()在运行时添加正确的地址。这是我编写的“Hello, world”程序的一个简单示例。

一、目标文件的反汇编:

00000000 <_main>:
   0:   8d 4c 24 04             lea    0x4(%esp),%ecx
   4:   83 e4 f0                and    $0xfffffff0,%esp
   7:   ff 71 fc                pushl  -0x4(%ecx)
   a:   55                      push   %ebp
   b:   89 e5                   mov    %esp,%ebp
   d:   51                      push   %ecx
   e:   83 ec 04                sub    $0x4,%esp
  11:   e8 00 00 00 00          call   16 <_main+0x16>
  16:   c7 04 24 00 00 00 00    movl   $0x0,(%esp)
  1d:   e8 00 00 00 00          call   22 <_main+0x22>
  22:   b8 00 00 00 00          mov    $0x0,%eax
  27:   83 c4 04                add    $0x4,%esp
  2a:   59                      pop    %ecx
  2b:   5d                      pop    %ebp
  2c:   8d 61 fc                lea    -0x4(%ecx),%esp
  2f:   c3                      ret    

然后是搬迁:

main.o:     file format pe-i386

RELOCATION RECORDS FOR [.text]:
OFFSET   TYPE              VALUE 
00000012 DISP32            ___main
00000019 dir32             .rdata
0000001e DISP32            _puts

正如您所看到的,那里有一个 for 的重定位_puts,这就是调用 to 的内容printf。该重定位将在链接时被注意到并修复。在动态库链接的情况下,重定位和修复可能在程序运行之前无法完全解决,但我希望你会从这个例子中得到想法。

于 2010-05-06T22:26:41.573 回答
5

调用在 x86 中是相对的,IIRC 如果你有 e8 ,调用位置是 addr+5。

e1 fe ff ffa 是小端编码的相对跳转。这真的意味着fffffee1

现在将其添加到调用指令的地址 + 5: (0xfffffee1 + 0x4004da + 5) % 2**32 = 0x4003c0

于 2010-05-06T22:32:29.660 回答