QEMU中裸机ARM的str指令无执行效果问题排查
我正在构建无操作系统、无引导加载程序的裸机应用,在QEMU中运行时发现str指令完全没有执行效果的异常。
我希望直接将程序注入RAM运行,参考修改了裸机链接脚本和启动文件startup.S来规划内存布局、搭建C环境,使用的是vexpress-a9平台(搭载cortex-a9处理器)。
我修改了启动文件,让程序直接从0x0地址的起始异常向量处执行(将其视为ROM),计划将.text段放在此处,完成.data、.bss和栈的初始化后跳转到main。
内存布局的链接脚本片段:
MEMORY { ROM (rx) : ORIGIN = 0x00000000, LENGTH = 1M RAM (rwx): ORIGIN = 0x00400000, LENGTH = 4M }
当前能正常启动QEMU、连接GDB并单步调试初始化代码,但本该在起始地址为0x00400000的"RAM"中完成的初始化完全没有生效。
以下汇编代码的目的是用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 <<<< 问题所在 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
内存未被更新,但我可以直接通过GDB修改该地址的值,说明内存本身可写:
>>> 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分配足够内存,完全不清楚问题原因,恳请帮助。
根据要求,补充以下实体的状态信息:
_fiq_stack_start的值?0x00400008—— 符合预期,FIQ栈应在8字节的.data段之后开始_fiq_stack_end的值?0x00401008—— 符合预期,我设置栈大小为4096字节cmp指令执行时r1的内容?r1 = 0x00400008—— 符合预期,r1应存储栈起始地址- sp寄存器的内容?
0x00401008—— 符合预期,这是栈的结束地址 strlt执行时的条件码位?比较前
CPSR = 0x40000111,比较后CPSR = 0x80000111。这符合预期,因为r1的值小于sp,有符号正数比较结果会将第31位置1。r0的内容?0xfefefefe—— 符合预期,通过两条mov指令将r0设为预期的栈填充值- 将
strlt改为str会发生什么?我已测试过,结果完全相同。
我还尝试了以下简单指令:
mov r0, #0x1234 mov r3, #0x2 str r0, [r3] /* 将R0的值存入r3指向的地址 */
执行后预期r0的值0x1234会存入r3指向的0x2地址,但实际并未生效:
>>> p/x $r0 $7 = 0x1234 >>> p/x $r3 $8 = 0x2 >>> x/2hx $r3 0x2 <_Reset+2>: 0xea00 0x0041
看起来str指令被完全忽略了。
内容的提问来源于stack exchange,提问作者i_am_human_probably

