Подобно обычным разделяемым библиотекам, разделяемые объекты должны компоноваться явным образом с каждой библиотекой, которая их вызывает. Это позволит гарантировать, что динамический загрузчик корректно разрешит работу всех внешних ссылок при загрузке разделяемого объекта. Если этого не сделать, то внешние ссылки будут разрешены только в контексте того приложения, которое в данном случае будет загружать разделяемый объект. Теоретически разделяемые объекты могут быть стандартными объектными файлами. Однако так поступать не рекомендуется, поскольку внешние зависимости разделяемой библиотеки не будут разрешены должным образом, как и разделяемой библиотеки, явным образом не скомпонованной относительно всех библиотек, от которых она зависит.
Символьные имена, используемые в разделяемых объектах, не обязательно должны быть уникальными среди различных разделяемых объектов, загружаемых в одну и ту же программу; обычно они таковыми и не являются. Различные разделяемые объекты, написанные для одного и того же интерфейса, обычно используют точки входа с одинаковыми именами. Для обычных разделяемых библиотек такая практика будет истинным бедствием, а для разделяемых объектов, динамически загружаемых во время выполнения, именно так и поступают.
Пожалуй, чаще всего разделяемые объекты, загружаемые во время выполнения, применяются при создании интерфейса для некоторого общего средства, которое может иметь множество различных реализаций. Рассмотрим, к примеру, процедуру сохранения графического файла. Приложение может иметь один внутренний формат для управления его графикой, однако существует много других форматов файлов, в которых приложению понадобится сохранить графические данные, и еще больше форматов создано для разнообразных частных ситуаций [21]. Обобщенный интерфейс для сохранения графического файла, который экспортируется разделяемыми объектами, загружаемыми во время выполнения, позволяет программистам добавлять новые форматы графических файлов в приложение без повторной его компиляции. Если интерфейс хорошо документирован, то даже независимые разработчики, не имеющие исходного кода приложения, смогут включать новые форматы графических файлов.
Точно так же используется и код
27.1. Интерфейс dl
Процесс динамической загрузки заключается в открытии библиотеки, поиске любого количества символов, обработке любых возникающих ошибок и закрытии библиотеки. Все функции динамической загрузки объявляются в одном заголовочном файле,
, и определяются в libdl
(чтобы воспользоваться функциями динамической загрузки скомпонуйте приложение с -ldl
).
Функция dlerror()
возвращает строку, описывающую самую последнюю ошибку, которая возникла в одной из трех других функций динамической загрузки:
const char * dlerror(void);
Каждый раз при возврате значения она очищает состояние ошибки. Если не будет создано другое состояние ошибки, она продолжит выполнение, чтобы вернуть NULL
вместо строки. Объяснение этого необычного поведения можно найти в описании функции dlsym()
.
Функция dlopen()
открывает библиотеку. Этот процесс включает поиск библиотечного файла, открытие файла и выполнение некоторой предварительной обработки. Переменные окружения и параметры, переданные функции dlopen()
, определяют детали этого процесса.
void * dlopen(const char * filename, int flag);
Если filename
является абсолютным путем (то есть начинается с символа /
), то функции dlopen()
не нужно производить поиск библиотеки. Это обычный способ применения функции dlopen()
в коде приложения. Если filename
является простым именем файла, то функция dlopen()
произведет поиск библиотеки filename
в перечисленных ниже местах.
• Набор каталогов, разделенных двоеточием, который определен в переменной окружения LD_ELF_LIBRARY_PATH
, или, если ее не существует, в переменной LD_LIBRARY_PATH
.