Как сделать статическую линковку

Добавил пользователь Валентин П.
Обновлено: 05.10.2024

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

С динамической загрузкой вызываемых подпрограмм тесно связан другой родственный механизм – динамическая линковка: линковка во время исполнения программы. Разумеется, это не означает, что во время выполнения область кода программы расширяется, и к ней добавляется код динамически линкуемой подпрограммы. Используется иная схема. В коде программы размещается заглушка для исполнения (execution stub) – небольшой фрагмент кода, выполняющий системный вызов модуля ОС, размещающего в памяти код динамически линкуемой библиотечной подпрограммы. При первом вызове заглушка заменяет себя на код обращения по адресу динамически размещенной в памяти подпрограммы. Операционная система при вызове динамически линкуемого модуля должна проверить, размещен ли его код в адресном пространстве процесса . Очевидно, что динамическая линковка наиболее целесообразна для библиотек. Файл бинарного кода динамически линкуемой библиотеки имеет в системе UNIX расширение имени .so ( аббревиатура термина shared object ), в системе Windows – расширение имени .dll (аббревиатура от dynamically linked library ).

Возникает, однако, вовсе не философский вопрос: каково должно быть оптимальное соотношение статической и динамической линковки в системе? Следует ли ограничиваться только статической или только динамической загрузкой и линковкой? На наш взгляд, следует соблюдать "золотую середину". В операционных системах прошлых лет в этом отношении принимались подчас самые экзотические решения. В ОС "Эльбрус", например, разработчики пошли по чересчур радикальному, на наш взгляд, пути – вообще исключили статическую линковку и все независимые программы загружали только динамически (с помощью механизма ПРОГР, который в "Эльбрусе" назывался открытием программы,или динамическим знакомством ). К чему это привело на практике, хорошо помнят мои коллеги из СПбГУ – разработчики математических пакетов прикладных программ , которые мы с ними в 1980-х гг. переносили с ЕС ЭВМ на "Эльбрус". Они быстро освоили новую конструкцию ПРОГР и обращения ко всем независимо компилируемым модулям оформили именно таким образом. В результате очень сильно замедлилось суммарное время выполнения программы. Это и понятно: реализация каждой математической функции как динамически загружаемой программы – слишком "дорогая" операция, требующая вмешательства ОС, по крайней мере, при первом обращении к каждой такой программе, по сравнению с обычным обращением, например, к функции sin как к подпрограмме (процедуре), элементу статически линкуемой библиотеки, обычной машинной командой вызова процедуры.

Оверлейная структура программы

Как мы уже отмечали во вводных лекциях, в ранних ОС, в особенности – для персональных компьютеров, для пользовательского процесса были вынужденно введены очень жесткие ограничения по памяти, - например, в MS DOS – не более 640 килобайт . При таком дефиците основной памяти, если программа оказывается настолько велика, что полностью не помещается в память максимально разрешенного объема, необходимо предпринимать специальные меры при разработке программы, чтобы разбить ее на непересекающиеся группы модулей, такие. что в каждой группе модули логически взаимосвязаны и должны присутствовать в памяти одновременно, модули же разных групп не обязательно должны вместе загружаться в память . Во время исполнения такой программы должен использоваться специальный системный механизм, называемый оверлейная структура ( overlay ,дословно – наложение ), обеспечивающий поочередную загрузку в одну и ту же область памяти то одной, то другой исполняемой группы модулей. Простая программа , которая выполняет эти действия, называется драйвер оверлея ( overlay driver ).Интегрированная среда разработки Турбо Паскаль обеспечивала специальные опции компилятора, которые позволяли явно указывать модули, входящие в каждый оверлей.

И я'вэ успешно удалось связать статически с libstdc++-6.dll " и " libgcc_s_sjlj-1.dll с помощью -статический-на libgcc -статический-с libstdc++ параметров, но я не могу найти команда делает то же самое с libwinpthread-1.dll .

Если ваш набор инструментов включает в себя статические winpthreads, добавив опцию

Будет тянуть в статической версии всех библиотек она может.

Кроме того, вы можете удалить libwinpthread.библиотека DLL.сам и dll из инструментов для работы с каталогами. Это может испортить программ связывание с libstdc++ и на libgcc DLL файлов, так что будьте осторожны.

