Формат файлів elf

В описі формату будуть використовуватися типи даних [u] intN_t. де u є ознакою беззнакову, а N визначає розмір типу, наприклад, uint16_t. Ці типи визначені в стандартному заголовному файлі stdint.h.

Всі типи даних і константи описані в заголовному файлі elf.h.

Тема файлу

На початку файлу (зі зміщення 0 від початку) йде заголовок ELF-файлу, що описується наступною структурою:

Поле e_ident містить ідентифікаційну інформацію про файл. Поле являє собою масив байт для того, щоб мати однакове уявлення на архітектурі з різним розміром слова і різним порядком байт в слові. Елементи масиву мають таке призначення:

Надалі будуть приводитися значення констант для ОС Linux на архітектурі i386. За значеннями констант для інших операційних систем або архітектур звертайтеся до документації.

Поле e_type ідентифікує тип файлу: 0 (невідомо), 1 (об'єктний файл), 2 (виконуваний файл), 3 (колективна бібліотека), 4 (core-файл).

Поле e_machine ідентифікує тип процесора: 0 (невідомо), 3 (Intel 80386 і сумісні).

Поле e_version ідентифікує версію файлу: 0 (неприпустима версія), 1 (поточна версія).

Поле e_phoff задає зсув від початку файлу до початку таблиці заголовків програми (program header table). Інформація про таблиці заголовків програми буде дана нижче.

Поле e_shoff задає зсув від початку файлу до початку таблиці заголовків секцій (program section table). Інформація про таблиці заголовків секцій буде дана нижче.

Поле e_flags задає додаткові процесорної-специфічні прапори. В даний час значення даного поля повинне завжди бути 0.

Поле e_ehsize зберігає розмір заголовка ELF-файлу. Його значення має дорівнювати 52 (sizeof (Elf32_Ehdr)).

Поле e_phentsize зберігає розмір одного запису в таблиці заголовків програми. Його значення має бути 32 (sizeof (Elf32_Phdr)) або 0, якщо таблиця заголовків програми порожня.

Поле e_phnum зберігає кількість записів в таблиці заголовків програми.

Поле e_shentsize зберігає розмір одного запису в таблиці заголовків секцій. Його значення має дорівнювати 40 (sizeof (Elf32_Shdr)) або 0, якщо таблиця заголовків секцій порожня.

Поле e_shnum зберігає кількість записів в таблиці заголовків секцій.

Поле e_shstrndx зберігає індекс заголовка секції, яка зберігає імена всіх секцій (див. Нижче).

Таблиця заголовків секцій

Інформація, що зберігається в ELF-файлі, організована в секції. Кожна секція має своє унікальне ім'я. Деякі секції зберігають службову інформацію ELF-файлу (наприклад, таблиці рядків), інші секції зберігають зневадження, треті секції зберігають код або дані програми.

Таблиця заголовків секцій являє собою масив структур Elf32_Shdr. Кількість елементів масиву визначається полем e_shnum заголовка ELF-файлу. Масив знаходиться по зсуву, що зберігається в поле e_shoff. Елемент масиву 0 зарезервований і не використовується для опису секцій. Таким чином, опису секцій знаходяться в елементах масиву з індексами від 1 і до e_shnum - 1.

Структура Elf32_Shdr визначена наступним чином:

Поле sh_name зберігає індекс імені секції. Індекс імені - це зміщення в даних секції, індекс якої задається в полі e_shstrndx заголовка ELF-файлу. З цього зміщення розміщується рядок, що завершується нульовим байтом, що є ім'ям секції.

Таким чином, щоб отримати ім'я секції необхідно виконати наступні дії:

  • Завантажити заголовок секції, індекс якої зберігається в поле e_shstrndx заголовка ELF-файлу.
  • Завантажити тіло відповідної секції.
  • По зсуву, заданому в полі sh_name щодо початку області пам'яті, в яку завантажена секція, знаходиться необхідна рядок імені секції.

Поле sh_type зберігає тип секції. Можливі значення поля перераховані нижче.

Секція містить інструкції процесора.

Прапори можуть комбінуватися за допомогою операції побітового або.

Поле sh_offset зберігає зсув від початку файлу, за яким розміщуються дані секції.

Поле sh_size зберігає розмір секції в байтах.

Поле sh_link зберігає індекс іншій секції (в деяких спеціальних випадках).

Поле sh_info зберігає додаткову інформацію про секції.

Поле sh_entsize зберігає розмір одного запису, якщо секція зберігає таблицю із записів фіксованого розміру.

Таблиця заголовків програми

Таблиця заголовків програми містить інформацію, необхідну для завантаження програми на виконання.

Таблиця заголовків програми являє собою масив структур Elf32_Phdr. Масив розміщується по зсуву від початку файлу, яке зберігається в поле e_phoff заголовка ELF-файлу, а кількість елементів масиву зберігається в поле e_phnum заголовка ELF-файлу.

Структура Elf32_Phdr визначена наступним чином.

Поле p_type зберігає тип заголовка. Деякі можливі значення типу заголовка наведені в таблиці нижче.

Поле p_offset зберігає зсув від початку файлу, за яким розташовується даний сегмент.

Значення поля p_paddr має дорівнювати 0.

Поле p_filesz зберігає розмір сегмента в файлі (може бути 0).

Поле p_memsz зберігає розмір сегмента в пам'яті (може бути 0).

Поле p_flags зберігає прапори доступу до сегменту в пам'яті (можуть об'єднуватися за допомогою побітового "або").

