我想用一条ldr指令替换这个movw / movt组合:
movw r0, #0x3800
movt r0, #0x4002
|
V
ldr r0, =0x40023800但是,在此更改之后,代码将不再工作。因为这是我更改的唯一部分,所以一定是它有问题。我的印象是这两个代码段是相同的。
.syntax unified
.cpu cortex-m4
.thumbarm-none-eabi-as -mcpu=cortex-m4 code.s -c -o code.o
arm-none-eabi-gcc -T link.ld -nostartfiles -o result code.o以防万一,代码的其余部分与托管在this page (底部)的代码完全相同。
为什么这个更改会破坏我的代码?是否有替代的单行方法可以将32位立即值写入寄存器?
在汇编程序上运行objdump的结果显示如下(请注意<main>的开头和<loop>的结尾
Disassembly of section .text:
00000000 <main>:
0: 4810 ldr r0, [pc, #64] ; (44 <.loop+0x2a>)
2: f240 0101 movw r1, #1
[removed]
18: 6001 str r1, [r0, #0]
0000001a <.loop>:
1a: f240 0100 movw r1, #0
[removed]
44: 40023800 andmi r3, r2, r0, lsl #16发布于 2021-11-18 18:59:00
首先,汇编语言是特定于汇编器的(armasm、gas等),而不是目标程序(Cortex M4),它们可以而且肯定经常是跨工具的不兼容语言。Ldr通常用作伪指令,这意味着汇编程序决定为您使用哪条指令,而不是您请求的指令。
.cpu cortex-m4
.thumb
ldr r0,=0x12345678
ldr r1,=0x00000003
ldr r2,=0xFFFFFFF5
nop
nop
b .组装和拆卸
00000000 <.text>:
0: 4803 ldr r0, [pc, #12] ; (10 <.text+0x10>)
2: f04f 0103 mov.w r1, #3
6: f06f 020a mvn.w r2, #10
a: 46c0 nop ; (mov r8, r8)
c: 46c0 nop ; (mov r8, r8)
e: e7fe b.n e <.text+0xe>
10: 12345678 eorsne r5, r4, #120, 12 ; 0x7800000首先使用gnu汇编程序ldr r0,=语法是受支持的,不要期望所有的arm/thumb汇编程序都支持该语法。其次,使用gnu汇编程序(也许还有另一个答案中指出的其他程序),如果该工具可以创建一个实际上不执行ldr的优化,它将会。
我们可以看到,对于第二个和第三个常量,汇编程序使用了非加载指令,并将常量嵌入为立即数。
对于0x12345678值,您不能简单地将32位立即数放入32位(或16位)指令中,您必须进行加载,并且它通过找到一个池来放置常量并对其进行pc相对加载来实现。
汇编语言绝对不适合编译器,只有一些编译器使用汇编语言。如果没有汇编语言,我们就不会有当前的处理器,也不会有新的处理器供人类用于处理器的开发和测试。因此,非人类的asm意味着没有处理器。我们将无法引导高级编程语言,因此将不会有编程语言。不会有编译器,因为您需要精通汇编语言指令集的人员(即使编译器不能编译为asm)才能成功创建编译器,因此不会有编译器,但由于其他原因,目前还没有编译器。如果人类指令集级别的编程消失了,处理器和所有的副作用也就消失了。每一代人都要带着火炬去寻找和教导其他人。
我很幸运(好吧,这是计划好的),工作不是在这里完成的,这样如何:
.cpu cortex-m4
.thumb
ldr r0,=0x12345678
ldr r1,=0x00000003
ldr r2,=0xFFFFFFF5
nop
b .
Disassembly of section .text:
00000000 <.text>:
0: 4803 ldr r0, [pc, #12] ; (10 <.text+0x10>)
2: f04f 0103 mov.w r1, #3
6: f06f 020a mvn.w r2, #10
a: 46c0 nop ; (mov r8, r8)
c: e7fe b.n c <.text+0xc>
e: 56780000 ldrbtpl r0, [r8], -r0
12: Address 0x0000000000000012 is out of bounds.常量被放置在非单词对齐的边界上。它可能已经成功组装,但ldr是一个未对齐的传输,这可能会导致异常,代码将无法工作。
快速解决方案:
.cpu cortex-m4
.thumb
ldr r0,=0x12345678
ldr r1,=0x00000003
ldr r2,=0xFFFFFFF5
nop
b .
.align
Disassembly of section .text:
00000000 <.text>:
0: 4803 ldr r0, [pc, #12] ; (10 <.text+0x10>)
2: f04f 0103 mov.w r1, #3
6: f06f 020a mvn.w r2, #10
a: 46c0 nop ; (mov r8, r8)
c: e7fe b.n c <.text+0xc>
e: bf00 nop
10: 12345678 eorsne r5, r4, #120, 12 ; 0x7800000这招很管用。我们仍在假设池将去往何处,我们可以尝试强制它。
.cpu cortex-m4
.thumb
one:
ldr r0,=0x12345678
ldr r1,=0x00000003
ldr r2,=0xFFFFFFF5
b .
.align
two:
ldr r0,=0x11223344
b .
.align
Disassembly of section .text:
00000000 <one>:
0: 4803 ldr r0, [pc, #12] ; (10 <two+0x4>)
2: f04f 0103 mov.w r1, #3
6: f06f 020a mvn.w r2, #10
a: e7fe b.n a <one+0xa>
0000000c <two>:
c: 4801 ldr r0, [pc, #4] ; (14 <two+0x8>)
e: e7fe b.n e <two+0x2>
10: 12345678 eorsne r5, r4, #120, 12 ; 0x7800000
14: 11223344 ; <UNDEFINED> instruction: 0x11223344
.cpu cortex-m4
.thumb
one:
ldr r0,=0x12345678
ldr r1,=0x00000003
ldr r2,=0xFFFFFFF5
b .
.align
.ltorg
two:
ldr r0,=0x11223344
b .
.align
00000000 <one>:
0: 4802 ldr r0, [pc, #8] ; (c <one+0xc>)
2: f04f 0103 mov.w r1, #3
6: f06f 020a mvn.w r2, #10
a: e7fe b.n a <one+0xa>
c: 12345678 eorsne r5, r4, #120, 12 ; 0x7800000
00000010 <two>:
10: 4800 ldr r0, [pc, #0] ; (14 <two+0x4>)
12: e7fe b.n 12 <two+0x2>
14: 11223344 ; <UNDEFINED> instruction: 0x11223344pc的相对负载是正的,所以我们没有完全控制:
.cpu cortex-m4
.thumb
one:
ldr r0,=0x12345678
ldr r1,=0x00000003
ldr r2,=0xFFFFFFF5
nop
b .
two:
ldr r0,=0x11223344
b .
.align
.ltorg
00000000 <one>:
0: 4804 ldr r0, [pc, #16] ; (14 <two+0x6>)
2: f04f 0103 mov.w r1, #3
6: f06f 020a mvn.w r2, #10
a: 46c0 nop ; (mov r8, r8)
c: e7fe b.n c <one+0xc>
0000000e <two>:
e: 4802 ldr r0, [pc, #8] ; (18 <two+0xa>)
10: e7fe b.n 10 <two+0x2>
12: bf00 nop
14: 12345678 eorsne r5, r4, #120, 12 ; 0x7800000
18: 11223344 ; <UNDEFINED> instruction: 0x11223344我们不需要在两个之前对齐,两个可以在非32位边界上着陆,所以我们有时会在那里保存半字。.align (汇编语言是特定于汇编器的,而不是目标,这是gnu汇编器支持的语言,并为这个目标做了特定的事情)允许它在4字节边界上对齐,这样ldr就不会出错,而.ltorg实际上并没有改变这里的事情,只是表明这是我们想要的地方。您还必须了解与pc相关的负载可以工作多远,每个指令集(arm、mips、x86等)的范围各不相同,因此您不能在大型项目结束时只使用一个池。
为什么这对你来说不一定有效呢?我们需要看到和理解--你只是简单地改变了汇编语言并重新汇编,还是你在二进制文件中修改了一些指令?后者有很多问题,包括指令大小以及如何将项目填充到池中。如果它只是代码,并且你组装了它,那么它很可能是未对齐的,你可能会得到一个未对齐的访问错误。
但是ldr rd,=可以覆盖所有可能的位模式,并且movw/movt也可以。但是ldr rd,= on gnu汇编程序会在可能的情况下进行优化,否则它需要池,并且池需要为它做好准备。如果你手工创建movw/movt,你所需要的就是这两条指令,没有池。
您需要创建一个更完整的示例,并定义“不再有效”的含义。
发布于 2021-11-16 17:26:43
ldr是一条伪指令,可转换为从池中加载。
LDR Rd,=const伪指令生成加载任意32位数的最有效的单指令。您可以使用此伪指令生成超出MOV与MVN指令范围的常量。LDR伪指令为指定的立即值生成最有效的单指令:
如果可以使用单个MOV或MVN指令构造立即值,则汇编程序会生成相应的指令。如果不能使用单个MOV或MVN指令构造立即值,则汇编程序:将该值放入文字池(嵌入在代码中以保存常量值的内存的一部分)中。生成具有PC相对地址的LDR指令,该指令从文字池中读取常量。例如:
LDR rn,pc,#偏移量到文字池;用一个字加载寄存器n;从地址pc + offset
您必须确保在汇编程序生成的LDR指令的范围内有一个文字池。
https://www.keil.com/support/man/docs/armasm/armasm_dom1359731147386.htm
https://stackoverflow.com/questions/69992454
复制相似问题