Третий вариант-использовать опцию -WL,-Bdynamic и -от WL,-Bstatic , чтобы выбрать, какую версию вы хотите связать в (что статические внутренне, когда ЛД называется). Пример:

Если вы выполните команду связь с -в добавлены, вы должны увидеть эти опции появляются в ЛД/вызов взыскать 2 Когда вы используете -статический-на libgcc и -статический-с libstdc++ .

Обратите внимание на -lstdc++ " до " - lpthread . Он работал для меня.

Обязательно добавьте это в конец командной строки г++ .

Вы, вероятно, следует проверить параметры командной строки в документации по GCC.

Это'ы не '-статический-то' команда, только стандартные библиотеки (на libgcc и libstdc++) может быть установлен для статической компоновки с одной командой. Для других библиотек, вы сначала переключиться на статическое связывание с " статический" и тогда список библиотек, чтобы включить команды отдельно, т. е. " по-lpthread-то".

Для статически winpthread даже если резьбы нет't, используемый в программе, проходят -Bstatic " и " . все-архиве параметры компоновщика:

Обратите внимание на следующее:

Для тех, кто работает в CMake, это решение легко реализуется в ваш файл CMakeLists.txt как следует.

Видимо, то CMake делает некоторые странные вещи, как -З флаги компилятора обрабатываются, что делает -от WL,-Bstatic -lstdc++ -lwinpthread -от WL,-Bdynamic решение не работает, только два других варианта, казалось бы, осталось: плохой компилятор флаг-static и уродливые компилятор флаг-от WL,--все-архиве .

Между тем хорошим вариантом, который фактически работает в CMake, но, кажется, достаточно документов, чтобы напрямую использовать флаги компоновщика. Следовательно, в CMake, это, кажется, лучший способ, чтобы статически связать все с MinGW-w64 с зависимостями++:

Следует отметить, что даже если там нет'т библиотека явно ниже `-динамическая, она должна еще быть применен для того, чтобы обеспечить стандарт, косвенно связанные библиотеки вам правильно связаны между собой.

Антон Бовкун

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

DELETED

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

DELETED

статически! только статически! преимуществ очень много - и удобно распространять проект, и не зависит от других файлов, и сжимается UPX'ом прекрасно, более медленный запуск приложения (на какие-то доли секунды медленней) окупается портативностью приложения. Пользователю не придется мудохаться с поиском необходимых библиотек, а огромное количество файлов, посылаемых в архиве с динамической программой впихивается в прогу, превращая ее в 1 файл, а вес архива с динамикой == весу проги статической. Лишь один минус.
>>возможность подменить библиотеку без пересборки всего приложения, .

