Главная страница » Как собрать библиотеку c из исходников

Как собрать библиотеку c из исходников

  • автор:

Как написать свою библиотеку на Си

Если ты встал на путь С/С++ разработчика, то скорее всего (помимо использования стандартной библиотеки — libc) рано или поздно вам потребуется занятся разработкой собственных библиотек. Зачем. Причин может быть несколько. Например вы написали свою структуру данных или свой алгоритм, и хотите использовать его повторно или распространять. Так же возможно вы написали несколько утилит и все они используют один и тот же кусок кода (например, как часто это бывает, логгер), и будет логично вынести этот кусок кода в отдельный модуль. Поскольку сопровождать такой код будет проще.

Реализация

И так, давайте начнем с примера. Создадим заголовочный файл somecode.h, который будет содержать объявление некоторой функции. Пусть будет простая функция которая разбивает предложение на слова и печатает каждое слово в новой строке. Простой синтетический пример.

И создадим файл somecode.c, в которой напишем реализацию нашей функции.

Далее создадим файл main.c, где будем вызывать нашу функцию.

Давайте для начала скомпилируем это все самым обычным способом для проверки работоспособности.

  • из исходных файлов получаем объектные файлы
  • из объектных файлов получаем исполняемый файл

Запускаем исполняемый файл и видим, что все работает.

А теперь рассмотрим пример получения библиотеки и линковки его к исполняемому файлу. Для компиляции используем вызов gcc со следующими опциями.

Из файла с расширением .c мы получаем файл с расширением .so. И так же обратите внимание, что библиотека имеет префикс lib. Еще мы видим, что появились два дополнительных аргумента -shared и -fpic. С помощью опции -shared мы говорим компилятору, что хотим получить а выходе библиотеку. А опция -fpic говорит компилятору, что объектные файлы должны содержать позиционно-независимый код (position independent code), который рекомендуется использовать для динамических библиотек.

Теперь скомпилируем наш исполняемый файл подключив к нему нашу библиотеку. Для этого нужно указать название библиотеки через опцию -l.

И опс.. мы получили ошибку… Линкер говорит нам, что он не знает где лежит наша библиотека. С помощью опции -L указываем текущую директорию, где лежит наша библиотека и компиляция проходит успешно.

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

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

Так же есть возможность задавать дополнительные директории с библиотеками с помощью переменной окружения LD_LIBRARY_PATH. С помощью утилиты ldd посмотрим от каких библиотек зависит наша программа.

Добавим в LD_LIBRARY_PATH текущую директорию.

Видим, что наша библиотека подгрузилась.

И теперь программа запускается и работает.

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

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

Разница в нашем случае может и маленькая, но в масштабах десятков и сотен файлов разница будет значительной.

Так что же мы сделали?

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

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

Как собирать статические библиотеки из исходников?

Здравствуйте!
Подскажите пожалуйста, как собирать lib файлы
Книга подсказала мне, что лучше всего собирать исходники на той машине, где я собираюсь программировать, но делать такого мне не доводилось, а уж тем-более компилировать чужой код, в неизвестный мне тип файла

5c225592d35b8535487414.png

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

  • Вопрос задан более трёх лет назад
  • 1280 просмотров

Простой 1 комментарий

  • Facebook
  • Вконтакте
  • Twitter

Конкретно GLUT не собирал, но другие библиотеки с открытыми исходниками регулярно приходится.
Самый простой вариант для MSVC — использовать микрософтовский менеджер пакетов vcpkg. У него уже есть в пакетах freeglut. Если это то что вам нужно, то рекомендую установить vcpkg с помощью него собрать glut и можете использовать.
Если этот вариант не подходит — изучайте документацию по сборке конкретно вашей версии под msvc. Вижу, что есть файл readme.win — там, скорее всего есть описание сборки под винду. Так же вижу файл glutmake.bat — видимо это запуск процесса сборки под винду.

Если используете mingw, то там обычно работают сценарии сборки для Linux:
.\configure
make
make install
Но я в этом случае использую msys2 + mingw — в msys2 входит свой менеджер пакетов pacman, устанавливаете нужную библиотеку с его помощью (аналогично как это делается в линуксе).

В любом случае в документации к библиотеке есть описание процесса сборки под поддерживаемые ОС с помощью поддерживаемых компиляторов. Если нет — то либо сборка тривиальна и нет зависимостей, либо этой библиотекой не стоит пользоваться.

