皮质 M33 缺失向量 table

Cortex M33 missing vector table

我想使用半主机在 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

我正在使用以下选项构建(来自 的修改工具链文件):

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.

您的来宾代码在启动时崩溃,这几乎总是因为您的异常向量问题 table。如果你使用 QEMU 的 -d 选项(例如 -d cpu,int,guest_errors,unimp,in_asm),这通常会提供更多关于究竟发生了什么的细节。

查看您的 ELF headers,您似乎没有将矢量 table 放入二进制文件中。 QEMU 需要这个(真正的硬件也是如此)。通常的方法是使用一个小的汇编源文件来布置数据 table 以及各种异常入口点的地址,尽管还有其他方法可以做到这一点。 (This is one example.)

你在 A-profile CPUs 上看不到这个的原因是 A-profile 异常处理是完全不同的:在 A-profile 上重置从地址 0x0 开始执行, 并且通过将 PC 设置为固定的低地址来采取类似的例外。在 M-profile 上,重置通过从向量 table 读取初始 PC 和 SP 值来工作,异常处理程序的起始地址也从向量 table 读取。 (也就是说,在 A-profile 上,魔术低地址是代码,而在 M-profile 上,它是数据,有效的函数指针)。

另请注意 QEMU -kernel 选项的行为在 A-profile 和 M-profile 之间是不同的:在 A-profile 上它会将 ELF 文件加载到内存中并接受 ELF 条目点(执行将从那里开始)。在 M-profile 上,它将加载 ELF 文件,然后以 hardware-specified 方式从重置开始 CPU,即不将 PC 设置为 ELF 入口点。 (这种变化主要是出于 historical/back-compat 原因。)如果你想“只加载我的 ELF 文件并将 PC 设置为其 ELF 入口点”,你应该使用 QEMU 的通用加载器设备,它在所有目标上的行为方式相同,并且not -kernel,这通常意味着“我是一个 Linux 内核,请将我加载到任何随机 target-specific 加上 do-what-I-mean 行为的最佳组合”。如果您尝试加载 bare-metal 二进制文件而不是实际的 Linux 内核,通常最好避免使用 -kernel。

This similar question about getting a working M-profile binary running on QEMU 也可能有帮助。