невозможен((( А так это просто КУЛ!

DELETED

Василий Радченко

DELETED

Василий Радченко

Станислав Ершов

Антон G-virus Алексеев

а в чем проблема передать архив с кучей файлов? религия? Если вас это так мучает, тогда зачем вы выбрали Qt?

DELETED

Какая религия?! Это извращение. Может еще вирусы писать на Qt xDD и динамически их собирать xDD

Василий vasist Радченко
Если даже для калькулятора будет требоваться установщик, что будет тогда потом. mp3 файл будет в архиве с плагином для его запуска?

Антон Бовкун

Станислав Ершов

Антон G-virus Алексеев

может сначала почитаете для чего динамическую линковку придумали и попытаете счасться получить просветление?
Вас же не смущает то что все виндовые приложения слинкованы с динамическими библиотками.

DELETED

Станислав Ершов

Вспомнил еще один не маловажный пункт против статичной сборки. LGPL ее запрещает. А в случае с GPL вы должны предоставлять сорцы своего творения. :)

DELETED

Ниче, если надо - предоставлю :)(вообще я бы и не знал о статической сборки проектов до поры до времени, если бы не одно но! Вы так упорно спорите, что динамика лучше. Я собирал Qt с такой ориентацией, что Debug собирает проект динамически, а Release - статически) Но проблема вот в чем. Когда собрал статически, почему-то как я все файлы засуну в папку с ехе, и пытаюсь запустить прогу, он какую-то строку не может найти в DLLили что-то типо того. Может чем спорить и троллить, объясните, что я неправильно делаю?

P.S. Мне малость понравилась идейка с заменой функций без пересборки проекта

Ігор Джулай

Я тоже раньше задался таким вопросом, что же лучше статик или шаред.
Решил протестировать какое приложение работает быстрее то что в статике или в шареде.
Собрал два дистриба Qt4.6.2 статик и шаред,
создал пустой проэкт, набросил 8 обьектов на UI, в мейне поставил return 0;
(это значит что программа загрузит все библиотеки построит UI и сразу после этого завершит работу) скомпилировал два варианта программы.
Дальше написал программу которая через QProcess вызвывает exe и ждет ее завершения посчитав время работы в мс.
Поочередно запустил оба варианты программы с одинаковым кодом.

Результаты:
Static: 292ms.
Shared: 330ms

Результаты под UPS сжатием ехе файлов:
Static: 433ms.
Shared: 573ms

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

Всем доброго утречка!

Речь идет о статической компиляции или статической линковке — я не знаю как правильно это называется. Но мне надо сделать следующее.
У меня есть проект в visual studio 2008 по созданию dll-ки. В ней я использую Qt-шную библиотеку (ну и mico еще): в данном случае QtCore. Как мне получить на выходе dll-ку, которая не нуждалась бы в Qt-шном файле типа QtCore.dll. то есть я хочу поместить необходимый код от Qt-шной библиотеки в свою dll-ку.

Я пытался сделать вот как.
Первый путь, подсказанный мне сердцем. выставлял в C/C++->Code Generation->Runtime Library->Multi-threaded Debug ( вместо Multi-threaded Debug DLL). Соответственно в Linker->Input прописан файл QtCore.lib. Размер dll-ки на выходе увеличивается, но все-равно необходимо использовать внешний файл QtCore.dll.

Второй путь. Пытался воспользоваться ссылками References (через Common Properties->Framework and References). Делал все как в примере на msdn. То есть добавил в Solution для dll-ки проект от QtCore, который лежат в папке с Qt. В References он обнаружился и я его подцепил. из Linker-Input убрал QtCore.lib. При компиляции пишет про ошибку линковки для Qt-шных функций. конечно, если вернуть QtCore.lib в Linker-Input их не возникает, но получается не то, что мне надо.

Вопрос как правильно сделать, то что я хочу?
И поясните, пожалуйста, за что отвечает первый путь, а за что — второй?
И что такое статическая линковка и статическая компиляция, и есть ли между ними разница?
Я понимаю, что меня сейчас, скорее всего, отошлют читать доки, но хотелось бы просто услышать ответ от людей с опытом.

Да прибудет с вам сила

Здравствуйте, mannyz, Вы писали:

M>Всем доброго утречка!

M>Вопрос как правильно сделать, то что я хочу?
M>И поясните, пожалуйста, за что отвечает первый путь, а за что — второй?
M>И что такое статическая линковка и статическая компиляция, и есть ли между ними разница?
Это в доки.

M>Я понимаю, что меня сейчас, скорее всего, отошлют читать доки, но хотелось бы просто услышать ответ от людей с опытом.
Чтобы сделать то что ты хочешь тебе нужна статическая библиотека QtCore, а не библиотека импорта, которую ты используешь сейчас. На данный момент ты просто статически линкуешься с DLLкой.

Здравствуйте, servancho, Вы писали:

S>Здравствуйте, mannyz, Вы писали:

M>>Всем доброго утречка!

M>>Вопрос как правильно сделать, то что я хочу?
M>>И поясните, пожалуйста, за что отвечает первый путь, а за что — второй?
M>>И что такое статическая линковка и статическая компиляция, и есть ли между ними разница?
S>Это в доки.
доки не всегда помогают, а иногда и запутывают. поэтому я и здесь. ведь назначение форума вывести к свету страждующих, а я как раз имеено такой ). поясните своими словами, если не сложно. с упором на то, что ваш слушатель из пту)).
или дайте, пожалуйста, правильные ссылки.

M>>Я понимаю, что меня сейчас, скорее всего, отошлют читать доки, но хотелось бы просто услышать ответ от людей с опытом.
S>Чтобы сделать то что ты хочешь тебе нужна статическая библиотека QtCore, а не библиотека импорта, которую ты используешь сейчас. На данный момент ты просто статически линкуешься с DLLкой.
ну, в том lib-файле (QtCore.lib) кажется имеется сам код, ибо весит он соразмерно c QtCore.dll.
И все-таки можно какую-нибудь инструкцию для одаренных по тому, как сделать то, что я хочу. Пожалуйтсаааааа!

