05 марта 2026 № 1 (2026)

ROSARIUM

газета советского программиста

Теория: библиотеки, SONAME, ldconfig и pkgconfig

На этой странице

Разделяемые библиотеки и SONAME

Что такое .so-файл

Когда вы компилируете C-программу, она может использовать код из библиотеки. Библиотека бывает статической (.a — код копируется внутрь программы при компиляции) и разделяемой (.so — shared object — код загружается в память при запуске программы).

Разделяемая библиотека экономит место на диске и в памяти: один файл libyaml.so используется всеми программами, которым нужен YAML.

Версионирование: три файла на одну библиотеку

После сборки libyaml вы увидите в /usr/lib64/ три файла:

/usr/lib64/libyaml-0.so.2.0.9   ← настоящий файл с кодом (реальная библиотека)
/usr/lib64/libyaml-0.so.2        ← символическая ссылка → libyaml-0.so.2.0.9
/usr/lib64/libyaml.so            ← символическая ссылка → libyaml-0.so.2

Зачем три файла? У каждого своя роль:

ФайлНазначениеКто использует
libyaml-0.so.2.0.9Реальный бинарный файл с кодом библиотекиСистема
libyaml-0.so.2SONAME — стабильное имя для бинарной совместимостиЗагрузчик (ld.so) при запуске программ
libyaml.soСсылка для компилятора (линковки)Компилятор при gcc -lyaml

Что такое SONAME

SONAME (Shared Object Name) — это имя, «зашитое» внутрь самой библиотеки. Когда вы компилируете программу с -lyaml, линковщик смотрит SONAME библиотеки и записывает его в вашу программу. Потом при запуске загрузчик ищет файл именно с этим именем.

Пример: SONAME у libyaml — libyaml-0.so.2. Это означает:

  • Если вышла libyaml 0.2.6 с тем же ABI, файл станет libyaml-0.so.2.0.10, но ссылка libyaml-0.so.2 будет указывать на него — и все старые программы продолжат работать без перекомпиляции.
  • Если ABI сломается, SONAME станет libyaml-0.so.3 — старые программы продолжат использовать libyaml-0.so.2, а новые — libyaml-0.so.3.

Проверить SONAME можно командой:

readelf -d /usr/lib64/libyaml-0.so.2 | grep SONAME

Ожидаемый вывод:

 0x000000000000000e (SONAME)             Library soname: [libyaml-0.so.2]

Зачем разделять на основной пакет и -devel

Представьте: на компьютере пользователя стоит программа, которая использует libyaml. Пользователю нужен только файл libyaml-0.so.2 (и реальный файл, на который он ссылается). Заголовочные файлы (yaml.h), ссылка для компилятора (libyaml.so) и pkgconfig — всё это нужно только разработчику, который пишет новую программу.

Поэтому пакет разделяется:

  • libyaml (основной) — то, что нужно для запуска программ:

    • /usr/lib64/libyaml-0.so.2.0.9 (реальный файл)
    • /usr/lib64/libyaml-0.so.2 (SONAME-ссылка)
    • Лицензия и документация
  • libyaml-devel — то, что нужно для разработки (компиляции новых программ):

    • /usr/lib64/libyaml.so (ссылка для линковщика)
    • /usr/include/yaml.h (заголовочный файл)
    • /usr/lib64/pkgconfig/yaml-0.1.pc (для pkg-config)
    • Зависимость Requires: libyaml (чтобы при установке -devel автоматически ставился основной пакет)

Полная карта файлов

Основной пакет (libyaml):
  /usr/lib64/libyaml-0.so.2.0.9    ← реальный бинарный файл
  /usr/lib64/libyaml-0.so.2        ← SONAME-ссылка (создаётся ldconfig)
  /usr/share/doc/libyaml/README     ← документация
  /usr/share/licenses/libyaml/LICENSE ← лицензия

Подпакет -devel (libyaml-devel):
  /usr/lib64/libyaml.so            ← ссылка для компилятора
  /usr/include/yaml.h              ← заголовочный файл
  /usr/lib64/pkgconfig/yaml-0.1.pc ← файл pkg-config

ldconfig

Что делает ldconfig

ldconfig — системная утилита, которая:

  1. Сканирует директории с библиотеками (/usr/lib64/, /usr/lib/ и др.)
  2. Создаёт SONAME-ссылки (например, libyaml-0.so.2 → libyaml-0.so.2.0.9)
  3. Обновляет кэш /etc/ld.so.cache, чтобы загрузчик быстро находил библиотеки

Что будет без ldconfig

Если не вызвать ldconfig после установки пакета, программы получат ошибку:

error while loading shared libraries: libyaml-0.so.2: cannot open shared object file: No such file or directory

Это одна из самых частых ошибок новичков. Программа установлена, библиотека лежит на диске, но загрузчик не знает о ней, потому что кэш не обновлён.

Как прописать ldconfig в SPEC

В SPEC-файле используются скриптлеты %post и %postun:

%post -p /sbin/ldconfig
%postun -p /sbin/ldconfig

Это означает: «после установки пакета запусти /sbin/ldconfig» и «после удаления пакета запусти /sbin/ldconfig». Флаг -p говорит, что /sbin/ldconfig — это сама программа, а не shell-скрипт.

pkgconfig

Что такое .pc-файл

Файл yaml-0.1.pc — это текстовый файл, который описывает, где лежат заголовки и библиотеки, какие флаги нужны компилятору. Пример содержимого:

prefix=/usr
exec_prefix=${prefix}
libdir=${exec_prefix}/lib64
includedir=${prefix}/include

Name: yaml-0.1
Description: YAML parser and emitter library
Version: 0.2.5
Libs: -L${libdir} -lyaml
Cflags: -I${includedir}

Как разработчики используют pkgconfig

Вместо того чтобы вручную писать пути, разработчик пишет:

# Узнать флаги для компилятора:
pkg-config --cflags yaml-0.1
# Вывод: -I/usr/include

# Узнать флаги для линковщика:
pkg-config --libs yaml-0.1
# Вывод: -L/usr/lib64 -lyaml

# Компиляция программы одной командой:
gcc myprogram.c $(pkg-config --cflags --libs yaml-0.1) -o myprogram

Именно поэтому .pc-файл должен быть в -devel — он нужен только при разработке.

.la-файлы (libtool archives)

При сборке через Autotools (configure/make) часто создаются файлы .la — это libtool archives. Они были нужны в старые времена для отслеживания зависимостей между статическими библиотеками. В современных дистрибутивах они бесполезны и вредны:

  • Содержат захардкоженные пути, которые ломаются при мультилиб-системах
  • Вызывают предупреждения rpmlint
  • Могут конфликтовать с pkgconfig

Правило: всегда удаляйте .la-файлы в секции %install:

find %{buildroot} -name '*.la' -delete