ILD

i386汇编实战:Control flow
作者:Herbert Yuan 邮箱:yuanjp89@163.com
发布时间:2017-7-5 站点:Inside Linux Development

Control flow不知道翻译成中文叫什么,就用Control flow吧。Control flow就是if else, while, for等实现。

1 EFLAGS

EFLAGS寄存器是非常重要的状态寄存器,算术与比较指令的一些状态存储在EFLAGS中的某些位中,跳转指令依赖于这些位。这是Control flow的基础。

图1 EFLAGS寄存器

如上标黄,本章主要涉及4个状态标志。

CF,Carry Flag,由算术运算指令设置,当进位或者借位发生时。

ZF,Zero Flag,大多数指令都会设置,如果一个操作是0的话。

SF,Sign Flag,大多数指令都会设置,根据最高位(符号位)的值。

OF,Overflow Flag,大多数算术指令都会设置,如果计算结果发生溢出。

2 cmp指令与状态

cmp指令按照sub指令修改EFLAGS的相关状态位。这两个标志和条件指令直接相关。具体如何设置的这里不研究。比如要想实现相等。

1
2
cmpl a, b
je label

如果a=b,则je条件为真,实现跳转。je和jz是完全相同的。

3 test指令与状态

如果按位与的结果为0,则ZF标志设置,此时jz生效,否则jnz生效。

会用上述两条指令后,我们就能使用条件跳转了,下面就是不同具体结构的应用了。

4 if else 结构

不是特别复杂,直接贴代码。

1
2
3
4
5
6
7
8
9
int func(int a)
{
    int b;
    if (a)
        b = 2;
    else
        b = 3;
    return b;
}

上述c代码的if else 汇编如下:

1
2
3
4
5
6
7
8
        cmpl    $0, 8(%ebp)
        je      .L2
        movl    $2, -4(%ebp)
        jmp     .L3
.L2:
        movl    $3, -4(%ebp)
.L3:
        movl    -4(%ebp), %eax

先比较a和0,如果相等则跳转到L2,将3赋给b。否则不等执行je下一条,将2赋给b,然后跳转到L3。

5 for 结构

不是特别复杂,直接贴代码。

1
2
3
4
5
6
7
8
9
10
11
int func(int a)
{
    int i;
 
    for (i = 0; i < 10; i++)
    {
        a += i;
    }
 
    return a;
}

上述c代码的for汇编代码如下:

1
2
3
4
5
6
7
8
9
        movl    $0, -4(%ebp)
        jmp     .L2
.L3:
        movl    -4(%ebp), %eax
        addl    %eax, 8(%ebp)
        addl    $1, -4(%ebp)
.L2:
        cmpl    $9, -4(%ebp)
        jle     .L3

先将i设置为0,然后无条件跳转到L2,L2判断条件,如果条件满足跳转到L3,L3前两行是for的动作,最后一行将i加1,然后继续执行L2判断条件。

6 while 结构

不是特别复杂,直接贴代码。

1
2
3
4
5
6
7
8
9
10
11
12
int func (int a)
{
    int i = 10;
 
    while (i)
    {
        a ++;
        i --;
    }
 
    return a;
}

上述c代码的while汇编代码如下:

1
2
3
4
5
6
7
8
        movl    $10, -4(%ebp)
        jmp     .L2
.L3:
        addl    $1, 8(%ebp)
        subl    $1, -4(%ebp)
.L2:
        cmpl    $0, -4(%ebp)
        jne     .L3

先将10赋给i,然后跳到L2判断条件,为真跳转到L3执行。结构和for是一模一样的。

7 do while 结构

不是特别复杂,直接贴代码。

1
2
3
4
5
6
7
8
9
10
11
12
int func (int a)
{
    int i = 10;
 
    do
    {
        a ++;
        i --;
    while (i);
 
    return a;
}

上述c代码的do while汇编代码如下:

1
2
3
4
5
6
        movl    $10, -4(%ebp)
.L2:
        addl    $1, 8(%ebp)
        subl    $1, -4(%ebp)
        cmpl    $0, -4(%ebp)
        jne     .L2

给i赋值后,直接执行do里面的语句,完了判断条件,是否在重复执行。相比while,少了第一步判断条件。

8 switch 结构

不是特别复杂,直接贴代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int func (int a)
{
    int i = 10;
 
    switch(a)
    {
    case 0:
        i+=2;
    case 1:
        i++;
        break;
    default:
        i--;
    }
 
    return i;
}

删除c代码,switch结构的汇编代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
        movl    $10, -4(%ebp)
        movl    8(%ebp), %eax
        testl   %eax, %eax
        je      .L3
        cmpl    $1, %eax
        je      .L4
        jmp     .L7
.L3:
        addl    $2, -4(%ebp)
.L4:
        addl    $1, -4(%ebp)
        jmp     .L5
.L7:
        subl    $1, -4(%ebp)
.L5:
        movl    -4(%ebp), %eax

可以看出,它的结构是,将所有的case比较依次放到一起,执行语句在比较之后依次排列。比较为真就跳转到case对应的执行语句,在执行语句中,有break就跳转到最后执行,没有跳转就接着往下执行。结构简单明了而且合理。

参考

[1] https://www.cs.princeton.edu/courses/archive/fall07/cos217/lectures/18FunctionCalls-2x2.pdf

[2] http://www.c-jump.com/CIS77/ASM/Instructions/I77_0070_eflags_bits.htm

[3] https://en.wikipedia.org/wiki/FLAGS_register

[4] https://cs61.seas.harvard.edu/cs61wiki/images/9/91/Asm-control.pdf

[4] https://c9x.me/x86/html/file_module_x86_id_35.html

Copyright © linuxdev.cc 2017-2024. Some Rights Reserved.