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

时间:2009-10-18 15:20:57

标签: c++ c assembly function

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

#include <iostream>    

void foo() {
    printf("in foo");
}

int main() {
    foo();
    return 0;
}

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

11 个答案:

答案 0 :(得分:43)

一般情况下,会发生这种情况:

  1. 函数的参数存储在堆栈中。按平台特定顺序。
  2. 返回值的位置在堆栈上“已分配”
  3. 该函数的返回地址也存储在堆栈或专用CPU寄存器中。
  4. 通过CPU特定call指令或通过普通jmpbr指令(跳转/分支)调用函数(或实际上,函数的地址)
  5. 该函数从堆栈中读取参数(如果有)并运行函数代码
  6. 函数返回值存储在指定位置(堆栈或专用CPU寄存器)
  7. 执行跳转回调用者并清除堆栈(通过将堆栈指针恢复为其初始值)。
  8. 上述细节因平台而异,甚至从编译器到编译器也有所不同(参见例如STDCALL与CDECL调用约定)。例如,在某些情况下,使用CPU寄存器而不是在堆栈上存储东西。一般的想法是相同的,但

答案 1 :(得分:12)

你可以自己看看:

在Linux下“编译”您的程序:

gcc -S myprogram.c

你将在汇编程序(myprogram.s)中获得程序列表。

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

  • 将变量放在堆栈上
  • 将变量b放在堆栈上
  • 将变量n放在堆栈上
  • 跳转到该功能的地址
  • 从堆栈加载变量
  • 执行功能
  • clean stack
  • 跳回主

答案 2 :(得分:3)

  

装配中会发生什么?

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

答案 3 :(得分:1)

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

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

答案 4 :(得分:1)

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

答案 5 :(得分:1)

会发生什么?在x86中,main函数的第一行可能类似于:

call foo

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

答案 6 :(得分:1)

答案 7 :(得分:1)

会发生什么?

C模仿装配中会发生什么......

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

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;
}

答案 8 :(得分:0)

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

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

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

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

答案 9 :(得分:0)

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

2-参数被压入堆栈

3-对方法执行“调用”

答案 10 :(得分:0)

一般的想法是你需要

  1. 保存当前的本地状态
  2. 将参数传递给函数
  3. 调用实际功能。这涉及将返回地址放在某处,以便RET指令知道继续的位置。
  4. 具体情况因架构而异。更具体的细节可能因各种语言而异。虽然通常有一些方法可以在一定程度上控制这种情况,以允许不同语言之间的互操作性。

    一个非常有用的起点是Wikipedia article on calling conventions。例如,在x86上,堆栈几乎总是用于将参数传递给函数。但是,在许多RISC架构中,主要使用寄存器,而只在特殊情况下才需要堆栈。