У многих языков программирования системы сборки поставляются вместе с компиляторами и являються де-факто стандартом для соответсвующего языка.

Для C/C++ де-факто стандрата сборки нет, но есть несколько систем сборки, которые обычно используються:

make — очень простая утилита, но неудобно для больших проектов.
Можно определить по наличию файла Makefile. Иногда этот файл генерируеться скриптом configure.
Если есть Makefile, запускаем:
$ make
Удалить сгенерированные файлы (чтобы запустить сборку заново):
$ make clean

cmake— более высокоуровневая система сборки, генерирует проекты для IDE или те же Makefile.
Можно определить по наличию CMakeLists.txt

qmake — система сборки разработаная для сборки Qt и проектов использующих Qt.
Можно определить по наличию .pro файлов. Билдим так:

Как собрать библиотеку c из исходников

Февраль 16, 2016

Библиотеки

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

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

С точки зрения операционной системы и прикладного программного обеспечения библиотеки бывают статическими и разделяемыми (динамическими).

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

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

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

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

2. Код разделяемой библиотеки используемый несколькими приложениями храниться в оперативной памяти в одном экземпляре (на самом деле не всё так просто. ), в результате сокращается потребность системы в доступной оперативной памяти.

3. Отпадает необходимость пересобирать каждый исполняемый файл в случае внесения изменений в код общей для них библиотеки. Изменения и исправления кода динамической библиотеки автоматически отразятся на каждой из использующих её программ.

Без парадигмы разделяемых библиотек не существовало бы прекомпиллированных (бинарных) дистрибутивов Linux (да ни каких бы не существовало). Представьте размеры дистрибутива, в каждый бинарный файл которого, был бы помещен код стандартной библиотеки C (и всех других подключаемых библиотек). Так же представьте что пришлось бы делать для того, что бы обновить систему, после устранения критической уязвимости в одной из широко задействованных библиотек.

Теперь немного практики.

Для иллюстрации воспользуемся набором исходных файлов из предыдущего примера. В нашу самодельную библиотеку поместим код (реализацию) функций first() и second().

В Linux принята следующая схема именования файлов библиотек (хотя соблюдается она не всегда) — имя файла библиотеки начинается с префикса lib, за ним следует собственно имя библиотеки, в конце расширение .a (archive) — для статической библиотеки, .so (shared object) — для разделяемой (динамической), после расширения через точку перечисляются цифры номера версии (только для динамической библиотеки). Имя, соответствующего библиотеке заголовочного файла (опять же как правило), состоит из имени библиотеки (без префикса и версии) и расширения .h. Например: libogg.a, libogg.so.0.7.0, ogg.h.

В начале создадим и используем статическую библиотеку.

Функции first() и second() составят содержимое нашей библиотеки libhello. Имя файла библиотеки, соответственно, будет libhello.a. Библиотеке сопоставим заголовочный файл hello.h.

Как уже было сказано — библиотека это набор объектных файлов. Первыми двумя командами мы и создали эти объектные файлы.

Далее необходимо объектные файлы скомпоновать в набор. Для этого используется архиватор ar — утилита «склеивает» несколько файлов в один, в полученный архив включает информацию требуемую для восстановления (извлечения) каждого индивидуального файла (включая его атрибуты принадлежности, доступа, времени). Какого-либо «сжатия» содержимого архива или иного преобразования хранимых данных при этом не производится.

Опция c arname — создать архив, если архив с именем arname не существует он будет создан, в противном случае файлы будут добавлены к имеющемуся архиву.

Опция r — задает режим обновления архива, если в архиве файл с указанным именем уже существует, он будет удален, а новый файл дописан в конец архива.

Опция s — добавляет (обновляет) индекс архива. В данном случае индекс архива это таблица, в которой для каждого определенного в архивируемых файлах символического имени (имени функции или блока данных) сопоставлено соответствующее ему имя объектного файла. Индекс архива необходим для ускорения работы с библиотекой — для того чтобы найти нужное определение, отпадает необходимость просматривать таблицы символов всех файлов архива, можно сразу перейти к файлу, содержащему искомое имя. Просмотреть индекс архива можно с помощью уже знакомой утилиты nm воспользовавшись её опцией -s (так же будут показаны таблицы символов всех объектных файлов архива):

Для создания индекса архива существует специальная утилита ranlib. Библиотеку libhello.a можно было сотворить и так:

