028-【X86-汇编语言】-过程--用堆栈保存寄存器的值

上一节的例程中存在一个问题,在SUBP中使用了寄存器EAX。导致MAINP出现不符合预期的结果。本节我们来解决这个问题。

思路

思路是在一个地方保存寄存器的值,然后在调用完SUBP后回复寄存器的值。

确定了思路后,面临两个问题:1,谁来保存和回复寄存器的值,MAINP还是SUBP?2,用什么保存寄存器的值。

问题1

首先看问题1。我们可以比较一下由MAINP和SUBP来保存和回复寄存器值的差别

MAINP SUBP

1.必须知道SUBP中都是用了哪些寄存器

2.修改SUBP时,如果SUBP使用了新的寄存器,MAINP调用SUBP的代码就需要改写。增加保存和回复寄存器的操作

3.为了解决问题二,可以将所有寄存器的值都保存起来,调用完之后继续全部回复。这样做存在一个问题,有时SUBP会将处理结果,也就是返回值,保存到某个寄存器中以便让MAINP获取到。如果采用这个方式就是使放回值被覆盖掉

1.SUBP本身知道自己使用了哪些寄存器

2.修改SUBP时调用MAINP调用SUBP的代码不需要改写

由上表MAINP的第2条和MAINP的第三条可以看出,让SUBP还保存和回复寄存器的值是较好的选择。

问题2

首先一些人会想到用变量来保存寄存器的值,这样当然可以。不过这样会面临一个问题,由于程序是复杂的,因此不能确定程序中到底有多少次过程调用,也就不能确定每次过程调用需要保存多少个寄存的值。因此无法在编写程序时确定申请多少个变量保存寄存器的值。即便是一个勤快人,每定义一个SUBP就为他申请对应的变量,不过此时还需要管理变量中的值是对应哪次过程调用的哪个寄存器,如果遇到递归调用(SUBP调用SUBP),这简直是一个噩梦。

还好除了变量之外我们还有另一个选择堆栈。因为堆栈只能在堆栈头插入和弹出数据,所以只需要在SUBP开始处将要保存的寄存器的值压入堆栈,在SUBP结束处将其再抛出到寄存器。只要PUSH和POP指令的数量是相同的,就能保证堆栈回复到调用SUBP之前的状态。只要堆栈够大调用嵌套多少次过程都不成问题。

程序实例

.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExit:DWORD
 
.code
SUBP PROC
    PUSH EAX	;将EAX的值PUSH进堆栈
	MOV EAX,2h
    POP EAX		;将EAX的值恢复
	RET
SUBP ENDP
 
MAINP PROC
	MOV EAX,1h
	CALL SUBP
	MOV EBX,EAX
INVOKE ExitProcess,0
MAINP ENDP
END MAINP

可执行单步调试查看内存和堆栈的变化

猜你喜欢

转载自blog.csdn.net/patronwa/article/details/89667734