怀疑可执行和可重定位目标文件

bal*_*486 7 c executable object-files

我写了一个简单的Hello World程序.

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

我想了解可重定位目标文件和可执行文件的外观.与main函数对应的目标文件是

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 
Run Code Online (Sandbox Code Playgroud)

这里对printf的函数调用是callq 13.我不明白的一件事是为什么它是13.这意味着在地址13调用函数,对吧?13有下一条指令,对吧?请解释一下这是什么意思?

与main对应的可执行代码是

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 
Run Code Online (Sandbox Code Playgroud)

这是callq 4003c0.但二进制指令是e8 e1 fe ff ff.没有任何东西与4003c0相对应.我错了什么?

谢谢.巴拉

Car*_*rum 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    
Run Code Online (Sandbox Code Playgroud)

然后重新安置:

main.o:     file format pe-i386

RELOCATION RECORDS FOR [.text]:
OFFSET   TYPE              VALUE 
00000012 DISP32            ___main
00000019 dir32             .rdata
0000001e DISP32            _puts
Run Code Online (Sandbox Code Playgroud)

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


AnT*_*AnT 7

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

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

call +0
Run Code Online (Sandbox Code Playgroud)

实际的地址printf尚不可知,因此编译器只将32位值0x00000000作为占位符.

具有零偏移的这种不完整呼叫自然将被解释为对当前IP值的调用.在您的平台上,IP是预先递增的,这意味着当执行某些指令时,IP包含下一条指令的地址.即,当0xE执行地址处的指令时,IP包含值0x13.这call +0自然被解释为对教学的呼唤0x13.这就是你0x13在不完整代码的反汇编中看到的原因.

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

call -287
Run Code Online (Sandbox Code Playgroud)

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


L̲̳*_*̲̳̳ 5

如果你有e8,呼叫位置是addr + 5,则在x86,IIRC中呼叫是相对的.

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

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