c - 使用 ldr 从汇编器中的文字池中加载变量
问题描述
我想从文字池中加载变量。文字池位于 asm 文件的末尾。
literal_pool_label:
.WORD POOL_EVENT_CHANNEL_2_START_REG_ADDR
.WORD POOL_EVENT_CHANNEL_4_START_REG_ADDR
在我写的代码中:
adr r12, literal_pool_label
ldr r5, [r12, #0]
ldr r5, [r12, #4]
在 C 模块中,变量的定义如下:
const uint32_t POOL_EVENT_CHANNEL_2_START_REG_ADDR = 0x4100e030;
const uint32_t POOL_EVENT_CHANNEL_4_START_REG_ADDR = 0x4100e040;
如果我以下列方式在池中写入值是正确的。
.WORD 0x4100e030 // POOL_EVENT_CHANNEL_2_START_REG_ADDR
.WORD 0x4100e040 // POOL_EVENT_CHANNEL_4_START_REG_ADDR
我必须怎么做才能通过一条指令从变量中获取值?
解决方案
ARM 没有双重间接寻址模式,例如 pdp11 或其他我想不到的(msp430?)。
您的另一个问题是基于 cortex-m 的,也许这就是您尝试在 ram 中执行此操作的原因,您为此付出了很多努力,但没有解释为什么需要此功能以及是否会在项目中保存一条指令在一些成功与失败中。如果这是一个性能问题,那么还有其他方法可以解决这个问题,并且很可能代码(一条指令)不会以明显的方式提高性能。(实际上它会使情况变得更糟,取决于)。
所以
ldr r0,hello
ldr r1,world_addr
ldr r2,[r1]
b .
hello: .word 0x12345678
world_addr: .word world_data
.data
world_data: .word 0x87654321
Disassembly of section .text:
00001000 <hello-0x10>:
1000: e59f0008 ldr r0, [pc, #8] ; 1010 <hello>
1004: e59f1008 ldr r1, [pc, #8] ; 1014 <world_addr>
1008: e5912000 ldr r2, [r1]
100c: eafffffe b 100c <hello-0x4>
00001010 <hello>:
1010: 12345678 eorsne r5, r4, #120, 12 ; 0x7800000
00001014 <world_addr>:
1014: 00002000 andeq r2, r0, r0
Disassembly of section .data:
00002000 <__data_start>:
2000: 87654321 strbhi r4, [r5, -r1, lsr #6]!
可以很容易地要求在该部分中生成相对于 pc 的寻址。在该部分之外,您通常会执行相对于 pc 的地址加载,然后第二级间接是访问项目本身。
如果你尝试这个 gnu 汇编器会抱怨。
ldr r0,hello
ldr r1,world_addr
ldr r2,[r1]
ldr r3,world_data
b .
hello: .word 0x12345678
world_addr: .word world_data
.data
world_data: .word 0x87654321
现在是的,这在技术上是可行的,因为有一种相对于 pc 的寻址模式,如果你可以通过这种方式访问变量,那么你可以在一条指令中完成,这只是告诉汇编程序的问题。
1000: e59f0008 ldr r0, [pc, #8] ; 1010 <hello>
...
1010: 12345678 eorsne r5, r4, #120, 12 ; 0x7800000
范围更广。
.cpu cortex-m4
.thumb
ldr r0,hello
b .
.space 0x20000000
.align
world_data: .word 0x87654321
但是汇编程序抱怨。
由于您正在编写汇编语言,那么您和我在屏幕上打开了 arm 架构参考手册,您可以看到 thumb 编码允许 5 位偏移,而 thumb2 编码 12 位偏移(均已签名)最佳情况.
指定添加到值或从值中减去以形成地址的立即偏移量。允许的值是 0-124 范围内 4 的倍数,用于编码 T1,4 的倍数在 0-1020 范围内,用于编码 T2,0-4095 范围内的任何值,用于编码 T3,以及 0-255 范围内的任何值用于编码 T4。对于偏移寻址语法,
<imm>
可以省略,表示偏移量为 0。
cortex-m 代码低于 0x20000000,ram 高于 0x20000000 到某个限制,如 0x40000000。
如果您可以让汇编器和链接器一起工作来完成它(例如,它们可以使用分支指令),那么这比您在闪存中的单个指令中所能达到的要多。
所以ram解决方案,你标记了gnu,所以假设gnu binutils。
.cpu cortex-m4
.thumb
ldr r0,hello
b .
.align
hello: .word 0x87654321
.data
.word 0x12345
MEMORY
{
rom : ORIGIN = 0x00000000, LENGTH = 0x1000
ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > rom
.rodata : { *(.rodata*) } > rom
.bss : { *(.bss*) } > ram
.data : { *(.rodata*) } > ram AT > rom
}
Disassembly of section .text:
00000000 <hello-0x4>:
0: 4800 ldr r0, [pc, #0] ; (4 <hello>)
2: e7fe b.n 2 <hello-0x2>
00000004 <hello>:
4: 87654321
Disassembly of section .data:
20000000 <.data>:
20000000: 00012345
00000000 00 48 fe e7 21 43 65 87 45 23 01 00 |.H..!Ce.E#..|
0000000c
S00A0000736F2E7372656338
S30D000000000048FEE72143658775
S309000000084523010085
S70500000000FA
所以使用 .data 我们会看到类似的内容,您可以看到 .data 项在闪存中,然后您将标签/变量添加到链接器脚本,然后使用这些标签/变量复制编译时初始化的基于 ram 的项在执行主程序之前进行 ram(假设 C,但在您的情况下,如果这纯粹是一个汇编程序,您可以随时执行)。
.cpu cortex-m4
.thumb
.thumb_func
fun:
ldr r0,something
bx lr
.align
something: .word 0x11223344
MEMORY
{
rom : ORIGIN = 0x00000000, LENGTH = 0x1000
ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
.text : { so.o(.text*) } > rom
.rodata : { *(.rodata*) } > rom
.bss : { *(.bss*) } > ram
.data : { *(.rodata*) } > ram AT > rom
.fun : { fun.o(.text*) } > ram AT > rom
}
Disassembly of section .text:
00000000 <hello-0x4>:
0: 4800 ldr r0, [pc, #0] ; (4 <hello>)
2: e7fe b.n 2 <hello-0x2>
00000004 <hello>:
4: 87654321 strbhi r4, [r5, -r1, lsr #6]!
Disassembly of section .fun:
20000004 <fun>:
20000004: 46c0 nop ; (mov r8, r8)
20000006: 4770 bx lr
(srec)
S00A0000736F2E7372656338
S30D000000000048FEE72143658775
S309000000084523010085
S3090000000CC04670472D
S70500000000FA
与 .data 一样,您可以添加链接器变量并使用它们在执行之前将函数从闪存复制到 ram,理想情况下是在引导程序中,但如果这是一个没有 C 的纯 asm 程序,那么在您使用它之前的任何地方。
(不,这不是一个有效的 cortex-m 程序,只是演示了这些工具)
您可能希望从与您正在使用的 C 库关联的链接器脚本开始,因为链接器脚本和引导程序通常存在于固定设置中(SDK、工具链、C 库等的一部分)复制 .data并从那里开始,但要明白你会遇到问题,因为我已经从上面的臀部方式拍摄中解决了
.text : { *(.text*) } > ram
将要从其他部分/文件中获取所有 .text 。至少在我的情况下,首先出现的东西变得混乱(当你想控制向量表等时,你可能需要做更多的工作,而不是简单地将它放在 .text 中并将文件按顺序放在命令行上)。因此,与任何链接器脚本和引导程序工作一样,您必须迭代、构建和反汇编,直到获得它。
如果你的理由是性能(或感知性能),那么你可以像有人提到的那样用完 ram,但你可以在 ram 中运行整个项目,如果你有空间,这会让生活更轻松,你应该获得最佳的 fetch 性能,(虽然皮质-m7 可能不是最好的)。
.cpu cortex-m4
.thumb
ldr r0,hello
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
b .
.align
hello: .word 0x87654321
MEMORY
{
ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > ram
.rodata : { *(.rodata*) } > ram
.data : { *(.data*) } > ram
.bss : { *(.bss*) } > ram
}
Disassembly of section .text:
20000000 <hello-0x1c>:
20000000: 4806 ldr r0, [pc, #24] ; (2000001c <hello>)
20000002: 46c0 nop ; (mov r8, r8)
20000004: 46c0 nop ; (mov r8, r8)
20000006: 46c0 nop ; (mov r8, r8)
20000008: 46c0 nop ; (mov r8, r8)
2000000a: 46c0 nop ; (mov r8, r8)
2000000c: 46c0 nop ; (mov r8, r8)
2000000e: 46c0 nop ; (mov r8, r8)
20000010: 46c0 nop ; (mov r8, r8)
20000012: 46c0 nop ; (mov r8, r8)
20000014: 46c0 nop ; (mov r8, r8)
20000016: 46c0 nop ; (mov r8, r8)
20000018: 46c0 nop ; (mov r8, r8)
2000001a: e7fe b.n 2000001a <hello-0x2>
2000001c <hello>:
2000001c: 87654321 strbhi r4, [r5, -r1, lsr #6]!
几行 C 代码可以获取输出并生成
copybase: .word 0x20000000
copysize: .word 0x00000008
copydata:
.word 0x46C04806 @0x20000000
.word 0x46C046C0 @0x20000004
.word 0x46C046C0 @0x20000008
.word 0x46C046C0 @0x2000000C
.word 0x46C046C0 @0x20000010
.word 0x46C046C0 @0x20000014
.word 0xE7FE46C0 @0x20000018
.word 0x87654321 @0x2000001C
然后在同一个临时 C 程序或外部,您可以执行以下操作:
.cpu cortex-m4
.thumb
.syntax unified
.globl _start
_start:
.word 0x20001000
.word reset
.thumb_func
reset:
ldr r0,copybase
ldr r1,copysize
ldr r2,=copydata
.align
copy_loop:
ldr r3,[r0],#4
str r3,[r2],#4
subs r1,#1
bne copy_loop
ldr r0,copybase
orr r0,#1
bx r0
copybase: .word 0x20000000
copysize: .word 0x00000008
copydata:
.word 0x46C04806 @0x20000000
.word 0x46C046C0 @0x20000004
.word 0x46C046C0 @0x20000008
.word 0x46C046C0 @0x2000000C
.word 0x46C046C0 @0x20000010
.word 0x46C046C0 @0x20000014
.word 0xE7FE46C0 @0x20000018
.word 0x87654321 @0x2000001C
这甚至不一定需要链接器脚本 -Ttext=0 就足够了,但如果不需要的话
MEMORY
{
rom : ORIGIN = 0x00000000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > rom
}
gnu 链接器确实存在与此类问题相关的错误,因此链接器脚本更清晰。
在这两种情况下,链接器脚本以及 C 的引导程序都变得微不足道,如果您正确地制作它,您的引导程序可以是:
reset:
bl main
b .
对于基于 ram 的程序。
您的 fetch 性能通常是 sram 的一个时钟,其中 flash 很慢,并且当您在许多 mcus 上使用更快的处理器时钟速度时会变得更糟。
你得到你的单周期ldr。
如果在 armv6-m 而不是 armv7-m 上,那么这是一个简单的调整......复制/跳转显然不会按原样工作。
请注意,如果它只是 ldr 你在你可以这样做之后
ldr r0,something
...
something: .word 0x11223344
并且两者都将落在 .text 中,并且根据指令集和距离理想地与 pc 相关。以上都不是必需的。如果您想从其他地方读取该值并让此代码简单地读取它,那么是的,数据需要在 ram 中。