Здравствуйте, mannyz, Вы писали:

M>>>Я понимаю, что меня сейчас, скорее всего, отошлют читать доки, но хотелось бы просто услышать ответ от людей с опытом.
S>>Чтобы сделать то что ты хочешь тебе нужна статическая библиотека QtCore, а не библиотека импорта, которую ты используешь сейчас. На данный момент ты просто статически линкуешься с DLLкой.
M>ну, в том lib-файле (QtCore.lib) кажется имеется сам код, ибо весит он соразмерно c QtCore.dll.
M>И все-таки можно какую-нибудь инструкцию для одаренных по тому, как сделать то, что я хочу. Пожалуйтсаааааа!
Как минимум открой доку по Qt либе и найди там про то как с ней компилиться. Мне ее читать лень.
Я ее не видел никогда эту либу, может в понедельник тебе имевшие дело с ней отпишут.

1>------ Build started: Project: omc_interface, Configuration: Release Win32 ------
1>Linking.
1> Creating library D:\пре-проектФедерук\__my_part\modelica-api\omc_interface\Release\\omc_interface.lib and object D:\пре-проектФедерук\__my_part\modelica-api\omc_interface\Release\\omc_interface.exp
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) protected: virtual void __thiscall QObject::disconnectNotify(char const *)" (__imp_?disconnectNotify@QObject@@MAEXPBD@Z)
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) protected: virtual void __thiscall QObject::connectNotify(char const *)" (__imp_?connectNotify@QObject@@MAEXPBD@Z)
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) protected: virtual void __thiscall QObject::customEvent(class QEvent *)" (__imp_?customEvent@QObject@@MAEXPAVQEvent@@@Z)
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) protected: virtual void __thiscall QObject::childEvent(class QChildEvent *)" (__imp_?childEvent@QObject@@MAEXPAVQChildEvent@@@Z)
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) protected: virtual void __thiscall QObject::timerEvent(class QTimerEvent *)" (__imp_?timerEvent@QObject@@MAEXPAVQTimerEvent@@@Z)
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: virtual bool __thiscall QObject::eventFilter(class QObject *,class QEvent *)" (__imp_?eventFilter@QObject@@UAE_NPAV1@PAVQEvent@@@Z)
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: virtual bool __thiscall QObject::event(class QEvent *)" (__imp_?event@QObject@@UAE_NPAVQEvent@@@Z)
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: virtual __thiscall QObject::~QObject(void)" (__imp_??1QObject@@UAE@XZ)
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __thiscall QObject::QObject(class QObject *)" (__imp_??0QObject@@QAE@PAV0@@Z)
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: static class QString __cdecl QDir::tempPath(void)" (__imp_?tempPath@QDir@@SA?AVQString@@XZ)
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: static class QChar __cdecl QDir::separator(void)" (__imp_?separator@QDir@@SA?AVQChar@@XZ)
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __int64 __thiscall QIODevice::readLine(char *,__int64)" (__imp_?readLine@QIODevice@@QAE_JPAD_J@Z)
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: class QByteArray __thiscall QString::toLatin1(void)const " (__imp_?toLatin1@QString@@QBE?AVQByteArray@@XZ)
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: class QString __thiscall QString::trimmed(void)const " (__imp_?trimmed@QString@@QBE?AV1@XZ)
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: int __thiscall QString::indexOf(class QString const &,int,enum Qt::CaseSensitivity)const " (__imp_?indexOf@QString@@QBEHABV1@HW4CaseSensitivity@Qt@@@Z)
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: class QString & __thiscall QString::operator=(class QString const &)" (__imp_??4QString@@QAEAAV0@ABV0@@Z)
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: virtual bool __thiscall QFile::open(class QFlags )" (__imp_?open@QFile@@UAE_NV?$QFlags@W4OpenModeFlag@QIODevice@@@@@Z)
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: bool __thiscall QFile::exists(void)const " (__imp_?exists@QFile@@QBE_NXZ)
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: void __thiscall QFile::setFileName(class QString const &)" (__imp_?setFileName@QFile@@QAEXABVQString@@@Z)
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: virtual __thiscall QFile::~QFile(void)" (__imp_??1QFile@@UAE@XZ)
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __thiscall QFile::QFile(void)" (__imp_??0QFile@@QAE@XZ)
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) private: static struct QString::Data QString::shared_null" (__imp_?shared_null@QString@@0UData@1@A)
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: class std::basic_string ,class std::allocator > __thiscall QString::toStdString(void)const " (__imp_?toStdString@QString@@QBE?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ)
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __thiscall QString::~QString(void)" (__imp_??1QString@@QAE@XZ)
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __thiscall QString::QString(void)" (__imp_??0QString@@QAE@XZ)
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __thiscall QString::QString(class QString const &)" (__imp_??0QString@@QAE@ABV0@@Z)
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: class QString & __thiscall QString::operator=(char const *)" (__imp_??4QString@@QAEAAV0@PBD@Z)
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __thiscall QString::QString(char const *)" (__imp_??0QString@@QAE@PBD@Z)
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: class QString & __thiscall QString::operator+=(class QString const &)" (__imp_??YQString@@QAEAAV0@ABV0@@Z)
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: class QString & __thiscall QString::operator+=(class QChar)" (__imp_??YQString@@QAEAAV0@VQChar@@@Z)
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __thiscall QByteArray::operator char const *(void)const " (__imp_??BQByteArray@@QBEPBDXZ)
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __thiscall QByteArray::~QByteArray(void)" (__imp_??1QByteArray@@QAE@XZ)
1>omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: bool __thiscall QBasicAtomicInt::ref(void)" (__imp_?ref@QBasicAtomicInt@@QAE_NXZ)
1>moc_omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: virtual int __thiscall QObject::qt_metacall(enum QMetaObject::Call,int,void * *)" (__imp_?qt_metacall@QObject@@UAEHW4Call@QMetaObject@@HPAPAX@Z)
1>moc_omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: virtual void * __thiscall QObject::qt_metacast(char const *)" (__imp_?qt_metacast@QObject@@UAEPAXPBD@Z)
1>moc_omc_communicator.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: static struct QMetaObject const QObject::staticMetaObject" (__imp_?staticMetaObject@QObject@@2UQMetaObject@@B)
1>D:\пре-проектФедерук\__my_part\modelica-api\omc_interface\Release\\omc_interface.dll : fatal error LNK1120: 36 unresolved externals
1>Build log was saved at "file://d:\пре-проектФедерук\__my_part\modelica-api\omc_interface\Release\BuildLog.htm"
1>omc_interface — 37 error(s), 0 warning(s)
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

