Формат ELF-файла

Заголовок файла ELF

Последнее обновление: 08.07.2023

В результате компиляции и компоновки ассемблером GAS создается файл формата ELF (Executable and Linkable Format), который содержит всю информацию, необходимую операционной системе для загрузки и запуска программы. Условно файл ELF состоит из трех частей:

  • Заголовок файла ELF

  • Заголовки программы

  • Заголовки разделов

Инструменты Arm GNU Toolchain предоставляют специальную утилиту для исследования ELF-файла - readelf. Мы ее можем найти в той же папке, где и располгааются исполняемые файлы компилятора gcc для arm64:

Утилита readelf в Arm GNU Toolchain

Например, в пакете Arm GNU Toolchain aarch64-none-elf эта утилита называется aarch64-none-elf-readelf. Передавая этой утилите различные параметры, можно получить представление различных частей файла в формате ELF. Для получения полной справки по данной утиилите ей надо передать опцию -H. Отмечу лишь некоторые основные опции, которые мы можем использовать для исследования файла в формате ELF:

  • -a или --all: вывод всей информации, аналогично применению опций -h -l -S -s -r -d -V -A -I

  • -h или --file-header : вывод заголовка файла

  • -l или --program-headers: вывод заголовков программы

  • -S или --section-headers (либо --sections ): вывод заголовков разделов

  • -g или --section-groups: вывод групп разделов

  • -t или --section-details: вывод информации о разделах

  • -e или --headers: вывод всех заголовков, эквивалентно набору опций -h -l -S

  • -s или --syms: вывод таблицы символов

Рассмотрим на примере простейшей программы, которая выводит приветственное соощение на консоль. Допустим, у нас есть файл hello.s со следующим кодом:

.global _start          // устанавливаем стартовый адрес программы
 
_start: mov X0, #1          // 1 = StdOut - поток вывода
 ldr X1, =hello             // строка для вывода на экран
 mov X2, #19                // длина строки
 mov X8, #64                // устанавливаем функцию Linux
 svc 0                      // вызываем функцию Linux для вывода строки
 
 mov X0, #0                 // Устанавливаем 0 как код возврата
 mov X8, #93                // код 93 представляет завершение программы
 svc 0                      // вызываем функцию Linux для выхода из программы
 
.data
hello: .ascii "Hello METANIT.COM!\n"    // данные для вывода

Скомпилируем из этого файла объектный файл:

aarch64-none-elf-as hello.s -o hello.o

Затем из скомпилированного объектного файла hello.o создадим бинарный файл в формате ELF:

aarch64-none-elf-ld hello.o -o hello.so

Итак, мы получили бинарный исполныемый файл hello.so в формате ELF. Теперь проанализируем его.

Заголовок файла ELF

Для получения заголовка выполним команду

aarch64-none-elf-readelf -h hello.so

В результате мы должны получить вывод наподобие следующего:

c:\arm>aarch64-none-elf-readelf -h hello.so
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:                              EXEC (Executable file)
  Machine:                           AArch64
  Version:                           0x1
  Entry point address:               0x400000
  Start of program headers:          64 (bytes into file)
  Start of section headers:          66152 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         2
  Size of section headers:           64 (bytes)
  Number of section headers:         6
  Section header string table index: 5

c:\arm>

Это и есть заголовок программы. Разберем его.

Вначале идет поле Magic. Оно всегда имеет размер 16 байт и призван указать, что файл является корректным файлом ELF. Это поле всегда начинается с байта 0x7f, за которым следуют 3 байта, соответствующие символам "ELF" из таблицы ASCII .

Поле Class указывает на разрядность. Значение ELF64 говорит, что файл использует 64-разрядный формат. В 32-разрядных программах это поле имело бы значение ELF32.

Поле Data сообщает операционной системе и загрузчику, в каком порядке должны считываться поля файла ELF - big-endian (обратный порядок) или little-endian (прямой порядок). Файлы ELF на Arm обычно используют кодировку little-endian для самого формата файла ELF.

Поле Version указывает на версию файла.

Поле Type указывает на тип файла

Поле Machine сообщает загрузчику, для какого типа процессоров предназначена программа. Наша 64-битная программа устанавливает в этом поле значение AArch64, указывая, что файл ELF будет работать только на 64-битных процессорах Arm. В случае с 32-разрядной программой это поле имело бы значение ARM, что означало бы, что она работает только на 32-разрядных процессорах Arm.

Поле Entry point address сообщает загрузчику, где находится точка входа программы. Когда программа была подготовлена в памяти операционной системой или загрузчиком и готова начать выполнение, это поле указывает, откуда начинать выполнение программы. В данном случае это адрес 0x400000 - стандартный адрес для ARM64.

Поле Flags указывает дополнительную информацию, которая может понадобиться загрузчику. Это поле зависит от архитектуры. В 64-битной программе, например, не определены флаги, зависящие от архитектуры, и это поле всегда будет содержать нулевое значение 0x0. Для 32-битной программы это поле может принимать ряд значений, которые информируют, что программа ожидает аппаратную поддержку операций с плавающей запятой:

  • EF_ARM_ABIMASK (0xff000000): старшие 8 бит значения содержат версию ABI, которая применяется файлом ELF. В настоящее время этот старший байт должен содержать значение 5 (т. е. 0x05000000), что означает, что файл ELF использует версию EABI 5.

  • EF_ARM_BE8 (0x00800000): файл ELF содержит код BE-8 для выполнения на процессоре Arm v6. Этот флаг должен быть установлен только для исполняемого файла.

  • EF_ARM_ABI_FLOAT_HARD (0x00000400): указывает, что файл соответствует стандарту вызова аппаратных процедур с плавающей точкой и что процессор будет Armv7 или выше и будет поддерживать аппаратное расширение VFP3-D16 с плавающей точкой.

  • EF_ARM_ABI_FLOAT_SOFT (0x00000200): указывает, что файл соответствует стандарту программного вызова процедур с плавающей точкой. Операции с плавающей точкой обрабатываются через вызовы библиотечных функций.

Поле Size of this header описывает размер заголовка файла.

Остальные поля описывают расположение и количество заголовков программы и разделов в файле:

  • Start of program headers: начало заголовков программы

  • Start of section headers: начало заголовков разделов

  • Size of program headers: размер заголовков программы

  • Number of program headers: количество заголовков программы

  • Size of section headers: размер заголовков разделов

  • Number of section headers: количество заголовков разделов

Загрузчик использует эти поля для подготовки ELF-файла в памяти к выполнению.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850