组装 THUMB 指令以在 Cortex-M3 上执行

Assembling THUMB instrutions to execute on Cortex-M3

作为练习,我想让STM32F103从内部SRAM执行。这个想法是手工编写一些 THUMB 程序集,assemble 用 arm-none-eabi-as,用 OpenOCD 的 mwh 指令将机器代码加载到 SRAM,用 [=14] 将 PC 设置到 SRAM 的开头=],最后step几次。

这是我要执行的汇编代码。这基本上是一个毫无意义的循环。

# main.S
.thumb
.syntax unified

mov r0, #40
mov r1, #2
add r2, r0, r1
mvn r0, #0x20000000
bx r0

我需要获取机器码以便将其加载到 SRAM 中,但是 disassembler 输出似乎不正确。

$ 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 个字节。

STM32F103 基于cortex-m3。您需要从 st 文档开始,然后转到 arms 网站获取 cortex-m3 技术参考手册。因为它告诉您这是基于 armv7-m 体系结构的,因此您可以获得体系结构参考手册。然后就可以开始编程了。

运行 from flash 通常使用矢量 table,运行ning from 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 扩展,它们是两个 16 位的一半,如 ARM 所记录的那样(armv7-m 体系结构参考手册向你展示了所有说明)。它们是可变长度的,第一个被解码,第二个只是操作数。非 thumb2 都是 16 位的,bl/blx were/are 两个独立的 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 汇编器和你指定了统一的语法,大多数人会告诉你这样做,我个人不会。只是想让你知道:

.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

只是警告你,以防你落入那个陷阱。难道你不喜欢反汇编程序使用 adds 吗?

总之。所以这些很好,这些是

.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

然后你需要link。但是...

.cpu cortex-m3
.thumb
.syntax unified

movs r0,#0
loop:
   adds r0,#1
   b loop

Link 没有 linker 脚本来快速完成

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>

打开两个windows,其中一个启动openocd连接到board/chip

在另一个

telnet localhost 4444

当您收到 openocd 提示时假设一切正常

halt
load_image so.elf
resume 0x20000000

或者您可以恢复 0x20000001,因为这样感觉更好,但无论哪种方式,该工具都很好。现在

halt
reg r0
resume
halt
reg r0
resume

作为一个 stm32 并且是所有 thumb 变体指令,这个例子将适用于我目前听说过的任何 stm32。

你会看到 r0 会递增,人类在恢复和停止之间的时间会计算很多次你可以看到数字变化表明程序正在 运行ning。

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>

重置向量需要是处理程序的地址与 1 或。向量 table 需要位于 0x08000000(或 0x00000000,但对于一些不是这个的,您最终会想要 0x08000000 或 0x02000000,对于这个,需要 0x08000000,请阅读文档)。

在telnet中进入openocd

flash write_image erase so.elf
reset
halt
reg r0
resume
halt
reg r0
resume

现在它是在闪存中编程的,所以如果你关机然后再打开就是这样 运行s。

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
>       

如果你想将闪存重置为内存,你可以这样做

.cpu cortex-m3
.thumb
.syntax unified

.word 0x20001000
.word 0x20000001

理想情况下应该重新启动 crash/fault 但是如果您像我们之前那样使用 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
> 

是的,不像 expected/desired 那样快乐。

注意 _start 来自默认 linker 脚本中的 ENTRY(_start),它既不特殊也不真正 hard-coded 进入工具(也不是 gcc 的 main,它来自默认 bootstrap).

所以你可以这样做

so.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 警告消失了。请注意,您在 linker 脚本(在本例中为 hello)中创建的部分名称不必是 ram、rom、flash 等,它们可以是您想要的,是的,您可以使用 linker 脚本,但文件中没有 MEMORY 部分,只有 SECTION。

如果你选择

arm-none-eabi-objcopy -O binary so.elf so.bin

openocd 可以读取 elf 文件和其他一些文件,但是像这样的原始内存映像你必须指定地址,否则你可能会得到 0x00000000 或者谁知道是什么

load_image so.bin 0x20000000

If/when 你得到了一些核板,你可以简单地将 bin 文件复制到虚拟拇指驱动器,它会为你将它加载到目标 mcu 中,虚拟驱动器将重新加载或将重新加载如果它不起作用,则显示 FAIL.TXT 一种情况是,如果您 link 表示 0x00000000 而不是 0x08000000。你不能以这种方式加载 sram,只是闪存。但我假设你有一个蓝色药丸而不是核板。

答案很长。

简答

那些是 thumb2 扩展,它们的大小是两个半字。有关指令说明,请参阅 armv7-m 体系结构参考手册。它们非常适合这个芯片。

您可能希望在 openocd 上使用 load_image 而不是 mwh,但是如果您的半字顺序正确,mwh 将起作用。

理想情况下,您希望 link 尽管您或我编写的代码与位置无关,因此可以说您可以只提取说明并使用 mwh。

芯片有一个从 sram 模式启动,would/should 使用向量 table 不只是启动指令,你需要正确设置启动引脚并使用 openocd 之类的东西来加载程序进入 ram,然后重置(不是电源循环)。

MVN 负向移动或取反在这里不是正确的指令,您需要在使用 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 和 orr 或使用 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

The literal pool is an area of memory (in the text segment), which is used to store constants.

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-relative 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

在另一个片段中,它无法在组装时解决这个问题,所以它为 linker

留下了一个占位符
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

表示在 link 时间或某个时候将某物的地址加载到 r0 中。 标签只是地址,只是 values/numbers 所以

ldr r0,=0x12345678

意思是标签是值本身,所以给我那个标签的地址,即 0x12345678 并将其放入 r0,所以这是 gas 或某人想到的那个概念的有趣扩展,可能arms assembler,我不记得其他人也采用它或改进它或其他什么。请注意,如果你想自己做,你可以这样做

ldr r0,something_address
b .
.align
something_address: .word something

因为某物是一个标签,它是一个地址,它是一个值,您没有将等号放在那里,等号仅用于 ldr 指令。与向量相同 table:

.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 标签,%function 你可以看到它在向量中生成了正确的 thumb 地址 table,但在两者都未使用的情况下,未正确生成损坏的标签,因此矢量会在 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 所以 1 个字远离 pc,指令 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 个

然后继续阅读文档理解伪代码

Calculate the PC or Align(PC,4) value of the instruction. The PC value of an instruction is its address plus 4 for a Thumb instruction. The Align(PC,4) value of an instruction is its PC value ANDed with 0xFFFFFFFC to force it to be word-aligned.

所以 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