如题
离线

这个是怎么回事呢?
离线
Thumb-2 多数16位,部分32位,自动变长。
怎么确定是 thumb,还是thumb-2呢?
离线
thumb指令,相比较与arm代码,储存器的功耗也较低。
thumb指令,基本都是无条件的,一共有18条基本指令,全部指令都是16bit。
Thumb-2指令,由16bit、32bit的指令混合组成,一共有16条基本指令,
搜了一下, 确实如此, 可是我的 gcc 命令行没有指定 thumb-2, 而是:
$(ARMGNU)-gcc -c $(COPS) -mthumb notmain.c -o notmain.o离线
我记得这里的BL看起好像是32位,实际上还是16位,但自动粘在一起了。
我看左边那个地址, bl应该是 4字节
离线
原文链接 https://blog.csdn.net/zerglurker/article/details/39451123?utm_source=blogxgwz1
>这其实是一个误解,因为 长跳转是由两条跳转指令组成的。
=======================================================
最近因为工作的原因,经常接触到长跳转指令,但是总是弄不明白,跳转的目标地址怎么得来的
经过多方面的资料查找,和自己的反复推算,现在总算弄明白了
先上例子:
下面是几个实际遇到的机器码机器对应的反汇编
示例一 3950a4: f13d feaa =>bl 4d2dfc
示例二 395082: f13d fe40 =>bl 4d2d06
示例三 395044: F7FF FFAc =>bl 394fa0
大家是不是也奇怪为什么16位的thumb指令里面,会出现32位的指令。
开始我也是这么认为的
这其实是一个误解,因为 长跳转是由两条跳转指令组成的。
其实际机器码和和含义如下:
1 1 1 1  0 +11位地址偏移高位(off0)
0xF             13D
1 1 1 1  1 +11位地址偏移低位(off1)
0xF             EAA
还是以上述三个例子做示范,
其偏移如下:(偏移是将指令去除高五bit得来的,f13d->13d 高五bit是1111 0【001】||| feaa->6aa高五bit是1111 1【110】)
示例一 off0=0x13d off1=0x6aa
示例二 off0=0x13d off1=0x640
示例三 off0=0x7FF  off1=0x7AC
首先要做的事情是将两个11位合并为一个22位数(注意由于off1是11位,意味着其最高位为off0的最低位,off0的值要除2)
即 offmid = ((off0/2)<<12)|((off0%2)<<11)|(off1)
按照这样计算,几个示例的偏移中间量为:
offmid = 0x9eeaa
offmid = 0x9ee40
offmid = 0x3fffac
接着将这个值乘2,得到一个23位数
offmid1 = 0x13dd54
offmid1 = 0x13dc80
offmid1 = 0x7fff58
对于前面两个这是一个正数,最高位为0,所以不必取反保留原来的值
对于最后一个,最高位为1,是负数,要减1后取反
处理后,如下:
offmid2 = 0x13dd54
offmid2 = 0x13dc80
offmid2 = 0x8000a8
最高位为1,则表示向前跳转,最高位为0表示向后跳转
跳转的基址是当前指令的末尾,需要再追加4个字节
所以实际跳转值就是:
off = 0x3950a4+4+0x13dd54 = 0x4d2dfc
off = 0x395082+4+0x13dc80 = 0x4d2d06
off = 0x395044+4- 0x0000a8 = 0x394fa0
反过来再看一看,其实 长跳转的偏移是这么来的:
首先取当前位置和目标位置的偏移差
地址因为内存对齐的原因,必然是偶数(即最低位必然为0),所以偏移值只有23位有意义,最低位永远为0
又由于最高位为符号位,所以,只有22位存储了真正的值
然后取补码(正数的补码是源码,负数的补码是符号位不变,剩余数取反加1),这样可以去除符号位对值的影响,将结果变为一个真正的22位值
最后再将其拆分为两个11位数,分成两个跳转指令执行
这么一想,顺着看,结果一目了然;但是反汇编的时候,难度就大大增加了,呵呵 
————————————————
版权声明:本文为CSDN博主「52编程」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:
 https://blog.csdn.net/zerglurker/article/details/39451123
离线
感谢大佬解惑,虽然还是一知半解,容我去给IQ充值再来提问。
离线
strap.s
.arm
_reset:
        ldr r0, =0x101f1000 //UART0发送寄存器地址
        ldr r1, =0x31       //输出1
        str     r1, [r0]
        ldr r0, =_thum_mode + 1
        bx r0
.thumb
_thum_mode:
        ldr r1, =0x32       //输出2
        str     r1, [r0]
        ldr r1, =0x33       //输出3
        str     r1, [r0]@aozima 再请教大佬一个问题,为什么这个代码只能输出 1呢, 不能输出 2, 3
