Chinaunix首页 | 论坛 | 博客
  • 博客访问: 539065
  • 博文数量: 252
  • 博客积分: 6057
  • 博客等级: 准将
  • 技术积分: 1635
  • 用 户 组: 普通用户
  • 注册时间: 2009-12-21 10:17
文章分类

全部博文(252)

文章存档

2013年(1)

2012年(1)

2011年(32)

2010年(212)

2009年(6)

分类:

2010-03-25 23:46:16

《深入理解计算机》的第三章已经看了两遍了,一直都是在看书里的程序,一些内容都是第一次接触,也一直没有在真正的Linux上用过,今天写了下发 现和书里的还是有些出入,下面是我自己的理解。

gdb除了反汇编外,还可以用于代码的调试。下面就简单记录下容易忘记的部分。

1.gcc -g -o code code.c   这里不要忘记-g

2.watchpoint的概念

   VC用惯了就是不好,在vc里ms没有这个概念,至少我没遇到过。在gdb中有一个watch命令,格式是watch + addr, 当内存中这个地址被写入新内容时程序就会中断。

3.查看内存内容的方法

  X/4b  &temp

  这个意思就是输出7个byte从temp所在地址开始的内存内容。

  比如我的temp = 0xfeff,那么会输出0xff 0xfe 0x00 0x00(小端地址法)

下面是自己花了点时间在Linux下写了一个简单的程序,并分析了下反汇编的代码,以及内存情况。

第一部分是c语言代码,编译时用gcc -o code code.c,没用-O2优化

  1. #include   
  2. int add(int a, int b)  
  3. {  
  4.   return a + b;  
  5. }  
  6. int main()  
  7. {  
  8.   int sum, a, b;  
  9.   scanf("%d%d",&a, &b);  
  10.   sum = add(a, b);  
  11.   printf("sum = %d\n", sum);  
  12.   return 0;  
  13. }  

第二部分是用gdb code的反汇编代码,';'后面是我自己的注释,关于前4句的原理可以看最下面的文章,对理解比较重要。

有疑问的部分:

0x080483d8 :   add    $0x10,%esp:从这里往前看,似乎只有3次push操作,应该add 0xc,只是在这个程序中多add0x4也没关系。

0x080483ec :   sub    $0x8,%esp: 这个好像没用到,好像也不是用于字节对齐,因为在它前面一次调用也是从这个值开始的。

在这里leave等价于:

mov %esp,%ebp;

pop %ebp

  1. Dump of assembler code for function main:  
  2. 0x080483a7 :    push   %ebp  
  3. 0x080483a8 :    mov    %esp,%ebp  
  4. 0x080483aa :    sub    $0x18,%esp  
  5. 0x080483ad :    and    $0xfffffff0,%esp  
  6. 0x080483b0 :    mov    $0x0,%eax  
  7. 0x080483b5 :   add    $0xf,%eax  
  8. 0x080483b8 :   add    $0xf,%eax  
  9. 0x080483bb :   shr    $0x4,%eax  
  10. 0x080483be :   shl    $0x4,%eax       ;%eax = 0x10  
  11. 0x080483c1 :   sub    %eax,%esp       ;%esp -= 0x10  
  12. 0x080483c3 :   sub    $0x4,%esp       ;%esp -= 0x4  
  13. 0x080483c6 :   lea    0xfffffff4(%ebp),%eax    0xfffffff4 = -12 = -0xc  
  14. 0x080483c9 :   push   %eax                     ;push &b  
  15. 0x080483ca :   lea    0xfffffff8(%ebp),%eax    0xfffffff8 = -8 = -0x8  
  16. 0x080483cd :   push   %eax                     ;push &a  
  17. 0x080483ce :   push   $0x80484e8               ;point to "%d%d"  
  18. 0x080483d3 :   call   0x80482c4                ;call scanf()  
  19. 0x080483d8 :   add    $0x10,%esp               ;%esp back, but I should it just need ot back 0xc        
  20. 0x080483db :   pushl  0xfffffff4(%ebp)         ;b  
  21. 0x080483de :   pushl  0xfffffff8(%ebp)         ;a  
  22. 0x080483e1 :   call   0x804839c           ;call add()  
  23. 0x080483e6 :   add    $0x8,%esp                ;%esp back 0x8     
  24. 0x080483e9 :   mov    %eax,0xfffffffc(%ebp)    ;0xfffffffc = -4 = -0x4  
  25. 0x080483ec :   sub    $0x8,%esp                ;reserve 0x8, seems not used  
  26. 0x080483ef :   pushl  0xfffffffc(%ebp)         ;push sum  
  27. 0x080483f2 :   push   $0x80484ed               ;point to "sum = %d\n"  
  28. 0x080483f7 :   call   0x80482e4                ;call printf()  
  29. 0x080483fc :   add    $0x10,%esp               %esp back 0x10  
  30. 0x080483ff :   mov    $0x0,%eax                ;set return_val 0  
  31. 0x08048404 :   leave  
  32. 0x08048405 :   ret  
  33. 0x08048406 :   nop  
  34. 0x08048407 :   nop  
  35. End of assembler dump.  
  36. (gdb)  
  37. Dump of assembler code for function add:  
  38. 0x0804839c :     push   %ebp  
  39. 0x0804839d :     mov    %esp,%ebp  
  40. 0x0804839f :     mov    0xc(%ebp),%eax  
  41. 0x080483a2 :     add    0x8(%ebp),%eax  
  42. 0x080483a5 :     leave  
  43. 0x080483a6 :    ret  
  44. End of assembler dump.  

