Библиотеки linux

3.1. Conventions

For shared libraries to support all of these desired properties,
a number of conventions and guidelines must be followed.
You need to understand the difference between a library’s
names, in particular its «soname» and «real name» (and how they interact).
You also need to understand where they should be placed in the filesystem.

3.1.1. Shared Library Names

Every shared library has a special name called the «soname».
The soname has the prefix «lib», the name of the library,
the phrase «.so», followed by a period and a
version number that is incremented whenever the interface changes
(as a special exception, the lowest-level C libraries don’t start
with «lib»).
A fully-qualified soname includes as a prefix the directory it’s in;
on a working system a fully-qualified soname is simply a symbolic
link to the shared library’s «real name».

Every shared library also has a «real name», which is the filename
containing the actual library code.
The real name adds to the soname a period, a
minor number, another period, and the release number.
The last period and release number are optional.
The minor number and release number
support configuration control by letting you know exactly what version(s)
of the library are installed. Note that these numbers might not be
the same as the numbers used to describe the library in documentation,
although that does make things easier.

In addition, there’s the name that the compiler uses when requesting a library,
(I’ll call it the «linker name»), which is simply the soname without
any version number.

The key to managing shared libraries is the separation of these names.
Programs, when they internally list the shared libraries they need,
should only list the soname they need.
Conversely, when you create a shared library, you only create the
library with a specific filename (with more detailed version information).
When you install a new version of a library, you
install it in one of a few special directories and then run the
program ldconfig(8).
ldconfig examines the existing files and creates the sonames as
symbolic links to the real names, as well as setting up the
cache file /etc/ld.so.cache (described in a moment).

ldconfig doesn’t set up the linker names; typically this is done during
library installation, and the linker name is simply created as a symbolic
link to the «latest» soname or the latest real name.
I would recommend having the linker name be a symbolic link to the soname,
since in most cases if you update the library
you’d like to automatically use it when linking.
I asked H. J. Lu why ldconfig doesn’t automatically set up the linker names.
His explanation was basically that
you might want to run code using the latest version of a library,
but might instead want development to link
against an old (possibly incompatible) library.
Therefore, ldconfig makes no assumptions about what you want programs to
link to, so installers must specifically modify symbolic links to
update what the linker will use for a library.

Thus, /usr/lib/libreadline.so.3
is a fully-qualified soname, which
ldconfig would set to be a symbolic link to some realname like
/usr/lib/libreadline.so.3.0.
There should also be a linker name,
/usr/lib/libreadline.so
which could be a symbolic link referring to
/usr/lib/libreadline.so.3.

История

Женщина работает рядом с картотечным шкафом, содержащим библиотеку подпрограмм на катушках с перфолентой для компьютера EDSAC.

В 1947 году Голдстайн и фон Нейман предположили, что было бы полезно создать «библиотеку» подпрограмм для их работы на машине IAS , раннем компьютере, который в то время еще не работал. Они представили физическую библиотеку записей на магнитных проводах , где на каждом проводе хранится компьютерный код многократного использования.

Вдохновленный фон Нейманом, Уилкс и его команда создали EDSAC . В картотеке с перфолентой хранилась библиотека подпрограмм для этого компьютера. Программы для EDSAC состояли из основной программы и последовательности подпрограмм, скопированных из библиотеки подпрограмм. В 1951 году группа опубликовала первый учебник по программированию «Подготовка программ для электронного цифрового компьютера» , в котором подробно описывались создание и цель библиотеки.

COBOL включал «примитивные возможности для библиотечной системы» в 1959 году, но Жан Саммет в ретроспективе охарактеризовал их как «неадекватные библиотечные возможности».

У JOVIAL был Коммуникационный пул (COMPOOL), примерно библиотека файлов заголовков.

Другой важный вклад в концепцию современной библиотеки внесла инновационная подпрограмма FORTRAN . Подпрограммы FORTRAN можно компилировать независимо друг от друга, но в компиляторе отсутствовал компоновщик . Таким образом, до введения модулей в Фортран-90 проверка типов между подпрограммами ФОРТРАНА была невозможна.