$ arm-linux-gnueabi-objdump -D strap.o
strap.o:     file format elf32-littlearm
Disassembly of section .text:
00000000 <_reset>:
   0:   e59f0014        ldr     r0, [pc, #20]   ; 1c <_thum_mode+0x8>
   4:   e3a01031        mov     r1, #49 ; 0x31
   8:   e5801000        str     r1, [r0]
   c:   e59f000c        ldr     r0, [pc, #12]   ; 20 <_thum_mode+0xc>
  10:   e12fff10        bx      r0
00000014 <_thum_mode>:
  14:   4903            ldr     r1, [pc, #12]   ; (24 <_thum_mode+0x10>)
  16:   6001            str     r1, [r0, #0]
  18:   4903            ldr     r1, [pc, #12]   ; (28 <_thum_mode+0x14>)
  1a:   6001            str     r1, [r0, #0]
  1c:   101f1000        andsne  r1, pc, r0
  20:   00000015        andeq   r0, r0, r5, lsl r0
  24:   00000032        andeq   r0, r0, r2, lsr r0
  28:   00000033        andeq   r0, r0, r3, lsr r0
Disassembly of section .ARM.attributes:
00000000 <.ARM.attributes>:
   0:   00001941        andeq   r1, r0, r1, asr #18
   4:   61656100        cmnvs   r5, r0, lsl #2
   8:   01006962        tsteq   r0, r2, ror #18
   c:   0000000f        andeq   r0, r0, pc
  10:   00543505        subseq  r3, r4, r5, lsl #10
  14:   01080306        tsteq   r8, r6, lsl #6
  18:   Address 0x0000000000000018 is out of bounds.我照着书本写的, 基于arm926, 明明切换到了thumb模式。
离线
1. 编译和链接时加 -mthumb-interwork
2. 跳转时,先跳到自动生成的 xx_from_thumb 和 xx_from_arm
   b1f0c:	002a      	movs	r2, r5
   b1f0e:	0020      	movs	r0, r4
   b1f10:	f018 ff2a 	bl	cad68 <__memmove_from_thumb> // 当前在thumb模式,调用arm模式的函数 
000cad68 <__memmove_from_thumb>: // 编译器自动生成的 interwork 代码
   cad68:	4778      	bx	pc
   cad6a:	46c0      	nop			; (mov r8, r8)
   cad6c:	eaffc71a 	b	bc9dc <memmove>
000bc9dc <memmove>:
   bc9dc:	e1500001 	cmp	r0, r1
   bc9e0:	e92d4070 	push	{r4, r5, r6, lr}
   ....
   bca48:	e8bd4070 	pop	{r4, r5, r6, lr}
   bca4c:	e12fff1e 	bx	lr
   136ac:	e1a05000 	mov	r5, r0
   136b0:	e1a00004 	mov	r0, r4
   136b4:	eb02ddd3 	bl	cae08 <__rt_ota_get_fw_version_from_arm> // 当前在ARM模式,调用thumb模式的函数 
000cae08 <__rt_ota_get_fw_version_from_arm>:
   cae08:	e59fc000 	ldr	ip, [pc]	; cae10 <__rt_ota_get_fw_version_from_arm+0x8>
   cae0c:	e12fff1c 	bx	ip
   cae10:	000b9261 	// 实际是 000b9260 thumb模式要奇数,用bx实现 跳转+切换
000b9260 <rt_ota_get_fw_version>:
   b9260:	b510      	push	{r4, lr}
   b9276:	bd10      	pop	{r4, pc}3. ARM模式的汇编函数,需要添加 `.type xxx, %function`,否则由thumb模式下跳转过来时不会生成`__func_asm_arm_from_thumb`
asm.S
	.code 32
    .globl _exit
_exit:
b .
	.type func_asm_arm, %function
    .globl func_asm_arm
func_asm_arm:
	BX LR
	.code 16
	.thumb_func
    .globl func_asm_thumb
func_asm_thumb:
	BX LR离线
@aozima 感谢大佬,我再学习学习。
离线
.arm
_reset:
        ldr r0, =0x101f1000 //UART0发送寄存器地址
        ldr r1, =0x31       //输出1
        str     r1, [r0]
//      ldr r0, =_thumb_mode + 1
        adr r0, _thumb_mode + 1
        bx r0
.thumb
_thumb_mode:
        ldr r0, =0x101f1000 //UART0发送寄存器地址
        ldr r1, =0x32       //输出2
        str     r1, [r0]
        ldr r1, =0x33       //输出3
        str     r1, [r0]这样可以输出 123 了, 前面没有初始化 r0 寄存器.
再加上
        ldr r0, =_thumb_mode + 1
替换
        adr r0, _thumb_mode + 1
$ qemu-system-arm -M versatilepb -m 128M -nographic -kernel notmain.bin
123请问@aozima老大,为什么ldr指令不可以呢?
离线
这个代码不行:
.arm
_arm_mode:
        ldr r0, =0x101f1000 //UART0发送寄存器地址
        ldr r1, =0x31       //输出1
        str     r1, [r0]
        ldr r6, =_thumb_mode + 1
        add r6, #1
//      adr r6, _thumb_mode + 1
        bx r6
.thumb
_thumb_mode:
        ldr r0, =0x101f1000 //UART0发送寄存器地址
        ldr r1, =0x32       //输出2
        str     r1, [r0]
        ldr r1, =0x33       //输出3
        str     r1, [r0]
        ldr r1, =0x34       //输出4
        str     r1, [r0]
_loop:
        b _loop            //死循环
        bx lr           //跳回去生成的代码:
$ arm-linux-gnueabi-objdump -D strap.o
strap.o:     file format elf32-littlearm
Disassembly of section .text:
00000000 <_arm_mode>:
   0:   e59f0024        ldr     r0, [pc, #36]   ; 2c <_loop+0x6>
   4:   e3a01031        mov     r1, #49 ; 0x31
   8:   e5801000        str     r1, [r0]
   c:   e59f601c        ldr     r6, [pc, #28]   ; 30 <_loop+0xa>
  10:   e2866001        add     r6, r6, #1
  14:   e12fff16        bx      r6
00000018 <_thumb_mode>:
  18:   4804            ldr     r0, [pc, #16]   ; (2c <_loop+0x6>)
  1a:   4906            ldr     r1, [pc, #24]   ; (34 <_loop+0xe>)
  1c:   6001            str     r1, [r0, #0]
  1e:   4906            ldr     r1, [pc, #24]   ; (38 <_loop+0x12>)
  20:   6001            str     r1, [r0, #0]
  22:   4906            ldr     r1, [pc, #24]   ; (3c <_loop+0x16>)
  24:   6001            str     r1, [r0, #0]
00000026 <_loop>:
  26:   e7fe            b.n     26 <_loop>
  28:   4770            bx      lr
  2a:   10000000        andne   r0, r0, r0
  2e:   0019101f        andseq  r1, r9, pc, lsl r0
  32:   00320000        eorseq  r0, r2, r0
  36:   00330000        eorseq  r0, r3, r0
  3a:   00340000        eorseq  r0, r4, r0
        ...这个是可以的:
.arm
_arm_mode:
        ldr r0, =0x101f1000 //UART0发送寄存器地址
        ldr r1, =0x31       //输出1
        str     r1, [r0]
//      ldr r6, =_thumb_mode + 1
//      add r6, #1
        adr r6, _thumb_mode + 1
        bx r6
.thumb
_thumb_mode:
        ldr r0, =0x101f1000 //UART0发送寄存器地址
        ldr r1, =0x32       //输出2
        str     r1, [r0]
        ldr r1, =0x33       //输出3
        str     r1, [r0]
        ldr r1, =0x34       //输出4
        str     r1, [r0]
_loop:
        b _loop            //死循环
        bx lr           //跳回去生成的代码:
$ arm-linux-gnueabi-objdump -D strap.o
strap.o:     file format elf32-littlearm
Disassembly of section .text:
00000000 <_arm_mode>:
   0:   e59f0020        ldr     r0, [pc, #32]   ; 28 <_loop+0x6>
   4:   e3a01031        mov     r1, #49 ; 0x31
   8:   e5801000        str     r1, [r0]
   c:   e28f6001        add     r6, pc, #1
  10:   e12fff16        bx      r6
00000014 <_thumb_mode>:
  14:   4804            ldr     r0, [pc, #16]   ; (28 <_loop+0x6>)
  16:   4905            ldr     r1, [pc, #20]   ; (2c <_loop+0xa>)
  18:   6001            str     r1, [r0, #0]
  1a:   4905            ldr     r1, [pc, #20]   ; (30 <_loop+0xe>)
  1c:   6001            str     r1, [r0, #0]
  1e:   4905            ldr     r1, [pc, #20]   ; (34 <_loop+0x12>)
  20:   6001            str     r1, [r0, #0]
00000022 <_loop>:
  22:   e7fe            b.n     22 <_loop>
  24:   4770            bx      lr
  26:   10000000        andne   r0, r0, r0
  2a:   0032101f        eorseq  r1, r2, pc, lsl r0
  2e:   00330000        eorseq  r0, r3, r0
  32:   00340000        eorseq  r0, r4, r0难道是ldr伪指令不能生成跳转到thumb?
离线
离线


同样都是 0x15 给 R6, 为什么 上面的不行呢?
离线

感觉应该是qemu虚拟机的bug, 因为我用 MDK + GCC工具链 + 模拟调试, 发现编译出来的代码并无二致。
寄存器值一模一样。
离线