Библиотеки, помощна програма libtool, разработка на софтуер за Linux

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

В нашия калкулатор ние включихме цялото съдържание на изходния код на Calculator.c в изпълнимия файл. Следователно, ако искаме да използваме същите функции в друга програма, трябва да ги напишем отново. Можете, разбира се, да използвате добре познатите команди Select-Copy-Paste. Но все пак това са допълнителни жестове, допълнително увеличаване на размера на изпълнимия файл поради задръстването му с рутинни функции. И самият източник може да не е под ръка. И може би искам да направя подобрения на тези функции. Това означава, че всички програми, които използват тези функции, ще трябва да бъдат пренаписани, така че всички тези програми да имат нови версии на функциите. Не може ли да са някъде на едно място и да се вземат от там за всички програми, когато има нужда?

Както вероятно се досещате, ние ще направим библиотека от изходния код на calculate.c, която след това ще свържем с изпълнимата програма, получена от един main.c.

И така, поемаме следващата промяна на нашия калкулатор.

Най-лесният начин за създаване на библиотека от файл calculate.c е следният:

gcc -c изчисли.c

ar cru libcalculate.a изчисляване.o

Тук gcc с флага -c получава вече познатия ни обектен файл, командата ar създава архив, под формата на който съществуват библиотеките. А командата ranlib генерира индекс на функциите, съдържащи се в библиотеката, и записва този индекс в свой собствен файл. Тази процедура винаги се препоръчвада направите, тъй като индексът ускорява процедурата за свързване на библиотеката с програмата.

По-късно този файл може да бъде поставен ръчно в директорията на стандартните библиотеки (това са /usr/lib и /usr/local/lib) и може да бъде свързан с помощта на флага -lcalculate (имайте предвид, че името на библиотеките трябва да започва с префикса lib-).

Но е ясно, че това е нерационален начин по отношение на производителността на труда. Нека да разгледаме по-практични варианти. За да направим това, ще се запознаем с друг компонент от инструментариума на GNU - libtool. Неговата цел е да автоматизира рутинните операции за работа с библиотеки и да предостави на разработчика удобство при писането и тестването им.

Създайте нова директория на проекта libtooldemo. Прехвърлете в него файловете calculate.c и main.c от C версията на калкулатора. Просто не забравяйте да премахнете трите реда от всеки от тях, които изискват връзката на заглавния файл config.h. Засега няма да използваме този заглавен файл.

Горните редове трябва да бъдат премахнати!

Но какво да кажем за файла calculate.h?

За него специален разговор. Нашата библиотека ще бъде компилирана от компилатора C. Но може да бъде извикана и от C++ програма. И компилаторът C ++ има такава функция - той променя имената на функциите по време на компилация. Следователно името на функцията в извикващата програма няма да съвпада с името на функцията в библиотеката.

За да избегнете тази ситуация, е необходимо изрично да посочите в заглавния файл, включен в кода на изпълнимата програма, че библиотечната функция е написана на C. По този начин нашият нов заглавен файл Calculate.h ще изглежда така:

float Calculate(float Numeral, char Operation[4]);

Нека създадем библиотека с връзки от източника Calculate.c. За товаНека първо създадем обектен файл.

libtool gcc -c изчисление.c

Вижте какво се появи в директорията на проекта. Това е, първо, вече познатият ни обектен файл calculcate.o. Второ, това е скриптът calculate.lo, генериран от помощната програма libtool. Той ще ви каже как да изградите библиотеката от обектните файлове. Нищо друго не виждаш ли? След това въведете:

И ще откриете, че сте се преместили в поддиректорията .libs, която не се вижда във файловия мениджър, защото по подразбиране не показва всички файлове и папки, чиито имена започват с точка. Тази поддиректория съдържа различна версия на обектния файл calculate.o. Разликата между тях е, че единият е с позиционно независим код, а другият с позиционно зависим. Но нека засега не навлизаме в подробности за това. Помощната програма libtool ще реши вместо нас коя опция е по-добре да вземем за формиране на библиотеката за дадена системна конфигурация.

И накрая, нека създадем самата библиотека.

libtool gcc -rpath /usr/local/lib -o libcalculate.la calculate.lo

Файлът libcalculate.la се появи в проекта. Това е скриптът, от който libtool ще се нуждае. Самата библиотека се намира в поддиректорията .libs. Ако отидете там, ще намерите файл със статично разширение на библиотеката libcalculate.a и файлове с разширение на динамична библиотека libcalculate.so (вече споменахме, че този тип библиотеки имат точно тези файлови разширения).

Съдържанието на файла libcalculate.a е свързано към програмата по време на връзката, но този файл не съдържа библиотечни функции. Той съдържа само връзка към друг файл - libcalculate.so, който съдържа библиотечни функции и е свързан към програмата динамично, тоест при поискване по време на изпълнение на програмата.

Опцията -rpath с пътя къмдиректорията на стандартната библиотека казва на линкера, че е необходимо да се създаде библиотека с динамично свързване, тоест да се осигурят както a-файлът, така и so-файлът, и че те ще бъдат поставени по време на инсталацията точно в директорията, посочена след -rpath. Пътят до тази директория ще бъде съхранен в a-файла и по този път ще се търси динамичната библиотека.

Можете да кажете на линкера да създаде статична библиотека. За да направите това, използвайте опцията -static.

libtool gcc -static -o libcalculate.la calculate.lo

Както можете да видите, тук няма опция -rpath. Не е необходимо, тъй като всички функции на библиотеката ще бъдат включени в изпълнимия файл и няма нужда да се извиква динамично нещо отвсякъде.

Вече няма да намерите so-файла в директорията .libs, тъй като цялото му съдържание е включено в статично свързания a-файл.

Нека сега създадем изпълним файл, който ще бъде свързан към тази библиотека.

libtool gcc -o kalkul main.c libcalculate.la -lm

По желание можете да направите математическите функции, включени в нашата библиотека. За да направите това, свържете го отново, като посочите задължителното включване на libm.

libtool gcc -rpath /usr/local/lib -o libcalculate.la calculate.lo -lm

След това вече не можете да свържете libm към изпълнимата програма.

libtool gcc -o kalkul main.c libcalculate.la

Тъй като генерирахме изпълнимия файл kalkul с помощта на libtool, той също се поставя в директорията .libs и скрипт със същото име kalkul се генерира в директорията на проекта. Но това не трябва да ви притеснява. Тези скриптове са създадени, за да осигурят основното удобство на libtool. Основното удобство е, че ви позволява едновременно да работите в една и съща директория както с изпълними програми, така ибиблиотеките, от които се нуждаят. Опитайте да стартирате изпълнимата програма. Програмата ще намери и отвори библиотеката без никакви проблеми, въпреки че тази библиотека не се намира в системната директория. Това избягва ненужния достъп до системния каталог по време на фазата на разработка.

Той също така дава възможност за едновременно инсталиране на всички библиотечни файлове в системната директория. Нека инсталираме библиотеката libcalculate в директорията /usr/local/lib (след като влезете като суперпотребител).

libtool cp libcalculate.la /usr/local/lib

Ако тази команда генерира грешки, просто повторете цялата процедура за компилиране и изграждане на библиотеката от самото начало. Може би по време на някои манипулации сте загубили някои помощни файлове.

Моля, обърнете внимание, че инсталацията трябва да се извърши точно в директорията, която е посочена в опцията -rpath. Не самият скрипт libcalculate.la, а статичните и динамичните библиотеки libcalculate.a и libcalculate.so ще бъдат копирани в тази директория.

Сега също с помощта на libtool ще направим нов изпълним файл.

libtool gcc -o kalkul main.c -lcalculate

Нека го копираме в системната директория.

libtool cp kalkul /usr/local/bin

И тук най-вероятно много от вас ще имат неприятна изненада - програмата няма да стартира за първи път. Тя ще даде съобщение:

грешка при зареждане на споделени библиотеки: libcalculate.so.0: не може да се отвори споделен обектен файл: Няма такъв файл или директория

Това означава, че линкерът знае пътя /usr/local/lib, но товарачът на динамичната библиотека не го знае. Това е свойство на повечето съвременни дистрибуции. Пътят до библиотеките за изтегляне ще трябва да се добави към системата ръчно. Ужасно неудобно е. Човек, който се сблъсква с този проблем за първи път, ще мисли дълго време какво е. Направено по този начин, вероятноза целите на сигурността, така че "лявата" библиотека, случайно инсталирана от невнимателен потребител, да не е в конфликт със системните библиотеки. Разбира се, трябва да опишете тази ситуация много подробно на потребителя във файла README.

Нека ръчно да добавим пътя, от който се нуждаем. За да направите това, задайте системната променлива LD_LIBRARY_PATH на /usr/local/lib (по подразбиране тази променлива изобщо не присъства в повечето версии на Linux).

Започваме калкул отново. Най-накрая той спечели!

Можете да прикриете следите от вашите експерименти по следния начин:

libtool rm /usr/local/lib/libcalculate.la

libtool rm /usr/local/bin/kalkul

Дмитрий Пантелейчев (dimanix2006 в rambler dot ru) - Библиотеки, помощна програма libtool Версия за печат