К середине 1960-х библиотеки копий и макросов для ассемблеров были обычным явлением. Начиная с популярности IBM System / 360 , библиотеки, содержащие другие типы текстовых элементов, например системные параметры, также стали обычным явлением.

Simula был первым объектно-ориентированным языком программирования , и его классы были почти идентичны современной концепции, используемой в Java , C ++ и C # . Класс концепция Симула был также родоначальником пакета в Ada и модуль из Modula-2 . Даже при первоначальной разработке в 1965 году классы Simula могли быть включены в файлы библиотеки и добавлены во время компиляции.

Step 3: Linking with a shared library

As you can see, that was actually pretty easy. We have a shared library. Let us compile our main.c and link it with libfoo. We will call our final program test. Note that the -lfoo option is not looking for foo.o, but libfoo.so. GCC assumes that all libraries start with lib and end with .so or .a (.so is for shared object or shared libraries, and .a is for archive, or statically linked libraries).

$ gcc -Wall -o test main.c -lfoo
/usr/bin/ld: cannot find -lfoo
collect2: ld returned 1 exit status

Telling GCC where to find the shared library

Uh-oh! The linker does not know where to find libfoo. GCC has a list of places it looks by default, but our directory is not in that list. We need to tell GCC where to find libfoo.so. We will do that with the -L option. In this example, we will use the current directory, /home/username/foo:

$ gcc -L/home/username/foo -Wall -o test main.c -lfoo

Библиотечные функции и системные вызовы

На рисунках 1 и 2 показан процесс перехвата библиотечной функции. Пожалуйста, рассмотрите их.

Рисунок 1: Вызов библиотечной функции

Рисунок 2: Вызов библиотечной функции с перехватом

Теперь давайте рассмотрим перехват функции разделяемой библиотеки. Простейшая программа prog1.c, исходный код которой приведен ниже, сначала резервирует 10 байт из кучи, а затем освобождает их:

#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
int main(void)
{
        int *p;
        printf("calling from main...\n");
        p=(int *)malloc(10);
        if(!p)
        {
                printf("Got allocation error...\n");
                exit(1);
        }
        printf("returning to main...\n");
        free(p);                           /* освобождение зарезервированной памяти */
        printf("freeing memory...\n");
        return 0;
}

Мы увидим следующий вывод после компиляции и запуска программы:

# gcc -o prog1 prog1.c
# ./prog1
calling from main...
returning to main...
freeing memory...
#

Следующая программа, исходный код которой находится в файле prog2.c, содержит простейшую функцию перехвата для функции malloc():

#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <dlfcn.h>                               /* заголовочный файл необходим для вызва dlsym() */

/* lcheck() - функция, необходимая для проверки на утечки памяти; ее код не приведен в данном листинге*/
void lcheck(void);
void* malloc(size_t size)
{
        static void* (*my_malloc)(size_t) = NULL;
        printf("inside shared object...\n");
        if (!my_malloc)
        my_malloc = dlsym(RTLD_NEXT, "malloc");  /* возвращает указатель на функцию malloc */
        void *p = my_malloc(size);               /* вызов malloc() с использованием указателя my_malloc */  
        printf("malloc(%d) = %p\n", size, p);
        lcheck();                                /* вызов функции do_your_stuff */
        printf("returning from shared object...\n");
        return p;
}
void lcheck(void)
{
        printf("displaying memory leaks...\n");
        /* выполнение необходимых действий */
}

Ход компиляции в выполнения выглядит следующим образом:

# gcc -shared -ldl -fPIC prog2.c -o libprog2.so
# LD_PRELOAD=/home/dibyendu/malloc_hook/libprog2.so ./prog1
calling from main...
inside shared object...
malloc(10) = 0x8191008
displaying memory leaks...
returning from shared object...
returning to main...
freeing memory...
#

Теперь мы можем подробнее рассмотреть наш первый пример перехвата функции разделяемой библиотеки. Функция dlsym() принимает два параметра: первый параметр — это идентификатор, возвращенный функцией dlopen(). В нашем случае мы должны использовать параметр RTLD_NEXT для перехвата функции.

