Создание операционной системы на ассемблере

             

Формат ELF.


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

Любой файл формата ELF (в том числе и объектные модули этого формата) состоит из следующих частей:

  • Заголовок ELF файла;
  • Таблица программных секций (в объектных модулях может отсутствовать);
  • Секции ELF файла;
  • Таблица секций (в выполняемом модуле может отсутствовать);
  • Ради производительности в формате ELF не используются битовые поля. И все структуры обычно выравниваются на 4 байта.

    Теперь рассмотрим типы, используемые в заголовках ELF файлов:



    Тип Размер Выравнивание Комментарий
    Elf32_Addr 4 4 Адрес
    Elf32_Half 2 2 Беззнаковое короткое целое
    Elf32_Off 4 4 Смещение
    Elf32_SWord 4 4 Знаковое целое
    Elf32_Word 4 4 Беззнаковое целое
    unsigned char 1 1 Безнаковое байтовое целое

    Теперь рассмотрим заголовок файла:

    #define EI_NIDENT 16

    struct elf32_hdr { unsigned char e_ident[EI_NIDENT]; Elf32_Half e_type; Elf32_Half e_machine; Elf32_Word e_version; Elf32_Addr e_entry; /* Entry point */ Elf32_Off e_phoff; Elf32_Off e_shoff; Elf32_Word e_flags; Elf32_Half e_ehsize; Elf32_Half e_phentsize; Elf32_Half e_phnum; Elf32_Half e_shentsize; Elf32_Half e_shnum; Elf32_Half e_shstrndx; };

    Массив e_ident содержит в себе информацию о системе и состоит из нескольких подполей.

    struct { unsigned char ei_magic[4]; unsigned char ei_class; unsigned char ei_data; unsigned char ei_version; unsigned char ei_pad[9]; }

    ei_magic - постоянное значение для всех ELF файлов, равное { 0x7f, 'E', 'L', 'F'}
    ei_class - класс ELF файла (1 - 32 бита, 2 - 64 бита который мы не рассматриваем)
    ei_data - определяет порядок следования байт для данного файла (этот порядок зависит от платформы и может быть прямым (LSB или 1) или обратным (MSB или 2)) Для процессоров Intel допустимо только значение 1.
    ei_version - достаточно бесполезное поле, и если не равно 1 (EV_CURRENT) то файл считается некорректным.
    В поле ei_pad операционные системы хранят свою идентификационную информацию. Это поле может быть пустым. Для нас оно тоже не важно.




    Поле заголовка e_type может содержать несколько значений, для выполняемых файлов оно должно быть ET_EXEC равное 2
    e_machine - определяет процессор на котором может работать данный выполняемый файл (Для нас допустимо значение EM_386 равное 3)
    Поле e_version соответствует полю ei_version из заголовка.
    Поле e_entry определяет стартовый адрес программы, который перед стартом программы размещается в eip.
    Поле e_phoff определяет смещение от начала файла, по которому располагается таблица программных секций, используемая для загрузки программ в память.
    Не буду перечислять назначение всех полей, не все нужны для загрузки. Лишь еще два опишу.
    Поле e_phentsize определяет размер записи в таблице программных секций.
    И поле e_phnum определяет количество записей в таблице программных секций.

    Таблица секций (не программных) используется для линковки программ. мы ее рассматривать не будем. Так же мы не будем рассматривать динамически линкуемые модули. Тема эта достаточно сложная, для первого знакомства не подходящая. :)

    Теперь про программные секции. Формат записи таблицы программных секций таков:

    struct elf32_phdr { Elf32_Word p_type; Elf32_Off p_offset; Elf32_Addr p_vaddr; Elf32_Addr p_paddr; Elf32_Word p_filesz; Elf32_Word p_memsz; Elf32_Word p_flags; Elf32_Word p_align; };

    Подробнее о полях.

    p_type - определяет тип программной секции. Может принимать несколько значений, но нас интересует только одно. PT_LOAD (1). Если секция именно этого типа, то она предназначена для загрузки в память.
    p_offset - определяет смещение в файле, с которого начинается данная секция.
    p_vaddr - определяет виртуальный адрес, по которому эта секция должна быть загружена в память.
    p_paddr - определяет физический адрес, по которому необходимо загружать данную секцию. Это поле не обязательно должно использоваться и имеет смысл лишь для некоторых платформ.
    p_filesz - определяет размер секции в файле.
    p_memsz - определяет размер секции в памяти. Это значение может быть больше предыдущего. Поле p_flag определяет тип доступа к секциям в памяти. Некоторые секции допускается выполнять, некоторые записывать. Для чтения в существующих системах доступны все.


    Содержание раздела