说明
我正在尝试构建一个裸机应用程序(无操作系统,无引导加载程序)并在QEMU中运行它,但我发现str
指令似乎没有任何作用,出现了一些奇怪的行为。
对于某些上下文,我只想将程序直接注入RAM并运行它。我使用了一个经过修改的裸机linker和startup.S作为内存布局和C环境设置的示例。我并不关心我使用的是哪个ARM平台,所以我使用了他们示例中的同一个平台,即采用cortex-a9处理器的vexpress-a9。
我修改了启动文件,以便直接在0x0
的启动异常向量处开始执行(我将其视为ROM,尽管我知道它不是)。我们的想法是把.text
部分放在这里,一些设置碰巧设置了.data
、.bss
和堆栈,然后我分支到main
。
MEMORY
{
ROM (rx) : ORIGIN = 0x00000000, LENGTH = 1M
RAM (rwx): ORIGIN = 0x00400000, LENGTH = 4M
}
字符串
这实际上是可行的,因为我可以启动QEMU,附加一个gdb会话,并逐步执行初始化代码,但对于应该在“RAM”中进行的设置(从0x00400000
开始),根本没有初始化任何内容。
对于下面的部分汇编,我的想法是用0xFEFEFEFE
填充FIQ堆栈部分。因此,我将r1
设置为堆栈的开始,将sp
设置为堆栈的结束,当r1
<sp
时,我使用r0
中的值填充r1
中包含的地址,并将r1
中的地址递增4个字节。
Reset_Handler:
/* FIQ stack */
msr cpsr_c, MODE_FIQ
ldr r1, =_fiq_stack_start
ldr sp, =_fiq_stack_end
movw r0, #0xFEFE
movt r0, #0xFEFE
fiq_loop:
cmp r1, sp
strlt r0, [r1], #4 <<<< ISSUE HERE
blt fiq_loop
型
对于正确的迭代次数(堆栈大小),这确实可以正确地循环,但对于strlt r0, [r1], #4
指令,* 什么都没有发生 *。
如果我在str
指令之前检查,r1
是堆栈的开始,值是0x0
:
>>> p/x $r1
$2 = 0x400008
>>> x/2hx $r1
0x400008: 0x0000 0x0000
型
跳过str
指令后,r1
移动了4个字节,但堆栈开始处的内存仍然是0x0
:
>>> p/x $r1
$3 = 0x40000c
>>> x/2hx 0x400008
0x400008: 0x0000 0x0000
型
内存不会更新,但我 * 可以 * 直接在那里设置值,因此我知道它 * 可以 * 更新:
>>> set *(0x400008)=0x12345678
>>> x/2hx 0x400008
0x400008: 0x5678 0x1234
型
我用以下命令启动qemu:
qemu-system-arm \
-nographic \
-s \
-S \
--no-reboot \
-machine vexpress-a9 \
-cpu cortex-a9 \
-m 12M \
-device loader,file=out.elf
型
我已经使用-mcpu=cortex-a9
选项进行了编译,并且相信我已经为QEMU提供了足够的RAM。我真的不知道这里发生了什么,任何帮助都是感激不尽的。
进一步调试
根据要求,我还对以下实体的状态进行了澄清:
_fiq_stack_start
的值是什么?
0x00400008
<-这是我所期望的,因为我期望fiq堆栈在.data部分之后开始,该部分包含8个字节
_fiq_stack_end
的值是多少?
0x00401008
<-这是我所期望的,因为我指定堆栈为4096字节
cmp
指令执行时r1
的内容是什么?
r1 = 0x00400008
<-这是我所期望的,因为r1应该包含堆栈的开始。
- sp寄存器的内容是什么?
0x00401008
<-这是我所期望的,因为这应该是堆栈的结尾
strlt
启动时的条件码位是什么?
在比较CPSR = 0x40000111
之前和比较CPSR = 0x80000111
之后。这是预期的,因为r1
中的值小于sp
的值,并且正符号比较的结果应将1置于位31中。
r0
的内容是什么呢?
0xfefefefe
<-根据两条mv
指令,这是我所期望的,用我想在堆栈中的值填充r0
寄存器。
- 如果将
strlt
更改为str
,会发生什么情况?
实际上我已经测试过了,我得到了同样的行为。
我也尝试过这些简单的指令:
mov r0, #0x1234
mov r3, #0x2
str r0, [r3] /* Store value of R0 into addr at r3 */
型
在单步执行每条指令之后,我希望将r0
中保存的值0x1
放入r3
中保存的0x2
的内存地址中。但经过检查却不是。
>>> p/x $r0
$7 = 0x1
>>> p/x $r3
$8 = 0x2
>>> x/2hx $r3
0x2 <_Reset+2>: 0xea00 0x0041
型
这就好像str指令被完全忽略了。
2条答案
按热度按时间lnvxswe21#
“当我试图存储到内存中时,就好像什么都没有发生”几乎总是意味着“我试图存储到实际上没有RAM的地方”。有时这是“什么都没有”,有时这是“有闪存或ROM在那里,所以写入被忽略”。根本原因通常是“程序链接到错误的地址”。
这些地址:
字符串
与vexpress-a9的QEMU型号不匹配。第一个是可以的(地址0是可重Map区域,QEMU将其建模为“始终是闪存,而不是客户运行时可配置的”),但是在0x 00400000处没有RAM--这个地址在闪存内部,所以它不像RAM那样可写。
您应该使用链接器Map,将RAM区域放置在内存Map称为“Local DDR2”的区域中,该区域从0x6000_0000开始。您开始的教程中的链接器脚本可以正确地实现这一点,因为它使用了0x6000_0000和0x7000_0000 --尽管注意,这只是因为该教程中的文档说使用-m 512 M,它提供了足够的RAM来达到0x7000_0000内存范围。
您的链接器脚本碰巧在xilinx-zynq-a9板上工作的原因是,该板将其RAM块放在地址0。
这里我认为重要的是要理解的是,当你写一个链接器脚本时,你不能随意选择地址。必须编写链接器脚本,以匹配您要在其上运行结果裸机二进制文件的电路板的地址Map,然后该二进制文件将不会在不同的电路板类型上运行。
(顺便说一句,这块板上有一个QEMU错误,我们试图在地址0MapRAM和闪存--我认为闪存实际上“赢了”,所以结果是低地址区域是闪存。)
biswetbf2#
我找到了一个答案,并让它工作。我从使用
vxexpress-a9
机器切换到使用xilinx-zynq-a9
机器(一种不同的cortex-a9设备),一切都开始工作了。所以我现在开始:字符串
我相信问题是我在重置向量处直接将.elf注入内存,就像我想要的那样,但是对于
vxexpress-a9
板来说,这可能无效。I found a memory map description,这表明我使用的内存部分可以重新Map,所以也许它不是作为DDR2 RAM?也就是说,QEMU说它是doesn't model theSCC
,这就是文档所说的允许配置该内存区域。老实说,我不是100%确定,但它似乎是我使用的设备。我猜想,我正在使用的示例使用u-boot来设置硬件并从模拟的SD卡加载映像,这意味着它适用于该示例。这一定是模拟硬件所期望的,但我不想这样做。
x1c 0d1x的数据