Этот параметр сообщает динамическому компоновщику о том, что необходимо найти следующую ссылку на заданную функцию, а не ту, которая находится при вызове dlsym(). Вторым параметром является имя функции (в нашем случае malloc) в форме строки. Функция dlsym() возвращает адрес функции, указанной в качестве второго параметра. Во время компиляции параметр fPIC используется для создания независимого от позиции объектного кода (position-independent object).

Переменная окружения LD_PRELOAD передает загрузчику динамических библиотек список библиотек, которые должны быть загружены в первую очередь. В нашем примере при помощи этой переменной мы подгружаем библиотеку libprog2.so и динамически связываем ее с программой prog1. Не забудьте, что при передаче параметра переменной окружения LD_PRELOAD, необходимо приводить абсолютный путь до файла разделяемой библиотеки. И конечно же, не забудьте объявление #define _GNU_SOURCE, если хотите использовать соответствующие расширения во время работы с GNU C Library, поскольку некоторые расширения могут быть недоступны на операционных системах, не имеющих отношения к проекту GNU, и добавление этого объявления улучшит переносимость на другие системы.

Загрузка разделяемых библиотек из приложений

Разделяемые библиотеки могут быть загружены из приложения не сразу, а в процессе ее выполнения. Приложение посылает запрос динамическому компоновщику на загрузку и связывание разделяемой библиотеки. При этом приложение даже может не содержать в себе вкомпилированных ссылок на эти разделяемые библиотеки. В Linux, Solaris и в других системах есть ряд функций, позволяющих таким образом загружать разделяемые объекты. В Linux существуют системные вызовы dlopen, dlsym и dlclose, позволяющие соответственно загрузить разделяемый объект, получить указатель на какой-либо содержащийся в нем символ, закрыть объект. В системах Windows есть функции LoadLibrary и GetProcAddress — это соответствующие замены вызовам dlopen и dlsym.

Переезд

Некоторые ссылки в программном или библиотечном модуле хранятся в относительной или символьной форме, которая не может быть разрешена до тех пор, пока всему коду и библиотекам не будут присвоены окончательные статические адреса. Перемещение — это процесс настройки этих ссылок, который выполняется компоновщиком или загрузчиком . В общем, перемещение не может быть выполнено для отдельных библиотек, потому что адреса в памяти могут различаться в зависимости от программы, использующей их, и других библиотек, с которыми они объединены. Позиционно-независимый код избегает ссылок на абсолютные адреса и, следовательно, не требует перемещения.

Создание динамической библиотеки

Для начала стоит сказать, что объектный файл создаваемый обычным способом не подходит для динамических
библиотек. Связано это с тем, что все объектные файлы создаваемые обычным образом не имеют представления о том в какие
адреса памяти будет загружена использующая их программа. Несколько различных программ могут использовать одну библиотеку,
и каждая из них располагается в различном адресном пространстве. Поэтому требуется, чтобы переходы в функциях библиотеки
(операции goto на ассемблере) использовали не абсолютную адресацию, а относительную. То есть генерируемый компилятором
код должен быть независимым от адресов, такая технология получила название PIC — Position Independent Code. В компиляторе
gcc данная возможность включается ключом

Теперь компилирование наших файлов будет иметь вид:

Динамическая библиотека это уже не архивный файл, а настоящая загружаемая программа, поэтому созданием динамических
библиотек занимается сам компилятор gcc. Для того, чтобы создать динамическую библиотеку надо использовать
ключ :

В результате получим динамическую библиотеку libfsdyn.so, которая по моей задумке будет динамической версией библиотеки
libfs.a, что видно из названия :) Теперь, чтобы компилировать результирующий файл с использованием динамической
библиотеки нам надо собрать файл командой:

Если теперь Вы сравните файлы, полученные при использовании статической и динамической библиотеки, то увидите, что их
размеры отличаются. В данном случае файл созданный с динамической библиотекой занимает чуть больше места, но это лишь
от того, что программа используемая нами совершенно примитивная и львиную долю там занимает специальный код для
использования динамических возможностей. В реальных условиях, когда используются очень большие функции размер программы
с использованием динамической библиотеки значительно меньше.

