QEMU "-bios" против "-kernel" против "-device loader, file = ..." - PullRequest
1 голос
/ 16 октября 2019

Для справки, я запускаю чистую версию QEMU-4.1.0 на aarch64.

Есть несколько способов заставить 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 во флэш-памяти и поместил туда. Я не знаю точно. Это также помещает что-то (маленькое) в 0x40000000. Я тоже не знаю, что это такое ... Я хочу быть осторожным, чтобы, если я что-то загрузил, мы не наступали друг на друга ...

  1. Тогда мой первый вопрос звучит так:Могу ли я включить некоторые сообщения отладки, чтобы понять, куда и где загружается QEMU, и, возможно, даже ПОЧЕМУ?

Продолжение ... Если я изменю свою командную строку на ЗАМЕНИТЕ переключатель -kernel my_file.elf с помощьюПереключатель «-bios 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

Все равно нулю. Я нигде не вижу MY кода, хотя загадочный код все еще загружается в 0x0 и 0x4000000. Как и следовало ожидать, ядра сразу же умирают с исключением «Undefined Instruction», как только я запускаю «nexti» в моем gdb.

Хммм ...

Хорошо, теперь яЗаменим "-bios my_file.elf" на "-device loader, file = my_file.elf". Я получаю тот же результат. Я не могу найти свой код в памяти.

Что происходит по-другому в QEMU между "-bios" и "-kernel"? Где это задокументировано или где в источнике я могу следить за этим? Как мне лучше всего это отладить?

Спасибо, сэр / мадам!


Редактировать:

Для отладки всеХороший / релевантный материал, кажется, находится в "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

1 Ответ

2 голосов
/ 17 октября 2019

Опции командной строки QEMU для загрузки кода в гостевую систему различны и часто имеют разную семантику между архитектурами или даже между типами компьютеров для одной и той же архитектуры. Это прискорбно, но является результатом обратной совместимости со старыми версиями QEMU и постепенным накоплением особых случаев «было бы неплохо сделать правильные вещи для этого типа файлов изображений».

Обобщенное резюме:

-kernel - это опция «загрузить ядро ​​Linux». Он загрузит и загрузит ядро ​​любым удобным для используемой архитектуры способом. Например, для компьютера с процессором x86 он просто передает файл в гостевой BIOS и полагается на гостевой BIOS для фактической загрузки файла в ОЗУ. На Arm загрузка ядра Linux означает, что мы следуем правилам, которые ядро ​​устанавливает для его загрузки (https://www.kernel.org/doc/Documentation/arm64/booting.txt для 64-разрядных или https://www.kernel.org/doc/Documentation/arm/Booting для 32-разрядных), и мы достигаем этогос небольшим количеством заглушки кода загрузчика (это то, что вы видите в нехватке памяти). Правила загрузки ядра также требуют, чтобы мы предоставили ему блоб дерева устройств в оперативной памяти, и это данные в 0x40000000. Мы также, в соответствии с ожиданиями загрузки ядра Linux, обрабатываем вторичные ЦП, удерживая их в выключенном состоянии PSCI или с помощью небольшого количества кода загрузчика вторичного ЦП, который использует цикл WFI, чтобы первичный мог их разбудить. (То, что мы делаем, зависит от используемой модели платы, потому что мы делаем то, что делает настоящая плата, что особенно для 32-битных плат сильно варьируется.)

Как исключение для странного шара, для Arm, если вы передаетеELF-файл для -kernel, мы предполагаем, что это , а не ядро ​​Linux, и загрузим его, просто начав с точки входа ELF. Мы предоставляем блоб DTB в основании ОЗУ, но только если он не перекрывается с загруженным файлом ELF. (Кроме того: для «virt», в частности, вы все равно хотите DTB, потому что мы не гарантируем, что устройства будут иметь одинаковые физические адреса между версиями QEMU - DTB - это то, как мы сообщаем гостевому коду, где он должен искать вещи. может полагаться на флэш-память в 0x0 и RAM, начинающуюся в 0x4000_0000, но на самом деле следует извлекать все другие адреса устройств из DTB. На практике мы предприняли усилия, чтобы избежать переупорядочения карты памяти платы, но чтение DTB - правильная вещь для гостевого кода дляdo.)

- загрузчик устройств - это «универсальный загрузчик», который ведет себя одинаково на любой архитектуре. Он просто загружает образ ELF в гостевую память и ничего не делает для изменения поведения сброса процессора. Это хороший выбор, если у вас есть полностью голое изображение, которое включает таблицу векторов исключений, и вы хотите, чтобы оно запускалось так же, как аппаратное обеспечение после сброса.

-bios - это «загрузитьОбраз биоса, каким бы ни казался хорошим вариантом для этой модели машины ». Опять же, это вариант «делай то, что я имею в виду», специфика которого варьируется от модели машины к модели машины и от архитектуры к архитектуре;некоторые машины вообще его не поддерживают. Некоторые машины (например, компьютер x86) всегда будут загружать BIOS, используя двоичный файл по умолчанию, если пользователь не указал. Некоторые загружают BIOS, если пользователь спрашивает, но не иначе (плата arm virt такая). Как правило, ожидается, что образ BIOS представляет собой двоичный образ «чистого металла», который будет загружен в некоторую флэш-память или ПЗУ, которая соответствует тому, где аппаратное обеспечение начинает выполнение, когда оно выходит из режима сброса. По крайней мере, на некоторых компьютерах, включая «virt», вы можете вместо этого предоставить содержимое устройств флэш-памяти / ROM с помощью командной строки, например «-drive if = pflash, ...». Это пример общего шаблона в QEMU, где вы можете использовать короткий вариант «делать то, что я имею в виду», который удобен, но в нем много магии, или более длинный «ортогональный» параметр, который позволяет указывать множествоподопции и получите именно то поведение, которое вы хотите. Обратите внимание, что образы BIOS не должны быть файлами ELF, они, как ожидается, будут просто необработанными данными для помещения в ПЗУ.

Многое из этого недокументировано, потому что "Я хочу запустить голую металлическую программу своего собственного изобретения" - это очень нишевый вариант использования, и потому что у нас нет хорошего места в нашей документации, чтобы упростить документированиеСпецифика различных моделей плат.

...