C: создание и применение shared library в Linux

Автор: | 31/07/2017

Библиотека – это файл, содержащий скопилированный код из нескольких объектных файлов в один файл библиотеки, который может содержать функции используемые другими программами.

Библиотеки могут быть статичными (static) и динамическими или разделяемыми (dynamic, shared).

Ниже – краткий пример создания и применения shared library на C в Linux.

Доступ к общей библиотеке может осуществляться по нескольким именам:

  • имя, используемое “линкером” /usr/bin/ld (linker name), в виде слова lib + имя библиотеки + расширение .so, например – libpthread.so
  • Полное имя (fully qualified name или soname), в виде lib + name + .so + версия (например – libpthread.so.1)
  • реальное имя – полное имя файла, содержащего версию библиотеки, в виде lib + имя + .so + версия + минорная версия и опционально – версия релиза (например – libpthread.so.1.1)

Версия для общей бибилиотеки меняется в случае, когда изменения в коде этой бибилиотеки делают её несовместимой с предыдущими версиями, например – если из библиотеки была убрана какая-то функция (libpthread.so.1)

Минорная версия меняется, если изменения не затронули совметимость библиотеки, например – какой-то фикс в одной из функций. В таком случае версия останется прежней, а изменится только минорная часть (libpthread.so.1.1).

Такое соглашение об именах версий библиотек позволяет существование разных версий одной библиотеки в одной системе.

Программа, которая будет линковаться с этой бибилиотекой, не будет привязана к определённому файлу с последней версией библиотеки. Вместо этого, после установки последней версии – все связанные программы будут использовать её.

Создание библиотеки

Создадим простой файл libhello.c с одной функцией:

#include <stdio.h>
 
void print_hello() {
    printf("Hello, World!\n");
}

Создаём заголовочный файл библиотеки libhello.h с прототипом функции:

void print_hello();

Приступаем к сборке библиотеки.

Создаём объектный файл, указав опцию PIC (Position Independent Code), Warning (-Wall - warning all), -g для добавления дебаг-информации и -c – что бы создать только файл библиотеки, без вызова линкера:

[simterm]

$ gcc -fPIC -Wall -g -c libhello.c

[/simterm]

Проверяем – теперь у нас имеется объектный файл .o:

[simterm]

$ ls -l
total 16
-rw-r--r-- 1 setevoy setevoy   75 Jul 31 13:21 libhello.c
-rw-r--r-- 1 setevoy setevoy   18 Jul 31 13:22 libhello.h
-rw-r--r-- 1 setevoy setevoy 5032 Jul 31 13:33 libhello.o

[/simterm]

Теперь создадим общую библиотеку с дебагом (-g), тип общая (-shared), указав (с помощь -Wl) линковщику (/usr/bin/ld) на использование имени libhello и версии 0, результат сохранить в файл libhello.so.0.0 (опция -o), и использовать объектный файл libhello.o:

[simterm]

$ gcc -g -shared -Wl,-soname,libhello.so.0 -o libhello.so.0.0 libhello.o -lc

[/simterm]

Проверяем:

[simterm]

$ gcc -g ls -l
total 28
-rw-r--r-- 1 setevoy setevoy    75 Jul 31 13:21 libhello.c
-rw-r--r-- 1 setevoy setevoy    18 Jul 31 13:22 libhello.h
-rw-r--r-- 1 setevoy setevoy  5032 Jul 31 13:33 libhello.o
-rwxr-xr-x 1 setevoy setevoy 10312 Jul 31 13:47 libhello.so.0.0

[/simterm]

Далее – используем ldconfig, что бы создать файл с linker name, который будет являться симлинком на файл с soname (полным именем), который в свою очередь – будет ссылкой на реальное имя:

[simterm]

$ ldconfig -v -n .
.:
        libhello.so.0 -> libhello.so.0.0 (changed)

[/simterm]

Проверяем:

[simterm]

$ ls -l
total 28
-rw-r--r-- 1 setevoy setevoy    75 Jul 31 13:21 libhello.c
-rw-r--r-- 1 setevoy setevoy    18 Jul 31 13:22 libhello.h
-rw-r--r-- 1 setevoy setevoy  5032 Jul 31 13:33 libhello.o
lrwxrwxrwx 1 setevoy setevoy    15 Jul 31 13:50 libhello.so.0 -> libhello.so.0.0
-rwxr-xr-x 1 setevoy setevoy 10312 Jul 31 13:47 libhello.so.0.0

[/simterm]

Создаём символьную ссылку на файл (для linker name):

[simterm]

$ ln -sf libhello.so.0 libhello.so

[/simterm]

Проверяем:

[simterm]

$ ls -l
total 28
-rw-r--r-- 1 setevoy setevoy    75 Jul 31 13:21 libhello.c
-rw-r--r-- 1 setevoy setevoy    18 Jul 31 13:22 libhello.h
-rw-r--r-- 1 setevoy setevoy  5032 Jul 31 13:33 libhello.o
lrwxrwxrwx 1 setevoy setevoy    13 Jul 31 13:59 libhello.so -> libhello.so.0
lrwxrwxrwx 1 setevoy setevoy    15 Jul 31 13:50 libhello.so.0 -> libhello.so.0.0
-rwxr-xr-x 1 setevoy setevoy 10312 Jul 31 13:47 libhello.so.0.0

[/simterm]

Ещё раз про имена:

  1. libhello.so: linker name файл, симлинк на Fully qualified name или soname файл, в данном случае это libhello.so.0
  2. libhello.so.0: soname, который указывает на файл с real namelibhello.so.0.0
  3. libhello.so.0.0: непосредственно файл общей библиотеки

Использование shared lib

Попробуем применить созданную библиотеку.

Создаём файл программы, hello.c:

#include "libhello.h"
 
int main(){
  print_hello();
  return 0;
}

Собираем программу, указав через -l – имя библиотеки (без lib), и с помощью -L – путь, по которому необходимо искать файл библиотеки:

[simterm]

$ gcc hello.c -o hello -lhello -L.

[/simterm]

Добавляем текущий каталог в $LD_LIBRARY_PATH:

[simterm]

$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.

[/simterm]

Проверяем:

[simterm]

$ ./hello 
Hello, World!

[/simterm]

Проверить используемые программой библиотеки можно с помощью ldd:

[simterm]

$ ldd hello
        linux-vdso.so.1 (0x00007ffeef1b9000)
        libhello.so.0 (0x00007f44cf513000)
        libc.so.6 => /usr/lib/libc.so.6 (0x00007f44cf16d000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f44cf715000)

[/simterm]

Если убрать файл билиотеки – ldd сразу сообщит об ошибке:

[simterm]

$ mv libhello.so.0 /tmp/
$ ldd hello
        linux-vdso.so.1 (0x00007ffcab1ee000)
        libhello.so.0 => not found
        libc.so.6 => /usr/lib/libc.so.6 (0x00007fb85152e000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fb8518d4000)

[/simterm]

И программа откажется запускаться:

[simterm]

$ ./hello 
./hello: error while loading shared libraries: libhello.so.0: cannot open shared object file: No such file or directory

[/simterm]

Готово.