Главная страница » Что такое статическая библиотека

Что такое статическая библиотека

  • автор:

Статические и динамические библиотеки

Библиотека — это «сборник» кода, который можно многократно использовать в самых разных программах. Как правило, библиотека в языке C++ состоит из двух частей:

Заголовочный файл, который объявляет функционал библиотеки.

Предварительно скомпилированный бинарный файл, содержащий реализацию функционала библиотеки.

Некоторые библиотеки могут быть разбиты на несколько файлов и/или иметь несколько заголовочных файлов.

Типы библиотек

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

Есть 2 типа библиотек: статические и динамические.

Статическая библиотека (или «архив») состоит из подпрограмм, которые непосредственно компилируются и линкуются с вашей программой. При компиляции программы, которая использует статическую библиотеку, весь функционал статической библиотеки (тот, что использует ваша программа) становится частью вашего исполняемого файла. В Windows статические библиотеки имеют расширение .lib (сокр. от «library»), тогда как в Linux статические библиотеки имеют расширение .a (сокр. от «archive»).

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

Динамическая библиотека (или «общая библиотека») состоит из подпрограмм, которые подгружаются в вашу программу во время её выполнения. При компиляции программы, которая использует динамическую библиотеку, эта библиотека не становится частью вашего исполняемого файла — она ​​так и остается отдельным модулем. В Windows динамические библиотеки имеют расширение .dll (сокр. от «dynamic link library»), тогда как в Linux динамические библиотеки имеют расширение .so (сокр. от «shared object»). Одним из преимуществ динамических библиотек является то, что разные программы могут совместно использовать одну копию динамической библиотеки, что значительно экономит используемое пространство. Еще одним преимуществом динамической библиотеки является то, что её можно обновить ​​до более новой версии без необходимости перекомпиляции всех исполняемых файлов, которые её используют.

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

Библиотека импорта (англ. «import library») — это библиотека, которая автоматизирует процесс подключения и использования динамической библиотеки. В Windows это обычно делается через небольшую статическую библиотеку (.lib) с тем же именем, что и у динамической библиотеки (.dll). Статическая библиотека линкуется с вашей программой во время компиляции, и тогда функционал динамической библиотеки может эффективно использоваться в вашей программе, как если бы это была обычная статическая библиотека. В Linux общий объектный файл (с расширением .so) дублируется сразу как динамическая библиотека и библиотека импорта. Большинство линкеров при создании динамической библиотеки автоматически создают к ней библиотеку импорта.

Установка библиотек

Теперь, когда мы уже разобрались с типами библиотек, давайте поговорим о том, как их использовать в наших программах. Установка библиотеки в языке C++ состоит из четырех последовательных шагов:

Шаг №1: Получите библиотеку. Наилучшим вариантом является найти уже предварительно скомпилированный код (если он вообще существует) под вашу операционную систему, чтобы вам не пришлось компилировать библиотеку самостоятельно. В Windows библиотеки обычно распространяются в виде архивов (файлов .zip), а в Linux это пакеты кода (например, пакеты .rpm).

Шаг №2: Установите библиотеку. В Linux это делается путем вызова менеджера пакетов, а дальше он всё делает сам. В Windows вам придется разархивировать библиотеку самостоятельно в любую выбранную вами папку. Рекомендуется хранить все используемые библиотеки в одном месте для быстрого доступа к ним. Например, создайте папку Libs ( C:\Libs ) и выделяйте для каждой (используемой вами) библиотеки свою отдельную подпапку.

Шаг №3: Убедитесь, что компилятор знает, где искать заголовочные файлы библиотеки. В Windows это обычно подпапка include внутри основной папки библиотеки (например, если вы установили библиотеку в C:\Libs\SDL-1.2.11 , то заголовочные файлы находятся в C:\Libs\SDL-1.2.11\include ). В Linux библиотеки обычно устанавливаются в /usr/include . Однако, если файлы находятся в другом месте, вам нужно будет сообщить компилятору, где именно.

Шаг №4: Сообщите линкеру, где искать файлы с реализацией функционала библиотеки. Аналогично с предыдущим шагом, вам нужно указать линкеру место, где находятся файлы с реализацией библиотеки. В Windows это обычно подпапка \lib внутри основной папки библиотеки ( C:\Libs\SDL-1.2.11\lib ), а в Linux это обычно /usr/lib .

