Обновить

В заголовке моего ELF есть точка входа. Оказалось, её никто не читает

Уровень сложностиСредний
Время на прочтение10 мин
Охват и читатели6K
Всего голосов 4: ↑2 и ↓20
Комментарии6

Комментарии 6

Вроде как -device loader в qemu должен честно грузить и запускать ELF согласно заголовкам.

Не вроде, а так и есть.

Loading Files
^^^^^^^^^^^^^

The loader device also allows files to be loaded into memory. It can load ELF,
U-Boot, and Intel HEX executable formats as well as raw images.

Более того в данном случае можно запускать с ключём -bios, тоже будет работать.

Если запускать бинарник c произвольным entry point, тогда:

-device loader,addr=<address>,file=<file>,cpu-num=0 -device loader,addr=<entry_point>,cpu-num=0 

Что имел ввиду автор я после первого прочтения не понял.

https://github.com/riscv-software-src/riscv-tests без проблем запускаются с ключом -bios=файл или через loader.

Проверил -bios, работает, спасибо.
Статья вот про что: машина стартует туда, куда зашито, и файл на это не влияет, ровно как на кристалле (по крайней мере я на это надеюсь). Ради этого весь стенд и стоит на -bios none. А -bios и loader с cpu-num=0 это возможности эмулятора, которых у железа нет: они дают файлу распорядиться тем, откуда машина стартует. В статье это надо было сказать прямо, а одна фраза там и вовсе говорит «QEMU не читает» вместо «-kernel не читает». Допишу дополнение в текст.

Проверил, вы правы: с cpu-num=0 точка входа читается. Одну фразу в статье надо было сказать точнее, там «QEMU не читает», а верно «-kernel не читает».

А пока проверял, нашлось кое-что поинтереснее. Вот слово по 0x1018, которое переходник машины читает перед прыжком:

-bios none -kernel hello.elf    0x80000000
-bios none -kernel high.elf     0x80000000
-bios hello.elf                 0x80000000
-bios high.elf                  0x80100000

hello.elf собран по 0x80000000, high.elf по 0x80100000. Под -kernel переходник машины не меняется вообще: какой файл ни дай, управление уйдёт туда, куда зашито. А -bios этот переходник переписал адресом из заголовка.

Похоже, в этом и вся разница. -bios и loader с cpu-num=0 дают файлу распорядиться тем, откуда машина стартует, а на кристалле адрес сброса зашит в железо. Стенд у меня стоит на -bios none как раз ради этого, и читать заголовок там просто некому.

Досюда всё про QEMU, а на плате читать заголовок обычно уже некому: objcopy -O binary срезает их физически, у меня из 5824 байт ELF остаётся 472 байта прошивки, и сигнатуры \x7fELF в ней нет. Правда, -O ihex и -O srec точку входа всё-таки несут, отдельной записью (:040000058000001C5B и S7058000001C5E), так что зависит от формата. А дальше от того, кто грузит: U-Boot в bootelf прыгает именно на e_entry (lib/elf.c, return ehdr->e_entry), а вот load_image в OpenOCD только кладёт байты, адрес там задаётся отдельной resume. Похоже, поле читает не машина, а загрузчик, и вопрос всегда в том, есть ли он. Своей платы у меня нет, на железе не проверял.

Как дотянусь до железок перепроверю(но это не точно)

Обычно так не бывает, чтобы точку входа игнорируют. При том, что я плохо понял статью (сейчас не могу её внятно прочитать), то относился бы к этой информации с кепсисом или, если это действтительно так, то изучал досконально момент где она игнорируется в ядре линукса.

Посмотрел чуть-чуть репозиторий, вы каждый файл ассемблера отдельно компилируете? Если так, то везде будет 0x8000000, надо либо объектные файлы, а потом собирать их вместе, либо один большой файл с ассемблером.

Забудьте, что я написал вторым абзацем, там глупость. Попробовал повторить опыт с изменением стартовой точки и у меня получилось следующее:

Скрытый текст

Правильный экзешник

m039@DESKTOP-FC78JS1:~/Code/test$ readelf -h ./a.out
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              DYN (Position-Independent Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x1060
  Start of program headers:          64 (bytes into file)
  Start of section headers:          13968 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         14
  Size of section headers:           64 (bytes)
  Number of section headers:         31
  Section header string table index: 30

Измененный виртуальный адрес, изменил с помощью хекс редактора.

m039@DESKTOP-FC78JS1:~/Code/test$ readelf -h ./a2.out
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              DYN (Position-Independent Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x101060
  Start of program headers:          64 (bytes into file)
  Start of section headers:          13968 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         14
  Size of section headers:           64 (bytes)
  Number of section headers:         31
  Section header string table index: 30

Там теперь `Entry point address: 0x101060`.

Пытаюсь его запустить:

m039@DESKTOP-FC78JS1:~/Code/test$ ./a2.out
-bash: ./a2.out: Permission denied
m039@DESKTOP-FC78JS1:~/Code/test$ qemu-x86_64 ./a2.out
Error while loading /home/m039/Code/test/a2.out: Exec format error

Кажется, я как-то не так все понял и не могу повторить эксперимент. У меня с измененной стартовой точкой не хочет запускаться экзешник, ни в линуксе, ни в qemu.

Можно еще поизучать исходники qemu и поискать там обращения к e_entry.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации