Почему мне нужно вручную связывать библиотеку времени выполнения C при создании EXE-файла из статических библиотек без каких-либо объектных файлов?
Я довольно новичок в работе с библиотеками, и я пытаюсь понять некоторые особенности статических библиотек и объектных файлов.
Резюме
Поведение, которое я замечаю, состоит в том, что я могу без проблем связать несколько объектов, чтобы сделать исполняемый файл, но если я сделаю промежуточный шаг по объединению этих объектов в статические библиотеки, я не смогу связать эти статические библиотеки, чтобы сделать исполняемый файл без дополнительного указания нужна библиотека времени выполнения C в команде link.
Кроме того, или запись, я делаю компиляцию / связывание с Visual Studio 2010 из командной строки. Более подробная информация о процессе, которому я следую, приведена ниже.
Во-первых, допустим, у меня есть четыре исходных файла в проекте: main.c
, util1.c
, util2.c
, а также util3.c
,
Что работает
Я могу скомпилировать эти источники с помощью следующей команды:
cl -c main.c util1.c util2.c util3.c
В результате у меня теперь есть четыре объектных файла:main.obj
,util1.obj
,util2.obj
, а такжеutil3.obj
, Каждый из этих объектных файлов содержит оператор DEFAULTLIB, предназначенный для информирования компоновщика о том, что он должен дополнительно проверить статическую C-библиотеку времени выполнения libcmt.lib на наличие неразрешенных внешних зависимостей в этих объектных файлах при их связывании.Я могу создать исполняемый файл с именем app_objs.exe, связав эти объекты с помощью следующей команды:
link -out:app_objs.exe main.obj util1.obj util2.obj util3.obj
Как упомянуто в шаге 1, компоновщик использовал библиотеку времени выполнения из-за шага компилятора по добавлению оператора библиотеки по умолчанию к объектам.
Где я запутался
Допустим, я хочу выполнить промежуточный шаг - объединить эти объекты в статические библиотеки, а затем связать полученные результирующие файлы LIB для создания моего исполняемого файла. Сначала я могу создать эти библиотеки с помощью следующих команд:
link -lib -out:main.lib main.obj
link -lib -out:util.lib util1.obj util2.obj util3.objТеперь моя первоначальная мысль заключалась в том, что я мог бы просто связать эти библиотеки и получить тот же исполняемый файл, который я создал на шаге 2 "Что работает". Я попробовал следующую команду и получил ошибку компоновщика LNK1561, в которой говорится, что необходимо указать точку входа:
link -out:app_libs.exe main.lib util.lib
Из документации Microsoft видно, что для связывания библиотек без каких-либо объектных файлов может потребоваться указание точек входа, поэтому я изменил команду, чтобы установить подсистему как "консоль", чтобы указать, что исполняемый файл предназначен для консольного приложения (которое кажется подразумевать определенные точки входа, тем самым устраняя эту ошибку):
link -out:app_libs.exe -subsystem:console main.lib util.lib
К сожалению, теперь я получаю ошибку компоновщика, утверждающую, что mainCRTStartup является неразрешенным внешним символом. Я понимаю, что это определено в библиотеке времени выполнения C, поэтому я могу решить эту проблему, указав вручную, что я хочу связать с libcmt.lib, и это дает мне работающий исполняемый файл:link -out:app_libs.exe -subsystem:console main.lib util.lib libcmt.lib
Я не понимаю, почему информацию библиотеки по умолчанию, которую компилятор помещал в каждый объектный файл, нельзя было использовать для разрешения зависимости от libcmt.lib. Если я могу связать объектные файлы без явного указания, что я хочу libcmt.lib, и я создал статические библиотеки, которые являются контейнерами для объектных файлов, почему я не могу связать эти статические библиотеки без необходимости явно заявлять, что я хочу libcmt.lib? Это так, как есть, или я мог бы создать статические библиотеки, чтобы компоновщик знал, проверять ли неразрешенные символы в библиотеке времени выполнения?
Спасибо за вашу помощь. Если у меня есть какие-то принципиально неправильные идеи, я бы с удовольствием порекомендовал хорошие рекомендации, чтобы узнать все это правильно.
1 ответ
Ответ на ваше недоразумение заключается в том, что файлы.lib часто сами по себе являются продуктом, и компилятор не может сделать эти предположения безопасно. Вот для чего "внешний".
Если я создаю двоичные файлы для чьей-то платформы, потому что ее пользователи абсолютно беспомощны и им нужна / нужна статическая связь, я должен предоставить им foo.h и libfoo.lib, не привязывая их к определенной точке входа во время выполнения. Возможно, они уже определили собственную точку входа для своего конечного продукта, будь то DLL или EXE.
Вы хотите либо среду выполнения, либо свой собственный.obj, содержащий вашу точку входа. Имейте в виду, что объявление и определение mainCRTStartup самостоятельно может означать, что вы не выполняете важные инструкции для целевой платформы.