Использование библиотек

Как только библиотека установлена, ​​и ваша IDE знает, где искать её файлы, то для того, чтобы вы могли использовать эту библиотеку в ваших проектах, вам необходимо выполнить следующие 3 шага:

Шаг №5: Если вы используете статические библиотеки или библиотеки импорта, сообщите линкеру, какие файлы библиотеки нужно связать с вашей программой.

Шаг №6: Подключите заголовочные файлы библиотеки к вашей программе.

Шаг №7: Если вы используете динамические библиотеки, то убедитесь, что исполняемые файлы будут иметь доступ к файлам библиотеки. Самый простой способ использовать .dll — это скопировать .dll в папку с исполняемым файлом. Поскольку .dll-ка обычно распространяется вместе с исполняемым файлом, то это не составит труда.

Заключение

Шаги №3-№5 включают настройку вашей IDE. К счастью, почти все IDE работают одинаково, когда дело доходит до выполнения подобных задач. На следующем уроке мы рассмотрим, как выполнить данные шаги в Visual Studio.

Динамические и статические библиотеки.

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

Статические библиотеки.

И мы зачем-то пытаемся вычленить sum.cpp как библиотеку. Тогда сделать надо вот что: Компилируем:

Что тут происходит?

  • ar — сокращение от «archive».
  • rcs — это некоторая магия (читайте man).
  • libsum.a — название библиотеки.

Чтобы скомпилировать каждый из файлов выше с этот библиотекой, делаем так:

А что происходит тут?

  • -L говорит, в каком каталоге искать библиотеку (в нашем случае в каталоге . — в текущем).
  • -lsum говорит, что нам нужна библиотека, которая называется libsum.a (т.е. к тому, что идёт после -l спереди приписывается lib», а сзади — «.a»).

Тут больше совершенно ничего интересного, потому что статическая библиотека — набор объектных файлов, а про объектные файлы мы всё знаем. То ли дело.

Динамические библиотеки.

Пусть у вас есть библиотека, которая используется везде вообще. Например, libc. Если она статическая, то код библиотеки есть в каждой из программ. А значит в каждой программе они занимают место и на диске, и в памяти. Чтобы этого избежать, применяют динамические библиотеки.
Идея динамических библиотек в том, что мы ссылаемся как-то на внешнюю библиотечку, а потом на этапе исполнения грузим по надобности её части. Тогда она на диске лежит всего одна, и в память мы можем загрузить её один раз.

Давайте в примере выше сделаем статическую библиотеку динамической:

Что значат все консольные опции тут, уже пояснить намного сложнее, и мы поясним их в следующем параграфе.
А пока обратим внимание на то, что когда мы запустим four или five, нам на этапе исполнения скажут, что библиотека libsum.so не найдена. Хотя, казалось бы, вот она рядом лежит. Дело в том, что по умолчанию Linux ищет библиотеки только по системным путям. (Windows ищет и в текущей директории.) Чтобы проверить, от каких библиотек зависит ваша программа, запустите ldd ./four, и вам скажут, что нужна libsum.so, но её нет.

Есть два способа поправить сию оказию:

Первый — явно при запуске прописывать путь до библиотек.
Для этого существует переменная окружения LD_LIBRARY_PATH , если присвоить ей точку, всё сработает

Если вам нужно несколько путей, разделяйте их двоеточиями.

Второй — записать в саму программу, где искать библиотеки.
Это можно посмотреть при помощи objdump в секции Dynamic Section , где есть RUNPATH . Чтобы записать туда что надо, делается вот что:

-Wl говорит, что опцию после него (т.е. -rpath) надо передать линковщику. Линковщику эта опция говорит, что в тот самый RUNPATH надо записать тот путь, который вы попросили.
А какой надо просить? Не «.» ведь, потому что это путь, из которого вы запускаете программу, а не то место, где сама программа.
И тут вам на помощи приходит псевдо-путь $ORIGIN , который и ссылается на место программы. Используя его Вы можете свободно написать что-нибудь по типу -rpath=’$ORIGIN/../lib/’.

