Skip to content

Home Программирование Создание совместно используемых библиотек
Создание совместно используемых библиотек

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

$ gcc -shared -о LIBRARY_NAME FILE1.о FILE2.о ...

В действительности gcc вызывает компоновщик Id с опциями для создания совместно используемой библиотеки. В принципе, этот процесс внешне ничем (кроме опции -shared) не отличается от компоновки обычного исполняемого файла. Но не все так просто. При создании динамических библиотек следует учитывать два нюанса:

  • в процессе компоновки совместно используемой библиотеки должны участвовать объектные файлы, содержащие позщионно-независимый код (Position Independent Code). Этот код имеет возможность подгружаться к программе в момент ее запуска. Чтобы получить объектный файл с позиционно-независимым кодом, нужно откомпилировать исходный файл с опцией -f pic;
  • опция -l, указанная при компоновке, позволяет дополнить список каталогов, в которых будет выполняться поиск библиотек. По умолчанию в исполняемом файле сохраняется лишь имя библиотеки, а во время запуска программы происходит повторный поиск библиотек. Поэтому программист должен учитывать месторасположение динамической библиотеки не только на своем компьютере, но и там, где будет впоследствии запускаться программа.

В момент запуска программы для поиска библиотек просматриваются каталоги, перечисленные в файле /etc/ld.so.conf и в переменной окружения LD_LIBRARY_PATH.

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

В процессе компоновки программы можно отдельно указать каталог, где будет размещаться библиотека. Для этого линковщику Id необходимо передать опцию -rpath при помощи опции -wi компилятора gcc. Например, чтобы занести в исполняемый файл prog месторасположение библиотеки Hbfoo.so, нужно сделать следующее:

$ gcc -о ргод ргод.о -L./lib/foo -lfoo -Wl,-rpath,/lib/foo Итак, опция -wi сообщает gcc о необходимости передать линковщику определенную опцию. Далее, после запятой, следует сама опция и ее аргументы, также разделенные запятыми. Такой подход выглядит лучше, чем применение ld_library_path, однако и здесь есть существенный недостаток. Нет никаких гарантий, что на компьютере у конечного пользователя библиотека Hbfoo.so будет также находиться в каталоге /lib/foo.

Есть еще один способ заставить программу искать совместно используемую библиотеку в нужном месте. Во время инсталляции программы можно добавить запись с каталогом месторасположения библиотеки в файл /etc/ld.so.conf. Но это делают крайне редко, поскольку слишком длинный список каталогов в этом файле может отразиться на скорости загрузки системы. Обычно к такому подходу прибегают только такие "именитые" проекты, как Qt или X11.

Многие Linux-системы при загрузке читают файл /etc/ld.so.conf и создают кэш динамических библиотек.

Наилучший выход из сложившегося положения — размещать библиотеки в специально предназначенных для этого каталогах (/usr/lib или /usr/local/lib). Естественно, программист в ходе работы над проектом может для удобства пользоваться переменной ld_library_path или опциями -wl и -rpath, но в конечной программе лучше избегать этих приемов и просто располагать библиотеки в обозначенных выше каталогах.

Теперь, уяснив все тонкости, переходим к делу. За основу возьмем пример из предыдущего раздела. Рассмотрим сначала концепцию использования переменной окружения ld_library_path. Чтобы переделать предыдущий пример для работы с динамической библиотекой, требуется лишь изменить Makefile.

Модифицированный файл

myenv: envmain.o libmyenv.so
gcc -о myenv envmain.o -L. -lmyenv
envmain.о: envmain.с gcc -с $^
libmyenv.so: mysetenv.o myprintenv.o gcc -shared -o lifctnyenv.so $^
mysetenv.o: mysetenv.c gcc -fPIC -с $^
myprintenv.o: myprintenv.c gcc -fPIC -с $^
clean:
rm -f myenv litmyenv.so *.o

Обратите внимание, что файлы mysetenv.o и myprintenv.o, участвующие в создании библиотеки, компилируются с опцией -fPic для генерирования позиционно-независимого кода. Файл envmain.o не добавляется в библиотеку, поэтому он компилируется без опции -fpic. Если теперь попытаться запустить исполняемый файл myenv, то будет выдано сообщение об ошибке:

$ ./myenv MYVAR Hello
./myenv: error while loading shared libraries: libmyenv.so:
cannot open shared object file: No such file or directory

Проблема в том, что программа не нашла библиотеку в стандартном списке каталогов. После установки переменной ld_library_path проблема исчезнет:

$ export LD_LIBRARY_PATH=. $ ./myenv MYVAR Hello Setting variable MYVAR MYVAR=Hello

Если подняться в родительский каталог и попытаться оттуда запустить программу myenv, то опять будет обнаружена ошибка:

$  cd  . .
$ myenv/myenv MYVAR Hello
myenv/myenv: error while loading shared libraries: libmyenv.so:
cannot open shared object file: No such file or directory

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

$ cd myenv
$ export LD_LIBRARY_PATH=$PWD
$ cd ..
$ myenv/myenv MYVAR Hello
Setting variable MYVAR
MYVAR=Hello

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

Попробуем теперь указать линковщику опцию -rpath. Для этого изменим в make-файле первую целевую связку:

myenv: envmain.o libmyenv.so
gcc -о myenv envmain.o -L. -lmyenv -Wl,-rpath;

Помимо этого, для чистоты эксперимента удалим из окружения переменную

ld_library_path:
$ unset LD_LIBRARY_PATH

Теперь программа запускается из любого каталога без манипуляций с окружением.

Комментарии (0)

RSS feed Comments

Написать комментарий

smaller | bigger

busy
 

Регистрация




Top