我试图理解以下函数的反汇编代码。
void func(char *string) {
printf("the string is %s\n",string);
}
下面给出了反汇编的代码。
1) 0x080483e4 <+0>: push %ebp
2) 0x080483e5 <+1>: mov %esp,%ebp
3) 0x080483e7 <+3>: sub $0x18,%esp
4) 0x080483ea <+6>: mov $0x80484f0,%eax
5) 0x080483ef <+11>: mov 0x8(%ebp),%edx
6) 0x080483f2 <+14>: mov %edx,0x4(%esp)
7) 0x080483f6 <+18>: mov %eax,(%esp)
8) 0x080483f9 <+21>: call 0x8048300 <printf@plt>
有人能告诉我4-7行是什么意思(不是文字解释)。为什么在第3行的堆栈上分配了24个字节?
答案 0 :(得分:6)
基本上这里发生了什么:
4) 0x080483ea <+6> : mov $0x80484f0,%eax
将"the string is %s\n"
的地址加载到eax
。
5) 0x080483ef <+11>: mov 0x8(%ebp),%edx
将参数string
移至edx
。
6) 0x080483f2 <+14>: mov %edx,0x4(%esp)
将edx
或string
的值推入堆栈,printf
的第二个参数
7) 0x080483f6 <+18>: mov %eax,(%esp)
将eax
或"the string is %s\n"
的值推入堆栈,printf
的第一个参数,然后它将调用printf
。
sub $0x18,%esp
是没有必要的,因为函数没有局部变量,gcc似乎喜欢创造额外的空间但老实说我不知道为什么。
答案 1 :(得分:1)
堆栈是一个连续的内存区域,从较高的地址开始,以esp
结束。无论何时需要堆栈增长,都要从esp
中减去。每个函数都可以在堆栈上有一个框架。它是函数拥有的堆栈的一部分,并在完成后负责清理。这意味着,当函数启动时,它会减少esp
以创建其框架。当它结束时它会增加它。 ebp
通常指向框架的开头。
最初,此函数将ebp
推送到堆栈,以便在函数结束时存储它,设置esp = ebp
以标记其帧的开始并分配28个字节。为什么28?用于对齐。它已经为ebp
分配了4个字节。 4 + 28 = 32。
第4-7行将准备对printf
的呼叫。它期望它的参数位于调用者的框架上。当我们阅读mov 0x8(%ebp), %edx
时,我们从调用者的框架中获取我们的参数char* string
。 printf
也会这样做。
请注意,您的程序集缺少leave
和ret
指令以清除堆栈并返回给调用者。