本文承接汇编语言学习笔记 上
上篇文章记录了汇编语言寄存器,汇编语言基本组成部分,数据传送指令,寻址指令,加减法指令,堆栈,过程,条件处理,整数运算的内容
高级过程
大多数现代编程语言在调用子程序的时候会把参数压入对战
子程序也常常把局部变量压入堆栈
- 子程序在C和C++中被称为函数
- 在Java中被称为方法
- 在宏汇编程序(MASM)中被称为过程
堆栈帧
堆栈参数
堆栈帧是一块堆栈保留区域
存放
- 被传递的实际参数
- 子程序的返回值
- 局部变量
- 被保存的寄存器
创建步骤
- 将被传递的实际参数压入堆栈
- 当子程序被调用时,该子程序的返回值压入堆栈
- 子程序开始执行的时候,EBP被压入堆栈
- 设置EBP等于ESP,EBP成为子程序所有参数的引用基址
- 如果有局部变量,修改ESP在堆栈中为其预留空间
- 需要保留的寄存器,将它们压入堆栈
Fastcall调用方式
顾名思义,是一种希望快速的调用方式
我们来分析一下这个调用方式:
调用自过程的时候,需要首先将参数传入EAX,EBX,ECX,EDX,少数情况还会传入ESI,EDI
我们知道寄存器是CPU内部的原件,堆栈在内存上,寄存器调用明显更快
但是我们知道通用寄存器很少,很多都有特定的功能,乘法需要用到EAX,还有许多寄存器用来循环数值和参与计算的操作数
因此寄存器不可能一直存放传递给过程的参数
在过程调用之前, 存放参数的寄存器需要首先入栈,然后向其分配过程参数
但是这些额外的入栈操作会让代码变得混乱,还有可能消除性能优势
值传递
一个参数通过数值传递时,该值的副本会被压入堆栈
.data
val1 DWORD 3
val2 DWORD 6
.code
push val2
push val1
call AddTwo
引用传递
通过引用来传递的参数包含的是对象的地址
push OFFSET val2
push OFFSET val1
传递数组
将数组的地址压入堆栈
不愿意采用将每个数组元素压入堆栈的原因是这样很慢而且浪费堆栈空间
访问堆栈的参数
1.将传递的参数压入堆栈,调用子过程
2.EBP寄存器存放的是原来栈帧的基址,我们需要现将EBP压入栈保存
3.然后将当前的ESP作为新的栈帧的基址
示例
int AddTwo(int x,int y)
{return x+y;
}
将EBP入栈,设置ebp位esp的值
AddTwo PROCpush ebpmov ebp,esp
ADD(5,6)
6 | [EBP+12] |
5 | [EBP+8] |
返回地址 | [EBP+4] |
EBP | mov ebp,esp |
这样通过当前EBP和偏移量就能访问传入的参数和原来的ebp(返回地址)