Эльф файл что это такое

Обновлено: 21.11.2024

Конечные изображения, созданные компиляторами, содержат как bin-файл, так и расширенный загрузочный файл формата ELf, в чем разница между ними, особенно полезность файла ELF.

5 ответов 5

Bin-файл – это чистый двоичный файл без исправления или перемещения памяти. Более чем вероятно, что он содержит явные инструкции для загрузки по определенному адресу памяти. Принимая во внимание.

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

"более чем вероятно, что он имеет явные инструкции для загрузки по определенному адресу памяти": означает ли это, что процесс создания bin-файла добавляет дополнительный код для загрузки данных по определенному адресу?

Насколько мне известно, файл bin похож на запуск программы со смещением 0, и в него встроен сегмент данных. Если это не так, поправьте меня.

@t0mm13b Значит, файлы .elf можно записывать на микроконтроллер так же, как и обычные файлы .hex, но для этого требуется больше флэш-памяти, и при каждом сбросе микроконтроллера адреса секций меняются?

@BlackyDucky, я не верю, что это возможно. Если бы микроконтроллер попытался выполнить данные ELF напрямую, он бы неправильно интерпретировал заголовки и другие данные как инструкции, верно?

Bin-файл — это просто биты и байты, которые попадают в ПЗУ или на определенный адрес, с которого вы будете запускать программу. Вы можете взять эти данные и загрузить их напрямую как есть, вам нужно знать, что такое базовый адрес, поскольку обычно его там нет.

Файл elf содержит информацию о bin, но он окружен большим количеством другой информации, возможной отладочной информацией, символами, может отличать код от данных в двоичном файле. Позволяет использовать более одного фрагмента двоичных данных (когда вы выгружаете один из них в корзину, вы получаете один большой файл корзины с данными заполнения, чтобы добавить его к следующему блоку). Сообщает вам, сколько у вас двоичных файлов и сколько данных bss нужно инициализировать нулями (инструменты gnu имеют проблемы с правильным созданием bin-файлов).

Формат файла elf является стандартным, arm публикует свои улучшения/вариации стандарта. Я рекомендую всем написать программу разбора эльфа, чтобы понять, что там находится, не беспокойтесь о библиотеке, довольно просто просто использовать информацию и структуры в спецификации. Помогает преодолеть проблемы gnu в целом при создании файлов .bin, а также при отладке скриптов компоновщика и других вещей, которые могут испортить ваш вывод bin или elf.

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

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

для «голого железа», особенно если этот файл elf является загрузчиком и/или первым запуском программы, то точка входа и _start не имеют значения, поскольку вы используете файл elf в качестве трамплина к инструменту, программирующему флэш-память ( например, openocd через jtag) или через что угодно-objcopy -O двоичный файл.elf файл.bin, а затем этот файл каким-то образом загружается во флэш-память. Не пошел и не попробовал загрузчик на x86, но предположил, что биос не может анализировать файлы elf, поэтому он также должен быть образом памяти. поэтому бинарный файл -O двоичного типа

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

Чтобы уточнить, что у вашей цели есть правила, будь то операционная система, процессор, многоступенчатый загрузчик и т. д.И вам нужно построить свой «двоичный файл» на основе этих правил, наиболее важными из которых являются начальная загрузка и скрипт компоновки. Затем он очень широк в отношении каждой цели и того, как вы применяете этот двоичный файл и какие форматы файлов поддерживаются. Предполагая, что gnu на ряде основных платформ разработки, формат файла elf является выходным по умолчанию, а затем вы используете инструменты по мере необходимости (если целевые утилиты/загрузчики) для извлечения или преобразования из elf во что-то другое.

Формат ELF обычно используется при компиляции по умолчанию. если вы используете цепочки инструментов GNU, вы можете перевести их в двоичный формат с помощью objcopy, например:

или с помощью утилиты fromELF (встроенной в большинство IDE, таких как ADS):

Это было добавлено после того, как был дан ответ на детали файла bin, и делает надстройку практически полезной техникой. +1 за это.

bin — это последний способ просмотра памяти перед тем, как ЦП начнет ее выполнять.

ELF – это его урезанная/сжатая версия, поэтому процессор/микроконтроллер не может работать напрямую.

