AT&T 汇编-5: 分支跳转

很多时候代码都不是从头到尾顺序依次执行的,往往需要根据条件进行跳转,改变代码的执行流程。

无条件跳转

无条件跳转就一条很简单的 jmp 命令:

jmp   location

其中 location 是要跳转到的代码位置,通常由一个标签表示。标签就是一个标识符后加一个冒号,像一直在使用的 main 和 _start 都是标签。

# jmp.s

.section .rodata
msg1:
   .ascii   "I like this mm.\n"
msg2:
   .ascii   "I like that mm.\n"

.section .text
.globl _start
_start:
   movl  $4,      %eax  # system call
   movl  $1,      %ebx  # file descriptor
   movl  $msg2,   %edx
   subl  $msg1,   %edx  # string length

   jmp   actually

   movl  $msg1,   %ecx
   int   $0x80

actually:
   movl  $msg2,   %ecx
   int   $0x80

end:
   movl  $1,   %eax
   movl  $0,   %ebx
   int   $0x80

第 14,15 两行计算了字符串的长度(write 系统调用的参数可以参考这里):先把 msg2 的地址放到 edx,再从 edx 中减去 msg1 的地址,就得到了字符串 msg1 的长度。因为在数据段中声明的数据都是依次存放的(同样代码段的指令也是顺序存放的),所以用 msg2 的地址减去 msg1 的地址,得到的就是字符串的长度,这样就不需要一个个地数字符串的长度了。

第 17 行的指令跳过 19,20 行的代码,从 22 行(应该是 23 行,标识符不占空间,只作为标识)开始执行,打印信息,然后退出程序。

jmp 命令就像高级语言中的 goto 命令,直接跳到指定的地方。

条件跳转

与无条件跳转相对的就是条件跳转了,也就是常说的分支。例如下面一个 c 程序

#include <stdio.h>

#define YES 1

int main()
{
   int mm_love_me = YES;

   if (mm_love_me == YES)
      printf("I love mm, too.\n");

   return 0;
}

对应的汇编程序是

# cmp.s

.section .rodata
   .equ  YES,  1
msg:
   .ascii   "I love mm, too.\n"
mm_love_me:
   .int  YES

.section .text
.globl _start
_start:
   cmp   $YES, mm_love_me
   jnz   end

   movl  $4,            %eax
   movl  $1,            %ebx
   movl  $msg,          %ecx
   movl  $mm_love_me,   %edx
   subl  $msg,          %edx
   int   $0x80

end:
   movl  $1,   %eax
   movl  $0,   %ebx
   int   $0x80

第 4 行定义了一个常量 YES,值为 1。使用 .equ 就像 c 语言中的 define,可以定义一些常量,格式是

.equ  symbol,  value

在第 7,8 行

mm_love_me:
   .int  YES

使用了刚定义的常量 YES,把变量 mm_love_me 的值设为常量 YES 的值。

第 13 行

cmp   $YES, mm_love_me

这里的 YES 前加了“$”,而第 8 行中的 YES 没有“$”——因为 .equ 跟 define 相似,汇编器对于定义的符号做的跟宏一样的工作,都是展开和替换,所以第 8 行相当于

   .int  1

而第 13 行相当于

cmp   $1,   mm_love_me

cmp 命令比较了 mm_love_me 和常量 YES 的值是否相等。cmp 命令的一般格式是

cmp operand1, operand2

比较 operand1 和 operand2 的值,第二个参数只能是内存中的位置或者寄存器。cmp 指令执行 operand2 – operand1 的操作,但是不会修改这两个值,最后根据结果设置 EFLAGS 寄存器中的 ZF 标志(零标志)。

EFLAGS 寄存器中有很多位,某些指令会根据执行的结果设置 EFLAGS 相应的位,接下来可以使用针对不同位的条件跳转指令执行不同的代码。

下一行

jnz   end

跳转指令 jnz(jump if not zero)根据 ZF 标志判断上一次运算的结果是否为 0,如果不为 0 则跳转到相应的位置,否则从下一条指令开始继续往下执行。在这里因为 mm_love_me 的值和常量 YES 的值相等,执行减法操作后结果为 0,所以跳转指令不成立,不会跳转到 end 位置而是从 jnz 的下一条指令继续往下执行,打印信息,然后程序结束。

很容易想到结果为 0 就跳转的指令就是 jz(jump if zero)。这个例子是一个简单的 if 判断,对于比较复杂的 if-else 的组合也就是不同的 jz 和 jnz 还有其它跳转指令的组合。例如下面的程序

# cmp-2.s

.section .rodata
   .equ  YES,  1
msg1:
   .ascii   "I'll buy something for mm.\n"
msg2:
   .ascii   "Rob a bank, please.\n"
my_money:
   .int  0
mm_love_me:
   .int  YES

.section .text
.globl _start
_start:
   cmp   $0,  my_money
   jz    no_money

   movl  $4,      %eax
   movl  $1,      %ebx
   movl  $msg1,   %ecx
   movl  $msg2,   %edx
   subl  $msg1,   %edx
   int   $0x80
   jmp   end

no_money:
   cmp   $YES,    mm_love_me
   jnz   end

   movl  $4,         %eax
   movl  $1,         %ebx
   movl  $msg2,      %ecx
   movl  $my_money,  %edx
   subl  $msg2,      %edx
   int   $0x80

end:
   movl  $1,   %eax
   movl  $0,   %ebx
   int   $0x80
发表于 2011年5月15日
本文目前尚无任何评论.

发表评论

XHTML: 您可以使用这些标签: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>