В обоих версиях добавлял не хватающие lib-ы в Linker->Input из примеров, идущих с Qt.

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

Подскажите, пожалуйста, где я мог напортачить? И как можно докопаться до истины, которую искали Скалли с Маудером?
Длл-ка компилится в debug-версии, но не подхватывается теперь.

Одной из самых важнейших проблем для разработчика в мире Linux является создание правильных пакетов. Это обычно вызывает множество вопросов, поэтому в данной статье мы решили подробно их разобрать.

Введение

Большинство разработчиков считают, что правильным является создание одного RPM и одного DEB пакета, однако это в корне неверно. Как RPM, так и DEB являются лишь контейнерами с метаданными для того, что находится у них внутри.

В каждом дистрибутиве RPM/DEB пакеты имеют свои, зачастую ни с кем не совместимые, особенности. Также каждый дистрибутив имеет совершенно разные наборы и версии библиотек.

Например если мы соберём пакет в Fedora 29 и динамически привяжемся к Qt 5.11 или ffmpeg 4.0, то он в большинстве случаев не будет работать в более старой версии Fedora 26 с Qt 5.6 и ffmpeg 3.1. Это же касается и Debian/Ubuntu. Обратная совместимость API/ABI внутри библиотек работает обычно лишь в более новых версиях относительно старых, однако некоторые на неё вообще забивают.

Также RPM пакет, собранный для Fedora, скорее всего не будет работать в openSUSE или ALT Linux, поэтому если и собирать пакеты, то это нужно делать для каждого дистрибутива в отдельности и в пределах как минимум двух поддерживаемых версий.

Способы линковки

Существует два способа компоновки приложения с необходимыми ему библиотеками:

  • динамическая — компоновщик линкует бинарник с уже существующими в системе динамическими библиотеками. При запуске они подгружаются в адресное пространство;
  • статическая — все необходимые зависимости вшиваются внутрь бинарника статически.