Сначала (динамический) компоновщик должен в достаточной степени обратить это (и, таким образом, изменить смещения обратно на правильные позиции).
Но на MCU нет компоновщика/ОС, поэтому вместо этого вам нужно прошить корзину.

Более того, Ахмед Гамаль прав.
Компиляция и компоновка — это отдельные этапы; весь процесс называется «сборкой», поэтому коллекция компиляторов GNU имеет отдельные исполняемые файлы:

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

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

Чтобы избежать путаницы, GCC выводит объектный код (после внутреннего использования ассемблера) в формате ELF. Компоновщик просто берет несколько из них (с расширением .o) и создает один объединенный результат, возможно, даже сжимая их (в "a.out").

Но все они, даже ".so", являются ELF. Это похоже на несколько документов Word, каждый из которых заканчивается на «.chapter», и все они объединяются в окончательный «.book», где все файлы технически используют один и тот же стандарт/формат и, следовательно, могут иметь расширение «.docx». /p>

Затем корзина похожа на преобразование книги в файл ".txt" с добавлением столько пробелов, сколько необходимо, чтобы соответствовать размеру окончательной книги (напечатанной на одной катушке), с местами для всех изображения для наложения.

Конечные изображения, созданные компиляторами, содержат как bin-файл, так и расширенный загрузочный файл формата ELf, в чем разница между ними, особенно полезность файла ELF.

5 ответов 5

Bin-файл – это чистый двоичный файл без исправления или перемещения памяти. Более чем вероятно, что он содержит явные инструкции для загрузки по определенному адресу памяти. Принимая во внимание.

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

"более чем вероятно, что он имеет явные инструкции для загрузки по определенному адресу памяти": означает ли это, что процесс создания bin-файла добавляет дополнительный код для загрузки данных по определенному адресу?

Насколько мне известно, файл bin похож на запуск программы со смещением 0, и в него встроен сегмент данных. Если это не так, поправьте меня.

@t0mm13b Значит, файлы .elf можно записывать на микроконтроллер так же, как и обычные файлы .hex, но для этого требуется больше флэш-памяти, и при каждом сбросе микроконтроллера адреса секций меняются?

@BlackyDucky, я не верю, что это возможно. Если бы микроконтроллер попытался выполнить данные ELF напрямую, он бы неправильно интерпретировал заголовки и другие данные как инструкции, верно?

Bin-файл — это просто биты и байты, которые попадают в ПЗУ или на определенный адрес, с которого вы будете запускать программу. Вы можете взять эти данные и загрузить их напрямую как есть, вам нужно знать, что такое базовый адрес, поскольку обычно его там нет.

Файл elf содержит информацию о bin, но он окружен большим количеством другой информации, возможной отладочной информацией, символами, может отличать код от данных в двоичном файле.Позволяет использовать более одного фрагмента двоичных данных (когда вы выгружаете один из них в корзину, вы получаете один большой файл корзины с данными заполнения, чтобы добавить его к следующему блоку). Сообщает вам, сколько у вас двоичных файлов и сколько данных bss нужно инициализировать нулями (инструменты gnu имеют проблемы с правильным созданием bin-файлов).

Формат файла elf является стандартным, arm публикует свои улучшения/вариации стандарта. Я рекомендую всем написать программу разбора эльфа, чтобы понять, что там находится, не беспокойтесь о библиотеке, довольно просто просто использовать информацию и структуры в спецификации. Помогает преодолеть проблемы gnu в целом при создании файлов .bin, а также при отладке скриптов компоновщика и других вещей, которые могут испортить ваш вывод bin или elf.

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

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

для «голого железа», особенно если этот файл elf является загрузчиком и/или первым запуском программы, то точка входа и _start не имеют значения, поскольку вы используете файл elf в качестве трамплина к инструменту, программирующему флэш-память ( например, openocd через jtag) или через что угодно-objcopy -O двоичный файл.elf файл.bin, а затем этот файл каким-то образом загружается во флэш-память. Не пошел и не попробовал загрузчик на x86, но предположил, что биос не может анализировать файлы elf, поэтому он также должен быть образом памяти. поэтому бинарный файл -O двоичного типа

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