На этом фокусы не кончаются, если Вы сейчас попробуете запустить файл rezultdyn, то получите ошибку:

Это сообщение выдает динамический линковщик. Он просто не может найти файл нашей динамической библиотеки. Дело в том, что
загрузчик ищет файлы динамических библиотек в известных ему директориях, а наша директория ему не известна. Но это мы чуть
отложим, потому что это достаточно сложный вопрос.

А сейчас стоит поговорить еще об одном моменте использования библиотек. Я специально создал динамическую библиотеку с
названием fsdyn, чтобы она отличалась от названия статической библиотеки fs. Дело в том, что если у Вас две библиотеки
статическая и динамическая с одинаковыми названиями, то есть libfs.a и libfs.so, то компилятор всегда будет использовать
динамическую библиотеку.

Связано это с тем, что в ключе -l задается часть имени библиотеки, а префикс lib и окончание .a или .so приставляет сам
компилятор. Так вот алгоритм работы компилятора таков, что если есть динамическая библиотека, то она используется по
умолчанию. Статическая же библиотека используется когда компилятор не может обнаружить файл .so этой библиотеки. Во всей
имеющейся у меня документации пишется, что если использовать ключ -static, то можно насильно заставить компилятор
использовать статическую библиотеку. Отлично, попробуем…

Как бы я не пробовал играть с позицией ключа -static, результирующий файл rez1 получается размером в 900 Кб. После
применения программы strip размер ее уменьшается до 200 Кб, но это же не сравнить с тем, что наша первая статическая
компиляция давала программу размером 10 Кб. А связано это с тем, что любая программа написанная на C/C++ в Linux
использует стандартную библиотеку «C» library, которая содержит в себе определения таких функций, как printf(), write()
и всех остальных. Эта библиотека линкуется к файлу как динамическая, чтобы все программы написанные на C++ могли
использовать единожды загруженные функции. Ну, а при указании ключа -static компилятор делает линковку libc статической,
поэтому размер кода увеличивается на все 200 Кб.

Этот эффект я не смог побороть. Поэтому я пришел к выводу, что статическую библиотеку и динамическую лучше всего создавать
с разными именами. Надеюсь в будущем мы с Вами еще разберемся с этой загвоздкой, но если действительно все так плохо,
то решение с различными именами будет единственно верным.

Статические библиотеки

Когда связывание выполняется во время создания исполняемого файла или другого объектного файла, это называется статическим связыванием или ранним связыванием . В этом случае связывание обычно выполняется компоновщиком , но также может выполняться компилятором . Статическая библиотека , также известный как архив , один предназначен для статически. Изначально существовали только статические библиотеки. Статическая компоновка должна выполняться при перекомпиляции любых модулей.

Все модули, необходимые программе, иногда статически связываются и копируются в исполняемый файл. Этот процесс и полученный автономный файл известны как статическая сборка программы. Статическая сборка может не нуждаться в дальнейшем перемещении, если используется виртуальная память и не требуется рандомизация разметки адресного пространства .

Библиотеки объектов

Хотя изначально динамическое связывание было впервые применено в 1960-х годах, оно не распространялось на операционные системы, используемые потребителями до конца 1980-х годов. К началу 1990-х годов он был доступен в той или иной форме в большинстве операционных систем

В тот же период объектно-ориентированное программирование (ООП) стало важной частью программирования. ООП с привязкой во время выполнения требует дополнительной информации, которую традиционные библиотеки не предоставляют

В дополнение к именам и точкам входа кода, расположенным внутри, им также требуется список объектов, от которых они зависят. Это побочный эффект одного из основных преимуществ ООП — наследования, что означает, что части полного определения любого метода могут находиться в разных местах. Это больше, чем просто перечисление того, что одна библиотека требует услуг другой: в настоящей системе ООП сами библиотеки могут быть неизвестны во время компиляции и могут различаться от системы к системе.