Каждый способ имеет как достоинства, так и недостатки. Рассмотрим их более подробно.

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

  • позволяет использовать разделяемую память библиотек, т.е. если какое-то запущенное приложение уже использует её, она не дублируется в памяти, что ускоряет запуск и снижает общее потребление памяти;
  • позволяет оперативно исправлять уязвимости в сторонних библиотеках. Например если в libfoo обнаружена уязвимость, её мейнтейнеры в дистрибутиве выпустят обновление и после его установки будет достаточно лишь перезапустить наше приложение, чтобы оно подействовало.
  • мы привязываем свой бинарник к конкретной версии библиотеки, которая затем должна быть установлена в системе и иметь ту же, либо более новую версию (если имеется обратная совместимость по API/ABI);
  • сложность распространения проприетарного ПО. В данном случае потребуется каким-то способом включать все используемые динамические библиотеки в дистрибутив (об этом далее).

Статическая линковка

  • всё, от чего зависит бинарник, вшивается непосредственно в него, поэтому он может быть запущен где угодно, на любой системе;
  • нет проблем с зависимостями.
  • если в используемой библиотеке libfoo будет найдена уязвимость, потребуется полная пересборка и перелинковка всего проекта, а затем распространение обновлённой версии. Часто такое ПО остаётся уязвимым навсегда;
  • нет возможности использования разделяемой памяти. Все библиотеки загружаются в адресное пространство процесса, даже если их копии уже присутствуют в памяти;
  • огромный размер бинарника;
  • проблемы с лицензированием для проприетарного ПО. Ряд лицензий, например GPL, запрещают статическую линковку с собственническим ПО. Лицензия LGPL (под ней распространяется например Qt) статическую линковку допускает, но при этом требует предоставления хотя бы объектных файлов всем покупателям для возможности перелинковки.

Альтернативы RPM/DEB пакетам

Чтобы не собирать пакеты под каждый из дистрибутивов по отдельности, были придуманы так называемые самодостаточные пакеты: Flatpak, AppImage, Snap.

Flatpak

Это наверное самый лучший из самодостаточных пакетов. Поддерживает динамическую линковку с большим количеством библиотек из рантаймов, что решает проблемы с лицензированием, их поддержкой в актуальном состоянии и исправлением в них ошибок, а также уязвимостей. Рантаймы оперативно обновляются мейнтейнерами в пределах версии. Например мы можем слинковаться с Qt версии 5.11 и openssl 1.1.0 и указать их в манифесте, тогда они будут автоматически загружены и установлены у пользователей. Ошибки и уязвимости в них будут исправляться автоматически.

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

Flatpak позволяет установить разные версии приложений и библиотек одновременно. К тому же для установки не требуются права администратора.

Также Flatpak поддерживает изоляцию приложения внутри контейнера. В манифесте может быть указано к каким каталогам и ресурсам будет иметь доступ приложение. Всё остальное для него будет запрещено. Права очень гибко настраиваются.

Созданные флатпаки могут загружаться и использоваться на любом дистрибутиве GNU/Linux, поддерживающем их использование.

Репозиторий Flatpak пакетов создаётся достаточно легко и может хоститься на любом ресурсе, позволяющем размещение статических файлов по прямым ссылкам (даже на GitHub pages).

Документацию по Flatpak можно найти на официальном сайте вместе с готовыми примерами.

Нужно ли вообще собирать RPM/DEB пакеты?

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

Для разработчиков собственнического ПО есть несколько вариантов:

  1. использовать динамическую линковку и собирать пакеты для каждого популярного дистрибутива в пределах поддерживаемых версий;
  2. использовать статическую линковку (если нет проблем с лицензиями) и создавать статические бинарники внутри generic RPM/DEB пакетов;
  3. использовать динамическую линковку и использовать RPATH при сборке бинарника (либо вместо RPATH использовать механизм LD_PRELOAD), а затем упаковывать все необходимые библиотеки внутрь generic RPM/DEB пакета вне стандартной library path, чтобы не вызывать проблем с зависимостями у общесистемных пакетов;
  4. использовать самодостаточные пакеты, например вышеупомянутый Flatpak.

Для всех разработчиков проприетарного ПО мы настоятельно рекомендуем внедрять Flatpak и по возможности максимально использовать его рантаймы.

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