Процесс компиляции - PullRequest
       17

Процесс компиляции

7 голосов
/ 04 февраля 2009

Может кто-нибудь объяснить, как работает компиляция?

Я не могу понять, как работает компиляция ..

Чтобы быть более конкретным, вот пример ... Я пытаюсь написать код в MSVC ++ 6 для загрузки состояния Lua ..

Я уже:

  • установить дополнительные каталоги для библиотеки и включить файлы в нужные каталоги
  • использовал extern "C" (потому что Lua только C или я так слышу)
  • включает нужные заголовочные файлы

Но я все еще получаю некоторые ошибки в MSVC ++ 6 о неразрешенных внешних символах (для функций Lua, которые я использовал).

Как бы мне ни хотелось знать, как решить эту проблему и двигаться дальше, я думаю, что для меня было бы намного лучше, если бы я понял основные процессы, вовлеченные в процесс, поэтому кто-нибудь мог бы написать хорошее объяснение этого ? То, что я хочу знать, это процесс .. Это может выглядеть так:

Шаг 1:

  • Ввод: исходный код (ы)
  • Процесс: Разбор (возможно, добавьте больше деталей здесь)
  • Вывод: все, что здесь выводится ..

Шаг 2:

  • Ввод: все, что было выведено на шаге 1, плюс, может быть, все, что еще нужно (библиотеки? DLL? .So? .Lib?)
  • Процесс: что бы ни делалось с помощью ввода
  • Вывод: что бы ни выводилось

и т. Д. *

Спасибо ..

Может быть, это объяснит, что такое символы, что такое "связывание", что такое "объектный" код или что-то еще ..

Спасибо .. Извините за то, что я такой нуб ..

P.S. Это не обязательно должно зависеть от языка .. Но не стесняйтесь выражать это на языке, который вам наиболее удобен ..:)

РЕДАКТИРОВАТЬ : Так или иначе, мне удалось устранить ошибки, оказалось, что мне нужно вручную добавить файл .lib в проект; простое указание каталога библиотеки (где находится .lib) в настройках IDE или в настройках проекта не работает ..

Однако приведенные ниже ответы несколько помогли мне лучше понять процесс. Большое спасибо! .. Если кто-то все еще хочет написать подробное руководство, пожалуйста, сделайте ..:)

РЕДАКТИРОВАТЬ : Просто для дополнительной справки, я нашел две статьи одного автора (Майк Дил), чтобы объяснить это довольно хорошо .. :) Исследование процесса компиляции: часть 1 Исследование процесса компиляции: часть 2

Ответы [ 5 ]

13 голосов
/ 04 февраля 2009

От исходного кода к исполняемому файлу обычно является двухэтапным процессом для C и связанных языков, хотя в среде IDE это, вероятно, представляется как один процесс.

1 / Вы кодируете свой исходный код и запускаете его через компилятор. На этом этапе компилятору нужны ваш исходный код и файлы заголовков других материалов, с которыми вы собираетесь связать (см. Ниже).

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

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

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

В качестве примера рассмотрим следующий упрощенный код C (xx.c) и команду.

#include <bob.h>
int x = bob_fn(7);

cc -c -o xx.obj xx.c

Компилирует файл xx.c в xx.obj. bob.h содержит прототип для bob_fn(), поэтому компиляция будет успешной. -c указывает компилятору генерировать объектный файл, а не исполняемый файл, а -o xx.obj устанавливает имя выходного файла.

Но фактический код для bob_fn() находится не в заголовочном файле, а в /bob/libs/libbob.so, поэтому для связи вам нужно что-то вроде:

cc -o xx.exe xx.obj -L/bob/libs;/usr/lib -lbob

Создает xx.exe из xx.obj, используя библиотеки (ищутся по заданным путям) вида libbob.so (lib и .so обычно добавляются компоновщиком). В этом примере -L устанавливает путь поиска для библиотек. -l указывает библиотеку, которую необходимо найти для включения в исполняемый файл, если это необходимо. Компоновщик обычно берет «bob» и находит первый соответствующий файл библиотеки в пути поиска, указанном -L.

Файл библиотеки на самом деле представляет собой набор объектных файлов (что-то вроде того, как zip-файл содержит несколько других файлов, но не обязательно сжатых) - когда обнаруживается первое соответствующее вхождение неопределенного внешнего объекта, объектный файл копируется из библиотека и добавлен в исполняемый файл, как ваш xx.obj файл. Обычно это продолжается до тех пор, пока не останется нерешенных внешних факторов. «Соответствующая» библиотека - это модификация текста «bob», она может выглядеть как libbob.a, libbob.dll, libbob.so, bob.a, bob.dll, bob.so и так далее. Актуальность определяется самим компоновщиком и должна быть задокументирована.

Как это работает, зависит от компоновщика, но это в основном все.

