刘政乔 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
引言
在现代计算机科学中,理解计算机的工作原理是至关重要的。本文将通过分析汇编代码的工作过程,特别是堆栈的变化,来探讨计算机是如何工作的。
实验环境与准备
在本次实验中,我们使用了以下环境和工具:
实验截图
总结
通过分析这个简单的C程序及其对应的汇编代码,我们可以看到计算机是如何通过堆栈管理函数调用和参数传递的。堆栈在函数调用过程中起到了关键作用,确保了每个函数都有自己的独立空间来存储局部变量和返回地址。
- 操作系统:Ubuntu 20.04
- 汇编语言:x86汇编
- 编译器:NASM
- 调试工具:GDB
代码示例
首先,我们有一个简单的C程序
main.c
:// main.c int g(int x) { return x + 279; } int f(int x) { return g(x); } int main(void) { return f(3619) + 719; }
使用以下命令将其编译为汇编代码:
gcc -S -o main.s main.c -m32
生成的汇编代码分析
生成的汇编代码
Assemblymain.s
如下所示(简化版):.file "main.c" .text .globl g .type g, @function g: pushl %ebp movl %esp, %ebp movl 8(%ebp), %eax addl $279, %eax popl %ebp ret .globl f .type f, @function f: pushl %ebp movl %esp, %ebp movl 8(%ebp), %eax pushl %eax call g addl $4, %esp popl %ebp ret .globl main .type main, @function main: pushl %ebp movl %esp, %ebp subl $8, %esp movl $3619, (%esp) call f addl $8, %esp addl $719, %eax leave ret
堆栈变化分析
让我们逐步分析堆栈在执行过程中如何变化:
-
main
函数开始执行:pushl %ebp
:保存旧的基址指针。movl %esp, %ebp
:设置新的基址指针。subl $8, %esp
:为局部变量分配空间。
-
调用
f(3619)
:movl $3619, (%esp)
:将参数3619压入堆栈。call f
:调用f
函数,返回地址压入堆栈。
-
f
函数执行:pushl %ebp
:保存旧的基址指针。movl %esp, %ebp
:设置新的基址指针。movl 8(%ebp), %eax
:将参数加载到eax
中。pushl %eax
:将参数压入堆栈。call g
:调用g
函数,返回地址压入堆栈。
-
g
函数执行:pushl %ebp
:保存旧的基址指针。movl %esp, %ebp
:设置新的基址指针。movl 8(%ebp), %eax
:将参数加载到eax
中。addl $279, %eax
:执行x + 279
。popl %ebp
:恢复旧的基址指针。ret
:返回调用点。
-
返回到
f
函数:addl $4, %esp
:清理堆栈。popl %ebp
:恢复旧的基址指针。ret
:返回调用点。
-
返回到
main
函数:addl $719, %eax
:执行f(3619) + 719
。leave
:恢复堆栈指针和基址指针。ret
:返回调用点。