对于背景,我在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 \
;
在另一个外壳中,如果我启动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
...
完美!我的ELF中的所有内容都位于0x40004000,而且我看到的全部都在内存中,这正是我的期望!我的第一个核心启动并按预期运行。
有趣的是,如果我将内存中零位置的内容转储了,那么那里就会有东西装载。我没有要求。我没有明确加载它。我不执行它不在我的ELF文件中。我不知道它是什么或它来自哪里。我的 GUESS 是QEMU假设我要在闪存中安装一些BIOS,并在其中放置了一个BIOS。我不确定它还将一些(小)放置在0x40000000。我也不知道那是什么...我要小心一点,如果我装载东西,我们将不会互相踩踏...
继续... 如果我更改命令行以将“ -kernel my_file.elf”开关替换为“ -bios my_file.elf”开关(不进行其他更改),然后重复执行run / 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
一切都为零。我看不到 MY 代码,尽管神秘代码仍在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 \
;
以下是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 . ..............
....
...但是当然...
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
答案 0 :(得分:2)
QEMU用于将代码加载到来宾的命令行选项多种多样,并且在体系结构之间甚至同一体系结构的机器类型之间通常具有不同的语义。这是不幸的,但是是与较旧的QEMU版本向后兼容的结果,并逐渐积累了“在这种情况下,对这种图像文件类型执行正确的操作会很好”的特殊情况。
广泛摘要:
-kernel是“加载Linux内核”选项。它将以最适合所使用体系结构的方式加载和引导内核。例如,对于x86 PC机,它将仅将文件提供给来宾BIOS,并依靠来宾BIOS将文件实际加载到RAM中。在Arm上,加载Linux内核意味着我们遵循内核制定的引导程序的规则(对于64位,https://www.kernel.org/doc/Documentation/arm64/booting.txt,对于32位,https://www.kernel.org/doc/Documentation/arm/Booting),我们可以通过少量的引导程序加载程序代码(这是您在低内存中看到的)。内核启动规则还要求我们在RAM中为其提供一个设备树blob,这是0x40000000处的数据。根据Linux内核引导的期望,我们还可以通过使辅助CPU保持PSCI关机状态或通过使用WFI循环的少量辅助CPU引导加载程序代码来处理辅助CPU,以便主处理器可以唤醒它们。 (我们的工作取决于所使用的电路板模型,因为我们做的是真实的电路板,特别是对于32位电路板,它的变化很大。)
作为一个奇怪的例外,对于Arm,如果将ELF文件传递给-kernel,我们将假定它不是Linux内核,并且将通过从ELF入口点开始进行引导。我们在RAM的底部提供DTB blob,但前提是它不会与加载的ELF文件重叠。 (除了:特别是对于“ virt”,您还是想要DTB,因为我们不保证在QEMU版本之间将设备保持在相同的物理地址中-DTB是我们告诉来宾代码应该在哪里寻找内容的方式。您可以依靠0x0的闪存和0x4000_0000开始的RAM,但实际上应该从DTB提取所有其他设备地址。在实践中,我们已尽力避免重新排列板卡内存映射,但是读取DTB是使来宾代码正确的做法做。)
-设备加载程序是“通用加载程序”,在任何体系结构上的行为都相同。它只是将ELF映像加载到来宾RAM中,并且不做任何更改CPU重置行为的操作。如果您有一个完全裸机的映像(包括异常向量表),并且希望以与硬件不重置相同的方式启动它,则这是一个不错的选择。
-bios是“以适合该计算机模型的任何方式加载bios映像”选项。同样,这是一种“按我的意思做”的选择,其具体内容因机器模型和机器模型以及架构而异。有些机器根本不支持它。如果用户未指定,某些计算机(例如x86 PC)将始终使用默认二进制文件加载BIOS。如果用户要求,一些将加载BIOS,但否则不会加载(手臂防病毒板是这样的)。通常,BIOS映像应该是“裸机原始二进制”映像,它将被加载到一些闪存或ROM存储器中,这与硬件从复位状态开始执行的位置相对应。在至少某些机器上,包括“ virt”,您可以使用“ -drive if = pflash,...”之类的命令行来提供Flash / ROM设备的内容。这是QEMU中常见模式的一个示例,您可以使用一个简短的“执行我的意思”选项,该选项很方便,但是背后有很多魔术,也可以使用一个较长的“正交”选项,您可以指定很多子选项,并获得您想要的行为。请注意,BIOS映像不应该是ELF文件,它们应该只是放入ROM中的原始数据。
其中许多未记录在案,因为“我想运行自己设计的裸机程序”是一个非常小众的用例,并且因为在我们的文档中没有很好的位置来使其易于记录不同电路板型号的详细信息。