NASM手册阅读笔记(7) - 预处理器之参数
简述
这一些预处理指令是为了方便我们使用堆栈传参的时候的程序调用的。
我们一般的调用过程如下
proc1:
push 参数1 ; 参数1 压栈
push 参数2 ;参数2 压栈
push 参数3 ;参数2 压栈
call proc2
proc2:
push bp ; bp内容压栈, 为的是保存bp寄存器的原始值
mov bp,sp ; 将SP 内容 保存到 bp 中, 之后再结束前bp的值都不应该再发生改变
sub sp, ? ; 减少 sp的指向,相当于预先开辟一篇空间 给我们的临时变量使用
mov ax, [bp + 2+ 2] ; 假设之前参数3 是2 字节,那么这就是获取参数3 的内容,注意bp寄存器实际指向的是存储BP寄存器原始内容的地方,+2 后指向call压栈的放回地址 再+2 才是压栈的最后一个参数内容
mov [bp - 2 ],ax ; 假设我们的临时变量的大小是2字节,那么这就是取我们
XXXXXXXXXXX
mov sp, bp
pop bp
ret
那么接下来要介绍的预处理指令就是为了方便这个过程的
注意一点: 接下来用到的指令内容辉县一些信息保存到上下文栈中,所以接下来的指令都是要配合上下文栈来使用的
%stacksize
格式
%stacksize flat
%stacksize flat64
%stacksize large
%stacksize small
说明
从上面的简述中可知,堆栈中的变量是通过bp寄存器作为基址来进行寻址的,那么这条指令就是告诉预处理器,我们到底用 BP 还是 EBP 还是RBP来寻址
flat64 指定使用 RBP 来进行栈的寻址
flat 指定使用 EBP 来进行栈的寻址
large 指定使用 BP 来进行栈的寻址
small 指定使用 BP 来进行栈的寻址,它会认为BP之前已经压栈,所以其他的是会自动将BP压栈一次吗?
%arg
格式
%arg param1:word, param2:type
说明
这个是定义其他函数传递过来的参数 参数名:参数类型 以逗号分隔
之后就可以直接直接使用定义的名称寻址
mov ax, [param1] => mov ax,[bp+4]
mov ax, [param2] => mov ax,[bp+6]
所以汇编器就会根据变量的类型以及 %stacksize 设置的寄存器将 param1 形式的参数自动替换为 bp+x形式的参数
%local
格式
%local param1:word, param2:type
说明
与 %arg 是同理的,只不过他计算的是本地变量也就是
%arg 计算的是 bp + ? 的形式
%local 计算的是 bp - ? 的形式
所以这里要注意一点,%arg的变量使用的时候内存是已经开辟并赋值好了的,这是调用它的函数负责的
而%local定义的变量是需要本函数自己去开辟空间的,即使用 sub sp, xxx 这样子的形式去开辟空间
补充说明
A. 如下表格是一个堆栈的展示,主要用于说明 %arg 和%local 定义变量的对应增长方向以及对应关系,注意堆栈是从高地址往低地址方向增长的
1 local2
2 local1
3 bp寄存器内容
4 call压栈的返回地址
5 arg1
6 arg2
B. ENTER / LEAVE
enter
等于 push ebp和mov ebp,esp
先将ebp保存到对账中,然后将esp保存到ebp中
Leave
等于mov esp,ebp和pop ebp
将保存在ebp中的内容还给 esp 并回复ebp原来的值
这是一般出现下一个函数开始的和结尾处的指令
只用时保持堆栈的平衡,因为函数开始的时候的堆栈保存在了ebp中,所以如果这个函数里面有堆栈不平和的情况,使用leave 指令的时候总能还原进来的时候的堆栈指针。当然前提是不能修改ebp的值
enter 可以跟参数
enter numbytes, nestinglevel
numbytes 表示为当前堆栈 保留多少 字节,自动对齐4 相当于指令 sub esp, numbytes
而 nestinglevel 词法嵌套级,没明白啥意思 填 0
C. 例子
silly_swap:
%push mycontext ; 之前说过有信息要使用上下文栈 所以这一先压栈一个上下文
%stacksize small ; 设置使用bp寄存器来寻址
%assign %$localsize 0 ; 这里定义了一个上下文栈的本地宏
%local old_ax:word, old_dx:word ; 这就是定义了本地参数
enter %$localsize,0 ; 保存bp,赋值 sp, 开辟空间
mov [old_ax],ax ; 存值
mov [old_dx],dx ; 存值
mov ax,bx
mov dx,cx
mov bx,[old_ax] ; 取值
mov cx,[old_dx] ; 取值
leave ; enter 返现操作
ret ;
%pop ; 销毁 之前创建的 mycontext 上下文堆栈,这样就不会对别的内容产生影响