控制
机器代码提供
- 测试数据值
- 根据测试结果改变控制流
条件码
CPU维护着一组条件码寄存器
- CF:进位标志。可以检查无符号操作的溢出。
- ZF:0标志
- SF:符号标志。最近的操作结果为负数。
- OF:溢出标志。检查有符号操作的溢出。
leaq
不改变任何条件码——它是用来进行地址计算的
除此之外,加法、乘法、移位、逻辑运算等指令都会设置条件码
逻辑操作会将进位标志CF与溢出标志OF设为0
移位操作会将进位标志CF设为最后溢出的位,溢出标志OF设为0
INC与DEC只会设置溢出标志与零标志,不会改变进位标志
特殊的指令——只改变条件码,不改变整数寄存器
- CMP
- TEST
二者均是二元操作,且每类都有4个分支
CMP执行的操作:CMP S1 S2
,比较S2-S1
(相当于SUB
,但是不设置寄存器)
TEST执行的操作:TEST S1 S2
,比较S1&S2
(相当于AND
,但是不设置寄存器)
访问条件码
条件码不会直接读取,常用的使用方法
- 根据条件码的某些组合,将一个字节设置为0或者1(
SET
类指令) - 条件跳转到程序的其它部分
- 条件传输数据
SET
类指令:区别在于考虑的条件码的组合——后缀表明了考虑的条件码的组合,而非操作数大小
例如setl
:小于xx的时候设置(有符号),setb
:低于xx时设置(无符号)
movzbl
不仅会将%eax
的高三个字节清零,还会将整个寄存器%rax
清零
// 因为觉得自己完全没用可能记得住那么多,所以也不打算看完
// 等到需要用的时候再用就行了
跳转指令
跳转指令的目的地使用一个label
(标号)表明
例如.L1
就是一个标号
跳转指令jmp
是无条件跳转,既可以直接把跳转目标编写到指令中,也可从寄存器或者内存中读取跳转目的
直接跳转:.L1
间接跳转:在*
后面跟上一个操作数提示符
// 因为觉得自己完全没用可能记得住那么多,所以也不打算看完
// 等到需要用的时候再用就行了
跳转指令的编码
理解跳转指令的目标如何编码,对于研究链接很重要
跳转指令有几种不同的编码,但是最常用的都是PC-relative
即将目标指令的地址,与紧跟着跳转指令后面的指令的地址的差,作为编码
这些地址的偏移量可以是1、2、4个字节
举例:
movq %rdi, %rax
jmp .L2
.L3
sarq %rax
.L2
testq %rax, %rax
jg .L3
rep; ret
使用反汇编器反汇编的结果
0: 48 89 f8 mov %rdi, %rax
3: eb 03 jmp 8<loop+0x8>
5: 48 d11 f8 sar %rax
8: 48 85 c0 test %rax, %rax
b: 7f f8 jg 5<loop+0x5>
d: f3 c3 repz retq
第一条跳转指令的目标编码为0x03
,加上0x05
后就是下一条指令的位置
第二条跳转指令的目标编码为0xf8
,加上0x0d
即13 - 8
(f8
为十进制-8
)为0x05
使用相对寻址时,PC计数器的值是跳转指令后的指令的地址,而不是跳转指令本身的地址*【
在使用链接器链接之后,地址跳转的指令并不会发生改变
- 小贴士:
rep
与repz
的区别repz
是rep
的同义名,retp
是ret
的同义名
它们通常用来实现重复的字符串操作
在rep
后面跟上ret
避免ret
指令称为条件跳转指令的目标,这里的rep
就是一个空操作,除了提高程序的运算速度之外没有其它操作。
第二种编码方式,是给出绝对地址,使用四个字节直接指定目标。
这两种跳转方式,编译器会使用最合适的方式进行编写