Ярлыки

понедельник, 25 апреля 2011 г.

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

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

В Linux, как правило, файл-статическая_библиотека имеет расширение ".a"

2) Статические библиотеки на языке C.

Исходный код библиотеки:

######################
#include

extern int hello()
{
printf("Hello world! I'm static library\n");
return 0;
};

######################

Сохраните его в файле 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 - создать статическую библиотеку, если та еще не существует.

Проиндексируем функции внутри библиотеки для более быстрой линковки:

ranlib libMY_STATIC.a

Итак, мы получили статическую библиотеку libMY_STATIC.a.

Теперь попытаемся использовать библиотеку в нашей программе:

Исходный текст программы (C):

######################
#include

int main()
{
int x = hello();
printf("Return code: %d\n",x);
return 0;
}

######################

Сохраните его в файле program1.c

Способы связывания библиотеки и программы:

- Скомпилируем и слинкуем (в том числе с нашей библиотекой) нашу программу:

gcc program1.c libMY_STATIC.a

(предполагается, что в качестве аргумента gcc будут переданы полные пути (!) к вашим библиотекам)

И запустим:

./a.out

На выходе получим:

Hello world! I'm static library
Return code: 0


Отлично!

- Скомпилируйте с помощью команды:

gcc program1.c -L. -lMY_STATIC -o a1.out

Смысл аргументов:

-L - путь к каталогу, содержащему наши библиотек (используйте "-L -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.

Итак, имеем вот такую программу:

######################
#include

int main()
{
printf("Hello world!\n");
return 0;
};

######################

Сохраните ее в файле program2.c

Скомпилируйте ее и запустите:

gcc program2.c
./a.out

Я получил:

Hello world!


Отлично.

Я привел этот пример, чтобы показать действительно возможность оптимизации программы с помощью библиотеки на Assembler'е. Вы можете заметить, что вызов printf в main() не оптимален, т.к. printf, по крайней мере, один раз использует цикл while для поиска вхождений конструкций "%..." в строку. Это не оптимально, т.к. очевидно, что таковых символов у нас нет. Оптимизируем нашу программу с помощью библиотеки на Assebmler'е:

######################
.text
.globl my_printf

my_printf:
movl $4,%eax
xorl %ebx,%ebx
incl %ebx
movl $hw,%ecx
movl $hw_e,%edx
int $0x80
xorl %eax,%eax
ret

.data
hw:
.string "Hello world!\012"
hw_e = . - hw

######################

Сохраните исходный код библиотеки в файле 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):

######################
#include

int my_printf();

int main()
{
int x = my_printf();
return 0;
}

######################

Сохраните текст программы в файле program3.c

Заметьте, я добавил прототип библиотечной функции для удобства.

Скомпилируем и слинкуем программу с библиотекой, после чего запустим программу на выполнение:

gcc program3.c static2.a
./a.out

На выходе получим:

Hello world!


* Принцип линкования статических библиотек с программами на Assembler'е аналогичен принципу для программ на C. Просто, когда будете использовать статические библиотеки в Assembler'е, помните о соглашениях C по передаче аргументов в функцию и возвращению результата.

4) Статические библиотеки на языке C++.

Принцип создания аналогичен статическим библиотекам на C, но перед каждой экспортируемой функцией не забывайте добавлять:

extern "C"

(экспортировать как функцию на C - т.е. без расширения имен).

* Кстати, используйте g++ вместо gcc, если захотите протестировать приведенные выше примеры.

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

extern "C" PROTOTYPE

Где PROTOTYPE - прототип импортируемой функции.

* При подключении статических библиотек на C++ к программе на C сопряжено с некоторыми трудностями - т.к. при компиляции и линковки программы необходимо будет также вручную подключить системные библиотеки для реализации функционала, предоставляемого библиотекой Standart C++ сверх того, что предоставляет библиотека Standart C.

----------------------------------------------------------------------------------------------------------------


link

Комментариев нет:

Отправить комментарий