Как известно, одним из преимуществ разделяемых библиотек является отсутствие необходимости в повторной компоновке исполняемых файлов, которые библиотеку используют: программа во время выполнения подтягивает необходимые ее части и не содержит копий объектных файлов, как в случае со статическими аналогами.
В Linux у вас может быть несколько версий одной и той же библиотеки. Следование правилам версионирования и компановки позволяет сохранить работоспособность программ при обновлениях, которые нарушают совместимость (добавление дополнительного параметра в публичный метод: подробнее можно ознакомиться здесь).
Корректно спроектированная динамическая библиотека должна состоять из 3 имен:
1. Реальное имя - название самой библиотеки “lib<name>.so.<major>.<minor>.<patch>”. Напрямую при линковке не используется (разве что для дебага).
Major - меняется в случае, когда правки приводят к несовместимости с приложениями (изменение API).
Minor - меняется при расширении API (добавлении функционала).
Patch - багфиксы, оптимизации (изменение функционала внутри готового API).
2. SONAME - метка, которая записывается в файл библиотеки на этапе создания (DT_SONAME в терминологии формата ELF). Именно она встраивается статическим компоновщиком в программу и, как следствие, используется динамическим компоновщиком для поиска на этапе выполнения. Структура, обычно, следующая “lib<library name>.so.<major>”.
/* Создаем libdemo.so.1.0.0 с меткой libdemo.so.1 */
$ gcc -shared -Wl,-soname=libdemo.so.1 -o libdemo.so.1.0.0 module1.o
3. Компоновочное имя - используется на этапе статического связывания приложения и библиотеки (не содержит номеров версий).
/* В исполняемый файл prog будет записан soname, к которому приведет нас libdemo.so */
$ gcc -g -Wall -o prog main.o libdemo.so
Для того, чтобы корректно подключить зависимость, нужно создать 2 символьные ссылки:
1) связывает компоновочное имя и soname;
2) связывает soname и реальное имя библиотеки;
Компоновочное имя может указывать на файл библиотеки, но желательно, чтобы оно ссылалось на soname, т.к. пользователь может удалить старую версию после минорного обновления и ссылка станет невалидной.
libdemo.so -> libdemo.so.1
libdemo.so.1 -> libdemo.so.1.0.0
libdemo.so.1.0.0
После корректного обновления библиотеки, ссылка soname должна указывать на самую последнюю ее версию, в результате чего, ваше приложение будет работать с актуальным экземпляром.
libdemo.so.1 -> libdemo.so.1.0.1
Если обновление меняет мажорную версию, то, при использовании утилиты ldconfig, автоматически будет создана ссылка, связывающая soname и имя библиотеки. При этом, приложение будет работать с последней совместимой версией (если не была удалена).
$ sudo ldconfig -v | grep libdemo
libdemo.so.2 -> libdemo.so.2.0.0 (changed)
/* Выводим диинамические зависимости программы */
$ ldd prog
libdemo.so.1 => ./libdemo.so.1 (0x0000ffff8f870000)
libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6 (0x0000ffff7ff50000)