1 / Все ваши объектные файлы содержат список неразрешенных внешних объектов, которые им необходимо разрешить. Компоновщик объединяет все эти объекты и исправляет связи между ними (разрешает как можно больше внешних объектов).

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

3 / Повторяйте шаг 2 до тех пор, пока не останется неразрешенных внешних элементов или не будет возможности разрешить их из списка библиотек (именно в этом ваша разработка, поскольку вы не включили файл библиотеки LUA).

Сложность, о которой я упоминал ранее, это динамическое связывание. Здесь вы связываете заглушку подпрограммы (своего рода маркер), а не фактическую подпрограмму, которая позже разрешается во время загрузки (когда вы запускаете исполняемый файл). Такие вещи, как общие элементы управления Windows, находятся в этих библиотеках DLL, так что они могут меняться без необходимости связывать объекты в новый исполняемый файл.

5 голосов
/ 04 февраля 2009

Шаг 1 - Компилятор:

  • Ввод: файл исходного кода [s]
  • Процесс: парсинг исходного кода и перевод в машинный код
  • Вывод: объектный файл [s], который состоит из [s] из:
    • Имена символов, которые определены в этом объекте и которые этот объектный файл "экспортирует"
    • Машинный код, связанный с каждым символом, который определен в этом объектном файле
    • Имена символов, которые не определены в этом объектном файле, но от которых зависит программное обеспечение в этом объектном файле и с которым оно должно быть впоследствии связано, т.е. имена, которые этот объектный файл "импортирует"

Шаг 2 - Связывание:

  • Input:
    • Объектный файл [s] с шага 1
    • Библиотеки других объектов (например, из O / S и другого программного обеспечения)
  • Процесс:
    • Для каждого объекта, который вы хотите связать
    • Получить список символов, которые этот объект импортирует
    • Найти эти символы в других библиотеках
    • Свяжите соответствующие библиотеки с вашими объектными файлами
  • Вывод: один исполняемый файл, который содержит машинный код всех ваших объектов, а также объекты из библиотек, которые были импортированы (связаны) с вашими объектами.
3 голосов
/ 04 февраля 2009

Два основных шага - это компиляция и компоновка.

Компиляция принимает отдельные модули компиляции (это просто исходные файлы со всеми включенными в них заголовками) и создает объектные файлы. Теперь в этих объектных файлах есть много функций (и других вещей, таких как статические данные), определенных в определенных местах (адресах). На следующем шаге, связывая, немного дополнительной информации об этих функциях также необходимо: их имена. Так что они также хранятся. Один объектный файл может ссылаться на функции (потому что он хочет вызывать их, когда выполняется код), которые на самом деле находятся в других объектных файлах, но поскольку мы имеем дело с одним объектным файлом, только символические ссылки (их «имена») на эти другие функции хранятся в объектном файле.

Далее идет ссылка (давайте ограничимся статической связью здесь). Связывание - это то, где объектные файлы, которые были созданы на первом этапе (либо непосредственно, либо после того, как они были брошены вместе в файл .lib), объединяются и создается исполняемый файл. На этапе компоновки все эти символические ссылки из одного объектного файла или lib в другой разрешаются (если они могут быть) путем поиска имен в правильном объекте, поиска адреса функции и помещения адресов в правильные место.

Теперь, чтобы объяснить кое-что о том, что вам нужно для "внешнего" C:

C не имеет функции перегрузки. Функция всегда узнаваема по имени. Поэтому, когда вы компилируете код в виде кода C, в объектном файле сохраняется только настоящее имя функции.

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

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

Для вашей точной проблемы: если это все еще не работает, эти подсказки могли бы помочь: * Были ли двоичные файлы Lua скомпилированы с той же версией VC ++? * Вы можете просто скомпилировать Lua самостоятельно, либо в рамках своего VC-решения, либо в виде отдельного проекта в виде кода C ++? * Вы уверены, что все «внешние» буквы C верны?

1 голос
/ 09 июня 2009
1 голос
/ 04 февраля 2009

Вы должны зайти в настройки проекта и добавить каталог, в котором у вас есть эта библиотека LUA * .lib, где-то на вкладке "компоновщик". Настройка называется "включая библиотеки" или что-то в этом роде, извините, я не могу ее найти.

Причина, по которой вы получаете «неразрешенные внешние символы», заключается в том, что компиляция в C ++ работает в два этапа. Сначала код компилируется, каждый файл .cpp в своем собственном файле .obj, затем запускается «компоновщик» и объединяет все эти файлы .obj в файл .exe. Файл .lib - это просто набор файлов .obj, объединенных для упрощения распространения библиотек. Поэтому, добавив все объявления "#include" и extern, вы сказали компилятору, что где-то можно найти код с этими сигнатурами, но компоновщик не сможет найти этот код, потому что он не знает, где находятся эти файлы .lib с реальным кодом находится.

Убедитесь, что вы прочитали REDME библиотеки, обычно они содержат довольно подробное объяснение того, что вы должны были сделать, чтобы включить ее в свой код.

...