Впрочем, есть ещё и третий путь — использовать CMake, который будет делать всю грязную работу за вас, если написать ему команду add_library .

Кстати, в Windows это работает иначе. В-нулевых, динамическая библиотека там называется не shared object, а dynamic load library. Во-первых, DLL-ки сразу же ищутся в текущем каталоге. Во-вторых, чтобы понять, что вы ссылаетесь на динамическую библиотеку, в Linux вы пишете -L. -lsum, а в Windows компиляция DLL создаёт вам специальный .lib-файл, который называется import-библиотекой, и с которым вы компилируете вашу программу, чтобы она сама поняла, откуда какие функции брать.

Причины нестандартной компиляции динамических библиотек.

Нас интересуют магические слова -fpic и -shared. Зачем на как-то особенно компилировать динамические библиотеки?

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

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

Самый простой способ жить с динамическими библиотеками был у Microsoft на 32-битной Windows. У каждой библиотеки был base-address — то куда библиотеке хочется загрузиться. Если там свободно — туда она и загружается, а если нет, то библиотеку загружают туда, где есть место, а в специальной отдельной секции (.reloc) хранится список адресов, которые надо исправить. Разумеется, в случае релокаций умирает переиспользование библиотеки, но Windows вам же полностью предоставляют, там можно расположить системные библиотеки так, как хочется, поэтому в проприетарных системах всё будет хорошо.

В Linux же это реализовано следующим образом. Смотрите как можем:

Тут идея в том, что мы записываем адрес текущей команды на стек, потом берём его со стека, а дальше вместо того, чтобы писать абсолютный адрес переменной myvar , пишем относительный.
Относительный адрес позволяет нам обращаться к ней, если мы не знаем, в какое место памяти будет загружена библиотека, что нам и надо.

Вообще мы не очень хорошо написали (процессор не любит непарные call и pop ), поэтому обычно это выглядит так:

Этот код называется position-independent code, и ключ -fpic именно генерацией такого кода и занимается. Вопрос — почему для этого не сделали специальную инструкцию? А вот сделали, но в 64-битном режиме. Всё что с квадратными скобками стало уметь обращаться в память начиная со смещения текущей инструкции. И называется это RIP-relative positioning.

GOT/IAT. PLT.

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

Например, библиотека для работы с JSON хочет делать fopen . То есть нужно подружить библиотеки друг с другом. Самый простой вариант — когда мы делаем call , в файл мы кладём нулевой адрес, а в секцию релокаций записываем, что вместо него нужно положить fopen , после чего при запуске динамический загрузчик всё разложит по местам. То есть то же самое, что с линковщиком. Почему так не делают? Потому что мы от’ mmap ‘или нашу библиотеку, а в ней дырки. И во все места дырок нужно что-то подставить. И опять вы не можете записать библиотеку в память один раз, что вам очень хочется.

Поэтому вместо этого просто заводят табличку со смещениями, и теперь все call ‘ы обращаются туда, и туда же динамический загрузчик подставляет истинные адреса функций. Эта таблица в Linux называется global offset table, а в Windows — import address table.

Но на самом деле есть ещё проблема. Давайте посмотрим, что происходит, когда мы делаем

Как мы обсуждали, тут будет call и пустой адрес (до линковки пустой). А что будет, если foo — это внешняя функция из какой-то библиотеки? Тогда надо бы вместо простого call ‘а сделать call qword [got_foo] . Но есть проблема — мы узнаём, откуда эта функция, только на этапе линковки, а компилировать надо раньше. Поэтому компилятор call foo , а потом, если это было неправильно, просто создаёт свою функцию foo , которая является прослойкой для jmp qword [got_foo] . Такие заглушки, которые просто совершают безусловный переход по глобальной таблице смещений имеют название. В Linux их называют PLT (procedure linkage table), а в Windows как-то по-другому.