Для того, чтобы расширить, что у вашей цели есть правила, будь то операционная система, процессор, многоэтапный загрузчик и т. д. И вам нужно построить свой «двоичный файл» на основе этих правил, причем наиболее важными являются загрузчик и скрипт компоновки. Затем он очень широк в отношении каждой цели и того, как вы применяете этот двоичный файл и какие форматы файлов поддерживаются. Предполагая, что gnu на ряде основных платформ разработки, формат файла elf является выходным по умолчанию, а затем вы используете инструменты по мере необходимости (если целевые утилиты/загрузчики) для извлечения или преобразования из elf во что-то другое.

Формат ELF обычно используется при компиляции по умолчанию. если вы используете цепочки инструментов GNU, вы можете перевести их в двоичный формат с помощью objcopy, например:

или с помощью утилиты fromELF (встроенной в большинство IDE, таких как ADS):

Это было добавлено после того, как был дан ответ на детали файла bin, и делает надстройку практически полезной техникой. +1 за это.

bin — это последний способ просмотра памяти перед тем, как ЦП начнет ее выполнять.

ELF – это его урезанная/сжатая версия, поэтому процессор/микроконтроллер не может работать напрямую.

Сначала (динамический) компоновщик должен в достаточной степени обратить это (и, таким образом, изменить смещения обратно на правильные позиции).
Но на MCU нет компоновщика/ОС, поэтому вместо этого вам нужно прошить корзину.

Более того, Ахмед Гамаль прав.
Компиляция и компоновка — это отдельные этапы; весь процесс называется «сборкой», поэтому коллекция компиляторов GNU имеет отдельные исполняемые файлы:

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

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

Чтобы избежать путаницы, GCC выводит объектный код (после внутреннего использования ассемблера) в формате ELF.Компоновщик просто берет несколько из них (с расширением .o) и создает один объединенный результат, возможно, даже сжимая их (в "a.out").

Но все они, даже ".so", являются ELF. Это похоже на несколько документов Word, каждый из которых заканчивается на «.chapter», и все они объединяются в окончательный «.book», где все файлы технически используют один и тот же стандарт/формат и, следовательно, могут иметь расширение «.docx». /p>

Затем корзина похожа на преобразование книги в файл ".txt" с добавлением столько пробелов, сколько необходимо, чтобы соответствовать размеру окончательной книги (напечатанной на одной катушке), с местами для всех изображения для наложения.

ELF (Executable and Linkable Format) был разработан Unix System Laboratories во время работы с Sun Microsystems над SVR4 (UNIX System V Release 4.0). Следовательно, ELF впервые появился в Solaris 2.0 (он же SunOS 5.0), основанном на SVR4. Формат указан в System V ABI.

Очень универсальный формат файлов, позже он был подхвачен многими другими операционными системами для использования как в качестве исполняемых файлов, так и в качестве файлов общих библиотек. Он различает ТЕКСТ, ДАННЫЕ и BSS.

Сегодня ELF считается стандартным форматом для Unix-подобных систем. Хотя у него есть некоторые недостатки (например, использование одного из редких регистров общего назначения IA-32 при использовании кода, не зависящего от позиции), он хорошо поддерживается и задокументирован.

Содержание

Структура файла

ELF — это формат хранения программ или фрагментов программ на диске, созданный в результате компиляции и компоновки. Файл ELF разделен на разделы. Для исполняемой программы это текстовая секция для кода, секция данных для глобальных переменных и секция родата, которая обычно содержит константные строки. Файл ELF содержит заголовки, описывающие, как эти разделы должны храниться в памяти.

Обратите внимание, что в зависимости от того, является ли ваш файл компонуемым или исполняемым, заголовки в файле ELF будут разными: process.o, результат gcc -c process.c $SOME_FLAGS

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

/bin/bash, настоящий исполняемый файл

Сам заголовок программы. занимает 224 байта и начинается со смещения 0x34 в файле

Программа, которая должна использоваться для «выполнения» двоичного файла. Здесь он читается как «/lib/ld-linux.so.2», что означает, что перед запуском программы потребуется привязка некоторых динамических библиотек.

Теперь нас просят прочитать 7411c байтов, начиная с начала файла (?) и занимая 7411c байтов (это практически весь файл!), который будет доступен только для чтения, но исполняемый. Чтобы программа работала корректно, они должны начинаться с виртуального адреса 0x08048000.

