博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Linux内核分析第一周作业
阅读量:5354 次
发布时间:2019-06-15

本文共 2852 字,大约阅读时间需要 9 分钟。

通过汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的

1. C语言的代码非常简单

1 int g(int x) 2 { 3   return x + 4; 4 } 5   6 int f(int x) 7 { 8   return g(x); 9 }10  11 int main(void)12 {13   return f(6) + 2;14 }

 

2. 直接利用实验楼的Linux环境编译。因为环境是64位的,所以加上了-m32参数指定生成32位程序。

gcc main.c -m32

可以看到生成了一个a.out文件。用objdump看一下格式,果然是32位的

3. 用gcc生成汇编代码,直接加上参数-S就可以得到汇编代码,-o可以指定生成文件的文件名

gcc –S –o main.s main.c -m32

4. 整理一下main.s文件,只保留汇编指令

1 g: 2     pushl    %ebp 3     movl    %esp, %ebp 4     movl    8(%ebp), %eax 5     addl    $4, %eax 6     popl    %ebp 7     ret 8 f: 9     pushl    %ebp10     movl    %esp, %ebp11     subl    $4, %esp12     movl    8(%ebp), %eax13     movl    %eax, (%esp)14     call    g15     leave16     ret17 main:18     pushl    %ebp19     movl    %esp, %ebp20     subl    $4, %esp21     movl    $6, (%esp)22     call    f23     addl    $2, %eax24     leave25     ret

 5. 分析代码的运行。纸上谈兵不如实际运行,直接用gdb来单步调试刚才编译的a.out

gdb ./a.out

 

看一下main函数的前3条汇编

和main.s里面的一样。

 

但是因为main并不是程序真正的入口(linux真正的入口是_start),为了观察main函数的运行,首先在main函数第一条汇编的地址处加断点

然后打开disassemble-next-line开关,每次停下的时候就会自动显示当前的汇编指令。

set disassemble-next-line on

 

运行程序,就会停在断点处

 

 察看一下当前的寄存器值

 

重点关注esp = 0xffffd48c,ebp=0,eip=0x804840b,后续步骤也只列出这几个寄存器。

 

然后单步运行一条汇编指令

esp = 0xffffd488,ebp=0,eip=0x804840c

看到因为上一条指令将ebp压栈,esp向下增长了4字节,eip跳到下一条指令。看一下esp指向的内容

确实是ebp的值。

 

 继续单步

esp = 0xffffd488,ebp=0xffffd488,eip=0x804840e(%esp)=0

 

esp = 0xffffd484,ebp=0xffffd488,eip=0x8048411,(%esp)=0

这两步将调用f的参数压栈

 

esp = 0xffffd484,ebp=0xffffd488,eip=0x8048418,(%esp)=6

接下来不能再用ni了,ni(next instruction)的作用类似于高级语言调试的step over,继续ni会直接把f的调用运行完。这里要用si(step instruction),即高级语言的step into。

esp = 0xffffd480,ebp=0xffffd488,eip=0x80483f8,(%esp)=0x0804841d

 可以看出call指令把下一条指令的地址压入栈内

esp = 0xffffd47c,ebp=0xffffd488,eip=0x80483f9,(%esp)=0xffffd488

esp = 0xffffd47c,ebp=0xffffd47c,eip=0x80483fb,(%esp)=0xffffd488

这两步保存并移动栈顶,从而保护上一个栈帧

 

 esp = 0xffffd478,ebp=0xffffd47c,eip=0x8048404,eax=6,(%esp)=6

这三步取出了在上一个栈帧(main)中保存的函数入参6,并存在eax中,接下来把该值压栈,作为下一个调用的参数。

 

 

esp = 0xffffd474,ebp=0xffffd47c,eip=0x80483ed,(%esp)=0x08048409

 

esp = 0xffffd470,ebp=0xffffd470,eip=0x80483f0,(%esp)=0xffffd47c

进入g函数,保存返回地址、保护f的栈帧,都是套路

 

esp = 0xffffd470,ebp=0xffffd470,eip=0x80483f3,eax=6,(%esp)=0xffffd47c

 从前一个栈帧中取出入参,并存入eax。接下来进行g函数中的运算a+4,结果仍然在eax中

 

esp = 0xffffd470,ebp=0xffffd470,eip=0x80483f6,eax=10,(%esp)=0xffffd47c

 下面开始一层一层返回,先恢复f的栈顶

esp = 0xffffd474,ebp=0xffffd47c,eip=0x80483f7,eax=10,(%esp)=0x08048409

 再恢复eip返回f

esp = 0xffffd478,ebp=0xffffd47c,eip=0x8048409,eax=10,(%esp)=6

 继续恢复main的栈顶

esp = 0xffffd480,ebp=0xffffd488,eip=0x804840a,eax=10,(%esp)=0x0804841d

  再恢复eip返回main

esp = 0xffffd484,ebp=0xffffd488,eip=0x804841d,eax=10,(%esp)=6

 接下来进行main中的计算

esp = 0xffffd484,ebp=0xffffd488,eip=0x8048420,eax=12,(%esp)=6

 最后退出main

 

6. 总结

从这个小程序的运行过程可以看出函数调用、堆栈保护的机制。和c语言对比一下,可以看出编译器替我们做了很多工作。

另外也练习了GDB的使用,以前没有用GDB调试过程序,都是套着IDE用的。

顺便给个彩蛋,这是编译器-O2优化后的汇编代码  _(:з」∠)_

 

转载于:https://www.cnblogs.com/cscat/p/6414785.html

你可能感兴趣的文章
As-If-Serial 理解
查看>>
洛谷P1005 矩阵取数游戏
查看>>
在Silverlight中使用HierarchicalDataTemplate为TreeView实现递归树状结构
查看>>
无线通信基础(一):无线网络演进
查看>>
关于python中带下划线的变量和函数 的意义
查看>>
linux清空日志文件内容 (转)
查看>>
Servlet接收JSP参数乱码问题解决办法
查看>>
Ajax : load()
查看>>
MySQL-EXPLAIN执行计划Extra解释
查看>>
Zookeeper概述
查看>>
Linux自己安装redis扩展
查看>>
HDU 1016 Prime Ring Problem(dfs)
查看>>
luoguP3414 SAC#1 - 组合数
查看>>
图片点击轮播(三)-----2017-04-05
查看>>
直播技术细节3
查看>>
《分布式服务架构:原理、设计于实战》总结
查看>>
java中new一个对象和对象=null有什么区别
查看>>
字母和数字键的键码值(keyCode)
查看>>
IE8调用window.open导出EXCEL文件题目
查看>>
Spring mvc初学
查看>>