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相对应.我错了什么?
谢谢.巴拉
在第一种情况下,看看指令编码 - 它是函数地址所有的零.那是因为该对象尚未链接,因此外部符号的地址尚未连接.当您执行可执行格式的最终链接时,系统会在其中粘贴另一个占位符,然后动态链接器最终将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.该重定位将在链接时被注意并被修复.在动态库链接的情况下,重定位和修正可能在程序运行之前无法完全解析,但我希望你能从这个例子中得到想法.
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)
-287是0xFFFFFEE1二进制的.这正是您在机器代码中看到的内容.只是您正在使用的工具向后显示它.
如果你有e8,呼叫位置是addr + 5,则在x86,IIRC中呼叫是相对的.
e1 fe ff ffa是小端编码的相对跳转.这真的意味着fffffee1.
现在将其添加到调用指令+ 5的地址:
(0xfffffee1 + 0x4004da + 5) % 2**32 = 0x4003c0
| 归档时间: |
|
| 查看次数: |
2725 次 |
| 最近记录: |