我想使用半主机在 QEMU 中测试我的 ARM 项目。最初我为 Cortex A7 和 A9 处理器构建并且运行我的代码没有问题,但是现在我切换到 CM33(和 CM33 板),它立即中断:
C:\Program Files\qemu>qemu-system-aarch64.exe -nographic -machine musca-a -cpu cortex-m33 -monitor none -serial stdio
-kernel app -m 512 -semihosting
qemu: fatal: Lockup: can't escalate 3 to HardFault (current priority -1)
R00=00000000 R01=00000000 R02=00000000 R03=00000000
R04=00000000 R05=00000000 R06=00000000 R07=00000000
R08=00000000 R09=00000000 R10=00000000 R11=00000000
R12=00000000 R13=ffffffe0 R14=fffffff9 R15=00000000
XPSR=40000003 -Z-- A S handler
FPSCR: 00000000
如果我理解正确, PC=00000000
表示重置处理程序问题。我想也许这个 musca-a
板期望 table 在其他地方,但看起来它完全丢失了:
psykana@psykana-lap:~$ readelf app -S
There are 26 section headers, starting at offset 0xb1520:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .init PROGBITS 00008000 008000 00000c 00 AX 0 0 4
[ 2] .text PROGBITS 00008010 008010 01d5b4 00 AX 0 0 8
[ 3] .fini PROGBITS 000255c4 0255c4 00000c 00 AX 0 0 4
[ 4] .rodata PROGBITS 000255d0 0255d0 003448 00 A 0 0 8
[ 5] .ARM.exidx ARM_EXIDX 00028a18 028a18 000008 00 AL 2 0 4
[ 6] .eh_frame PROGBITS 00028a20 028a20 000004 00 A 0 0 4
[ 7] .init_array INIT_ARRAY 00038a24 028a24 000008 04 WA 0 0 4
[ 8] .fini_array FINI_ARRAY 00038a2c 028a2c 000004 04 WA 0 0 4
[ 9] .data PROGBITS 00038a30 028a30 000ad8 00 WA 0 0 8
[10] .persistent PROGBITS 00039508 029508 000000 00 WA 0 0 1
[11] .bss NOBITS 00039508 029508 0001c4 00 WA 0 0 4
[12] .noinit NOBITS 000396cc 000000 000000 00 WA 0 0 1
[13] .comment PROGBITS 00000000 029508 000049 01 MS 0 0 1
[14] .debug_aranges PROGBITS 00000000 029551 000408 00 0 0 1
[15] .debug_info PROGBITS 00000000 029959 02e397 00 0 0 1
[16] .debug_abbrev PROGBITS 00000000 057cf0 005b3e 00 0 0 1
[17] .debug_line PROGBITS 00000000 05d82e 01629f 00 0 0 1
[18] .debug_frame PROGBITS 00000000 073ad0 004bf4 00 0 0 4
[19] .debug_str PROGBITS 00000000 0786c4 006a87 01 MS 0 0 1
[20] .debug_loc PROGBITS 00000000 07f14b 01f27e 00 0 0 1
[21] .debug_ranges PROGBITS 00000000 09e3c9 009838 00 0 0 1
[22] .ARM.attributes ARM_ATTRIBUTES 00000000 0a7c01 000036 00 0 0 1
[23] .symtab SYMTAB 00000000 0a7c38 006ec0 10 24 1282 4
[24] .strtab STRTAB 00000000 0aeaf8 002927 00 0 0 1
[25] .shstrtab STRTAB 00000000 0b141f 000100 00 0 0 1
我正在使用以下选项进行构建(来自 https://stackoverflow.com/questions/72199730/qemu-semihosting-doesnt-produce-output 的修改工具链文件):
add_compile_options(
-mcpu=cortex-m33
-specs=rdimon.specs
-O0
-g
-mfpu=fpv5-sp-d16
-mfloat-abi=hard
)
add_link_options(-specs=rdimon.specs -mcpu=cortex-m33 -mfpu=fpv5-sp-d16 -mfloat-abi=hard)
同样,这对我尝试过的所有 A 处理器都很好,但对 CM33 来说就中断了。事实上,它对任何 M 核和 M 核 QEMU 板都中断。
作为记录:
- arm-none-eabi-gcc (GNU Arm Embedded Toolchain 10.3-2021.10)
- QEMU emulator version 7.0.0 (v7.0.0-11902-g1d935f4a02-dirty)
- Microsoft Windows [Version 10.0.19044.1645]
- cmake version 3.22.
回答1
您的客户代码在启动时崩溃了,这几乎总是因为您的异常向量 table 存在问题。如果您使用 QEMU 的 -d 选项(例如 -d cpu,int,guest_errors,unimp,in_asm),这通常会更详细地说明究竟发生了什么。
查看您的 ELF 标头,您似乎没有将向量 table 放入二进制文件中。 QEMU 需要这个(就像真正的硬件一样)。执行此操作的常用方法是使用一个小汇编源文件,其中列出数据 table 以及各种异常入口点的地址,尽管还有其他方法可以做到这一点。 (https://git.linaro.org/people/peter.maydell/semihosting-tests.git/tree/start-microbit.S。)
您在 A-profile CPU 上看不到这一点的原因是 A-profile 异常处理完全不同:在 A-profile 上,重置从地址 0x0 开始执行,并且通过将 PC 设置为固定的低地址来处理类似的异常。在 M-profile 上,复位通过从向量 table 读取初始 PC 和 SP values 来工作,异常处理程序从也从向量 table 读取的地址开始。 (也就是说,在 A-profile 上,魔术低地址的东西是代码,而在 M-profile 上,它是数据,实际上是函数指针)。
另请注意,QEMU -kernel 选项的行为在 A-profile 和 M-profile 之间是不同的:在 A-profile 上,它会将 ELF 文件加载到内存中并遵循 ELF 入口点(从那里开始执行)。在 M-profile 上,它将加载 ELF 文件,然后以硬件指定的方式从复位启动 CPU,即不将 PC 设置为 ELF 入口点。 (这种变化本质上是出于历史/向后兼容的原因。)如果你想“只加载我的 ELF 文件并将 PC 设置为其 ELF 入口点”,你应该使用 QEMU 的通用加载器设备,它的行为方式相同所有目标,而不是 -kernel,这通常意味着“我是 Linux 内核,请以任何随机的目标特定加上我的意思行为的组合似乎最好的方式加载我”。如果您尝试加载 bare-metal 二进制文件而不是实际的 Linux 内核,通常最好避免使用 -kernel。
https://stackoverflow.com/questions/71486314/loading-an-elf-file-into-qemu/71487910#71487910 上运行 M-profile 二进制文件的类似问题也可能会有所帮助。