Но в Linux PLT используется ещё для одной цели. Рассмотрим, скажем, LibreOffice, в котором сотни динамических библиотек с тысячами функций в каждой. Поэтому заполнение GOT — это долго. И нам не хочется смотреть, где лежит каждая функция, после чего записывать её в таблицу. Поэтому эту операцию сделали ленивой:
GOT заполняется специальными заглушками, которые динамически ищут в хэш-таблице настоящий адрес функции, после чего записывают его в GOT вместо себя, и вызывают эту функцию, чтобы она отработала. В Microsoft по-умолчанию отложенная загрузка не используется, но его можно включить (delayed DLL loading или как-то так называется). Это фича загрузчика, а не самой Windows, и делает эта фича примерно то же самое. Однако есть разница. В Linux отсутствие библиотеки не позволяет запустить программу. В Windows же библиотека подгружается при первом вызове функции оттуда, что, по их словам, сделано чтобы вы могли за’ if ‘ать ситуацию, когда библиотеки нет.

Офф-топ на тему «Как страшно жить».

Поговорим про изменение so-файлов. Давайте возьмём и во время работы программы поменяем библиотечку на диске, втупую вписав туда другой текст. Результат поразителен — работа программы также изменится. Почему? Мы же, вроде как, исполняем библиотеку из оперативки, а не с диска. А дело в том, как работает copy-on-write в операционных системах. Когда вы пишете в некоторую страницу, вам копируют её. Но когда кто-то извне пишет в страницу, вам не дают копию старых данных. С исполняемым файлом такое не прокатывает, кстати. Это потому, что вашу программу загружает ядро, и оно может запретить изменять бинарники, а библиотеку загружает ваша программа, которая такого механизма не имеет.

Кстати, изменение и перекомпиляция — разные вещи. И если вы во время работы программы перекомпилируете библиотеку, она не обновится. Связано это с тем, что перекомпилированная библиотека — это новый файл, а не старый. По сути вы удалили старую библиотеку и создали новую, вместо того, чтобы библиотеку изменить. А в Linux пока кто-то имеет доступ к файлу, файл не удаляется до конца. И поэтому в вашей программе всё ещё есть та самая библиотека, которую вы загружали (а не новая).

Детали работы с динамическими библиотеками в Windows.

Никто не удивиться, что набор

не очень эффективен (три обращения в память вместо одного). Поэтому в Windows есть спецификатор __declspec(dllimport) , который сразу вместо call 000000 и замены нулей на foo@plt вставляет call qword [got_foo] .

Ещё в Windows есть такая штука как .def-файл — линковщик экспортирует из вашей DLL-ки только то, что нужно, и в .def-файле указывается, что именно. Это хорошо работает в C, где имена символов и имена функций совпадают, но не очень хорошо в C++, где вам придётся писать сложные декорируемые имена. Поэтому есть второй вариант — написать на самой функции __declspec(dllexport) .

И вроде бы всё хорошо, мы метите функции, которые экспортируете как __declspec(dllexport) , которые импортируете — как __declspec(dllimport) и всё классно работает. Но есть проблема: вы и в библиотеке, и в коде, который её использует подключаете один заголовочный файл, где объявлена функция. И непонятно, что там писать: __declspec(dllexport) или __declspec(dllimport) . Для этого заводится специальный макрос под каждую библиотеку, которым отличают, саму DLL вы компилируете или кого-то с её использованием.

Есть ещё одна проблема. Непонятно, что делать с глобальными переменными. Там проблема ещё более страшная: вы сначала читаете адрес переменной из GOT (извините, IAT), а потом по полученному адресу обращаетесь. Тут уже никакую функцию-прослойку не написать, увы. Поэтому если вы не пометите глобальную переменную как __declspec(dllimport) , тот тут вы уже точно совсем проиграете, у вас линковка не получится.

А ещё реализация DLL в Windows нарушает правила языка: если вы напишете inline -функцию в заголовочном файле. Она просто откопируется в каждую библиотеку, где вы этот заголовок подключился. С этим вы ничего не сделаете, тут вы просто проиграли.

Детали работы с динамическими библиотеками в Linux.

Если вы думаете, что в Windows проблемы с динамическими библиотеками, потому что Windows — какашка, то сильно заблуждаетесь, потому что в Linux нюансов тоже выше крыши.

Итак, мем первый и основной — interposition. Есть такая переменная окружения, как LD_PRELOAD . Она завставляет динамический загрузчик сначала обшарить в поисках динамических библиотек то, что вы в LD_PRELOAD написали, а уже потом смотреть какие-нибудь RUNPATH ‘ы и всё остальное. В частности, так можно подменить аллокатор (и мы так и делали, когда экспериментировали с mmap ‘ом и munmap ‘ом). Такая подмена и называется interposition. Теперь что же у него, собственно, за нюансы есть.

