织梦CMS - 轻松建站从此开始!

罗索

快速识别汇编中等价的C语言语句(if, while, for, switch)

落鹤生 发布于 2014-12-17 17:34 点击:次 
可能经常看汇编的朋友会一眼就认出跟C语言中一些语句等价的汇编代码, 经验使然也. 而不经常接触汇编的同学, 可能就对相对繁琐的寄存器操作指令有点云里雾里了.
TAG: 汇编  ASM  

可能经常看汇编的朋友会一眼就认出跟C语言中一些语句等价的汇编代码, 经验使然也. 而不经常接触汇编的同学, 可能就对相对繁琐的寄存器操作指令有点云里雾里了.

汇编是编译器翻译中级语言(也可以把C语言称作高级语言, 呵呵)的结果, 只要是机器做的事儿,一般都有规律可循. 那我们现在就来看看一下这些基本语句的汇编规律吧.

注意:本文使用的汇编格式为GAS(Gnu ASembler GNU汇编器). 它同Intel文档中的格式以及微软编译器使用的格式差异很大,

具体请看文章AT&T汇编格式与Intel汇编格式的比较.

条件转移语句- if

============================

C语言中的if-else语句的通用形式如下

  1. if(test-expr)
  2.     then-statement;
  3. else
  4.     else-statement;

对于这种通用形式, 汇编实现通常会使用下面这种形式

  1.     t= test-expr;
  2.     if (t)
  3.         goto true;
  4.     else-statement
  5.         goto done;
  6. true:
  7.     then-statement
  8. done:

也就是汇编器为then-statement 和else-statement各自产生代码块, 并插入条件和无条件分支, 以保证正确代码块的执行.

 

下面我们来看C语言源代码, 汇编结构的C代码, 和汇编代码的比较.

  1. //----------Classic C code------------ 
  2. int absdiff(int x, int y) 
  3.     if (x < y) 
  4.         return y - x; 
  5.     else 
  6.         return x - y; 
  7. //----------Classic C code------------ 

 

  1. //----------Equivalent Goto Version------------ 
  2. int gotodiff(int x, int y) 
  3.     int rval; 
  4.   
  5.     if (x < y) 
  6.         goto less; 
  7.     rval = x - y; 
  8.     goto done; 
  9. less: 
  10.     rval = y - x; 
  11. done: 
  12.     return rval; 
  13. //----------Equivalent Goto Version------------ 

 

  1. //----------Equivalent assembly Version------------ 
  2.     movl 8(%ebp),%edx          ;Get x 
  3.     movl 12(%ebp),%eax         ;Get y 
  4.     cmpl %eax,%edx             ;Compare x:y 
  5.     jl .L3                     ;If <, goto less: 
  6.     subl %eax,%edx             ;Compute y-x 
  7.     movl %edx,%eax             ;Set as return value 
  8.     jmp .L5                    ;Goto done: 
  9. .L3:                           ;less: 
  10.     subl %edx,%eax             ;Compute x-y as return value 
  11. .L5:                           ;done:Begin completion code 
  12. //----------Equivalent assembly Version------------ 

do-while循环

========================

do-while循环的通用形式是这样的:

  1. do
  2. {body-statement}
  3. while (test-expr);

循环的效果就是重复执行body-statement, 对test-expr求值, 如果不是0, 就继续循环. 注意, 循环体至少执行一次.

通常, do-while 的实现有下面的通用形式:

  1. loop:
  2.     body-statement
  3.     t= test-expr;
  4.     if (t)
  5.         goto loop;

下面是一个例子, 找找感觉吧.

  1. //----------Original C Version------------ 
  2. do
  3.     int t = val + nval; 
  4.     val = nval; 
  5.     nval = t; 
  6.     i++; 
  7. while (i < n); 
  8. //----------Original C Version------------ 

 

  1. //----------Corresponding assembly code------------ 
  2. .L6: loop: 
  3.     leal (%edx,%ebx),%eax ;Compute t = val + nval 
  4.     movl %edx,%ebx        ;copy nval to val 
  5.     movl %eax,%edx        ;Copy t to nval 
  6.     incl %ecx             ;Increment i 
  7.     cmpl %esi,%ecx        ;Compare i:n 
  8.     jl .L6 If less,       ;goto loop 
  9. //---------Corresponding assembly code------------ 

while循环

========================

while语句循环的通用形式是这样的

  1. while(test-expr)
  2.     body-statement

与do-while的不同之处在于对test-expr求值, 在第一次执行body-statement之前, 循环就可能终止了. 翻译成goto语句的形式就是

  1. loop:
  2.     t= test-expr;
  3.     if (!t)
  4.         goto done;
  5.     body-statement
  6.         goto loop;
  7. done:

这种翻译需要在内循环(也就是执行次数最多的代码部分)中, 有两条goto语句. 大多数的编译器将这段代码转换成do-while循环, 把一个条件分支语句从循环体中拿到外面来.

  1.     if (!test-expr)
  2.         goto done;
  3.     do
  4.     body-statement
  5.         while (test-expr);
  6. done:

然后, 再把这段代码换成带goto的语句的代码, 如下

  1.     t= test-expr;
  2.     if (!t)
  3.         goto done;
  4. loop:
  5.     body-statement
  6.         t= test-expr;
  7.     if (t)
  8.         goto loop;
  9. done:

 

for循环

========================

