前言
本次是该系列的的第0x1d篇,介绍了据说被各大编译器摒弃的enter指令,这是进入函数时保存栈帧环境的指令
实验环境:
- Windows10 + VS2022 + masm
0x1d
代码片段链接:xorpd | xchg rax,rax 0x1d
mov rsp,buff2 + n*8 + 8
mov rbp,buff1 + n*8
enter 0,n+1
代码分析
enter指令介绍
这里理解的难点在于enter指令:
介绍:enter指令是为了支持高级语言中的变量作用于而加入的,它不是一个伪指令,但是却由于效率原因被各大编译器所摒弃,取而代之的是用多条指令去模拟来提升效率
enter指令格式:enter reservedVarSize, level
- reservedVarSize:局部变量占用内存大小
- level:嵌套级别
当level = 0时,相当于:
push ebp
mov ebp, esp
add esp, -x
当level != 0 时,例如:enter 10, 3
相当于:
push ebp ;保存上一级的ebp
mov ebp, esp ;保存上一级的esp
sub esp, (3-1)*4;用来保存可访问的栈帧的指针,能访问几层就有几个
push ebp ;保存当前栈帧
sub esp, 10 ;用于保存局部变量
根据参考资料[2]里所述:这里的嵌套级别的意思是,函数嵌套调用会产生多个栈帧,这里把多个栈帧都保存起来,以便访问其他栈帧里的变量【不是很理解,但从直观上看,这里嵌套级别决定了这里的rsp和rbp中间会多留几个空间】
代码分析
测试代码:
.data
buff1 db "AAAAAAAA",0
buff2 db "BBBBBBBB",0
n equ 0
.code
main proc
lea rsp, qword ptr [buff2 + n*8 + 8]
; 加载buff2 + n*8 + 8的地址到rsp
lea rbp, qword ptr [buff1 + 8]
; 加载buff1 + 8的地址到rbp
enter 0, n+1
;push rbp
;mov rbp, rsp
;sub rsp, ((n+1) -1)*8+8 ;n*8+8
;push rbp
;sub esp, 0
ret
main ENDP
END
这里使用lea代替mov,在Windows上,这里要获取地址还是得用lea才行,也可能是我哪里没写对
当给n赋值为0的时候:
当给n赋值为1的时候:
这里显示了enter嵌套层级对栈帧范围的影响