补:第五周实验楼实践
实验楼实验
第一题
-
使用VIM编写代码
-
查看汇编文件
-
删除gcc产生代码中以"."开头的编译器指令
分析过程:
-
main
开始执行> pushl %ebp // 保存%ebp,设置新的帧指针 > movl %esp, %ebp
pushl $15//分配四字节的栈空间
call//调用f
f
初始换帧指针,分配栈空间
> pushl %ebp // 初始换帧指针,分配栈空间
> movl %esp, %ebp
pushl 8(%ebp)//将寄存器%esp中的8存入栈
call//调用g
g
初始换帧指针,分配栈空间
addl $3, %eax //执行1加上%eax
popl %ebp // 将g弹栈
ret // 返回f中call的位置
-
b
结束,同理ret
返回main
中call的位置 -
main
addl $3, %eax //执行%eax加3的操作
leave // 返回准备栈 ,此时%ebp出栈
ret // 结束
总结一下这个过程的流程:
-
这个程序的流程其实是f的过程调用了g的过程,f先把它的%ebp寄存器压入栈作为帧指针,然后压入被保存的寄存器、本地变量和临时变量,最上面是参数构造区域。然后再用call调用g,这时又把返回地址压入栈。
-
g被调用后,把它的帧指针%ebp压入栈,然后压入寄存器、本地变量、临时变量,最上面是参数构造区域。
-
g运算结束前,g会把%ebp弹出栈,然后ret指令弹出并跳转到之前call压入的地址,返回到f过程,最后因为leave,%ebp出栈。
下面展示用gdb单步跟踪各寄存器的值的变化
- 在main函数设一个断点,使用
disassemble
获取汇编指令,再用info registers
-
call指令,此时%esp,%ebp和堆栈的值为:
-
上一个函数基址入栈
-
实参的计算
-
f函数汇编,实参入栈
-
call指令
-
leave指令先将%esp对其到%ebp,然后把栈顶弹给%ebp
-
主函数汇编
指令 | %esp | %ebp | 堆栈 |
---|---|---|---|
push $0xf | 0xbffff048 | 0xbffff048 | 0x0 |
call 0x80483e6 | 0xfffff044 | 0xfffff048 | 0xf 0x0 |
push %ebp | 0xfffff040 | 0xfffff048 | 0x8048400 0xf 0x0 |
mov %esp,%ebp | 0xbffff03c | 0xfffff048 | 0xfffff048 0x8048400 0xf 0x0 |
mov 0x80483e7,%edx | 0xfffff03c | 0xfffff03c | 0xfffff048 0x8048400 0xf 0x0 |
call 0x80483db | 0xfffff034 | 0xfffff03c | 0xf 0xfffff048 0x8048400 0xf 0x0 |
push %ebp | 0xfffff030 | 0xfffff03c | 0x80483f1 0xf 0xfffff048 0x8048400 0xf 0x0 |
mov %esp,%ebp | 0xfffff030 | 0xfffff03c | 0xfffff03c 0x80483f1 0xf 0xfffff048 0x8048400 0xf 0x0 |
mov 0x80483f2 %eax | 0xfffff030 | 0xfffff030 | 0xfffff03c 0x80483f1 0xf 0xfffff048 0x8048400 0xf 0x0 |
ret | 0xfffff030 | 0xfffff03c | 0x80483f1 0xf 0xfffff048 0x8048400 0xf 0x0 |
leave | 0xfffff03c | 0xfffff03c | 0xfffff048 0x8048400 0xf 0x0 |
ret | 0xfffff040 | 0xfffff048 | 0x8048400 0xf 0x0 |
add $0x4,%esp | 0xfffff044 | 0xfffff048 | 0xf 0x0 |
mov $0x3,%edx | 0xfffff048 | 0xfffff48 | 0x0 |
ret | 0xfffff04c | 0x0 |
第二题:使用gdb的bt/frame/up/down指令动态查看调用线帧的情况