QEMU for AArch64:执行卡在ldr q1, [x0]导致yyy未打印的原因
问题:为何
ldr q1, [x0]导致程序无法打印yyy? 代码与环境
C代码
#include "uart.h" #include <string.h> char x[32]; __attribute__((noinline)) void foo(void) { strcpy(x, "xxxxxxxxxxxxxxxxxxxxxxxx"); } int main(void) { uart_puts("xxx\n"); foo(); uart_puts("yyy\n"); }
编译命令
$ aarch64-none-elf-gcc t78.c -mcpu=cortex-a57 -Wall -Wextra -g -O2 -c -std=c11 \ && aarch64-none-elf-ld -T linker.ld t78.o boot.o uart.o -o kernel.elf
运行命令
$ qemu-system-aarch64.exe -machine virt -cpu cortex-a57 -nographic -kernel kernel.elf
问题现象
程序运行后仅输出xxx,后续的yyy完全没有打印。
排查情况
- GCC并未生成调用
strcpy函数的代码,而是将strcpy的逻辑内联到了foo函数中 - 汇编代码里的
ldr q1, [x0]指令是问题根源:在这条指令前添加ret让foo直接返回后,yyy可以正常打印
foo函数的汇编代码
foo: .LFB0: .file 1 "t78.c" .loc 1 6 1 view -0 .cfi_startproc .loc 1 7 5 view .LVU1 adrp x0, .LC0 add x0, x0, :lo12:.LC0 adrp x1, .LANCHOR0 add x2, x1, :lo12:.LANCHOR0 ldr q1, [x0] <<== 问题根源 ldr q0, [x0, 9] str q1, [x1, #:lo12:.LANCHOR0] str q0, [x2, 9] .loc 1 8 1 is_stmt 0 view .LVU2 ret
工具版本
$ aarch64-none-elf-gcc --version aarch64-none-elf-gcc.exe (Arm GNU Toolchain 12.2.Rel1 (Build arm-12.24)) 12.2.1 20221205 $ qemu-system-aarch64 --version QEMU emulator version 7.2.0 (v7.2.0-11948-ge6523b71fc-dirty)
原因分析
这条ldr q1, [x0]指令触发了内存访问异常,导致程序直接崩溃,无法执行到后续的uart_puts("yyy\n"),具体原因如下:
- 字符串常量的存储位置:GCC会把字符串常量
"xxxxxxxxxxxxxxxxxxxxxxxx"放在.rodata(只读数据段)中,adrp x0, .LC0和add x0, x0, :lo12:.LC0这两条指令就是计算该字符串在内存中的地址。 - 裸机环境的内存映射问题:在当前裸机项目中,链接脚本
linker.ld可能没有将.rodata段正确映射到可访问的物理内存区域,或者启动代码(boot.o)没有配置MMU让CPU能正常访问该地址。 - 异常触发:当CPU执行
ldr q1, [x0]尝试读取.rodata段的内容时,由于该地址不可访问,会触发数据中止异常。而项目中没有安装对应的异常处理程序,CPU会直接进入错误状态,程序停止运行。
当在ldr q1, [x0]前添加ret指令时,foo函数直接返回,不会执行任何内存读取操作,自然不会触发异常,程序就能继续执行main函数中foo()之后的代码,成功打印yyy。
内容的提问来源于stack exchange,提问作者pmor
相关产品推荐
相关产品推荐

