首先先看图:
在main函数调用func_A的时候,首先在自己的栈帧中压入函数返回地址,然后为func_A创建新栈帧并压入系统栈在func_A调用func_B的时候,同样先在自己的栈帧中压入函数返回地址,然后为func_B创建新栈帧并压入系统栈在func_B返回时,func_B的栈帧被弹出系统栈,func_A栈帧中的返回地址被“露”在栈顶,此时处理器按照这个返回地址重新跳到func_A代码区中执行在func_A返回时,func_A的栈帧被弹出系统栈,main函数栈帧中的返回地址被“露”在栈顶,此时处理器按照这个返回地址跳到main函数代码区中执行
在实际运行中,main函数并不是第一个被调用的函数,程序被装入内存前还有一些其他操作,上图只是栈在函数调用过程中所起作用的示意图
ESP:栈指针寄存器(extended stack pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶
,其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶EBP:基址指针寄存器(extended base pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部函数栈帧:ESP和EBP之间的内存空间为当前栈帧,EBP标识了当前栈帧的底部,ESP标识了当前栈帧的顶部。
EIP:指令寄存器(extended instruction pointer), 其内存放着一个指针,该指针永远指向下一条待执行的指令地址。
函数调用大致包括以下几个步骤:
参数入栈:将参数从右向左依次压入系统栈中
返回地址入栈:将当前代码区调用指令的下一条指令地址压入栈中,供函数返回时继续执行
代码区跳转:处理器从当前代码区跳转到被调用函数的入口处
栈帧调整:具体包括保存当前栈帧状态值,已备后面恢复本栈帧时使用(EBP入栈)
将当前栈帧切换到新栈帧。(将ESP值装入EBP,更新栈帧底部)
给新栈帧分配空间。(把ESP减去所需空间的大小,抬高栈顶)
void func_A(arg_A1, arg_A2);
void func_B(arg_B1, arg_B2);
int main(int argc, char *argv[], char **envp)
{
func_A(arg_A1, arg_A2);
}
void func_A(arg_A1, arg_A2)
{
var_A;
func_B(arg_B1, arg_B2);
}
void func_B(arg_B1, arg_B2)
{
var_B1;
var_B2;
}
一个程序在运行过程中,一个函数会调用另一个函数(比如递归),那么函数在进入的时候都会往栈里压点什么东西
会压入: 原来ebp的值, 参数, 以及调用函数的下一个指令地址在调用一个函数时, 编译器就计算好函数需要的空间, 然后esp = ebp-需要的空间, 通过ebp+偏移量来访问. 在函数里调用另外一个函数时, 原来fun的ebp值压栈
push ebp
ebp = esp
esp = ebp-需要的空间
借此调用另外的函数
比如对于以上的程序
0x123 rbp(main)
0x11f c
0x11b b
0x117 a
0x113 rbp(foo) rsp(main,foo)
100000f30 <调用fun下一个指令地址>
函数退出时会弹出点什么东西,内层的函数是如何返回的,返回给外层函数的谁,返回到哪里,内层函数是怎么知道返回地址的
退出时会做函数调用时的逆操作, 看伪代码:
pop ebp
esp = ebp
esp = ebp+需要的空间
汇编与C之间的关系**
最后, 看此图就一目了然:
Method Calls in JavaFirst, some information about the Stack, local variables & parameters• The Stack keeps the state of all active methods(those that have been invoked but have not yet completed)• When a method is called a new stack frame for it is added tothe top of the stack, this stack frame is where space for themethod’s local variables and parameters are allocated• When a method returns, its stack frame is removed from thetop of the stack (space for its local vars and params is de-allocated)• Space for local variables and parameters exist only while themethod is active (it has a stack frame on the Stack)• local variables and parameters are only in scope when they are inthe top stack frame (when this method’s code is being executed)
An Example
public class TestMethodCalls {
public static void main(String[] args) {
Foo f1, f2;
int x=8;
f1 = new Foo(10);
f2 = new Foo(12);
f1.setVal(x);
x = f1.add(f2, x);
}
public class Foo {
private int x;
public Foo(int val) {
x = val;
}
public void setVal(int val) {
x = val;
}
public int getVal() {
return x;
}
public int plus(Foo f, int val) {
int result;
result = f.getVal() + x + val;
return result;
}
}