Тут при обычной компиляции вторая функция просто вернёт свой второй аргумент. А при компиляции с -fpic, вы cможете подменить sum , а значит оптимизации не будет. Чтобы это пофиксить, можно пометить sum как static (тогда эта функция будет у вас только внутри файла, а значит его не поменять извне) или как inline (потому что inline полагается на ODR, а значит функция должна быть везде одинаковой). Но есть ещё способ.
Linux по-умолчанию считает, что все функции торчат наружу (т.е. как __declspec(dllexport) в Windows). А можно их пометить, как не торчащие наружу, а нужные только для текущей компилируемой программы/библиотеки: __attribute__((visibility("hidden"))) .

На самом деле атрибут visibility может принимать несколько различных значений ( "default" , "hidden" , "internal" , "protected" ), где пользоваться сто́ит только вторым, потому что первый и так по-умолчанию, третий заставляет ехать все адреса, а четвёртый добавляет дополнительные аллокации.
При этом также есть различные ключи компиляции (типа -B symbolic), которые тем или иным образом немного меняют поведение, и пояснить разницу между ними всеми вам могут только избранные. И каждый из них может поменять вам поведение так, что вы легко выстрелите себе в ногу. То есть глобально в Linux поведение по умолчанию делаем вам хорошо, но, возможно, немного неоптимизированно, а когда вы начинаете использовать опции, вы погружаетесь в такую бездну, что ускорение заставляет вас очень много думать. Причём замедление от динамических библиотек может быть достаточно сильным: если взять компилятор clang-LLVM и компилировать при помощи его ядро Linux’а, то в зависимости от того, сложен ли clang-LLVM в один большой файл или разбит по библиотечкам, время компиляции отличается на треть. Поэтому ключи использовать придётся.
Один из самых безопасных из них — -fno-semantic-interposition. Это не то же самое, что и -fno-interposition потому, что бинарнику всё равно можно дать LD_PRELOAD , однако в нашем случае функция test будет оптимизирована.
Ещё один полезный ключ — -fno-plt. Он по сути вешает оптимизацию такую же, как __declspec(dllimport) , но на весь файл, поэтому функции, написанные в нём же, замедляются. Чтобы не замедлялись — visibility("hidden") . Вообще всё это детально и подробно рассказано не будет, если вам интересно, гуглите и читайте/смотрите по теме.
Впрочем, всякие -fno-plt и прочие штуки нужны нам тогда и только тогда, когда мы не включили linking-time оптимизации. В GCC все наборы ключей нафиг не нужны, если включить -flto. Так что в перспективе -flto и -fno-semantic-interposition — это единственное, что вам может быть нужно. Но только в перспективе.

A.1 – Статические и динамические библиотеки

Библиотека – это пакет кода, который предназначен для повторного использования многими программами. Обычно библиотека C++ состоит из двух частей:

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

Некоторые библиотеки могут быть разделены на несколько файлов и/или иметь несколько файлов заголовков.

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

Существует два типа библиотек: статические библиотеки и динамические библиотеки.

Статическая библиотека (иногда называемая archive, «архив») состоит из подпрограмм, которые скомпилированы и линкуются непосредственно с вашей программой. Когда вы компилируете программу, использующую статическую библиотеку, все функции статической библиотеки, которые использует ваша программа, становятся частью вашего исполняемого файла. В Windows статические библиотеки обычно имеют расширение .lib , а в Linux – расширение .a (archive, архив). Одним из преимуществ статических библиотек является то, что вам нужно распространять только исполняемый файл, чтобы пользователи могли запускать вашу программу. Поскольку библиотека становится частью вашей программы, это гарантирует, что с вашей программой всегда будет использоваться правильная версия библиотеки. Кроме того, поскольку статические библиотеки становятся частью вашей программы, вы можете использовать их так же, как функции, которые вы написали для своей программы. С другой стороны, поскольку копия библиотеки становится частью каждого исполняемого файла, который ее использует, это может привести к потере большого количества места. Статические библиотеки также не могут быть легко обновлены – для обновления библиотеки необходимо заменить весь исполняемый файл.