В то же время многие разработчики работали над идеей многоуровневых программ, в которых «дисплей», работающий на настольном компьютере, использовал бы услуги мэйнфрейма или миникомпьютера для хранения или обработки данных. Например, программа на компьютере с графическим пользовательским интерфейсом будет отправлять сообщения на миникомпьютер, чтобы вернуть небольшие образцы огромного набора данных для отображения. Вызов удаленных процедур (RPC) уже справлялся с этими задачами, но стандартной системы RPC не существовало.

Вскоре большинство производителей мини-компьютеров и мэйнфреймов инициировали проекты по их объединению, создавая формат библиотеки ООП, который можно было использовать где угодно. Такие системы назывались библиотеками объектов или распределенными объектами , если они поддерживали удаленный доступ (не все поддерживали). COM от Microsoft является примером такой системы для локального использования. DCOM, модифицированная версия COM, поддерживает удаленный доступ.

Некоторое время объектные библиотеки имели статус «следующей большой вещи» в мире программирования. Был предпринят ряд попыток создать системы, которые будут работать на разных платформах, и компании соревновались, пытаясь запереть разработчиков в их собственных системах. Примеры включают в себя IBM ‘s System Object Model (SOM / DSOM), Sun Microsystems ‘ Распределенные объекты Везде (МЭ), NeXT ‘s Portable Distributed Objects (PDO), Digital ‘ s ObjectBroker , от Microsoft Component Object Model (COM / DCOM) и любое количество систем на базе CORBA .

Основные библиотеки

Это библиотеки основных языков программирования, которые содержат базовую функциональность для взаимодействия с системой.

  • libanl.so — библиотека работы с DNS;
  • libc.a — основная статическая библиотека языка Си;
  • libdl.a — библиотека, отвечающая за загрузку других библиотек во время запуска программы;
  • libnsl.so — работа со службой NIS;
  • libstdc++.so — стандартная библиотека C++;
  • libutil.so — реализует основные часто используемые функции;
  • libelf.so — библиотека для работы с бинарными файлами ELF;
  • libevent.so — библиотека с реализацией механизма вызова функций после события;
  • libgnutls.so — библиотека с основными методами и возможностями, используемыми в стандартных утилитах Linux;
  • ld-linux.so — главная библиотека, она собрана в виде статической и не имеет зависимостей. Загружает другие библиотеки и исполняемые файлы. Вы можете вызвать эту библиотеку как программу и передать ей адрес исполняемого файла для запуска.

Библиотеки кодеков

Эти библиотеки содержат наборы кодеков для воспроизведения различных медиа данных. Некоторые из них имеют несвободную лицензию или закрытый исходный код:

  • liba52.so — свободный декодер ATSC A/52;
  • libavfilter6.so — библиотека фильтров ffmpeg;
  • libavcodec57.so — библиотека кодеков ffmpeg;
  • libavformat57.so — библиотека форматов ffmpeg;
  • libavutil55.so — библиотека дополнительных функций ffmpeg;
  • libdv.so — программный кодек для DV;
  • libmad.so — MPEG аудио декодер;
  • libmpeg2.so — потоковый декодер видео данных;
  • libmpg123.so — библиотека консольного плеера mpg123;
  • libwebp.so — декодирование формата Webp;
  • libxvidcore.so — медиа кодек MPEG-4.

Звуковые библиотеки

  • libao.so — библиотека проигрывания звука с простым интерфейсом;
  • libasound.so — библиотека взаимодействия со звуковой подсистемой ALSA;
  • libaudio2.so — библиотека работы со звуком, входит в состав ALSA;
  • libespeak.so — библиотека синтеза речи;
  • libpulse.so — библиотека с основными методами PulseAudio;

Графические библиотеки

  • libart.so — библиотека с реализацией функций для работы с 3d графикой;
  • libaa.so — библиотека ASCII графики;
  • libgtk-3.so — набор методов графического фреймворка GTK 3;
  • libgd.so — базовые функции работы с графикой и рисования;
  • libgif.so — работа с форматом изображений Gif;
  • libjpeg.so — работа с изображениями Jpeg;
  • libglapi.so — свободная реализация методов для работы с OpenGL;
  • libgtk-x11-2.0.so — набор методов библиотеки GTK 2;
  • libwx_baseu.so, libwx_baseu_net-3.0.so и другие — набор библиотек фремворка создания графических приложений WX;
  • libX11.so — основные методы и функции X сервера;
  • libncurses.so — одна из самых популярных библиотек псевдографики;
  • libQtGui.so, libQt5Svg.so, libQt5Widgets.so и другие — библиотеки фреймворка разработки графических приложений Qt.

