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 上下文堆栈,这样就不会对别的内容产生影响
posted @ 2020-02-09 18:43  蹦蹦骑士  阅读(713)  评论(0编辑  收藏  举报