再谈arm指令编码

By xia0

再谈arm指令编码

背景

最近在做一个项目中需要patch arm指令,由于之前只是对arm指令编码了解一个大概,但patch指令需要对其指令编码十分熟悉才可以,所以重新学习了一遍arm指令。记录下学习结果。

arm指令编码

与x86指令的编码不同,arm指令是一个定长的指令编码格式,这种方式有很多好处,方便流水线的方式指令解码。这里arm指令采用的是32位等长编码格式。按类型的位域分布,如下所示:

31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
cond op1 op

arm指令按照功能可以分为以下几类:

  • 数据处理与杂项指令
  • 加载存储指令
  • 媒介指令
  • 分支、块数据传输指令
  • 软中断与协处理指令
  • 无条件指令指令

每一类指令有有着自己的位域分布,具体的常见指令格式如下:

image-20190428140548217

image-20190428140740378

image-20190428140801936

thumb指令编码

和arm指令类似,thumb指令集作为arm指令集的一个子集,它具有16位的指令宽度,与arm指令的32位宽度想必,thumb指令集保留了32位宽度的优势下节省了系统的储存空间。

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
opcode

thumb常见的指令编码

image-20190428142331693

image-20190428142349612

有了上面的指令表,就可以对一个具体的指令进行分析了。

下面以实际的指令例子分析:

  • mov

    根据上面可得其位域分布如下

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
0 0 1 0 op Ld/Ln Immed8

分析如下汇编代码

MOVS R3, #0

上面的的thumb指令对应16 进制为0x2300,对应的位域分布如下

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
0 0 1 0 0 0 1 1 0 0 0 0 0 0 0

011对应R3寄存器,立即数为0,与我们分析得一致

  • b

    分支指令对应的位域分布如下
    | 15 14 13 12 | 11 | 10 9 8 7 6 5 4 3 2 1 |
    | ———– | —- | ——————– |
    | 1 1 1 0 | 0 | singed 11bit offset |

    这里的偏移计算公式如下:

    B ins_address+4+offset*2
    

    假设有如下指令:0xE7FF,其当前地址为0xBE86,所以offset为0x7ff,由于7ff对应的有符号数为-1

    所以跳转到地址为:0xBE86+(-1)*2=0xBE88,也可以解释为跳转到下一条指令。

  • it

    由于thumb指令只有16位长,所以需要一条条件执行指令即IT,这条指令并没有出现在上面之中,下面解释一下该指令,这条指令最多可以条件执行4条指令,第一条指令默认是If。

    现在有IT NE指令对应的16进制为0x8F18
    | 15 14 13 12 | 11 10 9 8 | 7 6 5 4 | 3 | 2 1 0 |
    | ———– | ——— | ——- | —- | ——- |
    | 1 0 1 1 | 1 1 1 1 | cond | 1 | If/else |

    根据上面格式可知,4-7位为条件值,0-3位位需要条件指令的指令情况。

    条件值对应表如下

    image-20190428153426483

    0x8F18对应的二进制如下

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
1 0 1 1 1 1 1 1 0 0 0 1 1 0 0 0

所以条件为0001=NE条件执行=000,即只有下一条指令需要条件执行,所以该指令为IT NE

参考