2023-2024-1 20232817 李毅龙
汇编代码生成和处理
打开实验楼
使用vim输入以下代码:
使用 gcc -S -o main.s main.c -m32
. 将c语言代码执行到汇编阶段。
命令解释:
使用vim打开main.s, 使用下面的命令使得main.s文件中所有的.开头的代码删除。
:g/.*\./d
得到下图:
解释汇编
现在开始解释生成的汇编代码:
g:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
addl $1, %eax
popl %ebp
ret
f:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl 8(%ebp), %eax
movl %eax, (%esp)
call g
leave
ret
main:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl $9, (%esp)
call f
addl $1, %eax
leave
ret
main:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl $9, (%esp)
call f
addl $1, %eax
leave
ret
函数入栈
pushl %ebp
movl %esp, %ebp
pushl %ebp:
这条指令将当前 %ebp 寄存器中的值压入栈,用于保存之前的基址指针。
movl %esp, %ebp:
这条指令将 %esp 中的值(即当前栈顶位置)复制到 %ebp 寄存器,实现了基址指针的更新,通常在函数的入口处执行,用于建立函数的堆栈帧(Stack Frame)。
这两条指令通常一起出现在函数的开头,用于建立函数的堆栈帧。
pushl %ebp 将之前的基址指针保存到栈中,movl %esp, %ebp 则将当前栈顶位置赋值给基址指针,完成堆栈帧的建立。在函数执行完毕后,通常会使用类似的指令恢复堆栈状态,释放堆栈帧,以便返回到调用函数。
subl $4, %esp
movl $9, (%esp)
subl $4, %esp:
此指令将栈指针向下移动4个字节,为在栈上分配空间做准备。通常用于为局部变量或参数分配空间。
movl $9, (%esp):
此指令将数字9移动到栈顶,即将数字9压入栈中作为参数。
这两条指令共同完成了在栈上分配4字节空间,并将数字9作为参数放入栈顶,为后续函数调用做准备。通常,函数参数在栈上被压入以供调用函数使用。
call f
addl $1, %eax
leave
ret
call f:
此指令调用函数 f。
addl $1, %eax:
此指令将 %eax 寄存器中的值加1,可能是对 f 函数调用的返回值进行加1操作。
leave:
此指令的作用相当于执行 mov %ebp, %esp 之后再执行 pop %ebp。
leave 恢复旧的栈帧,等效于 mov %ebp, %esp 后 pop %ebp。
ret:
此指令将程序控制流返回到调用 f 函数的地方。
f:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl 8(%ebp), %eax
movl %eax, (%esp)
call g
leave
ret
f 函数保存旧的基址指针到栈上,然后将当前栈顶位置赋给 %ebp。
在栈上分配4字节空间(subl $4, %esp)。
从偏移为 8(%ebp) 的位置读取值到 %eax 寄存器,然后将其放入栈顶。
调用函数 g。
执行 leave 指令,恢复栈帧。
返回到调用函数。
g:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
addl $1, %eax
popl %ebp
ret
g 函数保存旧的基址指针到栈上,然后将当前栈顶位置赋给 %ebp。
从偏移为 8(%ebp) 的位置读取值到 %eax 寄存器。
将 %eax 寄存器中的值加1。
恢复旧的基址指针。
返回到调用函数。