组装 THUMB 指令以在 Cortex-M3 上执行
Posted
技术标签:
【中文标题】组装 THUMB 指令以在 Cortex-M3 上执行【英文标题】:Assembling THUMB instrutions to execute on Cortex-M3 【发布时间】:2021-01-19 14:50:43 【问题描述】:作为练习,我想让 STM32F103 从内部 SRAM 执行。思路是手工写一些THUMB汇编,用arm-none-eabi-as
汇编,用OpenOCD的mwh
指令将机器码加载到SRAM中,用reg pc 0x20000000
将PC设置为SRAM的开头,最后step
几个次。
这是我要执行的汇编代码。这基本上是一个毫无意义的循环。
# main.S
.thumb
.syntax unified
mov r0, #40
mov r1, #2
add r2, r0, r1
mvn r0, #0x20000000
bx r0
我需要获取机器码以便将其加载到 SRAM 中,但反汇编程序的输出似乎不正确。
$ arm-none-eabi-as -mthumb -mcpu=cortex-m3 -o main.o main.S
$ arm-none-eabi-objdump -d -m armv7 main.o
main.o: file format elf32-littlearm
Disassembly of section .text:
00000000 <.text>:
0: f04f 0028 mov.w r0, #40 ; 0x28
4: f04f 0102 mov.w r1, #2
8: eb00 0201 add.w r2, r0, r1
c: f06f 5000 mvn.w r0, #536870912 ; 0x20000000
10: 4700 bx r0
THUMB 指令的长度不应该是 16 位吗?我得到的机器码每条指令占用 4 个字节。
【问题讨论】:
这能回答你的问题吗? What is the difference between the ARM, Thumb and Thumb 2 instruction encodings? @P__J__ 感谢您提供有用的链接,但它没有回答我的问题。接受的答案是“大多数指令都有 2 字节编码,但bl
和 blx
一直有 4 字节编码”,但在我的问题中,mov
、add
和 mvn
被编码为 32 -bit 字,尽管有.thumb
指令。我想要一个解释如何定位 Thumb 指令集或“Thumb 指令编码”的答案。
你的代码在我看来是 ARM。
这是拇指密码。这些指令是 32 位长的,因为它们都是 thumb2 指令。只有一部分指令可以用 16 位编码,而你的不在其中。
使用movs
和adds
;大多数指令的 16 位拇指形式是标志设置版本。
【参考方案1】:
STM32F103 基于 cortex-m3。你需要从 st 文档开始,然后去 arm 网站获取 cortex-m3 技术参考手册。因为它告诉您这是基于 armv7-m 架构的,因此您可以获得架构参考手册。然后你就可以开始编程了。
从闪存运行通常使用向量表,从 ram 运行可能意味着这取决于引导引脚,但是如果您想使用调试器下载程序,那么您就在正确的路径上,您之前只是卡住或停止了整理。
# main.S
.thumb
.syntax unified
mov r0, #40
mov r1, #2
add r2, r0, r1
mvn r0, #0x20000000
bx r0
你指定了统一的语法,也许是在命令行 cortex-m3 上?还是armv7-m?因此,您最终得到了 thumb2 扩展,它们是 ARM 记录的两个 16 位半部分(armv7-m 架构参考手册向您展示了所有说明)。它们是可变长度的,第一个被解码,第二个只是操作数。 non-thumb2 都是 16 位的,bl/blx 是/是两个单独的 16 位指令,但是 cortex-ms 希望那些背靠背在以前的内核上你可以将它们分开以证明它们确实是两个不同的说明。
例如
.cpu cortex-m3
.thumb
.syntax unified
add r2, r0, r1
adds r2, r0, r1
00000000 <.text>:
0: eb00 0201 add.w r2, r0, r1
4: 1842 adds r2, r0, r1
16 位“全拇指变体”编码仅带有标志,因此您必须添加;如果 gnu assembler 和你指定了统一的语法,大多数人会告诉你这样做,我个人不这样做。让你知道:
.cpu cortex-m3
.thumb
add r2, r0, r1
adds r2, r0, r1
so.s: Assembler messages:
so.s:6: Error: instruction not supported in Thumb16 mode -- `adds r2,r0,r1'
所以
.cpu cortex-m3
.thumb
add r2, r0, r1
add r2, r0, r1
00000000 <.text>:
0: 1842 adds r2, r0, r1
2: 1842 adds r2, r0, r1
只是为了警告你,以防你掉进那个陷阱。而且你不只是喜欢反汇编程序使用添加。
无论如何。所以这些很好,这些是
.cpu cortex-m3
.thumb
.syntax unified
mov r0, #40
mov r1, #2
add r2, r0, r1
mvn r0, #0x20000000
bx r0
00000000 <.text>:
0: f04f 0028 mov.w r0, #40 ; 0x28
4: f04f 0102 mov.w r1, #2
8: eb00 0201 add.w r2, r0, r1
c: f06f 5000 mvn.w r0, #536870912 ; 0x20000000
10: 4700 bx r0
就像添加 mov 的 16 位编码是带有标志的那样
movs r0, #40
movs r1, #2
00000000 <.text>:
0: 2028 movs r0, #40 ; 0x28
2: 2102 movs r1, #2
4: eb00 0201 add.w r2, r0, r1
8: f06f 5000 mvn.w r0, #536870912 ; 0x20000000
c: 4700 bx r0
我们知道现在添加
00000000 <.text>:
0: 2028 movs r0, #40 ; 0x28
2: 2102 movs r1, #2
4: 1842 adds r2, r0, r1
6: f06f 5000 mvn.w r0, #536870912 ; 0x20000000
a: 4700 bx r0
mvn 没有意义,你想分支到 0x20000000 两件事,首先你想要 0x20000000 而不是 0xDFFFFFFF 所以试试这个
0: 2028 movs r0, #40 ; 0x28
2: 2102 movs r1, #2
4: 1842 adds r2, r0, r1
6: f04f 5000 mov.w r0, #536870912 ; 0x20000000
a: 4700 bx r0
其次,这是一个 cortex-m,所以你不能 bx 到一个偶数地址,这是你切换到 arm 模式的方式,但这个处理器不这样做,所以你会出错。您需要 lsbit 集。所以试试这个
.cpu cortex-m3
.thumb
.syntax unified
movs r0, #40
movs r1, #2
adds r2, r0, r1
ldr r0, =0x20000001
bx r0
00000000 <.text>:
0: 2028 movs r0, #40 ; 0x28
2: 2102 movs r1, #2
4: 1842 adds r2, r0, r1
6: 4801 ldr r0, [pc, #4] ; (c <.text+0xc>)
8: 4700 bx r0
a: 0000 .short 0x0000
c: 20000001 .word 0x20000001
使用 gnu 汇编程序,ldr equals 会选择最有效(最小指令)的解决方案,否则它会从池中提取。
或者你可以这样做而不使用池
.cpu cortex-m3
.thumb
.syntax unified
movs r0, #40
movs r1, #2
adds r2, r0, r1
mov r0, #0x20000000
orr r0,r0,#1
bx r0
这让我的皮肤爬行,因为你想或不添加,但如果重要的话,这会使它缩短一个半字:
.cpu cortex-m3
.thumb
.syntax unified
movs r0, #40
movs r1, #2
adds r2, r0, r1
mov r0, #0x20000000
adds r0,#1
bx r0
00000000 <.text>:
0: 2028 movs r0, #40 ; 0x28
2: 2102 movs r1, #2
4: 1842 adds r2, r0, r1
6: f04f 5000 mov.w r0, #536870912 ; 0x20000000
a: 3001 adds r0, #1
c: 4700 bx r0
然后你需要链接。但是……
.cpu cortex-m3
.thumb
.syntax unified
movs r0,#0
loop:
adds r0,#1
b loop
无需链接器脚本即可快速链接
arm-none-eabi-as so.s -o so.o
arm-none-eabi-ld -Ttext=0x20000000 so.o -o so.elf
arm-none-eabi-ld: warning: cannot find entry symbol _start; defaulting to 0000000020000000
arm-none-eabi-objdump -d so.elf
so.elf: file format elf32-littlearm
Disassembly of section .text:
20000000 <_stack+0x1ff80000>:
20000000: 2000 movs r0, #0
20000002 <loop>:
20000002: 3001 adds r0, #1
20000004: e7fd b.n 20000002 <loop>
打开两个窗口,一次启动openocd连接板/芯片
在其他
telnet localhost 4444
当你得到 openocd 提示时假设一切正常
halt
load_image so.elf
resume 0x20000000
或者您可以恢复 0x20000001,因为这感觉更好,但无论哪种方式,该工具都很好。现在
halt
reg r0
resume
halt
reg r0
resume
作为一个 stm32 并且是所有拇指变体指令,这个示例将适用于我迄今为止听说过的任何 stm32(我已经(使用过)很多)。
您将看到 r0 会增加,从恢复到再次停止之间的人为时间将计算多次,您可以看到数字变化以了解程序正在运行。
telnet localhost 4444
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Open On-Chip Debugger
> halt
> load_image so.elf
6 bytes written at address 0x20000000
downloaded 6 bytes in 0.001405s (4.170 KiB/s)
> resume 0x20000000
> halt
target state: halted
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x20000002 msp: 0x20001000
> reg r0
r0 (/32): 0x000ED40C
> resume
> halt
target state: halted
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x20000002 msp: 0x20001000
> reg r0
r0 (/32): 0x001C8777
>
如果你想把它放进闪存,假设蓝色药丸(这是一个蓝色药丸,对吗?)没有一些写保护的闪存,但你可以轻松删除它(会让你弄清楚,并不一定容易,专业人士提示在某些时候会涉及完整的电源循环)。
.cpu cortex-m3
.thumb
.syntax unified
.word 0x20001000
.word reset
.thumb_func
reset:
movs r0,#0
loop:
adds r0,#1
b loop
arm-none-eabi-as so.s -o so.o
arm-none-eabi-ld -Ttext=0x08000000 so.o -o so.elf
arm-none-eabi-ld: warning: cannot find entry symbol _start; defaulting to 0000000008000000
arm-none-eabi-objdump -d so.elf
so.elf: file format elf32-littlearm
Disassembly of section .text:
08000000 <_stack+0x7f80000>:
8000000: 20001000 .word 0x20001000
8000004: 08000009 .word 0x08000009
08000008 <reset>:
8000008: 2000 movs r0, #0
0800000a <loop>:
800000a: 3001 adds r0, #1
800000c: e7fd b.n 800000a <loop>
重置向量需要是处理程序的地址 ORRED 加一。并且向量表需要位于 0x08000000 (或 0x00000000 但您最终会想要 0x08000000 或 0x02000000 对于一些不是这个,0x08000000 对于这个,阅读文档)。
在telnet进入openocd
flash write_image erase so.elf
reset
halt
reg r0
resume
halt
reg r0
resume
现在它是在闪存中编程的,所以如果你关闭电源然后打开它就会运行它。
openocd 会以这样的方式结束
Info : stm32f1x.cpu: hardware has 6 breakpoints, 4 watchpoints
然后是 telnet 会话
telnet localhost 4444
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Open On-Chip Debugger
> halt
target state: halted
target halted due to debug-request, current mode: Thread
xPSR: 0xa1000000 pc: 0x0800000a msp: 0x20001000
> flash write_image erase so.elf
auto erase enabled
device id = 0x20036410
flash size = 64kbytes
wrote 1024 bytes from file so.elf in 0.115819s (8.634 KiB/s)
> reset
> halt
target state: halted
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x0800000a msp: 0x20001000
> reg r0
r0 (/32): 0x002721D4
> resume
> halt
target state: halted
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x0800000a msp: 0x20001000
> reg r0
r0 (/32): 0x0041DF80
>
如果您希望闪存重置为 ram,您可以这样做
.cpu cortex-m3
.thumb
.syntax unified
.word 0x20001000
.word 0x20000001
电源循环它应该理想地崩溃/故障,但如果你使用 openocd 像我们之前所做的那样在 ram 中放入一些东西
flash.elf: file format elf32-littlearm
Disassembly of section .text:
08000000 <_stack+0x7f80000>:
8000000: 20001000 .word 0x20001000
8000004: 20000001 .word 0x20000001
so.elf: file format elf32-littlearm
Disassembly of section .text:
20000000 <_stack+0x1ff80000>:
20000000: 2000 movs r0, #0
20000002 <loop>:
20000002: 3001 adds r0, #1
20000004: e7fd b.n 20000002 <loop>
telnet localhost 4444
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Open On-Chip Debugger
> halt
target state: halted
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x0800000a msp: 0x20001000
> flash write_image erase flash.elf
auto erase enabled
device id = 0x20036410
flash size = 64kbytes
wrote 1024 bytes from file flash.elf in 0.114950s (8.699 KiB/s)
> load_image so.elf
6 bytes written at address 0x20000000
downloaded 6 bytes in 0.001399s (4.188 KiB/s)
> reset
> halt
target state: halted
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x20000002 msp: 0x20001000
> reg r0
r0 (/32): 0x001700E0
> resume
> halt
target state: halted
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x20000004 msp: 0x20001000
> reg r0
r0 (/32): 0x00245FF1
> resume
> halt
target state: halted
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x20000002 msp: 0x20001000
> reg r0
r0 (/32): 0x00311776
>
但是一个电源循环
telnet localhost 4444
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Open On-Chip Debugger
> halt
> reset
stm32f1x.cpu -- clearing lockup after double fault
target state: halted
target halted due to debug-request, current mode: Handler HardFault
xPSR: 0x01000003 pc: 0xfffffffe msp: 0x20000fe0
Polling target stm32f1x.cpu failed, trying to reexamine
stm32f1x.cpu: hardware has 6 breakpoints, 4 watchpoints
> halt
>
是的,不像预期/期望的那样快乐。
注意 _start 来自默认链接描述文件中的 ENTRY(_start),它并不特殊,也不是真正硬编码到工具中(gcc 的 main 也不是,它来自默认引导程序)。
所以你可以这样做
所以.s
.cpu cortex-m3
.thumb
.syntax unified
movs r0,#0
loop:
adds r0,#1
b loop
so.ld
MEMORY
hello : ORIGIN = 0x20000000, LENGTH = 0x1000
SECTIONS
.text : *(.text*) > hello
arm-none-eabi-as so.s -o so.o
arm-none-eabi-ld -T so.ld so.o -o so.elf
arm-none-eabi-objdump -d so.elf
so.elf: file format elf32-littlearm
Disassembly of section .text:
20000000 <loop-0x2>:
20000000: 2000 movs r0, #0
20000002 <loop>:
20000002: 3001 adds r0, #1
20000004: e7fd b.n 20000002 <loop>
_start 警告消失。请注意,您在链接描述文件中创建的部分名称(在这种情况下为您好)不必是 ram、rom、flash 等,它们可以是您想要的,是的,您可以使用链接描述文件但没有 MEMORY 部分来执行此操作在文件中,并且只有 SECTION。
如果你选择
arm-none-eabi-objcopy -O binary so.elf so.bin
openocd 可以读取 elf 文件和其他一些文件,但是像这样的原始内存映像,您必须指定地址,否则您可能会得到 0x00000000 或者谁知道呢
load_image so.bin 0x20000000
如果/当您获得一些核板时,您可以简单地将 bin 文件复制到虚拟拇指驱动器,它会为您将其加载到目标 mcu 中,虚拟驱动器将重新加载或重新加载并显示失败.TXT 如果它不起作用,一种情况是如果您链接 0x00000000 而不是 0x08000000。但是,您不能以这种方式加载 sram,只需闪烁即可。但我假设你有一个蓝色药丸而不是核板。
答案很长。
简答
这些是 thumb2 扩展,它们的大小是两个半字。有关指令说明,请参阅 armv7-m 架构参考手册。它们非常适合这个芯片。
您可能希望在 openocd 上使用 load_image 而不是 mwh,但如果您以正确的顺序获取半字,mwh 将起作用。
尽管您的代码或我的代码与位置无关,但您理想地希望链接,因此可以说您可以提取指令并使用 mwh。
该芯片具有从 sram 模式启动的功能,它将/应该使用向量表而不仅仅是启动指令,您需要正确设置启动引脚并使用 openocd 之类的东西将程序加载到 ram 中,然后重置 (不是电源循环)。
MVN movenegative or negate 在这里不是正确的指令,你需要在使用 bx 之前设置 lsbit 所以你想要寄存器中的 0x20000001 ,类似于
ldr r0,=0x20000001
bx r0
对于 gnu 汇编器,或者
mov r0,#0x20000000
orr r0,#1
bx r0
但是对于 armv7-m,对于 cortex-m0、m0+ 一些 -m8s 你不能使用那些指令它们不起作用。
.cpu cortex-m0
.thumb
.syntax unified
mov r0,#0x20000000
orr r0,#1
bx r0
arm-none-eabi-as so.s -o so.o
so.s: Assembler messages:
so.s:5: Error: cannot honor width suffix -- `mov r0,#0x20000000'
so.s:6: Error: cannot honor width suffix -- `orr r0,#1'
所以使用 ldr = 伪指令或手动从池中加载,或加载 0x2 或 0x20 或类似的东西,然后将其移位并用 1 加载另一个寄存器并对其或使用 add (yuck)。
编辑
.cpu cortex-m3
.thumb
.syntax unified
.globl _start
_start:
ldr r0,=0x12345678
b .
00000000 <_start>:
0: 4800 ldr r0, [pc, #0] ; (4 <_start+0x4>)
2: e7fe b.n 2 <_start+0x2>
4: 12345678 eorsne r5, r4, #120, 12 ; 0x7800000
如果它不能生成一条指令,那么它将生成一个 pc 相对加载并将变量放入一个文字池中,如果它可以找到一个分支,则放在分支之后的某个位置。
但你也可以自己做
.cpu cortex-m3
.thumb
.syntax unified
.globl _start
_start:
ldr r0,myvalue
b .
.align
myvalue: .word 0x12345678
00000000 <_start>:
0: 4800 ldr r0, [pc, #0] ; (4 <myvalue>)
2: e7fe b.n 2 <_start+0x2>
00000004 <myvalue>:
4: 12345678 eorsne r5, r4, #120, 12 ; 0x7800000
文字池是一块内存区域(在文本段中),用于存储常量。
unsigned int fun0 ( void )
return 0x12345678;
unsigned int fun1 ( void )
return 0x11223344;
00000000 <fun0>:
0: e59f0000 ldr r0, [pc] ; 8 <fun0+0x8>
4: e12fff1e bx lr
8: 12345678 .word 0x12345678
0000000c <fun1>:
c: e59f0000 ldr r0, [pc] ; 14 <fun1+0x8>
10: e12fff1e bx lr
14: 11223344 .word 0x11223344
让 C 编译器执行此操作并将其放在函数的末尾并不罕见。
.global fun1
.syntax unified
.arm
.fpu softvfp
.type fun1, %function
fun1:
@ Function supports interworking.
@ args = 0, pretend = 0, frame = 0
@ frame_needed = 0, uses_anonymous_args = 0
@ link register save eliminated.
ldr r0, .L6
bx lr
.L7:
.align 2
.L6:
.word 287454020
.size fun1, .-fun1
我没有为 thumb/cortex-m 构建它,但这很好,它会做同样的事情。但是,话说:
unsigned int fun0 ( void )
return 0x12345678;
unsigned int fun1 ( void )
return 0x00110011;
00000000 <fun0>:
0: 4800 ldr r0, [pc, #0] ; (4 <fun0+0x4>)
2: 4770 bx lr
4: 12345678 .word 0x12345678
00000008 <fun1>:
8: f04f 1011 mov.w r0, #1114129 ; 0x110011
c: 4770 bx lr
因为我对可以将哪些立即数用于各种 arm 指令集有一个粗略的了解。同样
.cpu cortex-m3
.thumb
.syntax unified
.globl _start
_start:
ldr r0,=0x12345678
ldr r1,=0x00110011
nop
nop
nop
b .
00000000 <_start>:
0: 4803 ldr r0, [pc, #12] ; (10 <_start+0x10>)
2: f04f 1111 mov.w r1, #1114129 ; 0x110011
6: bf00 nop
8: bf00 nop
a: bf00 nop
c: e7fe b.n c <_start+0xc>
e: 0000 .short 0x0000
10: 12345678 .word 0x12345678
通过使用 ldr = thing gnu 汇编程序将选择最佳指令。并非所有 arm 汇编器都支持这一点(汇编语言是由工具而不是目标定义的),并且并非所有人都会选择最佳指令,如果他们认识到,有些人可能总是生成相对于 pc 的 ldr 语法。
它有点用于获取标签的地址,例如
.cpu cortex-m3
.thumb
.syntax unified
.globl _start
_start:
ldr r0,=mydataword
ldr r1,[r0]
add r1,#1
str r1,[r0]
bx lr
.data
mydataword: .word 0
在另一个段中它无法在组装时解决这个问题,因此它为链接器留下了一个占位符
00000000 <_start>:
0: 4802 ldr r0, [pc, #8] ; (c <_start+0xc>)
2: 6801 ldr r1, [r0, #0]
4: f101 0101 add.w r1, r1, #1
8: 6001 str r1, [r0, #0]
a: 4770 bx lr
c: 00000000 .word 0x00000000
arm-none-eabi-ld -Ttext=0x1000 -Tdata=0x2000 so.o -o so.elf
arm-none-eabi-objdump -D so.elf
so.elf: file format elf32-littlearm
Disassembly of section .text:
00001000 <_start>:
1000: 4802 ldr r0, [pc, #8] ; (100c <_start+0xc>)
1002: 6801 ldr r1, [r0, #0]
1004: f101 0101 add.w r1, r1, #1
1008: 6001 str r1, [r0, #0]
100a: 4770 bx lr
100c: 00002000 andeq r2, r0, r0
Disassembly of section .data:
00002000 <__data_start>:
2000: 00000000
或者
.cpu cortex-m3
.thumb
.syntax unified
.globl _start
_start:
ldr r0,=somefun
ldr r1,[r0]
orr r1,#1
bx r1
.align
somefun:
nop
b .
即使在同一段
00000000 <_start>:
0: 4803 ldr r0, [pc, #12] ; (10 <somefun+0x4>)
2: 6801 ldr r1, [r0, #0]
4: f041 0101 orr.w r1, r1, #1
8: 4708 bx r1
a: bf00 nop
0000000c <somefun>:
c: bf00 nop
e: e7fe b.n e <somefun+0x2>
10: 0000000c .word 0x0000000c
00001000 <_start>:
1000: 4803 ldr r0, [pc, #12] ; (1010 <somefun+0x4>)
1002: 6801 ldr r1, [r0, #0]
1004: f041 0101 orr.w r1, r1, #1
1008: 4708 bx r1
100a: bf00 nop
0000100c <somefun>:
100c: bf00 nop
100e: e7fe b.n 100e <somefun+0x2>
1010: 0000100c andeq r1, r0, r12
如果你让工具完成工作
.cpu cortex-m3
.thumb
.syntax unified
.globl _start
_start:
ldr r0,=somefun
ldr r1,[r0]
bx r1
.align
.thumb_func
somefun:
nop
b .
你不需要在 lsbit 中 orr,这个工具会帮你搞定
00001000 <_start>:
1000: 4802 ldr r0, [pc, #8] ; (100c <somefun+0x4>)
1002: 6801 ldr r1, [r0, #0]
1004: 4708 bx r1
1006: bf00 nop
00001008 <somefun>:
1008: bf00 nop
100a: e7fe b.n 100a <somefun+0x2>
100c: 00001009 andeq r1, r0, r9
这些是文字池的全部或大部分情况,用于帮助处理这样的指令集,该指令集的长度有些固定,因此对立即值有限制。
有时您可以帮助 gnu 汇编器将池数据放在哪里
.cpu cortex-m3
.thumb
.syntax unified
.globl fun0
.thumb_func
fun0:
ldr r0,=0x12345678
bx lr
.globl fun1
.thumb_func
fun1:
ldr r0,=0x11223344
bx lr
.align
.word 0x111111
00000000 <fun0>:
0: 4802 ldr r0, [pc, #8] ; (c <fun1+0x8>)
2: 4770 bx lr
00000004 <fun1>:
4: 4802 ldr r0, [pc, #8] ; (10 <fun1+0xc>)
6: 4770 bx lr
8: 00111111 .word 0x00111111
c: 12345678 .word 0x12345678
10: 11223344 .word 0x11223344
但是如果我
.cpu cortex-m3
.thumb
.syntax unified
.globl fun0
.thumb_func
fun0:
ldr r0,=0x12345678
bx lr
.pool
.globl fun1
.thumb_func
fun1:
ldr r0,=0x11223344
bx lr
.align
.word 0x111111
00000000 <fun0>:
0: 4800 ldr r0, [pc, #0] ; (4 <fun0+0x4>)
2: 4770 bx lr
4: 12345678 .word 0x12345678
00000008 <fun1>:
8: 4801 ldr r0, [pc, #4] ; (10 <fun1+0x8>)
a: 4770 bx lr
c: 00111111 .word 0x00111111
10: 11223344 .word 0x11223344
所以
ldr r0,=something
表示在链接时或有时将某物的地址加载到 r0 中。 标签只是地址,只是值/数字,所以
ldr r0,=0x12345678
意味着相同的东西标签而不是值本身所以给我那个标签的地址是 0x12345678 并将它放在 r0 中,所以这是气体或某人想到的这个概念的有趣扩展,可能是武器组装商,我不记得当时其他人也采用了它或对其进行了改进或其他什么。请注意,如果您想自己做,请这样做
ldr r0,something_address
b .
.align
something_address: .word something
因为某物是一个标签,它是一个地址,它是一个值,你没有将等号放在那里,所以等号仅用于 ldr 指令。同向量表:
.word 0x20001000
.word reset
最后,您可以执行其中一项来获取正确的函数地址 所谓的拇指互通
.cpu cortex-m3
.thumb
.syntax unified
.word 0x20001000
.word reset
.word handler
.word broken
.thumb_func
reset:
b .
.type handler,%function
handler:
b .
broken:
b .
Disassembly of section .text:
08000000 <_stack+0x7f80000>:
8000000: 20001000 .word 0x20001000
8000004: 08000011 .word 0x08000011
8000008: 08000013 .word 0x08000013
800000c: 08000014 .word 0x08000014
08000010 <reset>:
8000010: e7fe b.n 8000010 <reset>
08000012 <handler>:
8000012: e7fe b.n 8000012 <handler>
08000014 <broken>:
8000014: e7fe b.n 8000014 <broken>
可以使用 .thumb_func 如果在 thumb 中你可以在 arm 模式和 thumb 模式下使用 .type label,%function 并且你可以看到它生成了正确的 向量表中的拇指地址,但是在两者都没有使用的地方,损坏的标签没有正确生成,因此向量会在 cortex-m 上出错。
有些人可悲地这样做:
.word reset + 1
.word handler + 1
.word broken + 1
尝试解决该问题,而不是按预期使用该工具。 arm/thumb 的其他汇编语言意味着其他工具(ARM、Kiel 等)有自己的语法和规则,这仅限于 gnu 汇编程序。
还要注意这个答案有多少只是命令行的东西,我检查了工具的输出并对其进行操作,直到我得到我想要的,不必加载和运行代码来查看发生了什么。只需使用工具即可。
编辑 2
在评论中阅读您的其余问题
.cpu cortex-m3
.thumb
.syntax unified
ldr r0,=0x12345678
nop
b .
00000000 <.text>:
0: 4801 ldr r0, [pc, #4] ; (8 <.text+0x8>)
2: bf00 nop
4: e7fe b.n 4 <.text+0x4>
6: 0000 .short 0x0000
8: 12345678 .word 0x12345678
将 .word 放在偏移量 6 处对于 ldr 来说是对齐错误,因此他们需要填充它以将其放在字对齐地址。
现在您应该已经从 ARM 网站或其他地方下载了 armv7-m 架构参考手册。您至少可以在我正在查看的(这些是不断发展的文档)中看到 T1 编码
imm32 = ZeroExtend(imm8:'00', 32); add = TRUE;
再往下
Encoding T1 multiples of four in the range 0 to 1020
和
address = if add then (base + imm32) else (base - imm32);
data = MemU[address,4];
R[t] = data;
指令中编码的偏移量(立即数)是相对于 pc 的字数。 pc 是“前两个”或指令地址加 4 所以对于 ldr r0 指令
0: 4801 ldr r0, [pc, #4] ; (8 <.text+0x8>)
2: bf00 nop
4: e7fe b.n 4 <.text+0x4> <--- pc is here
6: 0000 .short 0x0000
8: 12345678 .word 0x12345678
8 - 4 = 4; 4>>2 = 1 所以离 pc 1 个字,指令 0x48xx xx 是 0x4801 表示一个字。这里再次对齐使用这条指令。
如果我们
.cpu cortex-m3
.thumb
.syntax unified
nop
ldr r0,=0x12345678
b .
00000000 <.text>:
0: bf00 nop
2: 4801 ldr r0, [pc, #4] ; (8 <.text+0x8>)
4: e7fe b.n 4 <.text+0x4>
6: 0000 .short 0x0000
8: 12345678 .word 0x12345678
好像坏了
Operation
if ConditionPassed() then
EncodingSpecificOperations();
base = Align(PC,4);
address = if add then (base + imm32) else (base - imm32);
data = MemU[address,4];
if t == 15 then
if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE;
else
R[t] = data;
当你看到所有的伪代码时,在这种情况下是 6 个 pc
然后继续阅读文档了解伪代码
计算指令的 PC 或 Align(PC,4) 值。对于 Thumb 指令,指令的 PC 值是其地址加 4。一条指令的 Align(PC,4) 值是它的 PC 值与 0xFFFFFFFC 进行“与”运算以强制它进行字对齐。
所以 0x6 & 0xFFFFFFFC = 4. 8 - 4 = 4; 4>>2 = 1;所以 0x4801。
如果我们强制使用 thumb2 指令
.cpu cortex-m3
.thumb
.syntax unified
ldr.w r0,=0x12345678
b .
它仍然对齐可能是为了让我们避免出现错误,thumb2 版本可以达到奇数
00000000 <.text>:
0: f8df 0004 ldr.w r0, [pc, #4] ; 8 <.text+0x8>
4: e7fe b.n 4 <.text+0x4>
6: 0000 .short 0x0000
8: 12345678 .word 0x12345678
注意指令末尾的 4 是 pc + 4,但如果我们尝试这样做会怎样:
.cpu cortex-m3
.thumb
.syntax unified
ldr.w r0,something
b .
something: .word 0x12345678
【讨论】:
很好的答案!!!不过,您能否详细说明“从池中提取(地址)”部分?伪指令ldr r0, =0x20000001
转换为ldr r0, [pc, #4]
,地址常量存储在0xC
的一个字中。为什么gas不把它放在0xA
而不是0xC
来节省半个字,为什么[pc, #4]
如果0xC - 0x6 = 6
?
我相信这是因为 cpu 无法从非 %4 地址加载 32 位——它会出现对齐错误。
大声笑我再次达到了字符数限制以获得答案。最后一件事确实生成了一条未对齐访问的指令。这些工具可以帮助您避免这种情况,但您可以做到这一点,因此您应该将 .aligns 洒在整个地方和/或检查拆卸。 16 位 ldr(字面量)只能进行对齐访问,如果可以的话,如果你没有指定你想要的指令,它会尝试使用 16 位。
另请注意,在 gnu 汇编程序中,大多数目标(如果不是所有目标)都支持 .align,但它与有或没有数字的对齐方式会有所不同,因此您必须小心,到目前为止,对于 arm没有数字的目标在字边界上对齐。 .balign 更便携,并且在 gnu 汇编程序中通常更受欢迎。 YMMV。当启动一个数据块(.text 或 .data)时,将 .align 放在那里并不是一个坏主意,它会做你想做的事,你会看到编译后的代码它将在每个函数前面使用它,可能会烧掉一个几个字节。以上是关于组装 THUMB 指令以在 Cortex-M3 上执行的主要内容,如果未能解决你的问题,请参考以下文章
为啥要使用thumb模式,与ARM相比较,Thumb代码的两大优势是啥?