本文主要讨论一下 32 位程序下的 thumb 模式相关东西,属于选读内容。
thumb模式
ARM模式的指令集宽度是32位而Thumb是16位宽度(但也可以是32位)。
Thumb也有很多不同的版本。不过不同的名字仅仅是为了区分不同版本的Thumb指令集而已(也就是对于处理器来说,这些指令永远都是Thumb指令)。
Thumb-1(16位宽指令集):在ARMv6以及更早期的版本上使用。
Thumb-2(16位/32位宽指令集):在Thumb-1基础上扩展的更多的指令集(在ARMv6T2以及ARMv7即很多32位Android手机所支持的架构上使用)
Thumb-EE:包括一些改变以及对于动态生成代码的补充(即那些在设备上执行前或者运行时编译的代码)。
thumb模式下有长指令与短指令,长指令是4个字节,短指令是2个字节。
在短指令里面,只能使用 R0-R7 这几个寄存器,如果要使用R8-R12,则会生成长指令。这里可以在指令编码中体现出来,我们看一个 add 指令的例子:
对寄存器的描述只使用了3位。
短指令里面会强行影响标志位,看一个例子:
00 24 MOVS R4, #0
这里生成的 MOV 是带 S 的,当我们尝试去掉这个 S 的时候,发现它生成的指令如下:
4F F0 00 04 MOV R4, #0
反而变成了长指令。
运算指令优先对第一,第二操作数相同的情况进行短指令编码:
7E 44 ADD R2, PC
之前学过ADD指令,它是有3个操作数的,但是这里只有俩个,是因为它是简写,相当于:
ADD R2, R2, PC
在 IDA 中,thumb 模式下有些指令带了 W 后缀,其实不用管,带 W 就理解成长指令就行,主要是看指令部分,后缀可忽略,比如下面的指令其实就是 STR 指令:
C3 F8 AC 01 STR.W R0, [R3, #(dword_4969C - 0x494E0)]
IT块
为了在Thumb模式下使用条件执行指令,Thumb提出了"IT"分支指令。然而,这条指令在之后的版本又被更改移除了,说是为了让一些事情变得更加简单方便。我觉得不用深究这个问题。了解一下知识,遇到再深入学习。
一些版本的ARM处理器上允许在Thumb模式下通过IT汇编指令进行条件执行。条件执行减少了要被执行的指令数量,以及用来做分支跳转的语句,所以具有更高的代码密度。
IT 是 IF-THEN 的简写。IF-THEN(IT)指令围起一个块,里面最多有 4 条指令,它里面的指令可以条件执行。需要注意的是,在IDA调试的时候,IT指令块并不是一句一句的执行,而是一次性全部执行完,所以我们无法在IT指令块中间下断点。
IT 指令已经带了一个“T”,因此还可以最多再带 3 个“T”或者“E”。并且对 T 和 E 的顺序没有要求。其中 T 对应条件成立时执行的语句,E 对应条件不成立时执行的语句。在 If-Then 块中的指令必须加上条件后缀,且 T 对应的指令必须使用和 IT 指令中相同的条件,E 对应的指令必须使用和 IT指令中相反的条件。
IT 的使用形式总结如下:
-
IT;围起 1 条指令的 IF-THEN 块
-
IT;围起 2 条指令的 IF-THEN 块
-
IT;围起 3 条指令的 IF-THEN 块
-
IT;围起 4 条指令的 IF-THEN 块
其中,,的取值可以是“T”或者“E”。而则是在下中列出的条件(AL 除外 ):
IT 指令优化 C 代码的例子如下面伪代码所示:
if (R0==R1)
{
R3 = R4 + R5;
R3 = R3 / 2;
}
else
{
R3 = R6 + R7;
R3 = R3 / 2;
}
可以写作:
CMP R0, R1 ; 比较 R0 和 R1
ITTEE EQ ; 如果 R0 == R1, Then-Then-Else-Else
ADDEQ R3, R4, R5 ; 相等时加法
ASREQ R3, R3, #1 ; 相等时算术右移
ADDNE R3, R6, R7 ; 不等时加法
ASRNE R3, R3, #1 ; 不等时算术右移
IT 块的编码非常的有意思:
mask 占据了后4位,这个是我们需要关注的。
当我们写指令 ITTTT 时,生成的指令是 01。
当我们写指令 ITTTE 时,生成的指令是 03,发现多了一位 1。
当我们写指令 ITTEE 时,生成的指令是 07,发现又多了一位 1。
当我们写指令 ITEEE 时,生成的指令是 0F,发现又多了一位 1。
所以,我们发现,最后一位一直是1,它表示IT后跟着的第一条指令的条件永远是正,即与 IT 保持一致,后面的几条指令,需要根据对应的bit位来决定条件是正还是反。