Динамическая библиотека (также называемая shared library, «общая библиотека») состоит из подпрограмм, которые загружаются в ваше приложение во время выполнения. Когда вы компилируете программу, использующую динамическую библиотеку, библиотека не становится частью вашего исполняемого файла – она ​​остается отдельной единицей. В Windows динамические библиотеки обычно имеют расширение .dll (dynamic link library, библиотека динамической компоновки), а в Linux – расширение .so (shared object, общий объект). Одним из преимуществ динамических библиотек является то, что многие программы могут совместно использовать одну копию библиотеки, что экономит место. Возможно, большим преимуществом является то, что динамическую библиотеку можно обновить до более новой версии без замены всех исполняемых файлов, которые ее используют.

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

Библиотека импорта – это библиотека, которая автоматизирует процесс загрузки и использования динамической библиотеки. В Windows это обычно делается с помощью небольшой статической библиотеки ( .lib ) с тем же именем, что и динамическая библиотека ( .dll ). Статическая библиотека подключается к программе во время компиляции, и затем функциональные возможности динамической библиотеки можно эффективно использовать, как если бы это была статическая библиотека. В Linux файл общих объектов ( .so ) выполняет функции динамической библиотеки и библиотеки импорта. Большинство компоновщиков при создании динамической библиотеки могут создать библиотеку импорта для этой динамической библиотеки.

Установка и использование библиотек

Теперь, когда вы знаете о различных типах библиотек, давайте поговорим о том, как на самом деле использовать библиотеки в вашей программе. Установка библиотеки на C++ обычно состоит из 4 шагов:

  1. Получите библиотеку. Лучший вариант – загрузить предварительно скомпилированный пакет для вашей операционной системы (если он существует), чтобы вам не пришлось компилировать библиотеку самостоятельно. Если для вашей операционной системы не предусмотрен пакет, вам придется загрузить пакет, содержащий только исходный код, и скомпилировать его самостоятельно (что выходит за рамки этого урока). В Windows библиотеки обычно распространяются в виде файлов .zip . В Linux библиотеки обычно распространяются в виде пакетов (например, .RPM ). В вашем диспетчере пакетов могут быть некоторые из наиболее популярных библиотек (например, SDL ), которые уже перечислены для упрощения установки, поэтому сначала проверьте там.
  2. Установите библиотеку. В Linux это обычно включает вызов диспетчера пакетов и предоставление ему возможности выполнить всю работу. В Windows это обычно включает разархивирование библиотеки в каталог по вашему выбору. Для облегчения доступа рекомендуем хранить все свои библиотеки в одном месте. Например, используйте каталог C:\libs и поместите каждую библиотеку в отдельный подкаталог.
  3. Убедитесь, что компилятор знает, где искать файл(ы) заголовков для данной библиотеки. В Windows обычно это подкаталог include каталога, в который вы установили файлы библиотеки (например, если вы установили свою библиотеку в C:\libs\SDL-1.2.11 , файлы заголовков, вероятно, находятся в C:\libs\SDL-1.2.11\include ). В Linux файлы заголовков обычно устанавливаются в /usr/include , который уже должен быть частью пути поиска включаемых файлов. Однако если файлы установлены в другом месте, вам придется указать компилятору, где их найти.
  4. Сообщите компоновщику, где искать файл(ы) библиотеки. Как и в шаге 3, это обычно включает добавление каталога в список мест, где компоновщик ищет библиотеки. В Windows это обычно подкаталог /lib каталога, в который вы установили файлы библиотеки. В Linux библиотеки обычно устанавливаются в /usr/lib , который уже должен быть частью пути поиска ваших библиотек.

