From: LogRus <logrusadm@pisem.net>
Date: Mon, 25 Dec 2003 14:31:37 +0000 (UTC)
Subject: Совместно используемые библиотеки в C под Linux для начинающих.
Совместно используемые библиотеки в C под Linux для начинающих.
Для начала напишем два файлика test.c и app.c
Листинг test.c
---------------------------------------------------
int f()
{
return 3;
}
---------------------------------------------------
Листинг app.c
--------------------------------------------------
int main()
{
return f();
}
--------------------------------------------------
Конечно можно всё сразу собрать и всё зарабоет. Но мы пойдем другим
путём берём компилируем test.c в test.o.
$ gcc -c -o test.o test.c
Опция -c значит скомпилировать не соберая. Теперь делаем из test.o
библиотечку статическую. Выполняем вот эту команду.
$ ar cr libtest.a test.o test1.o test2.o
Файлы test1.o и test2.o я вписал просто показать, что можно собрать
некий архив объектных файлов ("архив" это второе название статической
библиотеки). Заметьте, что у слова тест появился префикс lib и
окончание поменяли с .o на .a . Команда опции команды ar означают: c
не ругатся если надо создать файл; r запихать в архив файлы или
заменить существующие.
app.o(.text+0x11): In function 'main':
: undefined reference to 'f'
collect2: ld returned. 1 exit status
Спрашиваете, что буквально за фигня. Отвечаю: компоновщик смотрит свои
параметры слева на право и когда он доходит до app.o он "думает", что
libtest.a ему нафиг не нужна никто её не пользует, отсюды вывод надо
поставить app.o сразу после app и всё заработает. Проверяем.
$ gcc -o app app.o -L. -ltest
Работает. Далее надо проверить работает ли наш файлик app который мы
собрали.
$ ./app
$ echo $?
3
$
Работает, как не странно! Опять на счет опций -L. значит искать
библиотеки сначала в директории указанной после -L в нашем случае у
нас стоит там точка значит в дериктории. где мы находимся. Опция
-ltest как раз и значит использовать библиотеку libtest.
С этим надеюсь ясно. Тепер переходим к совместно используемым
библиотекам(динамически подключаемым). Эти библиотеки похожы на
архивы, но гдавное отличие в том, что вместо кода из библиотеки в
программу включается ссылка на библиотеку.
Для создания библиотеки составляющие её файлы надо собрать использую
опцию -fPIC (Position-Independent Code):
Опция -shared заставляет компоновщик создать именно совместно
используемую библиотеку. Заново собераем нашу программу:
$ gcc -o app app.o -L. -ltest
Теперь выполним команду:
$ ldd app
libtest.so => not found
libc.so.6 => /lib/tls/libc.so.6 (0x42000000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
Эта команда нам показывает используемые библиотеки. Как видите нашу
библиотечку программа не нашла. А всё потому что в файл помещается
только имя библиотеки, а не путь к ней и при запуске библиотека ищется
только в стандартных каталогах /lib и /usr/lib. При попытке запустить
программу получаем сообщение о том, что не найдена библиотека. Первое
решение проблемы установить переменную окружения LD_LIBRARY_PATH
Выполнив эту команду мы сообщили ситеме, что библиотеки могут хранится
еще и в директориях /home/logrus/Documents/test и /usr/local/lib .
Другое решение использование при компиляции опции -Wl,-rpath примерно
так:
В этом случае в вашем приложении будет прописан полный путь до
библиотеки.
У вас наверняка возник вопрос, а что, если у меня есть оба вариата
библиотеки и .a и .so? В этом случае если компоновщик обнаруживает обе
библиотеки, то он выберет динамическую, если не указано иначе. Иначе
это использовать опцию -static.
Рекомендую всё это срочно протестировать! Набавать текста не много.
Если вам захочится использовать математические функции, которые не
входят в состав libc (стандартаня библиотека C) автоматически
подключаемой к вашей программе (это если вы этого не заметили в
результатах работы комады ldd), то компилируем так:
# gcc -o compute compute.c -lm
При компоновке програм на C++ к вашей программе подключается
библиотека libstdc++.
Часто библиотеки связаны между собой.
Например соберем такую штуку:
Листинг testtiff.c
----------------------------------------------------------------------
#include <stdio.h>
#include <tiffio.h>
int main (int argc, char** argv)
{
TIFF* tiff;
tiff = TIFFOpen (argv[1], "r");
TIFFClose(tiff);
return 0;
}
----------------------------------------------------------------------
Это говорит нам о том, что динимические библиотеки зависят от друг
друга и что эти библиотеки также подключены. Посмотри и запешите или
запомните разменр исполняемого файла tifftest.
Соберем его со статическими библиотеками.
$ gcc -static -o tifftest tifftest.c -ltiff
/usr/local/lib/libtiff.a(tif_luv.o)(.text+0xf74): In function `LogL16toY':
../libtiff/tif_luv.c:657: undefined reference to `exp'
/usr/local/lib/libtiff.a(tif_luv.o)(.text+0x1014): In function `LogL16fromY':
../libtiff/tif_luv.c:672: undefined reference to `log'
/usr/local/lib/libtiff.a(tif_luv.o)(.text+0x1036):../libtiff/tif_luv.c
:672: undefined reference to `log'
/usr/local/lib/libtiff.a(tif_luv.o)(.text+0x10b8):../libtiff/tif_luv.c
:674: undefined reference to `log'
/usr/local/lib/libtiff.a(tif_luv.o)(.text+0x10e8):../libtiff/tif_luv.c
:674: undefined reference to `log'
/usr/local/lib/libtiff.a(tif_luv.o)(.text+0x1467): In function `LogL10toY':
../libtiff/tif_luv.c:736: undefined reference to `exp'
/usr/local/lib/libtiff.a(tif_luv.o)(.text+0x14cd): In function `LogL10fromY':
../libtiff/tif_luv.c:750: undefined reference to `log'
/usr/local/lib/libtiff.a(tif_luv.o)(.text+0x14ef):../libtiff/tif_luv.c
:750: undefined reference to `log'
collect2: ld returned 1 exit status
Ну как красиво? Это из-за того, что статические библиотеки не могут
ссылаться друг на друга. Значит нам надо вручною указать все
необходимые библиотеки.
Проверьте размер исполняемого файла. Ну как впечатляет? Проверьте
подключеныль динамические библиотеки.
И на последок. Допустим есть ситуация когда библиотеки циклически
зависят друг от друга, т.е. libtest1.a зависит от libtest2.a, а
libtest2.a зависит от libtest1.a тогда просто следует сделать например
так:
$ gcc -o app app.o -ltest1 -ltest2 -ltest1
Пока всё! ;)
ЧАСТЬ 2.
Иногда требуется загрузить какую нибуть библиотеку я вно не компануя
её. Например страшное слово плагин. Для этих целей у нас есть dlopen,
dlclose, dlsym и dlerror объявленны в dlfcn.h, компилируется с флагом -ldl.
void *dlopen(const char *filename, int flag);
const char *dlerror(void);
void *dlsym(void *handle, char *symbol);
int dlclose(void *handle);
Функция dlopen открывает файл filename с флагами flag. Вернее
откравает она его в том случае, если он еще небыл кем-то открыт в этом
случае она увеличивет счетчик ссылок возврашает дискриптор библиотеки.
Путь к библиотеки можно не указывать, тогда бибилиотека ищется .
dlclose совершает обратную работу уменьшает счетчик и закрывает
библиотеку, если её больше не использует.
Функция dlsys возврашает указатель на функцию или переменную с именем
symbol для библиотеки Handle.
Функция dlerror возвращает струказатель на строку с описанием ошибки.
Пример:
Если библиотека пишется на C++ имеет смысл объявить обще доступные
функции со спецификатором extern "C".
extern "C" void my_func();
Это делается из-за того, что компилятор C++ имеет свойство подменять
имена функций своими именами в которых закодированна информация о
функции.
Пример:
Листинг testdl.c:
------------------------------------------------------------
#include <stdio.h>
#include <dlfcn.h>
int main(int argc,char** argv[])
{
/* Проверяем кол-во параметров.
Если чего не так выводим сообщение об ошибке и уходим. */
if (argc<2) {
fprintf(stderr,"Usage: %s libnamen",argv[0]);
exit(1);
}
/* Пытаемся загрузить библиотеку. */
printf("Try to load library %sn",argv[1]);
void * handle = dlopen(argv[1], RTLD_LAZY);
/* Если чего не так выводим сообщение об ошибке и уходим. */
if (handle == NULL) {
fprintf(stderr,"%s",dlerror());
exit(2);
}
printf("Ok! Loaded!n");
/* Пытаемся загрузить функцию библиотеки. */
void (*libfunc)(int) = dlsym(handle, "libfunc");
/* Если чего не так выводим сообщение об ошибке и уходим. */
if (libfunc == NULL) {
fprintf(stderr,"%sn",dlerror());
exit(3);
}
/* Выводим название загруженной библиотеки. */
printf("Loaded library: %s nLib output:n",argv[1]);
/* Вызываем функцию из библиотеки передаем при этом дискриптор
для вывода данных. */
libfunc(stderr);
/* Закрываем библиотеку. */
dlclose(handle);
return 0;
}
С файлами надеюсь всё понятно. Testdl.c наша главная программа которая
будет использовать наши библиотечки. Файл mygettime.c и myversion.c
файлы для библиотек выводят текушее время и версию системы. Makefile
это, что бы нам руками всё время команды не писать для компиляции, а
набрали make и всё собралось, набрали make clean и всё собраное
удалилось.
$ cat test.log
ASPLinux release 9 (Ural)
Kernel 2.4.20-9asp on an i686
$
Спрашивается. а зачем. Ну даже не знаю. Можно например запустить две
программы терминала в графическом режиме. Затем в одной запустить
другую программу например компиляцию чего-нибуть и перенаправить вывод
ощибок в другое окно, чтобы не путались сообщения об ошибках и
сообщения о ходе выполнения. Или в файли можно лог ошибок сохранить.
Да малоли чего. Пример
$ echo "Hi, everybody." >>/dev/pts/1
или
$ make 2>test.log 1>/dev/pts/1
Всё!
1158 Прочтений • [Совместно используемые библиотеки в C под Linux для начинающих. (share lib gcc linux)] [08.05.2012] [Комментариев: 0]