$ ar cr libhello.a first.o second.o
$ ranlib libhello.a

Впрочем библиотека будет прекрасно работать и без индекса архива.

Теперь воспользуемся нашей библиотекой:

$ gcc -Wall -c main.c
$ gcc -o main main.o -L. -lhello
$ ./main
First function.
Second function.
Main function.
Работает.

Ну теперь комментарии. Появились две новые опции gcc:

Опция -lname — передаётся линковщику, указывает на необходимость подключить к исполняемому файлу библиотеку libname. Подключить значит указать, что такие-то и такие-то функции (внешние переменные) определены в такой-то библиотеке. В нашем примере библиотека статическая, все символьные имена будут ссылаться на код находящийся непосредственно в исполняемом файле. Обратите внимание в опции -l имя библиотеки задается как name без приставки lib.

Опция -L/путь/к/каталогу/с/библиотеками — передаётся линковщику, указывает путь к каталогу содержащему подключаемые библиотеки. В нашем случае задана точка ., линковщик сначала будет искать библиотеки в текущем каталоге, затем в каталогах определённых в системе.

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

$ gcc -Wall -c main.c
$ gcc -o main -L. -lhello main.o
main.o: In function `main’:
main.c:(.text+0xa): undefined reference to `first’
main.c:(.text+0xf): undefined reference to `second’
collect2: ld returned 1 exit status
$ gcc -o main main.o -L. -lhello
$ ./main
First function.
Second function.
Main function.

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

Существует альтернативный способ указания местоположения библиотек в системе. В зависимости от дистрибутива, переменная окружения LD_LIBRARY_PATH или LIBRARY_PATH может хранить список разделенных знаком двоеточия каталогов, в которых линковщик должен искать библиотеки. Как правило, по умолчанию эта переменная вообще не определена, но ни чего не мешает её создать:

$ gcc -o main main.o -lhello
/usr/lib/gcc/i686-pc-linux-gnu/4.4.3/../ ../../../i686-pc-linux-gnu/bin/ld: cannot find -lhello
collect2: выполнение ld завершилось с кодом возврата 1
$ export LIBRARY_PATH=.
$ gcc -o main main.o -lhello
$ ./main
First function.
Second function.
Main function.

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

Теперь создадим и используем библиотеку динамическую.

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

$ gcc -Wall -fPIC -c first.c
$ gcc -Wall -fPIC -c second.c
$ gcc -shared -o libhello.so.2.4.0.5 -Wl,-soname,libhello.so.2 first.o second.o

Что получили в результате?

$ file libhello.so.2.4.0.5
libhello.so.2.4.0.5: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, not stripped

Файл libhello.so.2.4.0.5, это и есть наша разделяемая библиотека. Как её использовать поговорим чуть ниже.

Опция -fPIC — требует от компилятора, при создании объектных файлов, порождать позиционно-независимый код (PIC — Position Independent Code), его основное отличие в способе представления адресов. Вместо указания фиксированных (статических) позиций, все адреса вычисляются исходя из смещений заданных в глобальной таблицы смещений (global offset table — GOT). Формат позиционно-независимого кода позволяет подключать исполняемые модули к коду основной программы в момент её загрузки. Соответственно, основное назначение позиционно-независимого кода — создание динамических (разделяемых) библиотек.

Опция -shared — указывает gcc, что в результате должен быть собран не исполняемый файл, а разделяемый объект — динамическая библиотека.

Опция -Wl,-soname,libhello.so.2 — задает soname библиотеки. О soname подробно поговорим в следующем абзаце. Сейчас обсудим формат опции. Сея странная, на первый взгляд, конструкция с запятыми предназначена для непосредственного взаимодействия пользователя с линковщиком. По ходу компиляции gcc вызывает линковщик автоматически, автоматически же, по собственному усмотрению, gcc передает ему необходимые для успешного завершения задания опции. Если у пользователя возникает потребность самому вмешаться в процесс линковки он может воспользоваться специальной опцией gcc -Wl,-option,value1,value2. Что означает передать линковщику (-Wl) опцию -option с аргументами value1, value2 и так далее. В нашем случае линковщику была передана опция -soname с аргументом libhello.so.2.

Теперь о soname. При создании и распространении библиотек встает проблема совместимости и контроля версий. Для того чтобы система, конкретно загрузчик динамических библиотек, имели представление о том библиотека какой версии была использована при компиляции приложения и, соответственно, необходима для его успешного функционирования, был предусмотрен специальный идентификатор — soname, помещаемый как в файл самой библиотеки, так и в исполняемый файл приложения. Идентификатор soname это строка, включающая имя библиотеки с префиксом lib, точку, расширение so, снова точку и оду или две (разделенные точкой) цифры версии библиотеки — libname.so.x.y. То есть soname совпадает с именем файла библиотеки вплоть до первой или второй цифры номера версии. Пусть имя исполняемого файла нашей библиотеки libhello.so.2.4.0.5, тогда soname библиотеки может быть libhello.so.2. При изменении интерфейса библиотеки её soname необходимо изменять! Любая модификация кода, приводящая к несовместимости с предыдущими релизами должна сопровождаться появлением нового soname.

Как же это все работает? Пусть для успешного исполнения некоторого приложения необходима библиотека с именем hello, пусть в системе таковая имеется, при этом имя файла библиотеки libhello.so.2.4.0.5, а прописанное в нем soname библиотеки libhello.so.2. На этапе компиляции приложения, линковщик, в соответствии с опцией -lhello, будет искать в системе файл с именем libhello.so. В реальной системе libhello.so это символическая ссылка на файл libhello.so.2.4.0.5. Получив доступ к файлу библиотеки, линковщик считает прописанное в нем значение soname и наряду с прочим поместит его в исполняемый файл приложения. Когда приложение будет запущено, загрузчик динамических библиотек получит запрос на подключение библиотеки с soname, считанным из исполняемого файла, и попытается найти в системе библиотеку, имя файла которой совпадает с soname. То есть загрузчик попытается отыскать файл libhello.so.2. Если система настроена корректно, в ней должна присутствовать символическая ссылка libhello.so.2 на файл libhello.so.2.4.0.5, загрузчик получит доступ к требуемой библиотеки и далее не задумываясь (и ни чего более не проверяя) подключит её к приложению. Теперь представим, что мы перенесли откомпилированное таким образом приложение в другую систему, где развернута только предыдущая версия библиотеки с soname libhello.so.1. Попытка запустить программу приведет к ошибке, так как в этой системе файла с именем libhello.so.2 нет.

Таким образом, на этапе компиляции линковщику необходимо предоставить файл библиотеки (или символическую ссылку на файл библиотеки) с именем libname.so, на этапе исполнения загрузчику потребуется файл (или символическая ссылка) с именем libname.so.x.y. При чем имя libname.so.x.y должно совпадать со строкой soname использованной библиотеки.

В бинарных дистрибутивах, как правило, файл библиотеки libhello.so.2.4.0.5 и ссылка на него libhello.so.2 будут помещены в пакет libhello, а необходимая только для компиляции ссылка libhello.so, вместе с заголовочным файлом библиотеки hello.h будет упакована в пакет libhello-devel (в devel пакете окажется и файл статической версии библиотеки libhello.a, статическая библиотека может быть использована, также только на этапе компиляции). При распаковке пакета все перечисленные файлы и ссылки (кроме hello.h) окажутся в одном каталоге.

Пример именования библиотек в Linux. Ubuntu
Убедимся, что заданная строка soname действительно прописана в файле нашей библиотеки. Воспользуемся мега утилитой objdump с опцией -p:

Утилита objdump — мощный инструмент, позволяющий получить исчерпывающую информацию о внутреннем содержании (и устройстве) объектного или исполняемого файла. В man странице утилиты сказано, что objdump прежде всего будет полезен программистам, создающими средства отладки и компиляции, а не просто пишущих какие-нибудь прикладные программы 🙂 В частности с опцией -d это дизассемблер. Мы воспользовались опцией -p — вывести различную метаинформацию о объектном файле.

В приведенном примере создания библиотеки мы неотступно следовали принципам раздельной компиляции. Разумеется скомпилировать библиотеку можно было бы и вот так, одним вызовом gcc:

$ gcc -shared -Wall -fPIC -o libhello.so.2.4.0.5 -Wl,-soname,libhello.so.2 first.c second.c

Теперь попытаемся воспользоваться получившейся библиотекой:

$ gcc -Wall -c main.c
$ gcc -o main main.o -L. -lhello -Wl,-rpath,.
/usr/bin/ld: cannot find -lhello
collect2: ld returned 1 exit status

Линковщик ругается. Вспоминаем, что было сказано выше о символических ссылках. Создаем libhello.so и повторяем попытку:

$ ln -s libhello.so.2.4.0.5 libhello.so
$ gcc -o main main.o -L. -lhello -Wl,-rpath,.

Теперь все довольны. Запускаем созданный бинарник:

$ ./main
./main: error while loading shared libraries: libhello.so.2: cannot open shared object file: No such file or directory

Ошибка. Ругается загрузчик, не может найти библиотеку libhello.so.2. Убедимся, что в исполняемом файле действительно прописана ссылка на libhello.so.2:

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

$ ln -s libhello.so.2.4.0.5 libhello.so.2
$ ./main
First function.
Second function.
Main function.

Заработало. Теперь комментарии по новым опциям gcc.

Опция -Wl,-rpath,. — уже знакомая конструкция, передать линковщику опцию -rpath с аргументом .. С помощью -rpath в исполняемый файл программы можно прописать дополнительные пути по которым загрузчик разделяемых библиотек будет производить поиск библиотечных файлов. В нашем случае прописан путь . — поиск файлов библиотек будет начинаться с текущего каталога.

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

$ mv main ..
$ ../main
First function.
Second function.
Main function.
$ cd ..
$ ./main
./main: error while loading shared libraries: libhello.so.2: cannot open shared object file: No such file or directory

Узнать какие разделяемые библиотеки необходимы приложению можно и с помощью утилиты ldd:

$ ldd main
linux-vdso.so.1 => (0x00007fffaddff000)
libhello.so.2 => ./libhello.so.2 (0x00007f9689001000)
libc.so.6 => /lib/libc.so.6 (0x00007f9688c62000)
/lib64/ld-linux-x86-64.so.2 (0x00007f9689205000)

В выводе ldd для каждой требуемой библиотеки указывается её soname и полный путь к файлу библиотеки, определённый в соответствии с настройками системы.

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

В соответствии с соглашениями FHS (Filesystem Hierarchy Standard) в системе должны быть два (как минимум) каталога для хранения файлов библиотек:

/lib — здесь собраны основные библиотеки дистрибутива, необходимые для работы программ из /bin и /sbin;

/usr/lib — здесь хранятся библиотеки необходимые прикладным программам из /usr/bin и /usr/sbin;

Соответствующие библиотекам заголовочные файлы должны находиться в каталоге /usr/include.

Загрузчик по умолчанию будет искать файлы библиотек в этих каталогах.

Кроме перечисленных выше, в системе должен присутствовать каталог /usr/local/lib — здесь должны находиться библиотеки, развернутые пользователем самостоятельно, минуя систему управления пакетами (не входящие в состав дистрибутива). Например в этом каталоге по умолчанию окажутся библиотеки скомпилированные из исходников (программы установленные из исходников будут размещены в /usr/local/bin и /usr/local/sbin, разумеется речь идет о бинарных дистрибутивах). Заголовочные файлы библиотек в этом случае будут помещены в /usr/local/include.

В ряде дистрибутивов (в Ubuntu) загрузчик не настроен просматривать каталог /usr/local/lib, соответственно, если пользователь установит библиотеку из исходников, система её не увидит. Сиё авторами дистрибутива сделано специально, что бы приучить пользователя устанавливать программное обеспечение только через систему управления пакетами. Как поступить в данном случае будет рассказано ниже.

В действительности, для упрощения и ускорения процесса поиска файлов библиотек, загрузчик не просматривает при каждом обращении указанные выше каталоги, а пользуется базой данных, хранящейся в файле /etc/ld.so.cache (кэшем библиотек). Здесь собрана информация о том, где в системе находится соответствующий данному soname файл библиотеки. Загрузчик, получив список необходимых конкретному приложению библиотек (список soname библиотек, заданных в исполняемом файле программы), посредством /etc/ld.so.cache определяет путь к файлу каждой требуемой библиотеки и загружает её в память. Дополнительно, загрузчик может просмотреть каталоги перечисленные в системных переменных LD_LIBRARY_PATH, LIBRARY_PATH и в поле RPATH исполняемого файла (смотри выше).

Для управления и поддержания в актуальном состоянии кэша библиотек используется утилита ldconfig. Если запустить ldconfig без каких-либо опций, программа просмотрит каталоги заданные в командной строке, доверенные каталоги /lib и /usr/lib, каталоги перечисленные в файле /etc/ld.so.conf. Для каждого файла библиотеки, оказавшегося в указанных каталогах, будет считано soname, создана основанная на soname символическая ссылка, обновлена информация в /etc/ld.so.cache.

Убедимся в сказанном:

$ ls
hello.h libhello.so libhello.so.2.4.0.5 main.c
$ gcc -Wall -o main main.c -L. -lhello
$ ./main
./main: error while loading shared libraries: libhello.so.2: cannot open shared object file: No such file or directory
$ sudo ldconfig /полный/путь/к/катаогу/c/примером
$ ls
hello.h libhello.so libhello.so.2 libhello.so.2.4.0.5 main main.c
$ ./main
First function.
Second function.
Main function.
$ sudo ldconfig
$ ./main
./main: error while loading shared libraries: libhello.so.2: cannot open shared object file: No such file or directory

Первым вызовом ldconfig мы внесли в кэш нашу библиотеку, вторым вызовом исключили. Обратите внимание, что при компиляции main была опущена опция -Wl,-rpath. в результате загрузчик проводил поиск требуемых библиотек только в кэше.

Теперь должно быть понятно как поступить если после установки библиотеки из исходников система её не видит. Прежде всего необходимо внести в файл /etc/ld.so.conf полный путь к каталогу с файлами библиотеки (по умолчанию /usr/local/lib). Формат /etc/ld.so.conf — файл содержит список разделённых двоеточием, пробелом, табуляцией или символом новой строки, каталогов, в которых производится поиск библиотек. После чего вызвать ldconfig без каких-либо опций, но с правами суперпользователя. Всё должно заработать.

Ну и в конце поговорим о том как уживаются вместе статические и динамические версии библиотек. В чем собственно вопрос? Выше, когда обсуждались принятые имена и расположение файлов библиотек было сказано, что файлы статической и динамической версий библиотеки хранятся в одном и том же каталоге. Как же gcc узнает какой тип библиотеки мы хотим использовать? По умолчанию предпочтение отдается динамической библиотеки. Если линковщик находит файл динамической библиотеки, он не задумываясь цепляет его к исполняемому файлу программы:

$ ls
hello.h libhello.a libhello.so libhello.so.2 libhello.so.2.4.0.5 main.c
$ gcc -Wall -c main.c
$ gcc -o main main.o -L. -lhello -Wl,-rpath,.
$ ldd main
linux-vdso.so.1 => (0x00007fffe1bb0000)
libhello.so.2 => ./libhello.so.2 (0x00007fd50370b000)
libc.so.6 => /lib/libc.so.6 (0x00007fd50336c000)
/lib64/ld-linux-x86-64.so.2 (0x00007fd50390f000)
$ du -h main
12K main

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

Существует опция gcc -static — указание линковщику использовать только статические версии всех необходимых приложению библиотек:

$ gcc -static -o main main.o -L. -lhello
$ file main
main: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.15, not stripped
$ ldd main
не является динамическим исполняемым файлом
$ du -h main
728K main

Размер исполняемого файла в 60 раз больше, чем в предыдущем примере — в файл включены стандартные библиотеки языка C. Теперь наше приложение можно смело переносить из каталога в каталог и даже на другие машины, код библиотеки hello внутри файла, программа полностью автономна.

Как же быть если необходимо осуществить статическую линковку только части использованных библиотек? Возможный вариант решения — сделать имя статической версии библиотеки отличным от имени разделяемой, а при компиляции приложения указывать какую версию мы хотим использовать на этот раз:

Как собрать библиотеку из исходников под x32 разрядную систему?

Написал небольшую программу. Теперь необходимо портировать её для запуска на 32-разрядной системе. Для работы была использована библиотека cURL, которую я собрал из исходников. Как я понимаю, теперь мне нужно пересобрать ей с указанием требуемой системы (видимо, при вызове ./configure нужно указать какой-то параметр). Не могли бы вы подсказать, что именно мне нужно выполнить, для сборки библиотеки?
P.S. программа будет запускаться на процессоре armhf, если это важно.

Андрей Солодовников's user avatar

нужно пересобрать её с указанием требуемой системы (видимо, при вызове ./configure нужно указать какой-то параметр)

Если это «нормальный» скрипт configure, то у него должен быть параметр —target=<целевая платформа> Под словом «платформа» понимается триада, разделённая знаками «-«, которая кодирует архитектуру железа, целевую ос и вариант API этой ос.

Запустите ./configure —help что бы узнать, как получить полный список поддерживаемых целей.

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

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

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