Разница между исполняемым и связываемым форматом (.elf) и файлом объекта (.o) - PullRequest
0 голосов
/ 08 ноября 2018

Я искал в руководстве по gcc для Linux (man gcc), для опции -c (gcc -c infile), которая гласит:

-c: скомпилировать или собрать исходные файлы, но не создавать ссылки. Этап связывания просто не завершен. Окончательный результат в форме объектного файла для каждого исходного файла.
По умолчанию имя файла объекта для исходного файла создается путем замены суффикса .c, .i, .s и т. Д. На .o.

более того, при проверке ELF-файла и объектного файла (с командой file) вывод совпадает:

file ./out/main.o: ELF 32-bit LSB relocatable, Atmel AVR 8-bit, version 1 (SYSV), not stripped
file ./out/main.elf: ELF 32-bit LSB executable, Atmel AVR 8-bit, version 1 (SYSV), statically linked, not stripped

Так что у них обоих одинаковое описание. Мои вопросы:

  • В чем практическая разница между обоими файлами или в случае, если у меня несколько исходных файлов?
  • Какой файл нужно запустить и как его сгенерировать?
  • Нужны ли объектные файлы или они являются только промежуточными файлами?
  • Если я скомпилирую некоторые исходные файлы с параметром -c и некоторыми флагами (-Wall -g -std=c99 -Os) и получу от них объектные файлы, сохранятся ли флаги при генерации файла ELF (могу ли я пропустить флаги при генерации файла ELF, если я использовал их в объектных файлах)?

Ответы [ 2 ]

0 голосов
/ 08 ноября 2018

Давайте сделаем простой пример. У вас есть три файла:

cnt.h

void inc_counter();
void print_counter();

cnt.c

#include <stdio.h>
#include <cnt.h>

static int counter= 0;

void inc_counter() {
    couner++;
}

void print_counter() {
    printf("Counter: %d\n", counter);
}

main.c

#include <counter.h>

int main(char** args) {
    inc_counter();
    print_counter();
    return 0;
}

Затем вы компилируете cnt.c и main.c для создания cnt.o и main.o.

  • cnt.o будет содержать исполняемый код для get_counter и inc_counter. Для каждого есть точка входа. Но код не исполняемый. Вызов printf не будет работать, так как адрес printf еще не известен. Таким образом, файл содержит информацию о том, что это нужно будет исправить позже.
  • main.o будет содержать исполняемый код для main и точку входа для него. Опять же, ссылки для inc_counter и print_counter не будут работать.

На втором шаге файлы cnt.o, main.o и стандартная библиотека C будут связаны, и будет создан исполняемый файл вывода (с расширением .elf или без него). Компоновщик создаст недостающие ссылки между вызовом inc_counter и функцией inc_counter. И он сделает то же самое для print_counter и printf, включая код printf из стандартной библиотеки.

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

Примечание. Существуют дополнительные варианты при создании или использовании динамически связанных библиотек. Но ради простоты я их исключил.

0 голосов
/ 08 ноября 2018
  • В чем практическая разница между обоими файлами, или если у меня несколько исходных файлов?

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

  • Какой файл нужно запустить и как его сгенерировать?

Это исполняемый файл (.exe в windows). Он генерируется этапом компоновки (компоновщиком), который ищет библиотеки и другие объектные файлы для разрешения внешних ссылок из файлов .o.

  • Нужны ли объектные файлы или они являются только промежуточными файлами?

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

  • Если я скомпилирую некоторые исходные файлы с параметром -c и некоторыми флагами (-Wall -g -std = c99 -Os) и получу из них объектные файлы, сохранятся ли флаги при генерации файла ELF (можно ли пропустить флаги при создании файла ELF, если я использовал их в объектных файлах)?

Некоторые флаги «сохраняются» в том смысле, что они определяют файлы .o, но не все. -Wall выдает только предупреждения во время компиляции, -Os указывает некоторый тип оптимизации, который приведет к .o файлу с некоторой оптимизацией по выполненному коду.

...