基础知识备用:

栈帧的定义:为单个过程分配的那部分栈。

栈帧的作用:传递过程参数;存储返回信息;保存寄存器内容用于以后恢复;本地存储。

栈底指针(帧指针):ebp寄存器。

栈顶指针(栈指针):esp寄存器。

栈的生长方向:向低地址方向增长。



调用者的栈帧存储内容:


A:被调用者的参数。


B:调用者的返回地址。



被调用者的栈帧存储内容是


A:从保存ebp的值开始的。


在被调用过程中第一个参数的位置放在相对于ebp偏移量为8的位置处。(先存储参数,再存储返回地址,最后是ebp的值,返回地址是四个字节,参数是四个字节)。




call的作用:(函数调用时)

A:将返回地址入栈。

B:跳转到被调用函数过程的起始过程。

以上内容参考《深入理解计算机组成系统》。

实践部分:

#include<stdio.h>
#include<windows.h>


//隐式调用函数改变函数的返回值。
void Debug()
{
	printf("哈哈哈哈,修改main函数的返回值!\n");
	Sleep(3000);
}
int *p_resever = NULL;


//通过栈帧结构修改参数的值。(函数内部局部变量和参数)
int fun(int x, int y)
{


	int c = 30;
	int *p_y = &x;//修改形参的内容。
	int *p_c = &x;//修改局部变量的内容。
	int *p_ret = &x;//修改函数的返回值。


	p_ret--;
	*p_resever = *p_ret;
	*p_ret =Debug;


	p_c = p_c - 4;
	*p_c = 66;


	p_y++;
	*p_y = 66;


	printf("y的值:%d\n", y);
	printf("c的值:%d\n", c);
	return c;
}

int main()
{
	int a = 20;


	int b = 10;


	//调用函数
	int ret = fun(a, b);


	printf("you should running here!\n");
	//平衡栈帧
	_asm{
		sub esp,4
	}
	system("pause");
	return 0;
}

A:建立函数的栈帧

建立main函数的栈帧结构如图红色部分所示,此时红色的ebp表示main 函数栈帧的栈底,esp表示main函数栈帧的栈顶。

j建立fun函数的栈帧结构:使esp先减二,后压入mian函数ebp的内容。然后让esp的内容覆盖ebp的内容,esp减去某个值,给fun函数

开辟栈帧结构。

Java 栈栈帧 栈帧里面有什么_调用函数

B:局部变量的存储

局部变量存储有两种方式:第一种是存储在调用函数的栈帧结构中,第二种是存储在被调用函数的栈帧结构中,并且在返回前恢复该值。

Java 栈栈帧 栈帧里面有什么_局部变量_02


C:修改形参的值。

原理:由于形参实例化的时候,第一个参数是最后一个入栈的,所以第一个参数的地址向高地址的方向分别是各个形参的内容。所以通过第一个形参便可以修改函数的形参值。

Java 栈栈帧 栈帧里面有什么_局部变量_03

D:修改函数的返回值

原理:由于形参实例化的时候,第一个参数是最后一个入栈的,所以第一个参数的地址向低地址的方向分别是,main函数栈帧的栈顶(main的返回值),和fun函数栈帧的栈底(

ebp的内容)。所以通过第一个形参便可以修改函数的返回值。

作用:可以隐式的调用函数。由于函数没有被显示调用,所以没有形成Debug函数的栈帧结构。

修改函数的返回值缺陷:由于修改的是main 函数的返回值,所以程序在释放栈帧结构,返回程序的时候,由于Debug函数并不知道返回值在哪里,所以无法返回到main函数中,导致程序出错。

第一层改进方法:当在修改函数的返回值时,可以使用一个全局变量去保存修改前函数的返回值。然后在Debug函数中设置一个变量,(在vs2013下第一个变量和函数返回值之间相差16个字节)通过这个变量可以找到Debug函数的返回值,将Debug函数的返回值修改成全局变量的内容,便可以时程序重新回到main函数中!

第一层改进缺陷:在函数执行完之后依然会报错。

理由:因为Debug函数并没有调用,所以没有保存返回值,但是却返回了保存值,也就是push的内容比pop的内容少了一次。所以栈帧结构上移了

4个字节。

第二层改进方法:采用平衡栈帧的方法。

_asm{

sub esp 4

}

Java 栈栈帧 栈帧里面有什么_main函数_04

E:函数调用结束。

函数调用结束,局部变量被释放。ebp的值被弹出,ebp回到main函数,程序返回到保存的返回值所在的地址处。main函数中的局部变量被释放,esp回到局部变量(main局部变量)之前。