首先ARM的官方资料中是这样介绍B跳转指令的,如下:
有几个疑点:
- 偏移量是24位有符号立即数,那符号位是哪一位?
- 分支指令B限制在当前指令±32M字节地址范围内,这个±32M字节是如何计算出来的?
- 跳转指令B是位置无关指令,如何实现的位置无关?
要解决这个问题,需要分析一段汇编程序.
绿色框内的汇编程序是BL跳转指令,其实和B道理想通,只是多了带状态位的跳转,偏移量计算是相同的.
首先这条指令存储在0X30006A18处,我们知道这个地址是当前程序执行的地址,而PC指针地址为0X30006A20,这是因为此ARM处理器是3级流水线,当前执行的指令的下一条指令正在译码,即0X30006A1C地址处的指令正在译码,再下一条指令0X30006A20地址处正在执行取指,也就是pc指针指向的位置.
从绿色框内的指令可知,机器码是0XEBFFFB0B.根据B跳转指令的编码格式的值b31-b28是条件码,b27-b24是命令码,b23-b0是偏移量.则此命令偏移量gap=0XFFFB0B.当前PC=0X30006A20,从绿色框内命令的注释得知,此命令的跳转目的地址是0X3000564C,那如何从偏移地址和当前PC指针得出来跳转目的地址呢?
首先偏移地址是24位有符号数,按32位有符号数的惯例,最高位是符号位,那此偏移量的bit23应该是符号位,0XFFFB0B转换成二进制格式为1111 1111 1111 1011 0000 1011,可知最高位是1,则此数值是二进制补码的格式表示的负数,将一个正数转换为对应的负数的方法是取反加一,那么这个二进制补码的偏移量转换为对应的正数就是逆过程,减1取反.减1后为1111 1111 1111 1011 0000 1010,再取反为0000 0000 0100 1111 0101,此数值转换为16进制为0X4F5,那么偏移量就是-0X4F5.那这个就是实际的偏移地址了吗,非也,看分支指令的最后一句话,arm指令为字对齐,最低两位为0,所以这个偏移量是偏移的命令条数,不是地址,如果要转换成地址需要对此偏移量乘以4,因为没条指令为4字节宽度,最总的偏移量是gap=-0X4F5*4=-0X13D4.
最后我们知道了gap的值,也知道了pc的值,那最终的目标跳转地址adr=pc+gap=0X30006A20+(-0X13D4)=0X3000564C,此时你会惊奇的发现,这个结果与绿色框内的指令注释的地址相同,单步程序后,发现程序精准的跳到了0X3000564C处执行了.
实践证明我们的推测是正确的,如果偏移量是个正数呢,你会推倒吗?