第三部分是内存变化的简单示意图:

-----------------------------------------------------------------------------------------------------------------------

下面来自于网上

C 代码:

int main(){
}

push %ebp
mov %esp,%ebp
sub $0x8,%esp//这句可以勉强理解,可能是为以后定义变量欲留位置,不知对否?
and $0xfffffff0,%esp//这里就不好理解了,为什么要把后面的4位全都置0???
mov $0x0, %eax
sub %eax,%esp
leave
ret

首先我觉得有必要把LEAVE替换一下,他等价于mov esp,ebp;pop ebp这两条指令.因此有如下的解释:

因此在执行了1之后的堆栈图是: ----> 这个ARG其实就是int main(int argc, char argv[]),其实这里还可以跟一个env来着
ARG[N]
..........
ARG[0]
EIP
EBP //ARG表示涵数的参数,对于MAIN涵数,POSIX定义了两个,历史上有三个的.
因此3语句的作用正好把ESP指向了为ARG[0]保留的地址,也就是跳过为EBP和EIP保留的位置
那么4语句就是为ARG[0]ARG[1]保留位置了
由于MAIN被POSIX规定了两个参数,因此不论有没有都要保留两个位置,又因为没有ARG[2]和MAIN的局部变量,所以把EAX置0,这样一举两 得,EAX又可以作为返回值,(通常涵数返回值是INT的,都用EAX表示).
其实大家可能还有不理解,那就是既然涵数的参数都已经压栈了,直接用不就行了?为什么还要拷贝过来呢?
这是C语言的要求,为了局部化,从逻辑上,EBP(含)以上都是属于主调涵数的,所以拷过来当然是必要的.否则C语言如何实现传值而不改变主调涵数的值 呢?
整个注释如下:
1:push %ebp
2:mov %esp,%ebp
3:sub $0x8,%esp //为EBP,EIP保留位置
4:and $0xfffffff0,%esp //为ARG[0],ARG[1]保留位置
5:mov $0x0, %eax //一举两用,既作返回值,有表示不需要保留位置
6:sub %eax,%esp //保留0个位置
mov %epb ,%esp //恢复ESP到PUSH %EBP指令时的位置
pop %ebp //恢复EBP的值
ret

阅读(1502) | 评论(2) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2010-03-25 23:56:29

汇编指令解释 http://wenda.sogou.com/question/56224570.html?fr=rqm 1.Rn 表示R0~R7中的一个 2.#data 表示8位的数值 00H~FFH 3.direct 表示8位的地址 00H~FFH 4.@Ri 表示寄存器间接寻址 只能是R0或者R1 5.@DPTR 表示数据指针间接寻址 6.bit 表示位地址 7.$ 表示当前地址 寄存器寻址 MOV A,R1 将R1中的数值赋予A 直接寻址 MOV A,3AH 将地址3AH中的数值赋予A 立即寻址 MOV A,#3AH 将3AH数值赋予A 寄存器间址 MOV A,@R0 将 R0中地址的数值赋予A 变址寻址 MOVC A,@A+DPTR 以A中的数值为地址偏移量进行查表 相对寻址 AJMP MATN 跳转到行号为MATNC处 位寻址 MOV C,7FH 将位地址7FH的数值赋予C MOV A,#3AH 数据传输、赋值命令 PUSH direct 将direct为地址的数值压入堆栈中 POP direct 将direct为地址的数值弹出堆栈

chinaunix网友2010-03-25 23:50:23

MOV指令 http://www.diybl.com/course/3_program/hb/hbjs/20071226/93647.html MOV DEST,SRC 操作:DEST=SRC 格式: MOV REG,IDATA 立即数赋值给寄存器 MOV MEM,IDATA 立即数赋值给内存变量 MOV REG,REG 寄存器赋值给寄存器 MOV REG,MEM 内存变量赋值给寄存器 MOV MEM,REG 寄存器赋值给内存变量 —————————————————————— MOV指令不影响任何标志位 MOV的源操作数与目标操作数类型必须一致 MOV指令的操作数不能全为内存变量 MOV指令的操作数不能全为段寄存器 段寄存器不可以直接赋值,可以通过内存变量或除段寄存器之外的其它寄存器给段寄存器赋值 CS为代码段寄存器,它是只允许读,不允许写的。(可以用跳转指令改变CS)