После того, как библиотека установлена, ​​и среда IDE знает, где ее искать, обычно необходимо выполнить следующие 3 шага для каждого проекта, который хочет использовать библиотеку:

  1. Если вы используете статические библиотеки или библиотеки импорта, сообщите компоновщику, какие файлы библиотеки нужно линковать.
  2. Включите с помощью #include заголовочный файл(ы) библиотеки в вашу программу. Это сообщит компилятору обо всех функциях, предлагаемых библиотекой, чтобы ваша программа могла правильно компилироваться.
  3. Если вы используете динамические библиотеки, убедитесь, что программа знает, где их найти. В Linux библиотеки обычно устанавливаются в /usr/lib , который находится в пути поиска по умолчанию после путей в переменной среды LD_LIBRARY_PATH . В Windows путь поиска по умолчанию включает каталог, из которого запускается программа, каталоги, установленные вызовом SetDllDirectory() , каталоги Windows, System и System32 , а также каталоги в переменной среды PATH . Самый простой способ использовать .dll – скопировать .dll в расположение исполняемого файла. Поскольку вы обычно распространяете .dll вместе со своим исполняемым файлом, в любом случае имеет смысл хранить их вместе.

Шаги 3-5 включают настройку вашей IDE – к счастью, когда дело доходит до выполнения этих вещей, почти все IDE работают одинаково. К сожалению, поскольку каждая среда IDE имеет свой интерфейс, самая сложная часть этого процесса – просто определить правильное место для выполнения каждого из этих шагов. Следовательно, в следующих нескольких уроках этого раздела мы расскажем, как выполнить все эти шаги как для Visual Studio, так и для Code::Blocks. Если вы используете другую IDE, прочтите оба урока – к тому времени, когда вы закончите, у вас должно быть достаточно информации, чтобы сделать то же самое с вашей собственной IDE и небольшим гуглением.

Static Libraries

In C programming, a library is a file containing a collection of object/header files. These files contain declarations of predefined functions.

Generally, the filename for a library has the prefix lib and the suffix .a For example:

There are two types of libraries in C: static libraries and dynamic libraries. I will be talking about static libraries.

Why use libraries?

Libraries give you the ability to reuse code. So instead of writing the same functions over and over again in your code, you could put the function into a library and reuse that in other programs.

What are static libraries?

Static libraries are object files which, during the linking phase of compilation, are combined with another object file to form a final executable file. If you would like to learn more about the compiling stages, read this article.

During the linking phase, the compiler searches in the library for the object code of any functions that had been called in the program. The linker makes a copy of that code and links it with the object code to produce a final executable file.

Creating a static library

To create a static library you will first need to compile the files that you want to include in your library, stopping just short of the linking (final) phase of compilation. You can use the gcc compilier with the -c flag to do this. The -c flag tells the compiler to go through the first three stages, pre-process, compile, and assemble. When we stop right after the assembly phase, we get object files with the .o extention instead of executable files ( a.out , .exe )

If you run gcc — c *.c , it will create object files from the .c files in your directory.

After creating the object files, we can now create an archive (library) by using the ar command. To create a static library named _mylibrary with the .o (object files) from our current working directory use the ar command with the -r and -c flags. The -r flag tells ar to replace the older object files contained in the library with the new ones. The -c flag tells ar to make a library if it doesn’t exist already.

ar -rc lib_mylibrary.a *.o

The *.o searches for all the .o (object files) in the current working directory, then passes those into ar . ar then copies the object code into the library specified.

The next step after creating a static library is to index it.

Why index?

Indexing makes it faster to link to your library and lets functions inside of your library call one another. To index your library you can use the ranlib command. ranlib will create an index for your library.

If you want to see what’s inside of a library you can use the commands nm or ar -t . nm will list symbols (value and type) of the object files and the -t flag in ar will display a table that lists the contents of your archive (library).

Now that you created a static library, let’s use it!

I made a static library, now what?

In order to use your static library, you will need to link it to the program you want to compile. To link and compile a program with a static library you can use the -l and -L flags. -l tells gcc to search the library named when linking. When you use the -l flag you can exclude the lib prefix and .a suffix when linking your library. The -L. flag tells gcc where it should look for your library and also where other libraries, such as the standard libraries, are held.

Compiling will look something like this:

First gcc, then the name of the file you want to compile and link to your library, then the -L. flag followed by -l and the name of your library without its prefix and suffix. Finally, use the -o flag followed by a name to indicate where the executable file goes to.

Then you enter ./filename to run your code.

Now I will create a main.c file to run my puts.c program. Then, I will compile it using the -c flag (creating the object file main.o ), and then I will link my main.o to my lib_mylib.a library and compile. Finally I will run my program: ./main

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *