调用后被调用函数如何返回其调用者?

hac*_*cks 12 c compiler-construction abi function-call stack-frame

我读到当程序进行函数调用时,被调用函数必须知道如何返回其调用者.

我的问题是:被调用函数如何知道如何返回其调用者?是否有通过编译器在幕后工作的机制?

Car*_*rum 12

编译器遵循特定的"调用约定",定义为您要定位的ABI的一部分.该调用约定将包括一种系统知道返回什么地址的方法.调用约定通常利用硬件对过程调用的支持.例如,在Intel上,返回地址被推送到堆栈:

...处理器在堆栈上按下EIP寄存器的值(包含指令后面的CALL指令的偏移量)(以后用作返回指令指针).

从函数返回是通过以下ret指令完成的:

...处理器将返回指令指针(偏移)从堆栈顶部弹出到EIP寄存器中,并在新指令指针处开始执行程序.

相比之下,在ARM上,返回地址放在链接寄存器中:

BLBLX指令复制下一条指令到的地址lr(r14中,链接寄存器).

返回通常通过执行movs pc, lr将链接寄存器中的地址复制回程序计数器寄存器来完成.

参考文献:

  1. 英特尔软件开发人员手册
  2. ARM信息中心


Dev*_*lus 8

  1. 编译器知道如何调用函数以及使用哪个调用约定.例如,在C中,函数的参数被推送到堆栈上.调用者可以清除堆栈,因此被调用的函数不必删除参数.其他调用约定可以包括在堆栈上推送参数,并且被调用的函数必须清除它.在这种情况下,生成的代码是这样的,该函数在它返回之前纠正堆栈.调用约定可以在寄存器中传递参数,因此在这种情况下,被调用的函数也不必小心.

  2. CPU具有调用子例程的机制.这将把当前执行地址存储在堆栈上,然后将处理转移到新地址.当函数完成时,它执行一个return语句,它将获取调用者地址并在那里继续执行.

如果返回地址被销毁,因为堆栈没有被正确清理,或者内存被覆盖,那么你会得到未定义的行为.当然,具体的实现细节取决于所使用的平台.