作为背景,我在 aarch64 上运行裸机 QEMU-4.1.0。
有几种方法可以让 QEMU 将编译后的代码加载到内存中。我想了解潜在的差异是什么,因为我看到了非常不同的行为,并且文档没有说明。
考虑这个第一个命令行:
qemu-system-aarch64 \
-s -S \
-machine virt,secure=on,virtualization=on \
-m 512M \
-smp 4 \
-display none \
-nographic \
-semihosting \
-serial mon:stdio \
-kernel my_file.elf \
-device loader,addr=0x40004000,cpu_num=0 \
-device loader,addr=0x40004000,cpu_num=1 \
-device loader,addr=0x40004000,cpu_num=2 \
-device loader,addr=0x40004000,cpu_num=3 \
;
Run Code Online (Sandbox Code Playgroud)
在另一个 shell 中,如果我启动 gdb 以查看 QEMU 已加载到内存中的内容,则它完全符合我的预期。事实上,gdb 有一个内置的命令用于这个......
(gdb) compare-sections
Section .start, range 0x40004000 -- 0x40006164: matched.
Section .vectors, range 0x40006800 -- 0x40006f90: matched.
Section .text, range 0x40006fc0 -- 0x4002ca7c: matched.
...
Section .stacks, range 0x4207c120 -- 0x420bc120: matched.
(gdb) x/10x 0x40004000
0x40004000 <_start>: 0x14000800 0x00000000 0x00000000 0x00000000
...
Run Code Online (Sandbox Code Playgroud)
完美的!我的 ELF 中的所有内容都在 0x40004000 处,我在内存中看到了所有内容,这正是我所期望的!我的第一个核心启动并按我的预期运行。
有趣的是,如果我转储内存中位置零处的内容,那么那里就会加载一些内容。我没有要求。我没有明确加载它。我不执行。它不在我的 ELF 文件中。我不知道它是什么,也不知道它来自哪里。我的猜测是 QEMU 假设我想要一些 BIOS 在闪存中,并在那里放了一个。我不确定。它还在 0x40000000 处放置了一些东西(小)。我也不知道那是什么......我确实要小心,如果我加载了一些东西,我们不会互相踩踏......
继续...如果我将命令行更改为用“-bios my_file.elf”开关替换“-kernel my_file.elf”开关(不更改其他任何内容),并且我重复运行/gdb,那么我会看到两件事是不同的...
首先,我看到我的所有内核都在运行。我不需要使用 PSCI 调用来启动它们。好的,但我认为这与我的问题无关。其次(也是非常重要的)是我的记忆不包含我所期望的!
(gdb) compare-sections
Section .start, range 0x40004000 -- 0x40006164: MIS-MATCHED!
Section .vectors, range 0x40006800 -- 0x40006f90: MIS-MATCHED!
Section .text, range 0x40006fc0 -- 0x4002ca7c: MIS-MATCHED!
...
Section .stacks, range 0x4207c120 -- 0x420bc120: matched.
(gdb) x/8x 0x40000000
0x40004000 <_start>: 0x00000000 0x00000000 0x00000000 0x00000000
0x40004010 <_start+16>: 0x00000000 0x00000000 0x00000000 0x00000000
(gdb) x/8x 0x40006800
0x40006800 <my_vector_name>: 0x00000000 0x00000000 0x00000000 0x00000000
0x40006810 <my_vector_name+16>: 0x00000000 0x00000000 0x00000000 0x00000000
(gdb) x/8x 0x40006fc0
0x40006800 <my_symbol_name>: 0x00000000 0x00000000 0x00000000 0x00000000
0x40006810 <my_symbol_name+16>: 0x00000000 0x00000000 0x00000000 0x00000000
Run Code Online (Sandbox Code Playgroud)
一切都是零。我在任何地方都看不到我的代码,尽管神秘代码仍在 0x0 和 0x4000000 处加载。正如您可能还期望的那样,一旦我在 gdb 中发出“nexti”命令,内核就会立即因“未定义指令”异常而死亡。
嗯...
好的,现在我将“-bios my_file.elf”更改为“-device loader,file=my_file.elf”。我得到相同的结果。我在内存中找不到我的代码。
谢谢好心的先生/女士!
编辑:
对于调试,所有好的/相关的东西似乎都在“virt.c”中......
更多编辑(添加来自“-device loader=my_file.elf”的信息)
我的命令行是:
/tools/gnu/qemu-4.1.0/bin/qemu-system-aarch64 \
-s -S \
-machine virt,secure=on,virtualization=on \
-cpu cortex-a53 \
-d int \
-m 512M \
-smp 4 \
-display none \
-nographic \
-semihosting \
-serial mon:stdio \
-device loader,file=NEW_AT_ZERO.elf \
;
Run Code Online (Sandbox Code Playgroud)
下面是 NEW_AT_ZERO.dis 的一些相关部分:
NEW_AT_ZERO.elf: file format elf64-littleaarch64
NEW_AT_ZERO.elf
architecture: aarch64, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x0000000000000000
Program Header:
LOAD off 0x0000000000010000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**16
filesz 0x00000000020b8120 memsz 0x00000000020b8120 flags rwx
NOTE off 0x0000000000043484 vaddr 0x0000000000033484 paddr 0x0000000000033484 align 2**2
filesz 0x0000000000000024 memsz 0x0000000000000024 flags r--
private flags = 0:
Sections:
Idx Name Size VMA LMA File off Algn
0 .start 00002164 0000000000000000 0000000000000000 00010000 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .vectors 00000790 0000000000002800 0000000000002800 00012800 2**11
CONTENTS, ALLOC, LOAD, READONLY, CODE
2 .text 00025bbc 0000000000002fc0 0000000000002fc0 00012fc0 2**6
CONTENTS, ALLOC, LOAD, READONLY, CODE
3 .bss 0000a904 0000000000028b80 0000000000028b80 00038b7c 2**3
ALLOC
....
Contents of section .start:
0000 00080014 00000000 00000000 00000000 ................
....
1000 fd170094 a00038d5 01044092 020c7892 ......8...@...x.
1010 261842aa 660000b5 00038052 00005ed4 &.B.f......R..^.
1020 7f2003d5 ffffff17 00000000 00000000 . ..............
....
Run Code Online (Sandbox Code Playgroud)
...但是当然...
GNU gdb (Linaro_GDB-2017.05.09) 7.12.1.20170417-git
Copyright (C) 2017 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=x86_64-unknown-linux-gnu --target=aarch64-none-elf".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Remote debugging using localhost:1234
warning: No executable has been specified and target does not support
determining executable automatically. Try using the "file" command.
0x0000000000000000 in ?? ()
Reading symbols from ./NEW_AT_ZERO.elf...done.
(gdb) compare_sections
Undefined command: "compare_sections". Try "help".
(gdb) compare-sections
Section .start, range 0x0 -- 0x2164: MIS-MATCHED!
Section .vectors, range 0x2800 -- 0x2f90: MIS-MATCHED!
Section .text, range 0x2fc0 -- 0x28b7c: MIS-MATCHED!
....
Section .stacks, range 0x2078120 -- 0x20b8120: matched.
warning: One or more sections of the target image does not match
the loaded file
(gdb) x/4x 0
0x0 <_start>: 0x00000000 0x00000000 0x00000000 0x00000000
(gdb) x/12x 0x1000
0x1000 <symbol>: 0x00000000 0x00000000 0x00000000 0x00000000
0x1010 <symbol+16>: 0x00000000 0x00000000 0x00000000 0x00000000
0x1020 <end_symbol>: 0x00000000 0x00000000 0x00000000 0x00000000
Run Code Online (Sandbox Code Playgroud)
Pet*_*ell 16
QEMU 用于将代码加载到来宾中的命令行选项是多种多样的,并且通常在架构之间甚至同一架构的机器类型之间具有不同的语义。这是不幸的,但这是与旧 QEMU 版本向后兼容的结果,以及“为这种图像文件类型做正确的事情会很好”特殊情况的逐渐积累的结果。
概括总结:
-kernel 是“加载 Linux 内核”选项。它将以最适合所用架构的任何方式加载和引导内核。例如,对于 x86 PC 机器,它只会将文件提供给来宾 BIOS,并依靠来宾 BIOS 将文件实际加载到 RAM 中。在 Arm 上,加载 Linux 内核意味着我们遵循内核为如何启动它制定的规则(https://www.kernel.org/doc/Documentation/arm64/booting.txt用于 64 位或https:/ /www.kernel.org/doc/Documentation/arm/Booting对于 32 位),我们通过一些存根引导加载程序代码实现了这一点(这是您在低内存中看到的)。内核引导规则还要求我们在 RAM 中为它提供一个设备树 blob,这是 0x40000000 处的数据。我们还根据 Linux 内核引导预期,通过将辅助 CPU 保持在 PSCI 断电状态或通过使用 WFI 循环的少量辅助 CPU 引导加载程序代码来处理辅助 CPU,以便主 CPU 可以唤醒它们。(我们做什么取决于所使用的板卡型号,因为我们做的是真正的板卡,特别是对于 32 位板卡变化很大。)
作为一个奇怪的例外,对于 Arm,如果您将 ELF 文件传递给 -kernel,我们将假定它不是Linux 内核,并且将通过仅从 ELF 入口点启动来启动它。我们在 RAM 的基础上提供 DTB blob,但前提是它不会与加载的 ELF 文件重叠。(另外:对于 'virt' 特别是你想要 DTB,因为我们不保证在 QEMU 版本之间保持设备在相同的物理地址——DTB 是我们告诉客户代码它应该在哪里寻找东西的方式。你可以依靠 0x0 处的闪存和 0x4000_0000 处开始的 RAM,但确实应该从 DTB 中提取所有其他设备地址。在实践中,我们已经努力避免重新排列板内存映射,但读取 DTB 对访客代码来说是正确的做。)
-device loader 是“通用加载器”,它在任何架构上的行为都相同。它只是将一个 ELF 映像加载到客户 RAM 中,并没有做任何改变 CPU 重置行为的事情。如果您有一个包含异常向量表的完全裸机映像并希望它以硬件不复位的相同方式启动,这是一个不错的选择。
-bios 是“加载 bios 映像,无论以何种方式对该机器型号似乎都有利”选项。同样,这是一种“按我的意思行事”的选项,其细节因机器模型和机器模型而异;有些机器根本不支持它。某些机器(例如 x86 PC)将始终加载 bios,如果用户未指定,则使用默认二进制文件。如果用户要求,有些会加载 bios,但不是其他情况(arm virt 板就是这样)。通常,BIOS 映像应该是“裸机原始二进制”映像,它将被加载到一些闪存或 ROM 存储器中,这对应于硬件在复位后开始执行的位置。至少在某些机器上,包括“virt”,您可以改为使用“-drive if=pflash,...”之类的命令行来提供闪存/ROM 设备的内容。这是 QEMU 中常见模式的一个示例,您可以使用简短的“按我的意思做”选项,该选项很方便但背后有很多魔力,或者使用更长的“正交”选项,让您指定很多子选项并获得您想要的行为。请注意,BIOS 映像不应是 ELF 文件,它们应该只是放入 ROM 的原始数据。选项,它允许您指定许多子选项并获得您想要的行为。请注意,BIOS 映像不应是 ELF 文件,它们应该只是放入 ROM 的原始数据。选项,它允许您指定许多子选项并获得您想要的行为。请注意,BIOS 映像不应是 ELF 文件,它们应该只是放入 ROM 的原始数据。
其中很多是未记录的,因为“我想运行我自己设计的裸机程序”是一个非常小众的用例,而且因为我们的文档中没有很好的位置来轻松记录不同的板型号。
| 归档时间: |
|
| 查看次数: |
4902 次 |
| 最近记录: |