Больше битов для загрузки (вероятно, это раздел .data). Обратите внимание, что 'filesize' и 'memsize' различаются, что означает, что секция .bss фактически будет выделена с помощью этого оператора, но оставлена ​​как нули, в то время как "настоящие" данные занимают только первые байты 0x22ac, начиная с виртуального адреса 0x80bd120.

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

Разделы ПРИМЕЧАНИЕ содержат информацию, оставленную либо программистом, либо компоновщиком, для большинства программ, связанных с помощью компоновщика GNU 'ld', в нем просто указано "GNU"

Это для информации об обработчике исключений на тот случай, если нам придется ссылаться на некоторые двоичные файлы C++ во время выполнения (необходимо указать).

Мы можем распознать наши «биты кода» и «биты данных», указав, что второй должен быть загружен с адреса 0x080bd*120* и что он начинается в файле с адреса 0x00074*120*. сопоставление блоков диска (например, если страница 0x80bc000 отсутствует, просто выберите блоки файлов с 0x75000). Однако это означает, что часть кода отображается дважды, но с разными разрешениями. Я предлагаю вам также предоставить им разные физические страницы, если вы не хотите в конечном итоге получить модифицируемый код.

Загрузка двоичных файлов ELF

Заголовок ELF содержит всю необходимую информацию, необходимую для загрузки исполняемого файла ELF. Формат этого заголовка описан в Спецификации ELF. Наиболее подходящими для этой цели являются разделы с 1.1 по 1.4 и с 2.1 по 2.7. Инструкции по загрузке исполняемого файла содержатся в разделе 2.7.

