IA-32汇编语言笔记(6)——堆栈的作用

  • 记录汇编语言课笔记,可能有不正确的地方,欢迎指出
  • 教材《新概念汇编语言》—— 杨季文
  • 这篇文章对应书第二章 IA32处理器基本功能 3.1部分

一、过程调用和返回指令

(1)过程

  1. 过程汇编语言中的子程序称为过程(procedure),对应C语言中的函数
  2. 调用子程序(过程、函数)在本质上是控制转移,它与无条件转移的区别是调用子程序要考虑返回

(2)过程调用指令

名称CALL(过程调用指令)
格式CALL LABEL
动作先把返回地址偏移(EIP内容)压入堆栈,然后给EIP赋值为目标LABEL的地址偏移,实现转移
注意返回地址:紧随过程调用指令的下一条指令的地址(有效地址
目标地址:子程序开始处的地址(有效地址
与无条件转移指令相比,过程调用指令CALL只是多了第一步(保存返回地址)

在这里插入图片描述

(3)过程返回指令

名称RET(过程返回指令)
格式RET
动作从堆栈弹出地址偏移,并送到指令指针寄存器EIP
注意通常,这个返回地址就是在执行对应的调用指令时所压入堆栈的返回地址
过程返回指令的使用应该与过程调用指令相对应
  • 示例:
/*
dp32
	·演示子程序的调用和返回,说明call和RET指令的作用
	·调试时关注ESP的值,进出子程序时有压栈和出栈
	·这里都是用寄存器传递参数的,影响堆栈的只有进/出子程序时保存/弹出原EIP位置
	·特别注意,这里把子程序的汇编安排在return语句之后,这样能避免不经调用直接进入子程序。如果有编译器不支持这样写,需要用goto跳过
*/
#include <stdio.h>
char string[] = "abcde";

int main()
{
	_asm
	{
		LEA ESI,string		//指针寄存器ESI指向string首
		MOV AX,[ESI]		//从ESI取两个字节放入AX ('a''b')
		CALL TUPPER			//把这两个字节的字符小写转大写
		MOV [ESI],AX

		MOV AX,[ESI+2]
		CALL TUPPER			//把这两个字节的字符小写转大写(这里进栈了返回地址偏移,ret时自动平衡)
		MOV [ESI+2],AX
		
		MOV AL, [ESI+4]
		CALL UPPER			//把最后一个字节的字符小写转大写
		MOV [ESI+4],AL
	}

	printf("%s\n", string);
	system("pause");

	return 0;

	_asm
	{
	//UPPER函数用于把AL中的字符小写转大写
	UPPER:
		CMP AL, 'a'
		JB UPPER2
		CMP AL, 'z'
		JA UPPER2
		SUB AL, 20H	//确认是小写字符后,转为大写
	UPPER2 :
		RET

	//TUPPER函数用于把AX中的两个字符小写转大写
	TUPPER :
		CALL UPPER
		XCHG AH, AL
		CALL UPPER
		XCHG AH, AL
		RET
	}
}

二、参数传递

(1)参数传递

  1. 参数传递:主程序在调用子程序时,往往要向子程序传递一些参数;同样,子程序运行后也经常要把一些结果返回给主程序。主程序与子程序之间的这种信息传递被称为参数传递
  2. 入口参数由主程序传给子程序的参数称为子程序的入口参数
    出口参数由子程序传给主程序的参数称为子程序的出口参数
  3. 一般而言,子程序既有入口参数,又有出口参数。但有的子程序只有入口参数,而没有出口参数;少数子程序只有出口参数,而没有入口参数。

(2)参数传递方法

  1. 有多种传递参数的方法∶寄存器传递法、堆栈传递法、约定内存单元传递法和CALL后续区传递法等。有时可能同时采用多种方法,根据具体情况而事先约定好
  2. 常用的两种方法:
方法说明特点
寄存器传递参数把参数放在约定的寄存器中,在主程序中将参数放入寄存器,在子程序中取出实现简单和调用方便,但只适用于传递参数较少的情形
堆栈传递参数主程序在调用子程序之前,把需要传递的参数依次压入堆栈,然后子程序从堆栈中取入口参数不占用寄存器,也无需额外的存储单元。但较为复杂
  • 示例:
/*
dp33
	·演示利用堆栈传递子程序参数,子程序参数x,y,返回2x+5y+100
	·注意平衡堆栈:esp和ebp的值在子程序调用前后是一致的
	·如果反汇编C语言形式的这个函数,不要加_fastcall前缀,否则会用寄存器传参,不会生成这样的代码
*/

#include <stdio.h>

int main()
{
	int sum = 0;

	_asm
	{
		push 456		//参数y进栈,esp+=4
		push 23			//参数x进栈,esp+=4
		call cf34		//这里进栈了返回地址偏移(ret自动平衡)
		add esp,8		//手动平衡堆栈(x,y)

		mov sum,eax
	}

	printf("sum=%d\n", sum);
	system("pause");
	return 0;

	_asm
	{
	cf34:
		push ebp					//子程序返回时ebp要复原,这里先保存一下,esp+=4
		mov ebp,esp					//ebp指向栈顶(esp)——建立堆栈框架
		mov eax,DWORD PTR[ebp+12]	//之前入栈了3个值,这里地址往回找4*3=12,取出第一个参数y
		mov ecx,DWORD PTR[ebp+8]	//取第二个参数x
		
		//计算2x+5y+100,存入eax
		lea eax,DWORD PTR[eax+eax*4+100]
		lea eax, DWORD PTR[eax+ecx*2]
		pop ebp						//回复ebp的值,平衡堆栈——撤销堆栈框架
		ret
	}
}
  • 堆栈传递参数的堆栈变化示意
    在这里插入图片描述

三、局部变量

  1. 局部变量是高级语言中的概念。所谓局部变量指对其的访问仅限于某个局部范围。在C语言中,局部的范围可能是函数,或者是复合语句。局部变量还有动态和静态之分。
  2. 堆栈可以用于安排动态局部变量
  • 示例:
/*
dp36
	·演示利用堆栈安排动态局部变量
	·等价的C程序:
		int cf36(int x,int y)	//返回xy中较大的
		{
			int z;
			z=x;
			if(x<y)
				z=y;
			return z;
		}
*/

#include <stdio.h>

int main()
{
	int max = 0;
	int x, y;
	printf("输入两个整数:");
	scanf("%d %d", &x, &y);

	_asm
	{
		mov eax,y
		push eax
		mov eax,x
		push eax

		call cf36		//这里进栈了返回地址偏移
		add esp,8		//平衡堆栈

		mov max,eax
	}
	printf("max = %d\n", max);
	system("pause");
	return 0;

	_asm
	{
	cf36:
		push ebp
		mov ebp,esp		//建立堆栈框架
		push ecx		//在堆栈安排局部变量z
		mov eax,DWORD PTR[ebp+8]	//eax取得参数x
		mov DWORD PTR[ebp-4],eax	//z=x
		
		mov ecx, DWORD PTR[ebp+8]	//ecx取得参数x
		cmp ecx, DWORD PTR[ebp+12]	//比较x和y
		jge SHORT LN1cf36			//x>y则跳转,SHORT表示转移目的地就在附近
		mov edx, DWORD PTR[ebp+12]	//x<=y,则edx取得y,赋值给z
		mov DWORD PTR[ebp-4],edx
		
	LN1cf36:
		mov eax,DWORD PTR[ebp-4]	//eax取得z的值
		mov esp,ebp					//撤销局部变量z
		pop ebp						//撤销堆栈框架
		ret 
	}
}

  • 堆栈示意,安排局部变量并且由堆栈传递参数
    在这里插入图片描述
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云端FFF

所有博文免费阅读,求打赏鼓励~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值