$ gcc — g — shared — Wl, — Bsymbolic — o libfoo.so foo.o
$ gcc — g — o prog prog.c libfoo.so
$ LD_LIBRARY_PATH=. /prog
foo-xyz
Параметр компоновщика — Bsymbolic делает так, что ссылки на глобальный символ внутри разделяемой библиотеки в первую очередь должны привязываться к определению из этой библиотеки (если таковое существует). Стоит отметить: вне зависимости от данного параметра вызов xyz() из главной программы всегда приводит к запуску той версии xyz(), которая в ней определена.
Почти во всех случаях разделяемые библиотеки являются наиболее предпочтительным выбором, однако иногда бывают ситуации, в которых более подходящей оказывается статическая библиотека. В частности, преимуществом статических библиотек может быть тот факт, что они позволяют скомпоновать в приложение весь код, необходимый ей для работы. Статическая компоновка, к примеру, подходит для пользователей, которые не могут или не хотят устанавливать в своей системе разделяемые библиотеки, или в случаях, когда программа должна выполняться в среде, где разделяемые библиотеки недоступны (например, в «тюрьме» chroot). Кроме того, даже обновление разделяемой библиотеки до совместимой версии может привести к ошибкам, вызывающим сбой приложения. Компонуя программу статически, мы защищаем ее от внешних изменений и гарантируем, что она содержит весь код, необходимый для ее выполнения (за счет увеличения ее размера и, как следствие, повышенного потребления дискового пространства и памяти).
Если в распоряжении компоновщика имеются оба типа библиотеки с тем же именем — статическая и разделяемая (например, когда во время компоновки указаны параметры — Lsomedir — ldemo, и при этом существуют файлы libdemo.so и libdemo.a), то он по умолчанию выбирает последнюю. Чтобы заставить его использовать статическую версию библиотеки, можно выполнить одно из следующих действий:
• указать путь к статической библиотеке (включая расширение. a) в командной строке gcc;
• указать для gcc параметр — static;
• задействовать параметры gcc — Wl, — Bstatic и — Wl, — Bdynamic, чтобы явно переключить компоновщик в режим использования статических библиотек. При этом можно применять параметры — l в командной строке gcc. Компоновщик обработает их в том порядке, в котором они были указаны.
Объектная библиотека объединяет в себе скомпилированные объектные модули, которые могут быть использованы программами, скомпонованными с этой библиотекой. Как и другие реализации UNIX, Linux предоставляет два вида объектных библиотек: статические (не имевшие альтернативы в ранних UNIX-системах) и более современные разделяемые.
Разделяемые библиотеки имеют несколько преимуществ перед статическими, поэтому преобладают в современных реализациях UNIX. Данные преимущества по большей части следуют из того факта, что в итоговый исполняемый файл не записываются объектные модули библиотеки, с которой она скомпонована. Вместо этого (статический) компоновщик просто добавляет в файл программы информацию о разделяемых библиотеках, необходимые ей для работы. При запуске файла динамический компоновщик использует данную информацию для загрузки соответствующих библиотек. На этапе выполнения в память загружается только одна копия разделяемой библиотеки, которая может применяться разными программами. Как следствие фактов, описанных выше, разделяемые библиотеки уменьшают объем дискового пространства и памяти, необходимые системе.
Имя soname предоставляет уровень абстракции для разрешения ссылок на разделяемые библиотеки во время выполнения. Если оно присутствует, то записывается статическим компоновщиком в итоговый исполняемый файл вместо реального имени библиотеки. Существует также система версионирования, когда реальное имя библиотеки имеет вид имя_библиотеки. so.мажорный_идентификатор. минорный_идентификатор. Это позволяет создавать программы, которые автоматически загружают последнюю минорную версию разделяемой библиотеки (без необходимости проводить повторную компоновку); благодаря этому также можно генерировать мажорные версии библиотеки, несовместимые с предыдущими.
Для нахождения разделяемой библиотеки на этапе выполнения динамический компоновщик следует стандартному набору правил, подразумевающему поиск по списку каталогов (таких как /lib и /usr/lib), в которых эта библиотека может быть установлена.