Linux内核分析第一周作业

通过汇编一个简单的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    %ebp
10     movl    %esp, %ebp
11     subl    $4, %esp
12     movl    8(%ebp), %eax
13     movl    %eax, (%esp)
14     call    g
15     leave
16     ret
17 main:
18     pushl    %ebp
19     movl    %esp, %ebp
20     subl    $4, %esp
21     movl    $6, (%esp)
22     call    f
23     addl    $2, %eax
24     leave
25     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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值