如题
离线
这个是怎么回事呢?
离线
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工具链 + 模拟调试, 发现编译出来的代码并无二致。
寄存器值一模一样。
离线