for循环的通用形式是这样的:

  1. for (init-expr; test-expr; update-expr)
  2.     body-statement

C语言的标准说明, 这样的一个循环的行为与下面这段使用while循环的代码的行为一样:

  1. init-expr;
  2. while (test-expr){
  3.     body-statement
  4.     update-expr;
  5. }

然后再用前面讲过的从while到do-while的转换. 首先给出do-while形式

  1.     init-expr;
  2.     if (!test-expr)
  3.         goto done;
  4.     do{
  5.         body-statement
  6.         update-expr;
  7.     }while (test-expr);
  8. done:

再转换成goto代码

  1.     init-expr;
  2.     t= test-expr;
  3.     if (!t)
  4.         goto done;
  5. loop:
  6.     body-statement
  7.         update-expr;
  8.     t= test-expr;
  9.     if (t)
  10.         goto loop;
  11. done:

 

相信现在, 你已经对汇编中的循环指令簇有点模式的感觉了吧? 呵呵. 我们再来看一个switch语句, 然后收工.

switch语句

======================

switch语句提供了一个整数索引值, 通过它来进行多重分支. 那么switch语句和一组很长的if-else语句相比, 有什么优势呢? 我先把答案说出来, 然后看看汇编, 就知道了.

优势就是: 执行开关语句的时间与开关情况的数量无关.

能做到这样的原因是跳转表. 跳转表是一个数组, 表项i是一个代码段的地址, 这个代码段实现的就是开关索引值等于i的时候应该采取的动作.

 

让我们来看一个例子, 这个例子包含一些很有意思的特征, 情况标号(case label)不连续, 比如101, 105; 一个情况有多个标号, 比如104, 106; 有些情况会落入其他情况(102), 因为该情况没有用break结尾.

  1. //----------Original C code------------ 
  2. int switch_eg(int x) 
  3.     int result = x; 
  4.   
  5.     switch (x) { 
  6.   
  7.         case 100: 
  8.             result *= 13; 
  9.             break
  10.   
  11.         case 102: 
  12.             result += 10; 
  13.             /* Fall through */ 
  14.   
  15.         case 103: 
  16.             result += 11; 
  17.             break
  18.   
  19.         case 104: 
  20.         case 106: 
  21.             result *= result; 
  22.             break
  23.   
  24.         default
  25.             result = 0; 
  26.     } 
  27.   
  28.     return result; 
  29. //----------Original C code------------ 

说明问题的C的伪代码

  1. /* Next line is not legal C */ 
  2. code *jt[7] = { 
  3.     loc_A, loc_def, loc_B, loc_C, 
  4.     loc_D, loc_def, loc_D 
  5. }; 
  6. int switch_eg_impl(int x) 
  7.     unsigned xi = x - 100; 
  8.     int result = x; 
  9.     if (xi > 6) 
  10.         goto loc_def; 
  11.     /* Next goto is not legal C */ 
  12.     goto jt[xi]; 
  13. loc_A: /* Case 100 */ 
  14.     result *= 13; 
  15.     goto done; 
  16. loc_B: /* Case 102 */ 
  17.     result += 10; 
  18.     /* Fall through */ 
  19. loc_C: /* Case 103 */ 
  20.     result += 11; 
  21.     goto done; 
  22. loc_D: /* Cases 104, 106 */ 
  23.     result *= result; 
  24.     goto done; 
  25. loc_def: /* Default case*/ 
  26.     result = 0; 
  27. done: 
  28.     return result; 

 

  1. //----------Corresponding assembly code------------ 
  2. //*********** 
  3. // Code that Set up the jump table access 
  4. //*********** 
  5.     leal -100(%edx),%eax         ;Compute xi = x-100 
  6.     cmpl $6,%eax                 ;Compare xi:6 
  7.     ja .L9                       ;if >, goto done 
  8.     jmp *.L10(,%eax,4)           ;Goto jt[xi] 
  9. //Case 100 
  10. L4:                              ;loc A: 
  11.     leal (%edx,%edx,2),%eax      ;Compute 3*x 
  12.     leal (%edx,%eax,4),%edx      ;Compute x+4*3*x 
  13.     jmp .L3                      ;Goto done 
  14. //Case 102 
  15. L5:                              ;loc B: 
  16.     addl $10,%edx                ;result += 10, Fall through 
  17. //Case 103 
  18. L6:                              ;loc C: 
  19.     addl $11,%edx                ;result += 11 
  20.     jmp .L3                      ;Goto done 
  21. //Cases 104, 106 
  22. L8:                              ;loc D: 
  23.     imull %edx,%edx              ;result *= result 
  24.     jmp .L3                      ;Goto done 
  25. //Default case 
  26. L9:                              ;loc def: 
  27.     xorl %edx,%edx               ;result = 0 
  28. //Return result 
  29. L3:                              ;done: 
  30.     movl %edx,%eax               ;Set result as return value 
  31. //----------Corresponding assembly code------------ 

参考资料<深入理解计算机系统>

(awpatp)
本站文章除注明转载外,均为本站原创或编译欢迎任何形式的转载,但请务必注明出处,尊重他人劳动,同学习共成长。转载请注明:文章转载自:罗索实验室 [http://www.rosoo.net/a/201412/17162.html]
本文出处:博客园 作者:awpatp 原文
顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片
栏目列表
将本文分享到微信
织梦二维码生成器
推荐内容