Wikibooks中这个简单的x86汇编函数调用代码片段中发生了什么?

时间:2019-03-07 18:11:49

标签: assembly x86

代码

以下简单的x86汇编代码是CDECL调用约定的listed on Wikibooks

功能定义:

_MyFunction1:
  push ebp
  mov ebp, esp
  mov eax, [ebp + 8]
  mov edx, [ebp + 12]
  add eax, edx
  pop ebp
  ret

调用函数:

push 3
push 2
call _MyFunction1
add esp, 8

这是从以下C代码生成的内容:

_cdecl int MyFunction1(int a, int b)
{
  return a + b;
}

此函数调用:

 x = MyFunction1(2, 3);

问题

不幸的是,我无法完全围绕着这里发生的事情。从调用函数开始,这是我了解的事件列表:

  • 将整数3推入堆栈
  • 将整数2推入堆栈
  • 调用_MyFunction1,将指令指针IP推入堆栈
  • 将基本指针ebp推入堆栈,以便以后可以恢复它。我想这只是必要的,因为我们要在下一条指令中将值移到ebp中?
  • 将当前堆栈指针esp的值移动到ebp中,以供以后在内存处理中使用。为什么这样为什么不直接在接下来的两条指令中使用esp的值(即[esp + 8],[esp + 12])
  • 获取我们压入堆栈的两个整数值,并将它们保存在eax和edx中。显然,我们通过指向每个内存地址来实现。处理器如何知道直到完全读取例如它必须看多远。整数3?它怎么知道这里是32位还是16位整数?堆栈指针偏移量8和12的精确度如何?它们是什么意思,例如8代表8位偏移吗?
  • 将两个值一起添加到eax中
  • 从堆栈中恢复ebp
  • 返回,即从堆栈中弹出包含下一条指令的eip并跳转到它
  • 向堆栈指针esp加8(再次,是什么单位?)
    • 现在,使用CDECL,调用者必须清理堆栈。我认为这是本指令所发生的。但是,简单地前进指针是如何清理堆栈的方法呢?从未弹出3和2整数值。如果有的话,我会以为减小指针值可以解决问题,而像这里所做的那样增加它

1 个答案:

答案 0 :(得分:2)

  • 带有ebp和esp的指令是设置堆栈框架的标准方法。您是对的,没有真正的必要,许多编译器却没有。原因之一是帮助调试器跟踪堆栈帧。
  • 它知道从[ebp + 8]和[ebp + 12]读取4个字节,因为eax和edx是4字节寄存器。该大小在说明中进行了编码。
  • x86地址始终以字节为单位。因此,添加到ebp的8和12偏移量以字节为单位。
  • 将8加到esp时,即只是一个数字,没有单位。但是,当使用esp访问内存时,会将其视为持有字节地址。因此,有效地8表示8个字节。
  • 堆栈变小。推减堆栈指针。因此,添加到esp可以从堆栈中删除内容。值2和3仍在内存中,但是由于它们位于堆栈指针之下,因此实际上已消失。 (实际上,在某些罕见的情况下,它们可能实际上都消失了,例如调试器使用堆栈或a Windows SEH exception handler或在非Windows信号处理程序中评估函数调用。)