Библиотека — это набор уже скомпилированного кода и данных (функций, процедур, констант, таблиц), который может использоваться несколькими программами.
Выделяют два основных типа библиотек:
- Статически подключаемые библиотеки (static libraries,
.a,.lib) - Динамически разделяемые библиотеки (shared / dynamic libraries,
.so,.dll,.dylib)
Динамически разделяемая библиотека (DRB) — это библиотека, код которой не встраивается в исполняемый файл на этапе линковки, а загружается в память отдельно и может разделяться (share) между несколькими процессами.
Примеры:
- В Linux / Unix: файлы с расширением
.so(например,libc.so) - В Windows: файлы с расширением
.dll - В macOS:
.dylibили.framework
Ключевые особенности:
- Код DRB обычно загружается в момент запуска программы или во время её работы.
- Один экземпляр кода библиотеки в памяти может использоваться несколькими процессами, что экономит RAM.
- Связь с библиотекой происходит динамически — через механизмы загрузчика ОС и динамического линковщика.
- Код библиотеки копируется в исполняемый файл на этапе линковки.
- Исполняемый файл больше по размеру, но не требует внешних библиотек.
- Изменение библиотеки не влияет на уже собранные программы (их нужно пересобирать заново, чтобы использовать новую версию).
- Код библиотеки не копируется внутрь исполняемого файла.
- Вместо этого в исполняемом файле сохраняются символические ссылки на функции библиотеки (имена функций, таблицы импорта и т.д.).
- При запуске программы ОС и динамический линковщик/загрузчик:
- находят нужные библиотеки;
- загружают их в память;
- связывают адреса функций библиотеки с вызовами в программе.
Основные отличия:
| Характеристика | Статическая библиотека | Динамическая библиотека |
|---|---|---|
| Подключение | На этапе линковки | При загрузке/во время выполнения |
| Размер исполняемого файла | Большой | Меньший |
| Обновление библиотеки | Требуется пересборка программы | Можно обновлять отдельно |
| Память (RAM) | Код не разделяется | Код разделяется между процессами |
| Зависимость от внешних файлов | Нет (всё внутри .exe) | Да (нужны .so/.dll) |
- Библиотеки загружаются при старте программы.
- Список необходимых библиотек и импортируемых функций хранится в специальных секциях исполняемого файла:
- В Linux ELF: таблица динамических зависимостей (
.dynamic), таблица импорта/экспорта символов. - В Windows PE: Import Table.
- В Linux ELF: таблица динамических зависимостей (
- Если нужная библиотека не найдена, программа не запустится (ошибка загрузки).
Плюсы:
- Простота использования.
- Все зависимости проверяются при запуске.
Минусы:
- Нельзя гибко выбирать, какие библиотеки подгрузить.
- Ошибка при отсутствии библиотеки — программа вообще не запускается.
- Программа сама явно загружает библиотеку во время выполнения.
- В Linux:
dlopen()— загрузка библиотекиdlsym()— получение адреса функции по имениdlclose()— выгрузка библиотеки
- В Windows:
LoadLibrary(),GetProcAddress(),FreeLibrary()
Плюсы:
- Можно подгружать библиотеки по требованию (ленивая загрузка).
- Возможна плагинная архитектура: программа загружает только те модули, которые нужны.
- Можно выбирать библиотеку в зависимости от условий (настройки пользователя, ОС, версия).
Минусы:
- Программист должен сам обрабатывать ошибки (нет библиотеки, нет функции).
- Усложнение кода.
ОС использует специфичный формат:
- Linux / Unix: ELF (Executable and Linkable Format)
- Windows: PE (Portable Executable)
- macOS: Mach-O
Файл динамической библиотеки содержит:
- Машинный код функций.
- Таблицу экспортируемых символов (что библиотека предоставляет).
- Таблицу импортируемых символов (что самой библиотеке нужно от других библиотек).
- Информацию для динамического линковщика (перемещения, зависимые библиотеки).
При запуске программы:
- ОС читает заголовок исполняемого файла.
- Определяет список необходимых динамических библиотек.
- Передаёт управление динамическому линковщику (например,
ld-linux.soв Linux). - Линковщик:
- находит файлы библиотек (
/lib,/usr/lib, директории изLD_LIBRARY_PATH); - загружает их в виртуальную память процесса (обычно через
mmap); - выполняет релокацию — корректирует адреса в коде/данных, чтобы указатели ссылались на правильные функции и переменные;
- заполняет таблицы импорта (PLT/GOT в ELF).
- находит файлы библиотек (
После этого управление передаётся в main() программы.
Ключевые компоненты рантайма:
ld-linux.so*— динамический загрузчик, указан в сегментеINTERP; именно он ищет и мапит библиотеки.libc.so(glibc/musl и т.п.) — стандартная библиотека, предоставляющая API и обёртки над системными вызовами.
- Код библиотеки загружается в память как разделяемая (read-only) область.
- Разные процессы могут использовать один и тот же физический код через механизм страничной организации памяти.
- Данные библиотеки (глобальные переменные) обычно не разделяются, у каждого процесса — свой экземпляр данных (копия в его адресном пространстве).
Это позволяет:
- Сильно уменьшить суммарное потребление оперативной памяти.
- Ускорить запуск программ (код может уже быть в памяти после запуска другой программы).
- Экономия памяти (RAM)
- Один экземпляр кода DRB разделяется между многими процессами.
- Меньший размер исполняемых файлов
- В программу не включается код библиотек.
- Обновляемость и сопровождение
- Можно обновить библиотеку (исправить баги, улучшить безопасность), не пересобирая все программы.
- В Windows это часто приводит к эффекту “обновили DLL — сразу все программы получили фикс”.
- Гибкость архитектуры
- Поддержка модулей и плагинов.
- Возможность загружать функциональность по требованию.
- Кроссплатформенность на уровне ABI
- Можно компилировать разные программы (даже на разных языках), которые используют одну и ту же библиотеку через единый интерфейс (ABI).
- Зависимости во время исполнения
- Программе нужны соответствующие версии библиотек в системе.
- Если библиотека отсутствует или несовместима, программа не запустится (ошибки типа “missing DLL”).
- Версионные конфликты (DLL Hell)
- Разные программы могут требовать разные версии одной и той же библиотеки.
- Обновление или замена библиотеки может “сломать” старые программы.
- Для борьбы с этим вводятся:
- версионирование в имени файла (
libpng16.so,vcruntime140.dll); - системные каталоги с политикой загрузки;
- механизмы “side-by-side” в Windows.
- версионирование в имени файла (
- Немного более сложная загрузка
- Время запуска программы может увеличиться (нужно загрузить и связать библиотеки).
- Сложность отладки
- Ошибки могут проявляться только при определённых версиях библиотек.
- Возможны проблемы при подмене библиотек, несовместимости ABI (Application Binary Interface).
ОС и динамический линковщик используют определённые правила поиска библиотек.
Типичные места поиска:
- Каталоги по умолчанию:
/lib,/usr/lib,/lib64,/usr/lib64 - Каталоги из конфигурации (
/etc/ld.so.conf,ldconfig) - Переменная окружения
LD_LIBRARY_PATH— задаёт дополнительные пути. - Путь, зашитый при линковке (
rpath,runpath).
Поиск DLL обычно идёт:
- В каталоге самого
.exe. - В системных каталогах (
System32и т.п.). - В каталогах, указанных в переменной
PATH.
ldd <бинарник>— показать, какие.soнужны исполняемому файлу и откуда они будут подхвачены. Фактически запускает бинарник через динамический загрузчик, поэтому небезопасно использовать на подозрительных файлах.ldconfigи/etc/ld.so.conf.d/— формируют кешld.so.cacheдля быстрого поиска библиотек.LD_LIBRARY_PATH— временный способ добавить директории поиска (аккуратно применять в продакшене, чтобы не словить «DLL hell»).
ld(link editor) из binutils используется на этапе сборки для склейки объектных файлов и библиотек.- Динамический загрузчик (
/lib64/ld-linux.so.2или аналогичный для архитектуры) можно вызвать напрямую, чтобы «ручками» запустить бинарник:
/lib64/ld-linux.so.2 /usr/bin/echo. Это удобно для отладки путей и зависимостей.
- Динамические библиотеки — важная точка атаки:
- Подмена библиотеки (DLL injection, DLL hijacking).
- Загрузка библиотеки из ненадёжного пути.
- Для защиты:
- Используются цифровые подписи библиотек.
- Жёстко фиксируются пути к системным библиотекам.
- Применяются механизмы контроля целостности.
- Динамически разделяемая библиотека — это модуль кода, который загружается во время выполнения и разделяется между процессами.
- В отличие от статических библиотек:
- не встраивается в
.exe; - уменьшает размер программ;
- позволяет обновлять функциональность без пересборки.
- не встраивается в
- Связывание может быть:
- неявным (при старте программы);
- явным (по требованию через
dlopen,LoadLibraryи т.п.).
- Механизм работы основан на:
- формате исполняемых файлов (ELF, PE);
- динамическом линковщике;
- таблицах импорта/экспорта и релокации.
- Плюсы: экономия памяти, обновляемость, модульность, плагинность.
- Минусы: зависимости от версий библиотек, возможные конфликты, усложнение отладки и безопасности.
Этот конспект можно использовать как готовый ответ на экзаменационный билет по теме «Динамически разделяемые библиотеки».