Control flow不知道翻译成中文叫什么,就用Control flow吧。Control flow就是if else, while, for等实现。
EFLAGS寄存器是非常重要的状态寄存器,算术与比较指令的一些状态存储在EFLAGS中的某些位中,跳转指令依赖于这些位。这是Control flow的基础。
图1 EFLAGS寄存器
如上标黄,本章主要涉及4个状态标志。
CF,Carry Flag,由算术运算指令设置,当进位或者借位发生时。
ZF,Zero Flag,大多数指令都会设置,如果一个操作是0的话。
SF,Sign Flag,大多数指令都会设置,根据最高位(符号位)的值。
OF,Overflow Flag,大多数算术指令都会设置,如果计算结果发生溢出。
cmp指令按照sub指令修改EFLAGS的相关状态位。这两个标志和条件指令直接相关。具体如何设置的这里不研究。比如要想实现相等。
1 2 | cmpl a, b je label |
如果a=b,则je条件为真,实现跳转。je和jz是完全相同的。
如果按位与的结果为0,则ZF标志设置,此时jz生效,否则jnz生效。
会用上述两条指令后,我们就能使用条件跳转了,下面就是不同具体结构的应用了。
不是特别复杂,直接贴代码。
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。
不是特别复杂,直接贴代码。
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判断条件。
不是特别复杂,直接贴代码。
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是一模一样的。
不是特别复杂,直接贴代码。
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,少了第一步判断条件。
不是特别复杂,直接贴代码。
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