汇编语言寄存器参数的缺点

  • 内容
  • 评论
  • 相关

多年以来,Microsoft 在 32 位程序中包含了一种参数传递规则,称为 fastcall。如同这个名字所暗示的,只需简单地在调用子程序之前把参数送入寄存器,就可以将运行效率提高一些。相反,如果把参数压入堆栈,则执行速度就要更慢一点。

典型用于参数的寄存器包括 EAX、EBX、ECX 和 EDX,少数情况下,还会使用 EDI 和 ESI。可惜的是,这些寄存器在用于存放数据的同时,还用于存放循环计数值以及参与计算的操作数。

因此,在过程调用之前,任何存放参数的寄存器须首先入栈,然后向其分配过程参数,在过程返回后再恢复其原始值。例如,如下代码从 Irvine32 链接库中调用了 DumpMem:

push ebx               ;保存寄存器值
push ecx
push esi
mov esi,OFFSET array   ;初始 OFFSET
mov ecx,LENGTHOF array ;大小,按元素个数计
mov ebx, TYPE array    ;双字格式
call DumpMem           ;显示内存
pop esi                ;恢复寄存器值
pop ecx
pop ebx

这些额外的入栈和出栈操作不仅会让代码混乱,还有可能消除性能优势,而这些优势正是通过使用寄存器参数所期望获得的!此外,程序员还要非常仔细地将 PUSH 与相应的 POP 进行匹配,即使代码存在着多个执行路径。

例如,在下面的代码中,第 8 行的 EAX 如果等于 1,那么过程在第 17 行就无法返回其调用者,原因就是有三个寄存器的值留在运行时堆栈里。

push ebx                   ;保存寄存器值
push ecx
push esi
mov esi, OFFSET array      ;初始 OFFSET
mov ecx, LENGTHOF array    ;大小,按元素个数计
mov ebx, TYPE array        ;双字格式
call DumpMem               ;显示内存
cmp eax, 1                 ;设置错误标志?
je error_exit              ;设置标志后退出

pop esi                    ;恢复寄存器值
pop ecx
pop ebx
ret
error_exit:
mov edx, offset error_msg
ret

不得不说,像这样的错误是不容易发现的,除非是花了相当多的时间来检查代码。

堆栈参数提供了一种不同于寄存器参数的灵活方法:只需要在调用子程序之前,将参数压入堆栈即可。比如,如果 DumpMem 使用了堆栈参数,就可以通过如下代码对其进行调用:

push TYPE array
push LENGTHOF array
push OFFSET array
call DumpMem

子程序调用时,有两种常见类型的参数会入栈:

  • 值参数(变量和常量的值)
  • 引用参数(变量的地址)

值传递

当一个参数通过数值传递时,该值的副本会被压入堆栈。假设调用一个名为 AddTwo 的子程序,向其传递两个 32 位整数:

.data
val1 DWORD 5
val2 DWORD 6
.code
push val2
push val1
call AddTwo

执行 CALL 指令前,堆栈如下图所示:

本文标题:汇编语言寄存器参数的缺点

本文地址:http://www.hosteonscn.com/5423.html

评论

0条评论

发表评论

邮箱地址不会被公开。 必填项已用*标注