Chinaunix首页 | 论坛 | 博客
  • 博客访问: 970375
  • 博文数量: 633
  • 博客积分: 30780
  • 博客等级: 大将
  • 技术积分: 7532
  • 用 户 组: 普通用户
  • 注册时间: 2007-05-12 21:07
文章分类

全部博文(633)

文章存档

2011年(10)

2010年(500)

2009年(47)

2008年(76)

我的朋友

分类:

2011-03-07 00:41:40

1.  无条件分支
有三种类型的无条件分支
1)跳转

汇编语言的跳转与C原因的GOTO语句是等同的概念,说跳就跳,绝不拖泥带水。
这种跳转,又分为三种不同类型
  • 短跳转
  • 近跳转
  • 远跳转
这三种跳转类型是由当前指令的内存位置和目的点(“跳转到”的位置)的内存位置之间的距离决定的,依据跳过的字节数目决定使用哪种跳转类型。

当跳转偏移量小于128字节时使用短跳转,看一个例子:

[root@localhost assembly_study]# cat jumptest.s
.section .text
.global _start
_start:
        nop
        movl $1, %eax
        jmp overhere
        movl $10, %ebx
        int $0x80
overhere:
        movl $20, %ebx
        int $0x80

可以通过gdb看%eip的结果:

[root@localhost assembly_study]# gdb jumptest
GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)
Copyright 2003 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"...
(gdb) break *_start+1
Breakpoint 1 at 0x8048075: file jumptest.s, line 5.
(gdb) run
Starting program: /home/assembly_study/jumptest

Breakpoint 1, <function called from gdb>
Current language: auto; currently asm
(gdb) list
1 .section .text
2 .global _start
3 _start:
4 nop
5 movl $1, %eax
6 jmp overhere
7 movl $10, %ebx
8 int $0x80
9 overhere:
10 movl $20, %ebx
(gdb) list
11 int $0x80
(gdb) list
Line number 12 out of range; jumptest.s has 11 lines.
(gdb) print/x $eip
$1 = 0x8048075
(gdb) s
6 jmp overhere
(gdb) print/x $eip
$2 = 0x804807a
(gdb) s
10 movl $20, %ebx
(gdb) print/x $eip
$3 = 0x8048083
(gdb) cont
Continuing.

Program exited with code 024.



在分段模式下,当跳转到另一段中的指令时使用远跳转。(vxworks bsp中romInit.s里的ljmp就是长跳转,可以再结合cod理解一下)

ljmp    $0x08, $ ROM_TEXT_ADRS + romInit2 - romInit



近跳转用于其它所有跳转。
暂时无例子。

2)调用

调用和跳转指令类似,但是它保存发生跳转的位置,并且在它具有在需要的时候返回这个位置的能力。在汇编语言程序中实现函数时使用它。

调用指令有两个部分:
第一个部分是CALL指令: call addr
第二个部分是返回指令:ret

实现的细节是:
执行call指令时,它把EIP寄存器的值存放到堆栈中,然后修改EIP寄存器的值以指向被调用的函数地址。当被调用的函数完成以后,它从堆栈中获得过去的EIP寄存器的值,并且把控制权返回给原始程序。就这么简单。
照例,看一个例子:

[root@localhost assembly_study]# cat calltest.s
.section .data
output:
        .asciz "This is section %d\n"
.section .text
.global _start
_start:
        pushl $1
        pushl $output
        call printf
        add $8, %esp
        call overhere
        pushl $3
        pushl $output
        call printf
        add $8, %esp
        pushl $0
        call exit
overhere:
        pushl %ebp
        movl %esp, %ebp
        pushl $2
        pushl $output
        call printf
        add $8, %esp
        movl %ebp, %esp
        popl %ebp
        ret

编译,链接,看结果:

[root@localhost assembly_study]# as -gstabs -o calltest.o calltest.s
[root@localhost assembly_study]# ld -o calltest -dynamic-linker /lib/ld-linux.so.2 -lc calltest.o
[root@localhost assembly_study]# ./calltest
This is section 1
This is section 2
This is section 3
[root@localhost assembly_study]#




3)中断
中断是处理器中断当前指令码的执行路径,并且切换到不同路径的方式。中断有两种形式:
  • 软件中断
  • 硬件中断

2.  条件分支
条件跳转只允许两种跳转类型:短跳转和近跳转。
短跳转使用8位带符号地址的偏移量,而近跳转使用16位或者32位带符号地址的偏移量。偏移量值被加到指令指针上。

关于“溢出标志”,这里有一点心得:
看如下例子:
movl $1, %eax
movb $0x7f, %bl
addb $10, %bl
jo overhere
int $0x80
overhere:
movl $0, %ebx
int $0x80
这个代码片段把带符号字节值127加上10,结果应该是137,这对于字节来说是合法值,但是对带符号字节数是非法的(带符号字节数只能使用-127到127的值)。因为这个带符号值非法,所以设置溢出标志为1,并且执行JO指令。
以前一直觉得奇怪,有符号,无符号,这些概念都是编程人员自己想出来的,对于movl,addl这些指令来说,它们肯定不知道你这个是有符号还是无符号,因为这些指令没有符号的概念。 那么计算机又怎么知道是否溢出了呢?
看了上面这段代码以及分析以后,才知道,当做addl这些指令时,计算机是做了“两手抓,两手都要硬”的准备,既把此次add做了有符号加,又做了无符号加,至于是否溢出了,eflags中有对应的标志位OF(OverFlow)来控制。所以编程人员需要在代码中自己去检测这个OF是否置位了。如果编程人员认为这是有符号加,那么他/她/它就需要去检测这个OF标志;如果编程人员不认为这是有符号加,那么他/她/它就根本不用去care这个OF位。
再澄清一点,无符号数的溢出,程序员需要检测进位标志(CF);
有符号数的溢出,程序员需要检测溢出标志(OF)。
另外,和溢出标志不同,DEC,INC指令不影响进位标志,例如,下面这个代码片段不会设置进位标志:
movl $0xffffffff, %ebx
inc  %ebx
jc  overflow
但是,下面这个代码片段会设置进位标志,并且JC指令会跳转到overflow的位置:
movl $0xffffffff, %ebx
addl $1, %ebx
jc overflow


3.  循环
循环指令使用ECX寄存器作为计数器并且随着循环指令的执行自动递减它的值。
循环指令有如下这些:
LOOP       循环直到ECX寄存器为0
LOOPE/LOOPZ    循环直到ECX寄存器为0,或者没有设置ZF标志
LOOPNE/LOOPNZ  循环直到ECX寄存器为0,或者设置了ZF标志

LOOPE/LOOPZ LOOPNE/LOOPNZ指令提供了监视零标志的附加功能。
注意:循环指令只支持8位偏移量,所以只能进行短跳转。

这里提供一个例子:

[root@localhost assembly_study]# cat loop.s
.section .data
output:
        .asciz "The value is: %d\n"
.section .text
.global _start
_start:
        movl $100, %ecx
        xorl %eax, %eax
loop1:
        addl %ecx, %eax
        loop loop1
        pushl %eax
        pushl $output
        call printf
        addl $8, %esp
        movl $1, %eax
        movl $0, %ebx
        int $0x80
[root@localhost assembly_study]#

看一下执行结果:

[root@localhost assembly_study]# as -gstabs -o loop.o loop.s
[root@localhost assembly_study]# ld -o loop -dynamic-linker /lib/ld-linux.so.2 -lc loop.o
[root@localhost assembly_study]# ./loop
The value is: 5050


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