Работа с текстом

  • libaspell.so — библиотека проверки орфографии;
  • libfreetype.so — библиотека отрисовки шрифтов;
  • libharfbuzz.so — библиотека обработки символов Unicode;
  • libxml2.so — библиотека разбора XML;
  • libyaml-0.so — библиотека разбора Yaml.

Безопасность

  • libcrack.so — библиотека с реализацией методов перебора паролей для проверки их надежности;
  • libcrypt.so — библиотека, отвечающая за шифрование;
  • libssl3.so — библиотека шифрования SSLv3;
  • libapparmor.so — библиотека управления системой безопасности AppArrmor;
  • libaudit.so — библиотека слежения за состоянием системы и регистрации событий.

Библиотеки драйверов

  • libcups.so — библиотека работы с принтером;
  • libfuse.so — библиотека организации виртуальных файловых систем;
  • libgphoto2.so — библиотека взаимодействия с камерами по USB;
  • libsensors.so — используется для получения информации от датчиков на материнской плате;
  • libudisks2.so — библиотека usisks, которая отвечает за автоматическое монтирование и обнаружение подключенных устройств;
  • libv4l1.so — библиотека работы с веб-камерами;
  • libpci.so — библиотека работы с PCI устройствами;
  • libusb-1.0.so — библиотека управления USB;
  • libdrm.so — библиотека с общими возможностями Direct Rendering Manager, отрисовки графики с помощью видеокарты;
  • libdrm_amdgpu.so — DRM для драйвера AMDGPU;
  • libdrm_intel.so — DRM для карт Intel;
  • libdrm_nouveau.so — свободный DRM для видеокарт Nvidia;

Сеть

  • libresolv.so — библиотека получения IP адреса по имени хоста;
  • libpcap.so — библиотека анализа и захвата сетевых пакетов;
  • libproxy.so — настройка и управление прокси;

Эмуляция

  • libSDL.so — библиотека эмуляции загрузки компьютера и базовых возможностей BIOS;
  • libwine.so — библиотека прослойки для запуска приложений Windows в Linux.
  • libvirt.so — библиотека управления KVM;

Связывание

Библиотеки важны в процессе связывания или связывания программ , который разрешает ссылки, известные как ссылки или символы, на библиотечные модули. Процесс связывания обычно выполняется автоматически с помощью линкера или связующей программу , которая ищет набор библиотек и других модулей в заданном порядке. Обычно не считается ошибкой, если цель ссылки может быть найдена несколько раз в заданном наборе библиотек. Связывание может быть выполнено при создании исполняемого файла или всякий раз, когда программа используется во время выполнения .

Разрешаемые ссылки могут быть адресами переходов и других обычных вызовов. Они могут быть в основной программе или в одном модуле в зависимости от другого. Они преобразуются в фиксированные или перемещаемые адреса (из общей базы) путем выделения оперативной памяти для сегментов памяти каждого указанного модуля.

Некоторые языки программирования используют функцию, называемую интеллектуальным связыванием, при которой компоновщик знает о компиляторе или интегрирован с ним, так что компоновщик знает, как используются внешние ссылки, и код в библиотеке, который фактически никогда не используется , даже если на него есть внутренние ссылки, может быть исключен из скомпилированного приложения. Например, программа, которая использует только целые числа для арифметических операций или вообще не выполняет арифметических операций, может исключать библиотечные подпрограммы с плавающей запятой. Эта функция интеллектуального связывания может привести к уменьшению размера файлов приложения и уменьшению использования памяти.

Рейтинг
( Пока оценок нет )
Понравилась статья? Поделиться с друзьями:
Мой редактор ОС
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: