Компиляция библиотеки so linux
Как уже неоднократно упоминалось в предыдущей главе, библиотека — это набор скомпонованных особым образом объектных файлов. Библиотеки подключаются к основной программе во время линковки. По способу компоновки библиотеки подразделяют на архивы (статические библиотеки, static libraries) и совместно используемые (динамические библиотеки, shared libraries). В Linux, кроме того, есть механизмы динамической подгрузки библиотек. Суть динамической подгрузки состоит в том, что запущенная программа может по собственному усмотрению подключить к себе какую-либо библиотеку. Благодаря этой возможности создаются программы с подключаемыми плагинами, такие как XMMS. В этой главе мы не будем рассматривать динамическую подгрузку, а остановимся на классическом использовании статических и динамических библиотек.
С точки зрения модели КИС, библиотека — это сервер. Библиотеки несут в себе одну важную мысль: возможность использовать одни и те же механизмы в разных программах. В Linux библиотеки используются повсеместно, поскольку это очень удобный способ «не изобретать велосипеды». Даже ядро Linux в каком-то смысле представляет собой библиотеку механизмов, называемых системными вызовами.
Статическая библиотека — это просто архив объектных файлов, который подключается к программе во время линковки. Эффект такой же, как если бы вы подключали каждый из файлов отдельно.
В отличие от статических библиотек, код совместно используемых (динамических) библиотек не включается в бинарник. Вместо этого в бинарник включается только ссылка на библиотеку.
Рассмотрим преимущества и недостатки статических и совместно используемых библиотек. Статические библиотеки делают программу более автономной: программа, скомпонованная со статической библиотекой может запускаться на любом компьютере, не требуя наличия этой библиотеки (она уже «внутри» бинарника). Программа, скомпонованная с динамической библиотекой, требует наличия этой библиотеки на том компьютере, где она запускается, поскольку в бинарнике не код, а ссылка на код библиотеки. Не смотря на такую зависимость, динамические библиотеки обладают двумя существенными преимуществами. Во-первых, бинарник, скомпонованный с совместно используемой библиотекой меньше размером, чем такой же бинарник, с подключенной к нему статической библиотекой (статически скомпонованный бинарник). Во-вторых, любая модернизация динамической библиотеки, отражается на всех программах, использующих ее. Таким образом, если некоторую библиотеку foo используют 10 программ, то исправление какой-нибудь ошибки в foo или любое другое улучшение библиотеки автоматически улучшает все программы, которые используют эту библиотеку. Именно поэтому динамические библиотеки называют совместно используемыми. Чтобы применить изменения, внесенные в статическую библиотеку, нужно пересобрать все 10 программ.
В Linux статические библиотеки обычно имеют расширение .a (Archive), а совместно используемые библиотеки имеют расширение .so (Shared Object). Хранятся библиотеки, как правило, в каталогах /lib и /usr/lib. В случае иного расположения (относится только к совместно используемым библиотекам), приходится немного «подшаманить», чтобы программа запустилась.
3.2. Пример статической библиотеки
Теперь давайте создадим свою собственную библиотеку, располагающую двумя функциями: h_world() и g_world(), которые выводят на экран «Hello World» и «Goodbye World» соответственно. Начнем со статической библиотеки.
Начнем с интерфейса. Создадим файл world.h: Здесь просто объявлены функции, которые будут использоваться.
Теперь надо реализовать серверы. Создадим файл h_world.c: Теперь создадим файл g_world.c, содержащий реализацию функции g_world(): Можно было бы с таким же успехом уместить обе функции в одном файле (hello.c, например), однако для наглядности мы разнесли код на два файла.
Теперь создадим файл main.c. Это клиент, который будет пользоваться услугами сервера:
Теперь напишем сценарий для make. Для этого создаем Makefile: Не забывайте ставить табуляции перед каждым правилом в целевых связках.
Осталось только проверить, работает ли программа и разобраться, что же мы такое сделали:
Итак, в приведенном примере появились три новые вещи: опции -l и -L компилятора, а также команда ar. Начнем с последней. Как вы уже догадались, команда ar создает статическую библиотеку (архив). В нашем случае два объектных файла объединяются в один файл libworld.a. В Linux практически все библиотеки имеют префикс lib.
Как уже говорилось, компилятор gcc сам вызывает линковщик, когда это нужно. Опция -l, переданная компилятору, обрабатывается и посылается линковщику для того, чтобы тот подключил к бинарнику библиотеку. Как вы уже заметили, у имени библиотеки «обрублены» префикс и суффикс. Это делается для того, чтобы создать «видимое безразличие» между статическими и динамическими библиотеками. Но об этом речь пойдет в других главах книги. Сейчас важно знать лишь то, что и библиотека libfoo.so и библиотека libfoo.a подключаются к проекту опцией -lfoo. В нашем случае libworld.a «урезалось» до -lworld.
Опция -L указывает линковщику, где ему искать библиотеку. В случае, если библиотека располагается в каталоге /lib или /usr/lib, то вопрос отпадает сам собой и опция -L не требуется. В нашем случае библиотека находится в репозитории (в текущем каталоге). По умолчанию линковщик не просматривает текущий каталог в поиске библиотеки, поэтому опция -L. (точка означает текущий каталог) необходима.
3.3. Пример совместно используемой библиотеки
Для того, чтобы создать и использовать динамическую (совместно используемую) библиотеку, достаточно переделать в нашем проекте Makefile.
Внешне ничего не изменилось: программа компилируется, запускается и выполняет те же самые действия, что и в предыдущем случае. Изменилась внутренняя суть, которая играет для программиста первоочередную роль. Рассмотрим все по порядку.
Правило для сборки binary теперь содержит пугающую опцию -Wl,-rpath,. Ничего страшного тут нет. Как уже неоднократно говорилось, компилятор gcc сам вызывает линковщик ld, когда это надо и передает ему нужные параметры сборки, избавляя нас от ненужной платформенно-зависимой волокиты. Но иногда мы все-таки должны вмешаться в этот процесс и передать линковщику «свою» опцию. Для этого используется опция компилятора -Wl,option,optargs. Расшифровываю: передать линковщику (-Wl) опцию option с аргументами optargs. В нашем случае мы передаем линковщику опцию -rpath с аргументом . (точка, текущий каталог). Возникает вопрос: что означает опция -rpath? Как уже говорилось, линковщик ищет библиотеки в определенных местах; обычно это каталоги /lib и /usr/lib, иногда /usr/local/lib. Опция -rpath просто добавляет к этому списку еще один каталог. В нашем случае это текущий каталог. Без указания опции -rpath, линковщик «молча» соберет программу, но при запуске нас будет ждать сюрприз: программа не запустится из-за отсутствия библиотеки. Попробуйте убрать опцию -Wl,-rpath,. из Makefile и пересоберите проект. При попытке запуска программа binary завершится с кодом возврата 127 (о кодах возврата будет рассказано в последующих главах). То же самое произойдет, если вызвать программу из другого каталога. Верните обратно -Wl,-rpath. пересоберите проект, поднимитесь на уровень выше командой cd .. и попробуйте запустить бинарник командой world/binary. Ничего не получится, поскольку в новом текущем каталоге библиотеки нет.
Есть один способ не передавать линковщику дополнительных опций при помощи -Wl — это использование переменной окружения LD_LIBRARY_PATH. В последующих главах мы будем подробно касаться темы окружения (environment). Сейчас лишь скажу, что у каждого пользователя есть так называемое окружение (environment) представляющее собой набор пар ПЕРЕМЕННАЯ=ЗНАЧЕНИЕ, используемых программами. Чтобы посмотреть окружение, достаточно набрать команду env. Чтобы добавить в окружение переменную, достаточно набрать export ПЕРЕМЕННАЯ=ЗНАЧЕНИЕ, а чтобы удалить переменную из окружения, надо набрать export -n ПЕРЕМЕННАЯ. Будьте внимательны: export — это внутреннаяя команда оболочки BASH; в других оболочках (csh, ksh, . ) используются другие команды для работы с окружением. Переменная окружения LD_LIBRARY_PATH содержит список дополнительных «мест», разделенных двоеточиеями, где линковщих должен искать библиотеку.
Не смотря на наличие двух механизмов передачи информации о нестандартном расположении библиотек, лучше помещать библиотеки в конечных проектах в /lib и в /usr/lib. Допускается расположение библиотек в подкаталоги /usr/lib и в /usr/local/lib (с указанем -Wl,-rpath). Но заставлять конечного пользователя устанавливать LD_LIBRARY_PATH почти всегда является плохим стилем программирования.
Следующая немаловажная деталь — это процесс создания самой библиотеки. Статические библиотеки создаются при помощи архиватора ar, а совместно используемые — при помощи gcc с опцией -shared. В данном случае gcc опять же вызывает линковщик, но не для сборки бинарника, а для создания динамической библиотеки.
Источник
Блог радиста
Блог о Linux в частности и Open Source в общем, о программировании и немного о M$ Windows
Статические и динамические библиотеки в Linux
Статические и динамические библиотеки в Linux
Сегодня мы поговорим о библиотеках в Linux (подозреваю также, что многие описанные здесь вещи возможны и в других *nix-операционных системах, но это требует проверки 🙂 ).
1) Статические библиотеки (создание с помощью Assembler, C/C++; подключение и использование в программах на Assembler,C/C++);
2) Динамические библиотеки (создание с помощью Assembler, C/C++; подключение и использование в программах на C/C++l, Python).
1) Статическая библиотека — это такая библиотека, которая связывается (линкуется) с программой в момент компиляции оной. При этом объектный код библиотеки помещается в исполняемый файл программы. С этой точки зрения статическая библиотека похожа на исходный код программы, с которой она связывается, за исключением того, что библиотека компилируется «кем-то еще» и программист, использующий библиотеку, имеет дело исключительно только с результатом этой компиляции.
В Linux, как правило, файл-статическая_библиотека имеет расширение «.a»
2) Статические библиотеки на языке C.
Исходный код библиотеки:
Сохраните его в файле static.c
Ключевое слово extern необходимо для того, чтобы функция была видна в программе.
Теперь скомпилируем (! без линковки) библиотеку:
gcc -c static.c -o static.o
(на выходе имеем файл static.o, содержащий объектный код нашей библиотеки)
ar rc libMY_STATIC.a static.o
ar упаковывает несколько (! Это важно. Дело не ограничивается только одним объектным файлом) объектных файлов в одну статическую библиотеку. Статическая библиотека имеет расширение «.a», при этом ее название должно начинаться с «lib» (дань традиции).
Параметры ar:
r — предписывает заменять старые версии объектных файлов новыми — необходим для переупаковки библиотеки;
c — создать статическую библиотеку, если та еще не существует.
Проиндексируем функции внутри библиотеки для более быстрой линковки:
Итак, мы получили статическую библиотеку libMY_STATIC.a.
Теперь попытаемся использовать библиотеку в нашей программе:
Исходный текст программы (C):
Сохраните его в файле program1.c
Способы связывания библиотеки и программы:
— Скомпилируем и слинкуем (в том числе с нашей библиотекой) нашу программу:
gcc program1.c libMY_STATIC.a
(предполагается, что в качестве аргумента gcc будут переданы полные пути (!) к вашим библиотекам)
На выходе получим:
Hello world! I’m static library
Return code: 0
— Скомпилируйте с помощью команды:
gcc program1.c -L. -lMY_STATIC -o a1.out
— путь к каталогу, содержащему наши библиотек (используйте «-L
— название нашей библиотеки (это важно — название (!), а не имя файла — собственно, если библиотека имеет своим именем «libBLABLABLA.a», то ее названием будет «BLABLABLA» — т.е. имя без приставки «lib» и расширения «.a») (для нескольких библиотек используйте «-l -l . «)
Запустите файл a1.out на выполнение и удостовертесь, что результаты те же, что и в предыдущем пункте.
— Видоизменим предыдущий способ — уберем аргументы «-L»:
В начале проверим значение переменной LD_LIBRARY_PATH и содержимое файла /etc/ld.so.conf:
echo $LD_LIBRARY_PATH ; cat /etc/ld.so.conf
На экране появился некоторый список каталогов — это те каталоги, в которых система ищет библиотеки при их линковке с программой (еще к таким каталогам относятся:
/lib
/usr/lib
. Поместите libMY_STATIC.a в один из этих каталогов:
(Я, к примеру, засуну нашу библиотеку в каталог /usr/lib):
su -c ‘cp libMY_STATIC.a /usr/lib’
(в Ubuntu — sudo cp libMY_STATIC.a /usr/lib )
ldconfig
(ldconfig обновляет кеш данных о библиотеках линковщика)
Теперь скомпилируем и запустим нашу программу:
gcc program1.c -lMY_STATIC -o a2.out
./a2.out
Hello world! I’m static library
Return code: 0
Бинго! Кстати, таким вот способом вы можете подключать к своей программе любые статические библиотеки из приведенных выше каталогов.
* Бывает полезно определить все прототипы функций библиотеки в некотором заголовочном файле, который будет потом включаться в вашу программу. Это не обязательно, но удобно.
3) Статические библиотеки на языке Assembler.
Представьте, что вам необходимо оптимизировать выполнение некоторых действий в вашей программе. Разумеется, вы может применить ключевое слово asm (если пишите программу на C/C++), но лучшим решением будет создание оптимизированной вами библиотеки на языке Assembler и подключение ее к вашей программе. Давайте попробуем:
*Кстати, углубляться в процесс компиляции библиотеки и ее линковки с вашей программой я не буду (!). Этот процесс идентичен полностью (!) тому же процессу для библиотек, написанных на языке C.
Итак, имеем вот такую программу:
Сохраните ее в файле program2.c
Скомпилируйте ее и запустите:
Я привел этот пример, чтобы показать действительно возможность оптимизации программы с помощью библиотеки на Assembler’е. Вы можете заметить, что вызов printf в main() не оптимален, т.к. printf, по крайней мере, один раз использует цикл while для поиска вхождений конструкций «%. » в строку. Это не оптимально, т.к. очевидно, что таковых символов у нас нет. Оптимизируем нашу программу с помощью библиотеки на Assebmler’е:
my_printf:
movl $4,%eax
xorl %ebx,%ebx
incl %ebx
movl $hw,%ecx
movl $hw_e,%edx
int $0x80
xorl %eax,%eax
ret
Сохраните исходный код библиотеки в файле static2.s
Это AT&T наречие Assembler’а.
.globl my_printf — «my_printf» описывается как глобальная (видимая в других объектных файлах) последовательность
my_printf: — начало описание функции my_printf
movl $4,%eax — поместим 4 в eax (4 — номер системного вызова write)
xorl %ebx,%ebx и incl %ebx — поместим в ebx единицу — номер STDOUT
movl $message,%ecx — в ecx запишем адрес начала сообщения
movl $message_l,%edx — в edx поместим адрес конца сообщения
int $0x80 — произведем системный вызов write
xorl %eax,%eax — в eax — код возврата (0)
ret — вернемся в вызывающую процедуру
.data — секция данных (разумеется, мы могли бы передавать выводимую строку как параметр, но тогда вычисление ее конца потребовало бы от нас дополнительных усилий, что, согласитесь, лениво 🙂 )
Теперь получим библиотеку:
gcc -c static2.s -o static2.o
ar rc static2.a static2.o
ranlib static2.a
На выходе имеем статическую библиотеку static2.a
Теперь напишем программу, использующую эту статическую библиотеку (язык C):
Сохраните текст программы в файле program3.c
Заметьте, я добавил прототип библиотечной функции для удобства.
Скомпилируем и слинкуем программу с библиотекой, после чего запустим программу на выполнение:
gcc program3.c static2.a
./a.out
На выходе получим:
* Принцип линкования статических библиотек с программами на Assembler’е аналогичен принципу для программ на C. Просто, когда будете использовать статические библиотеки в Assembler’е, помните о соглашениях C по передаче аргументов в функцию и возвращению результата.
4) Статические библиотеки на языке C++.
Принцип создания аналогичен статическим библиотекам на C, но перед каждой экспортируемой функцией не забывайте добавлять:
(экспортировать как функцию на C — т.е. без расширения имен).
* Кстати, используйте g++ вместо gcc, если захотите протестировать приведенные выше примеры.
Подключение к вашей программе аналогично подключению к программе, написанной на C, за исключением необходимости явно добавлять к тексту программы прототипы импортируемых функций в следующем виде:
extern «C» PROTOTYPE
Где PROTOTYPE — прототип импортируемой функции.
* При подключении статических библиотек на C++ к программе на C сопряжено с некоторыми трудностями — т.к. при компиляции и линковки программы необходимо будет также вручную подключить системные библиотеки для реализации функционала, предоставляемого библиотекой Standart C++ сверх того, что предоставляет библиотека Standart C.
Динамические библиотеки (shared).
1) Динамическая библиотека — библиотека, подключаемая к программе в момент выполнения. Это означает, что при создании библиотеки производится не только ее компиляция, но и линковка с другими, нужными ей, библиотеками (!).
Динамические библиотеки полезны в случаях, если:
— Важно не перекомпилировать всю программу, а только перекомпилировать ту часть, которая реализует определенные функции — тогда эти функции выносятся в динамическую библиотеку;
— Важно использовать в программах на C библиотеки, подготовленные на C++ и при этом избежать лишних трудностей с линковкой программы;
— Кроме того, динамические библиотеки позволяют экономить место на жестком диске и в оперативной памяти, если одна и таже библиотека используется несколькими программами.
В Linux, обычно, динамические библиотеки имеют расширение «.so».
2) Подготовим исходный код динамической библиотеки (пример на C++).
Исходный код динамической библиотеки по принципам создания ничем (!) не отличается от исходного кода статических библиотек.
Здесь мы подготовим некоторый пример, который в дальнейшем будем использовать повсеместно во всей части 2.
Итак, исходный код библиотеки (C++):
extern «C» int hello()
<
cout 3) Компиляция и линковка динамических библиотек.
Давайте получим динамическую библиотеку:
Получим файл с объектным кодом:
g++ -fPIC -c dynamic.cpp -o dynamic.o
(используйте gcc для программ на С и Assembler’е)
-fPIC — использовать относительную адресацию в переходах подпрограмм — во избежание конфликтов при динамическом связывании
А теперь из объектного файла получим библиотеку:
g++ -shared -olibdynamic.so dynamic.o
(используйте gcc для программ на С и Assembler’е)
libdynamic.so — имя результирующей библиотеки;
-shared — предписывает создать динамическую (т.е. «разделяемую») библиотеку.
* Именуйте динамические библиотеки следующим способом:
Итак, на выходе мы имеем libdynamic.so — нашу динамическую библиотеку.
4) Использование динамической библиотеки в программе на C/C++.
— Связывание с библиотекой во время компиляции программы (C/C++):
—— Подготовим исходный код нашей программы:
Сохраните его в файле Dprogram1.c
extern «C» int hello();
Сохраните его в файле Dprogram1.cpp
(единственное отличие, как вы можете заметить, в ключевом слове extern — см. часть 1 пункт 4)
—— Теперь добьемся того, чтобы система смогла найти нашу библиотеку. Поместим libdynamic.so в один из каталогов:
cat /etc/ld.so.conf
и выполните потом » ldconfig «
—— И, наконец, скомпилируем программу и слинкуем ее с библиотекой:
gcc ИСХОДНИК -lИМЯ_БИБЛИОТЕКИ -o РЕЗУЛЬТИРУЮЩИЙ_БИНАРИК
В нашем случае: gcc Dprogram1.c -L/home/amv/c/libs/ -ldynamic
(используйте g++ для программы на C++)
Запустим на исполнение полученный файл:
В итоге должно получится:
— Связывание с библиотекой во время исполнения программы (C/C++):
Разумеется, предыдущий пример неплох. Однако бывает необходимо подключать библиотеку во время выполнения программы. Для этого можно использовать функционал из заголовочного файла .
Исходный код примера (C):
int main()
<
void *handle = dlopen(«libdynamic.so»,RTLD_LAZY);
int(*fun)(void) = dlsym(handle,»hello»);
int x = (*fun)();
dlclose(handle);
printf(«Return code: %d\n»,x);
return 0;
>;
######################
Сохраните его в файле Dprogram2.c
В dlfcn.h определены следующие функции:
void* dlopen(«PATH_AND_NAME»,FLAG) — загружает в память динамическую библиотеку с полным именем PATH_AND_NAME и возвращает ее описатель (HANDLE) (NULL в случае неудачи). FLAG — флаги, описанные в «man dlopen»;
void* dlsym(HANDLE,»NAME») — возвращает указатель на функцию/переменную, импортируемую из библиотеки;
int dlclose(HANDLE) — выгружает библиотеку из памяти;
const char *dlerror() — получить сообщение о последней возникшей ошибке (NULL — если ошибок не произошло с момента последнего вызова dlerror).
* Посмотрите на досуге вот этот перевод «man dlopen»: Привет, OpenNET
gcc -ldl Dprogram2.c
(используйте g++ для программы на C++)
Запустим на исполнение полученный файл:
В итоге должно получится:
* Важно! Нет необходимости помещать библиотеку в один из специальных каталогов, модифицировать переменные окружения и выполнять «ldconfig»
— Использование динамической библиотеки в программе на Python:
Все предельно просто.
—— Поместим libdynamic.so в один из каталогов:
cat /etc/ld.so.conf
и выполните потом «ldconfig»
Исходный текст программы на python’е:
Модуль ctypes входит в стандартную поставку модулей python версии 2.5 и выше.
Фуф. Мы проделали довольно большую работу, но ведь это только верхушка айсберга.
Источник