Сегмент доступний на читання

сегмент PT_NOTE

У сегменті ELF-файлу з типом PT_NOTE зберігається додаткова інформація про стан виконання програми. Сегмент сам містить довільну кількість записів довільного розміру. Сегмент завжди має розмір, кратний 4 байтам (для Elf32), і кожен запис в сегменті починається зі зсуву, кратного 4 байтам. На початку кожного запису знаходиться заголовок запису, описуваний наступною структурою:

Поле n_namesz містить довжину назви записи. Назва повинна бути непорожній рядком, що завершується байтом 0. Сама рядок назви записи починається відразу ж після структури Elf32_Nhdr.

Поле n_descsz містить довжину інформаційної частини запису. Довжина повинна бути кратна 4 байтам. Інформаційна частина запису починається відразу після назви запису з урахуванням вирівнювання по межі 4 байт.

Поле n_type містить тип запису. Можливі типи записи залежать від типу файлу (об'єктний, core) і розглядаються у відповідних розділах.

Нижче наведено приклад сегмента PT_NOTE.

Формат налагоджувальної інформації stabs

При компіляції в виконуваний файл може додаватися налагоджувальна інформація, яку відладчик використовує для відображення ходу виконання програми в термінах мови високого рівня. Існує декілька форматів налагоджувальної інформації (STABS, DWARF), тут описується формат STABS як найпростіший.

Для компіляції програми з додаванням налагоджувальної інформації в форматі STABS використовується опція gcc -gstabs. наприклад

У форматі STABS налагоджувальна інформація зберігається в секціях .stab і .stabstr ELF-файлу.

Секція .stab містить масив структур:

Секція .stabstr зберігає символьні рядки, що завершуються байтом 0, які використовуються в записах в секції .stab.

Поле n_type зберігає тип запису. Можливі типи записів можна знайти в stab.h. нас будуть цікавити тільки деякі з них.

Файл, яке встановлюється за допомогою директиви #line або ім'я файлу, що включається за допомогою #include
n_strx - індекс в секції .stabstr рядки імені файлу
Будемо припускати, що дія імені файлу, що встановлюється в запису N_SOL, починається з наступного запису.

Номер рядка коду,
n_value - зміщення першої інструкції рядки щодо початку функції
n_desc - номер рядка в початковому тексті

Записи N_SLINE впорядковані по зсувах всередині функції.

Перший запис в таблиці .stab є службовою і має тип N_UNDF. Індекс цього запису вважається рівним 0.

Файл, в якому розташовується функція, може перебувати в запису N_SOL після запису N_FUN самої функції, але до першого запису N_SLINE. Слід гадати, що ім'я файлу, в якому визначена функція збігається з ім'ям файлу, встановленому до першого запису N_SLINE в даній функції.

Приклад: файл file1.h: i

Схожі статті