Ниже приведен примерный перечень шагов, которые должен выполнять загрузчик исполняемого файла ELF:

  • Убедитесь, что файл начинается с магического числа ELF (4 байта), как описано на рис. 1–4 (и в следующей таблице) на стр. 11 спецификации ELF.
  • Прочитайте заголовок ELF.Заголовок ELF всегда находится в самом начале файла ELF. Заголовок ELF содержит информацию о том, как устроена остальная часть файла. Исполняемый загрузчик касается только заголовков программы.
  • Прочитайте заголовки программы исполняемого файла ELF. Они указывают, где в файле расположены сегменты программы и где их нужно загрузить в память.
  • Проанализируйте заголовки программы, чтобы определить количество программных сегментов, которые необходимо загрузить. Каждый заголовок программы имеет связанный с ним тип, как показано на рис. 2-2 спецификации ELF. Только заголовки с типом PT_LOAD описывают загружаемый сегмент.
  • Загрузить каждый из загружаемых сегментов. Это делается следующим образом:
    • Выделить виртуальную память для каждого сегмента по адресу, указанному элементом p_vaddr в заголовке программы. Размер сегмента в памяти определяется элементом p_memsz.
    • Скопируйте данные сегмента из смещения файла, указанного элементом p_offset, в адрес виртуальной памяти, указанный элементом p_vaddr. Размер сегмента в файле содержится в элементе p_filesz. Это может быть ноль.
    • Член p_memsz указывает размер, который сегмент занимает в памяти. Это может быть ноль. Если элементы p_filesz и p_memsz различаются, это означает, что сегмент дополнен нулями. Все байты в памяти между конечным смещением размера файла и размером виртуальной памяти сегмента должны быть очищены нулями.

    Переезд

    Перемещение становится удобным, когда вам нужно загрузить, например, модули или драйверы. Можно использовать параметр "-r" для ld, чтобы позволить вам связать несколько объектных файлов в один большой, что означает более простое кодирование и более быстрое тестирование.

    Основной план того, что вам нужно сделать для переезда:

    1. Проверьте заголовок объектного файла (например, он должен быть ELF, а не PE)
    2. Получить адрес загрузки (например, все драйверы начинаются с адреса 0xA0000000, нужен какой-то метод отслеживания расположения драйверов)
    3. Выделить достаточно места для всех разделов программы (ST_PROGBITS)
    4. Скопировать из образа в ОЗУ в выделенное место
    5. Пройтись по всем разделам, разрешающим внешние ссылки на таблицу символов ядра
    6. Если все успешно, вы можете использовать поле "e_entry" заголовка в качестве смещения от адреса загрузки для вызова точки входа (если она была указана), или выполнить поиск символа, или просто вернуть код ошибки успешного завершения. .

    Как только вы сможете перемещать объекты ELF, вы сможете загружать драйверы по мере необходимости, а не при запуске, что всегда хорошо (tm).

    Таблицы

    Заголовок

    Заголовок находится в начале файла ELF.

    Запись flags, вероятно, можно игнорировать для ELF x86, поскольку на самом деле флаги не определены.

    Архитектура набора инструкций:

    Наиболее распространенные архитектуры выделены жирным шрифтом.

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

    Это массив из N (указанных в основном заголовке) записей в следующем формате. Убедитесь, что используете правильную версию в зависимости от того, является ли файл 32-битным или 64-битным, поскольку таблицы сильно различаются.

    Типы сегментов: 0 = null – запись игнорируется; 1 = загрузить — очистить байты p_memsz в p_vaddr до 0, затем скопировать байты p_filesz из p_offset в p_vaddr; 2 = динамический — требуется динамическая компоновка; 3 = interp — содержит путь к исполняемому файлу для использования в качестве интерпретатора для следующего сегмента; 4 = раздел примечаний. Есть и другие значения, но в основном они содержат информацию об архитектуре/среде, которая, вероятно, не требуется для большинства файлов ELF.

    Флаги: 1 = исполняемый файл, 2 = доступный для записи, 4 = доступный для чтения.

    Динамическое связывание

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

    Чтобы реализовать это, вам необходимо иметь надлежащее расписание, библиотеку и программу для использования этой библиотеки. Вы можете создать библиотеку с помощью GCC:

    Эту библиотеку следует рассматривать как файл, который загружается, когда ОС обнаруживает попытку его использования. Вам нужно будет внедрить этот «Динамический компоновщик» в определенную классификацию кода, например, в раздел управления памятью или управления задачами. Когда программа ELF запущена, система должна прикрепить данные общего объекта к области памяти malloc(), где вызовы функций к библиотекам перенаправляются в эту область памяти malloc().Как только программа завершится, область может быть возвращена ОС с помощью вызова free().

    Программирование начинается с умной идеи, написания исходного кода на выбранном вами языке программирования, например C, и сохранения исходного кода в файле. С помощью адекватного компилятора, например GCC, ваш исходный код сначала транслируется в объектный код. В конце концов компоновщик переводит объектный код в двоичный файл, который связывает объектный код с библиотеками, на которые ссылаются. Этот файл содержит отдельные инструкции в виде машинного кода, которые понимает ЦП и выполняет, как только запускается скомпилированная программа.

    Упомянутый выше двоичный файл имеет определенную структуру, и один из наиболее распространенных файлов называется ELF, что означает Executable and Linkable Format. Он широко используется для исполняемых файлов, перемещаемых объектных файлов, общих библиотек и дампов ядра.

    Двадцать лет назад, в 1999 году, проект 86open выбрал ELF в качестве стандартного формата двоичных файлов для Unix и Unix-подобных систем на процессорах x86. К счастью, формат ELF ранее был задокументирован как в двоичном интерфейсе приложений System V, так и в стандарте интерфейса инструментов [4]. Этот факт значительно упростил соглашение о стандартизации между различными поставщиками и разработчиками операционных систем на базе Unix.

    Причиной этого решения была конструкция ELF — гибкость, расширяемость и кросс-платформенная поддержка различных форматов с порядком байтов и размеров адресов. Дизайн ELF не ограничивается конкретным процессором, набором инструкций или аппаратной архитектурой. Подробное сравнение форматов исполняемых файлов см. здесь [3].

    С тех пор формат ELF используется несколькими операционными системами. Среди прочего, это Linux, Solaris/Illumos, Free-, Net- и OpenBSD, QNX, BeOS/Haiku и Fuchsia OS [2]. Кроме того, вы найдете его на мобильных устройствах под управлением Android, Maemo или Meego OS/Sailfish OS, а также на игровых консолях, таких как PlayStation Portable, Dreamcast и Wii.

    Спецификация не уточняет расширение имени файла для файлов ELF. Используется множество комбинаций букв, таких как .axf, .bin, .elf, .o, .prx, .puff, .ko, .so и .mod или ни одного.

    Структура файла ELF

    В терминале Linux команда man elf дает удобную сводку о структуре файла ELF:

    Листинг 1: Справочная страница структуры ELF

    ELF(5) Руководство программиста Linux ELF(5)

    ИМЯ
    elf - формат файлов Executable и Linking Format (ELF)

    ОПИСАНИЕ
    Заголовочный файл определяет формат исполняемых
    бинарных файлов ELF. Среди этих файлов есть обычные исполняемые файлы, перемещаемые
    объектные файлы, основные файлы и общие библиотеки.

    Исполняемый файл, использующий формат файла ELF, состоит из заголовка ELF,
    за которым следует таблица заголовков программы или таблица заголовков разделов, или и то, и другое.
    Заголовок ELF всегда находится по нулевому смещению файла. Таблица заголовков программы
    и смещение таблицы заголовков разделов в файле
    определены в заголовке ELF. Две таблицы описывают остальные
    особенности файла.

    Как видно из приведенного выше описания, файл ELF состоит из двух разделов: заголовка ELF и данных файла. Раздел данных файла может состоять из таблицы заголовков программ, описывающей ноль или более сегментов, таблицы заголовков разделов, описывающих ноль или более сегментов, за которыми следуют данные, на которые ссылаются записи из таблицы заголовков программ, и таблицы заголовков разделов. Каждый сегмент содержит информацию, необходимую для выполнения файла во время выполнения, а разделы содержат важные данные для связывания и перемещения. На рис. 1 это схематично показано.

    Заголовок ELF

    Заголовок ELF имеет длину 32 байта и определяет формат файла. Он начинается с последовательности из четырех уникальных байтов: 0x7F, за которыми следуют 0x45, 0x4c и 0x46, что транслируется в три буквы E, L и F. Помимо других значений, заголовок также указывает, является ли это файлом ELF для 32 или 64-битный формат, использует прямой или прямой порядок следования байтов, показывает версию ELF, а также для какой операционной системы был скомпилирован файл, чтобы взаимодействовать с правильным двоичным интерфейсом приложения (ABI) и набором инструкций процессора.

    Шестнадцатеричный дамп бинарного файла touch выглядит следующим образом:

    .Листинг 2: шестнадцатеричный дамп двоичного файла

    $ hd /usr/bin/touch | голова -5
    00000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 |.ELF. |
    00000010 02 00 3e 00 01 00 00 00 e3 25 40 00 00 00 00 00 |..>. %@. |
    00000020 40 00 00 00 00 00 00 00 28 e4 00 00 00 00 00 00 |@. (. |
    00000030 00 00 00 00 40 00 38 00 09 00 40 00 1b 00 1a 00 |[email protected]@.|
    00000040 06 00 00 00 05 00 00 00 40 00 00 00 00 00 00 00 |[email protected]|

    Debian GNU/Linux предлагает команду readelf, входящую в состав пакета GNU ‘binutils’. В сочетании с ключом -h (сокращенная версия «–file-header») он красиво отображает заголовок ELF-файла. В листинге 3 это показано для команды touch.

    .Листинг 3: Отображение заголовка файла ELF

    $ readelf -h /usr/bin/touch
    Заголовок ELF:
    Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
    Класс: ELF64 < br />Данные: дополнение до 2, обратный порядок байтов
    Версия: 1 (текущая)
    ОС/ABI: UNIX - System V
    Версия ABI: 0
    Тип: EXEC (Исполняемый файл )
    Машина: Advanced Micro Devices X86-64
    Версия: 0x1
    Адрес точки входа: 0x4025e3
    Начало заголовков программы: 64 (байт в файле)
    Начало заголовки разделов: 58408 (байт в файл)
    Флаги: 0x0
    Размер этого заголовка: 64 (байты)
    Размер заголовков программы: 56 (байт)
    Количество заголовков программы : 9
    Размер заголовков разделов: 64 (байта)
    Количество заголовков разделов: 27
    Индекс таблицы строк заголовков разделов: 26

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

    Заголовок программы показывает сегменты, используемые во время выполнения, и сообщает системе, как создать образ процесса. Заголовок из листинга 2 показывает, что файл ELF состоит из 9 заголовков программ размером 56 байт каждый, причем первый заголовок начинается с 64-го байта.

    Опять же, команда readelf помогает извлечь информацию из файла ELF. Переключатель -l (сокращение от –program-headers или –segments) раскрывает дополнительные сведения, как показано в листинге 4.

    .Листинг 4: Отображение информации о заголовках программы

    $ readelf -l /usr/bin/touch

    Тип файла Elf - EXEC (исполняемый файл)
    Точка входа 0x4025e3
    Есть 9 заголовков программы, начиная со смещения 64

    Заголовки программ:
    Смещение типа VirtAddr Phangaddr
    Флаги Memsiz FilesAddr
    PHDR 0x00000000000000004000000000000000000000040000000000000040004000000000000000400040000000000000000000001F8 Re 8
    Интерп 0x0000000000400238 0x0000000000400238

    0x00000000000000001C 0x00000000000000001C R 1
    [Запрос интерпретатора программы: /lib64/ld-linux-x86-64.so.2]
    Нагрузка 0x000000000000000000000000000000000000000000000000000040000000000000000 00000000D494 0x000000000000d494 Re 200000


    0x000000000000DE10 0x000000000060de10 0x000000000060de10
    0x0000000000000524 0x0000000000000748 RW 200000
    ДИНАМИЧНЫЙ 0x000000000000de28 0x000000000060de28 0x000000000060de28
    0x00000000000001d0 0x00000000000001d0 RW 8
    Примечание 0x0000000000000254 0x0000000000400254 0x0000000000400254
    0x0000000000000044 0x0000000000000044 R 4
    GNU_EH_FRAME 0x000000000000bc40 0x000000000040bc40 0x000000000040bc40
    0x00000000000003a4 0x000000000000003a4 R 4 GNU_STACK 0x000000000000000000 0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006DE10 0x000000000060De10
    0x000000000060de10
    0x00000000000001f0 r 1

    Сопоставление раздела с сегментом:
    Разделы сегмента.
    00
    01 .interp
    02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r . rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame
    03 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss
    04 . динамический
    05 .note.ABI-tag .note.gnu.build-id
    06 .eh_frame_hdr
    07
    08 .init_array .fini_array .jcr .dynamic .got

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

    Третья часть структуры ELF — заголовок раздела. Он предназначен для перечисления отдельных разделов двоичного файла. Переключатель -S (сокращение от –section-headers или –sections) перечисляет различные заголовки. Что касается команды touch, то имеется 27 заголовков разделов, и в листинге 5 показаны только первые четыре плюс последний. Каждая строка содержит размер раздела, тип раздела, а также его адрес и смещение в памяти.

    .Листинг 5: Детали раздела, раскрытые readelf

    $ readelf -S /usr/bin/touch
    Имеется 27 заголовков разделов, начиная со смещения 0xe428:

    Заголовки разделов:
    [Nr] Имя Тип Адрес Смещение
    Размер EntSize Флаги Информация о ссылке Выравнивание
    [ 0] NULL 00000000000000000 00000000
    00000000000000000 00000000000000000 0 0 0
    [1] .interp PROGBITS 0000000000400238 00000238
    000000000000001c 0000000000000000 A 0 0 1
    [2] .note.ABI-тега Примечание 0000000000400254 00000254
    0000000000000020 0000000000000000 А 0 0 4
    [3 ] .note.gnu.build-i ПРИМЕЧАНИЕ 0000000000400274 00000274
    .
    .
    [26] .shstrtab STRTAB 0000000000000000 0000e334
    00000000000000ef 0000000000000000 0 0 1
    Ключ к флагам:
    W (запись), A (распределение), X (выполнение), M (объединение), S (строки), l (большой)
    I (информация), L (порядок ссылок), G (группа), T (TLS), E (исключить), x (неизвестно)
    O (требуется дополнительная обработка ОС) o (зависит от ОС), p (зависит от процессора)

    Инструменты для анализа файла ELF

    Как вы, возможно, заметили из приведенных выше примеров, GNU/Linux дополнен рядом полезных инструментов, которые помогут вам проанализировать файл ELF. Первым кандидатом, которого мы рассмотрим, является файловая утилита.

    отображает основную информацию о файлах ELF, включая архитектуру набора инструкций, для которой предназначен код в перемещаемом, исполняемом или совместно используемом объектном файле. В листинге 6 указано, что /bin/touch — это 64-битный исполняемый файл, соответствующий стандарту Linux Standard Base (LSB), динамически связанный и созданный для ядра GNU/Linux версии 2.6.32.

    .Листинг 6: Основная информация с использованием файла

    $ файл /bin/touch
    /bin/touch: исполняемый файл ELF 64-bit LSB, x86-64, версия 1 (SYSV), динамическая компоновка, интерпретатор /lib64/l,
    для GNU /Linux 2.6.32, BuildID[sha1]=ec08d609e9e8e73d4be6134541a472ad0ea34502, удалено
    $

    Второй кандидат — readelf. Он отображает подробную информацию о файле ELF. Список переключателей сравнительно длинный и охватывает все аспекты формата ELF. Использование ключа -n (сокращение от –notes). В листинге 7 показаны только те разделы примечаний, которые существуют в файле touch – тег версии ABI и битовая строка идентификатора сборки.

    .Листинг 7: Отображение выбранных разделов файла ELF

    $ readelf -n /usr/bin/touch

    Отображение заметок, найденных по смещению файла 0x00000254 с длиной 0x00000020:
    Владелец Размер данных Описание
    GNU 0x00000010 NT_GNU_ABI_TAG (тег версии ABI)
    ОС: Linux, ABI: 2.6.32

    Отображение заметок, найденных по смещению файла 0x00000274 с длиной 0x00000024:
    Владелец Размер данных Описание
    GNU 0x00000014 NT_GNU_BUILD_ID (уникальная битовая строка идентификатора сборки)
    Идентификатор сборки: ec08d609e9e8e73d4be6134541a472ad0ea34502

    Обратите внимание, что в Solaris и FreeBSD утилита elfdump [7] соответствует readelf. По состоянию на 2019 г. с 2003 г. не было новых выпусков или обновлений.

    Номер три — это пакет elfutils [6], доступный только для Linux. Он предоставляет инструменты, альтернативные GNU Binutils, а также позволяет проверять файлы ELF. Обратите внимание, что все имена утилит, представленных в пакете, начинаются с eu для «elf utils».

    И последнее, но не менее важное: мы упомянем objdump. Этот инструмент похож на readelf, но фокусируется на объектных файлах. Он предоставляет аналогичную информацию о файлах ELF и других форматах объектов.

    .Листинг 8: Информация о файле, извлеченная objdump

    $ objdump -f /bin/touch

    Существует также программный пакет под названием «elfkickers» [9], который содержит инструменты для чтения содержимого файла ELF, а также для управления им. К сожалению, количество релизов невелико, поэтому мы просто упомянем об этом, а не приводим дальнейших примеров.

    Как разработчик, вы можете вместо этого взглянуть на pax-utils [10,11]. Этот набор утилит предоставляет ряд инструментов, помогающих проверять файлы ELF. В качестве примера, dumpelf анализирует файл ELF и возвращает заголовочный файл C, содержащий подробности — см. рис. 2.

    Заключение

    Благодаря сочетанию продуманного дизайна и отличной документации формат ELF работает очень хорошо и все еще используется спустя 20 лет. Утилиты, показанные выше, позволяют вам получить представление о файле ELF и понять, что делает программа. Это первые шаги для анализа программного обеспечения — удачного взлома!

    Ссылки и ссылки
    • [1] Executable and Linkable Format (ELF), Wikipedia
    • [2] Фуксия ОС
    • [3] Сравнение форматов исполняемых файлов, Википедия
    • [4] Linux Foundation, Справочные спецификации
    • [5] Чиро Сантилли: Учебное пособие по ELF Hello World
    • [6] Пакет Debian elfutils
    • [7] эльфдамп
    • [8] Майкл Боэлен: 101 файл ELF в Linux: понимание и анализ
    • [9] эльфкикеры
    • [10] Защищенные/PaX-утилиты
    • [11] pax-utils, пакет Debian
    Благодарности

    Автор хотел бы поблагодарить Акселя Беккерта за поддержку в подготовке этой статьи.

    Читайте также: