曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 5295|回复: 3
打印 上一主题 下一主题

控制程序流程---无条件分支

[复制链接]

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
跳转到指定楼层
楼主
发表于 2009-12-6 22:53:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
在控制程序的流程中,在遇到无条件分支时,指令指针(EIP) 会自动跳转到另外一个位置。可以使用的无条件分支有 3 种:
      跳转
      调用
      中断
每种无条件分支在程序中的行为都不同,可以决定在程序逻辑中使用哪一种。

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
沙发
 楼主| 发表于 2009-12-7 22:57:29 | 只看该作者

跳转

在结构化程序设计中,GOTO 被认为是不良的编码标志;良好的风格是,将程序划分为几个区域并且按照顺序的流程方式执行,调用函数,而不是在程序代码中跳转。但在汇编语言中,不认为跳转指令是不良的程序设计。跳转指令使用单一的指令码:
jmp  location
其中 location 是要跳转到的内存地址。

在幕后,单一汇编跳转指令被汇编为跳转操作码的 3 种不同类型之一:
      短跳转
      近跳转
      远跳转
这三种跳转类型是由当前指令的内存位置和“目的点” (“跳转到”的位置)的内存位置之间的距离决定的。依据跳过的字节数目决定使用哪种跳转类型。当跳转偏移量小于 128 字节时使用短跳转。在分段内存模式下,当跳转到另一个段中的指令时使用远跳转。近跳转用于所有其他跳转。

使用汇编语言指令时,不需要担心跳转的长度,单一跳转指令用于跳转到程序代码中的任何位置。

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
板凳
 楼主| 发表于 2009-12-7 23:34:46 | 只看该作者

调用

调用是一种能够返回的跳转。调用指令是 CALL ,它只有一个操作数 --- 跳转到的位置地址:
call address
address 操作数引用程序中的标签,它被转换为函数中的第一条指令的内存地址。
调用返回使用 RET 指令,该指令没有操作数。

执行 CALL 指令时,它把 EIP 寄存器的值放到堆栈中,然后修改 EIP 寄存器以指向被调用的函数地址。当被调用函数完成后,它从堆栈获得过去的 EIP 寄存器值,并且把控制权返回给原始程序。过程如下图所示:

# calltest.s - An example of using CALL instruction

  .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
编译连接生成可执行文件 calltest 后,先 objdump 看一下可执行文件相关内容:
080481b8 <_start>:
 80481b8:       6a 01                   push   $0x1
 80481ba:       68 ac 92 04 08          push   $0x80492ac
 80481bf:       e8 d4 ff ff ff          call   8048198 <printf@plt>
 80481c4:       83 c4 08                add    $0x8,%esp
 80481c7:       e8 16 00 00 00          call   80481e2 <overhere>
 80481cc:       6a 03                   push   $0x3
由上知道 output 标签所在的内存地址为: $0x80492ac;call overhere 语句的下一条的地址为 0x080481cc 。

现在用 gdb 对 calltest 进行调试,就在 call overhere 处下断,然后跟到 overhere 函数里的 pushl output 处,这时查看寄存器内容:
(gdb) info registers
eax            0x12     18
ecx            0x0      0
edx            0xb560f4 11886836
ebx            0x632fc0 6500288
esp            0xbf9d6110       0xbf9d6110
ebp            0xbf9d6118       0xbf9d6118
esi            0xbf9d612c       -1080205012
edi            0x80481b8        134513080
eip            0x80481ec        0x80481ec <overhere+10>
eflags         0x200292 [ AF SF IF ID ]
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x33     51
然后再查看一下堆栈:
(gdb) x/8x 0xbf9d6110
0xbf9d6110:     0x080492ac      0x00000002      0x00000000      0x080481cc
0xbf9d6120:     0x00000001      0xbf9d78b6      0x00000000      0xbf9d78d4
堆栈内容从 0xbf9d6110 开始,依次为:
0x080492ac  :   output 标签地址
0x00000002  :  常数 2
0x00000000  :  EBP 寄存器的内容
0x080481cc  :  正是 call overhere 的下一条指令地址,ret 返回后,就返回到此处。

最后,运行一下程序:
[beyes@localhost assembly]$ ./calltest
This is section 1
This is section 2
This is section 3

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
地板
 楼主| 发表于 2009-12-8 10:11:26 | 只看该作者

中断

中断是处理器“中断”当前指令码路径并且切换到不同路径的方式。中断有两种形式:
      软件中断
      硬件中断
硬件设备生成硬件中断。使用硬件中断发出信号,表示硬件层发生的事件(比如 I/O 端口接收到输入信号时)。程序生成软件中断,它们是把控制交给另一个程序的信号。

当一个程序被中断调用时,发出调用的程序暂停,被调用的程序接替它运行。并且,在某些情况下,甚至可以接触底层的 BIOS 系统。在 Microsoft DOS 操作系统中,为许多函数提供了 0x21 中断;在 Linux 里,0x80 中断用于提供低级内核函数。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2025-5-3 13:01 , Processed in 0.065193 second(s), 21 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表