一个简单程序的汇编执行过程分析
- 题目自拟,内容围绕计算机是如何工作的进行;
- 博客中需要使用实验截图
- 博客内容中需要仔细分析汇编代码的工作过程中堆栈的变化
- 总结部分需要阐明自己对“计算机是如何工作的”理解。
实验环境:ubuntu64位
源代码:
int g(int x){ return x + 4; } int f(int x){ return g(x); } int main(){ return f(10) + 5; }
编译命令:
gcc -S -o anlysis_assemble.s anlysis_assemble.c -m32 //
生成的汇编代码为:
.file "anlysis_assemble.c" .text .globl g .type g, @function g: .LFB0: .cfi_startproc pushl %ebp .cfi_def_cfa_offset 8 .cfi_offset 5, -8 movl %esp, %ebp .cfi_def_cfa_register 5 movl 8(%ebp), %eax addl $4, %eax popl %ebp .cfi_restore 5 .cfi_def_cfa 4, 4 ret .cfi_endproc .LFE0: .size g, .-g .globl f .type f, @function f: .LFB1: .cfi_startproc pushl %ebp .cfi_def_cfa_offset 8 .cfi_offset 5, -8 movl %esp, %ebp .cfi_def_cfa_register 5 subl $4, %esp movl 8(%ebp), %eax movl %eax, (%esp) call g leave .cfi_restore 5 .cfi_def_cfa 4, 4 ret .cfi_endproc .LFE1: .size f, .-f .globl main .type main, @function main: .LFB2: .cfi_startproc pushl %ebp .cfi_def_cfa_offset 8 .cfi_offset 5, -8 movl %esp, %ebp .cfi_def_cfa_register 5 subl $4, %esp movl $10, (%esp) call f addl $5, %eax leave .cfi_restore 5 .cfi_def_cfa 4, 4 ret .cfi_endproc .LFE2: .size main, .-main .ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2" .section .note.GNU-stack,"",@progbits
删除伪代码,最简化后的代码为:
g: pushl %ebp movl %esp, %ebp movl 8(%ebp), %eax addl $4, %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 $10, (%esp) call f addl $5, %eax leave ret
首先对push指令进行分析:
pushl %eax 等价于以下两句:
subl $4, %esp # esp = esp - 4, 栈向下增长, 栈顶指针向下增长
movl %eax, (%esp) # 将寄存器eax内容取出到esp指向的地址的内存,即栈顶位置
分析pop指令:
popl %eax 等价于以下两句:
movl (%esp), %eax # 将esp指向的栈顶处内存值赋值给eax
addl $4, %esp # esp栈顶向上缩小
可以用这两个指令替代push 或 pop
分析call指令
call 0x12345 等价与以下两句:
pushl %eip
movl $0x12345, %eip
分析 ret 指令
ret 等价与 popl %eip
这里注意的是eip不能在写汇编代码时直接修改,只能间接修改
分析几段汇编指令片段
例子一
pushl $8 # 假设当前栈为空,栈顶和栈底都指向相同地址,esp = esp - 4, %esp = 8
movl %esp, %ebp #则ebp也指向esp的位置
subl $4,%esp #esp向下移动一个位置
movl $8, (%esp) # 将8赋值到栈顶位置
例子二
pushl $8
movl %esp, $ebp
pushl %esp # 将当前位置放到栈中
pushl $8
addl $4, %esp
popl %esp
分析leave和enter指令,它们分别由两条指令组成
enter: #进入函数调用堆栈
pushl %ebp
movl %esp, %ebp
leave: #撤销函数调用堆栈
movl %ebp, %esp
popl %ebp
函数调用堆栈是由逻辑上多个堆栈叠加起来的。
ebp , esp是相对的栈底和栈顶,每个函数都有自己的栈顶和栈底
函数的返回值默认使用eax寄存器返回给上一级函数