调用方法/函数时汇编语言会发生什么?

如果我有一个C ++ / C程序(语言无关紧要,只需说明一个概念):

#include  void foo() { printf("in foo"); } int main() { foo(); return 0; } 

assembly中会发生什么? 我实际上并不是在寻找汇编代码,因为我还没有那么远,但基本原理是什么?

一般来说,这是发生的事情:

  1. 函数的参数存储在堆栈中。 按平台特定顺序。
  2. 返回值的位置在堆栈上“已分配”
  3. 该函数的返回地址也存储在堆栈或专用CPU寄存器中。
  4. 通过CPU特定的call指令或通过正常的jmpbr指令(跳转/分支) call函数(或实际上,函数的地址)
  5. 该函数从堆栈中读取参数(如果有)并运行函数代码
  6. 函数返回值存储在指定位置(堆栈或专用CPU寄存器)
  7. 执行跳转回调用者并清除堆栈(通过将堆栈指针恢复为其初始值)。

上述细节因平台而异,甚至从编译器到编译器也有所不同(参见例如STDCALL与CDECL调用约定)。 例如,在某些情况下,使用CPU寄存器而不是在堆栈上存储东西。 一般的想法是相同的

你可以自己看看:

在Linux下’编译’您的程序:

 gcc -S myprogram.c 

并且您将获得汇编程序(myprogram.s)中的程序列表。

当然你应该对汇编程序有一点了解它(但它值得学习,因为它有助于理解你的计算机是如何工作的)。 调用函数(在x86架构上)基本上是:

  • 把变量放在堆栈上
  • 将变量b放在堆栈上
  • 把变量n放在堆栈上
  • 跳转到该函数的地址
  • 从堆栈加载变量
  • 做function的东西
  • 干净的堆栈
  • 跳回主要

assembly中会发生什么?

简要说明:保存当前堆栈状态,创建新堆栈并加载并运行要执行的函数的代码。 这会给微处理器的一些寄存器带来不便,一些来回读取/写入内存,一旦完成,就会恢复调用函数的堆栈状态。

参数被推入堆栈并进行“调用”指令

调用是一个简单的“jmp”,将指令的地址压入堆栈(“ret”在弹出它并跳过它的方法的末尾)

我想你想看一下调用堆栈,以便更好地了解函数调用期间发生的事情: http : //en.wikipedia.org/wiki/Call_stack

怎么了? 在x86中,main函数的第一行可能如下所示:

call foo

call指令将推送堆栈上的返回地址,然后jmp到foo的位置。

一个很好的例子: http : //www.cs.uleth.ca/~holzmann/C/system/memorylayout.pdf

怎么了?

C模仿assembly中会发生什么……

它离机器很近,你可以意识到会发生什么

 void foo() { printf("in foo"); /* db mystring 'in foo' mov eax, dword ptr mystring mov edx , dword ptr _printf push eax call edx add esp, 8 ret //thats it */ } int main() { foo(); return 0; } 

1-在堆栈上建立调用上下文

2-参数被推入堆栈

3-对该方法执行“调用”

一般的想法是你需要

  1. 保存当前的本地状态
  2. 将参数传递给函数
  3. 调用实际function。 这涉及将返回地址放在某处,以便RET指令知道继续的位置。

具体情况因建筑而异。 更具体的细节可能因各种语言而异。 虽然通常有一些方法可以在一定程度上控制它,以允许不同语言之间的互操作性。

关于调用约定的维基百科文章是一个非常有用的起点。 例如,在x86上,堆栈几乎总是用于将参数传递给函数。 然而,在许多RISC架构中,主要使用寄存器,而在特殊情况下仅需要堆栈。

常见的想法是,调用方法中使用的寄存器被压入堆栈(堆栈指针位于ESP寄存器中),此过程称为“推送寄存器”。 有时他们也被归零,但这取决于。 汇编程序员倾向于释放更多寄存器,然后使用通用4( EAXEBXECXEDX )以在函数内提供更多可能性。

当函数结束时,反过来也会发生这种情况:堆栈恢复到调用之前的状态。 这称为“弹出寄存器”。

更新:此过程不一定必须发生。 编译器可以优化它并内联您的function。

更新:通常函数的参数以相反的顺序被推入堆栈,当从堆栈中检索它们时,它们看起来好像是正常的顺序。 C不保证此订单。(参考:Rick Booth的Inner Loops