The OpenNET Project / Index page

[ новости /+++ | форум | wiki | теги | ]

Совместно используемые библиотеки в C под Linux для начинающих. (share lib gcc linux)


<< Предыдущая ИНДЕКС Поиск в статьях src Установить закладку Перейти на закладку Следующая >>
Ключевые слова: share, lib, gcc, linux,  (найти похожие документы)
From: LogRus <logrusadm@pisem.net> Date: Mon, 25 Dec 2003 14:31:37 +0000 (UTC) Subject: Совместно используемые библиотеки в C под Linux для начинающих. Оригинал: http://club.shelek.com/viewart.php?id=106 Совместно используемые библиотеки в 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 запихать в архив файлы или заменить существующие. Попробуем это всё собрать. $ gcc -c -o app.o app.c $ gcc -o app -L. -ltest app.o 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): $ gcc -c -fPIC test.c Теперь соберём библиотеку: $ gcc -shared -fPIC -o libtest.so test.o test1.o test2.o Опция -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 $ export LD_LIBRARY_PATH=/home/logrus/Documents/test:/usr/local/lib Выполнив эту команду мы сообщили ситеме, что библиотеки могут хранится еще и в директориях /home/logrus/Documents/test и /usr/local/lib . Другое решение использование при компиляции опции -Wl,-rpath примерно так: $ gcc -o app app.o -L. -ltest -Wl,-rpath,/home/logrus/Documents/test В этом случае в вашем приложении будет прописан полный путь до библиотеки. У вас наверняка возник вопрос, а что, если у меня есть оба вариата библиотеки и .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; } ---------------------------------------------------------------------- При компилляции используем флаг -ltiff $ gcc -o tifftest tifftest.c -ltiff Посмотрим что нам выдаст ldd $ ldd tifftest libtiff.so.3 => /usr/lib/libtiff.so.3 (0x40022000) libc.so.6 => /lib/tls/libc.so.6 (0x42000000) libjpeg.so.62 => /usr/lib/libjpeg.so.62 (0x40063000) libz.so.1 => /usr/lib/libz.so.1 (0x40082000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000) Это говорит нам о том, что динимические библиотеки зависят от друг друга и что эти библиотеки также подключены. Посмотри и запешите или запомните разменр исполняемого файла 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 Ну как красиво? Это из-за того, что статические библиотеки не могут ссылаться друг на друга. Значит нам надо вручною указать все необходимые библиотеки. $ gcc -static -o tifftest tifftest.c -ltiff -ljpeg -lz -lm Проверьте размер исполняемого файла. Ну как впечатляет? Проверьте подключеныль динамические библиотеки. И на последок. Допустим есть ситуация когда библиотеки циклически зависят друг от друга, т.е. 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 возвращает струказатель на строку с описанием ошибки. Пример: void * handle = dlopen("mylib.so", RTLD_LAZY); void (* test)() = dlsym( handle, "myfunc"); (* test)(); dlclose(handle); Если библиотека пишется на 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 libname\n",argv[0]); exit(1); } /* Пытаемся загрузить библиотеку. */ printf("Try to load library %s\n",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,"%s\n",dlerror()); exit(3); } /* Выводим название загруженной библиотеки. */ printf("Loaded library: %s \nLib output:\n",argv[1]); /* Вызываем функцию из библиотеки передаем при этом дискриптор для вывода данных. */ libfunc(stderr); /* Закрываем библиотеку. */ dlclose(handle); return 0; } ------------------------------------------------------------ Листинг mygettime.c: ------------------------------------------------------------ #include <sys/time.h> #include <time.h> #include <stdio.h> #include <unistd.h> void libfunc(int fd) { struct timeval tv; struct tm* ptm; char * time_string[40]; /* Получаем локальное время. */ gettimeofday(&tv, NULL); ptm = localtime(&tv.tv_sec); /* Преобразуем в строку. */ strftime(time_string, sizeof(time_string), "%H:%M:%S", ptm); /* Выводим время. */ fprintf(fd,"Local time: %s\n",time_string); return; } ------------------------------------------------------------ Листинг myversion.c: ------------------------------------------------------------ #include <fcntl.h> void libfunc(int fd) { int in_fd; int rval; char *buffer=calloc(512,sizeof(char)); /* Открываем файл /etc/issue. */ in_fd = open("/etc/issue",O_RDONLY); if(in_fd == -1) { fprintf(fd,"Error open /etc/issue\n"); exit(1); } /* Читаем из /etc/issue и пишем в fd. */ while ((rval=read(in_fd,buffer,512))!= 0) fprintf(fd,"%s",buffer); return; } ------------------------------------------------------------ Листинг Makefile: ------------------------------------------------------------ ### Конфигурация. ################################### SOURCES = testdl.c MODULES = mygettime.so myversion.so ### Правила. ######################################## # Служебный целевой модуль .PHONY: all clean # Стандартный целевой модуль: компиляция всех фалов. all: $(MODULES) testdl # Удаление всего чего насобирали. clean: rm -f $(MODULES) testdl # Собираем гвавный файл. testdl: $(SOURCES) $(CC) $(CFLAGS) -Wl,-export-dynamic -o $@ $^ -ldl # Собираем библиотеки. $(MODULES): \ %.so: %.c $(CC) $(CFLAGS) -fPIC -shared -o $@ $< ------------------------------------------------------------ С файлами надеюсь всё понятно. Testdl.c наша главная программа которая будет использовать наши библиотечки. Файл mygettime.c и myversion.c файлы для библиотек выводят текушее время и версию системы. Makefile это, что бы нам руками всё время команды не писать для компиляции, а набрали make и всё собралось, набрали make clean и всё собраное удалилось. По тестируем всё это: $ make $ export LD_LIBRARY_PATH=/path_to_current_dir $ ./testdl mygettime.so && ./testdl myversion.so Try to load library mygettime.so Ok! Loaded! Loaded library: mygettime.so Lib output: Local time: 09:44:00 Try to load library myversion.so Ok! Loaded! Loaded library: myversion.so Lib output: ASPLinux release 9 (Ural) Kernel 2.4.20-9asp on an i686 Спрашивается, на зачем мы передавали в функции stderr, если всё равно идет вывод на экран. а мы за одно посмотрим как перенаправить поток. Есть три стандартаных дескриптора файлов это 0, 1, 2 или stdin, stdout, stderr. Перенаправим в файлик test.log стандартный вывод ошибок. $ ./testdl myversion.so 2>test.log Try to load library myversion.so Ok! Loaded! Loaded library: myversion.so Lib output: $ 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 Всё!

<< Предыдущая ИНДЕКС Поиск в статьях src Установить закладку Перейти на закладку Следующая >>

Обсуждение [ RSS ]
  • 1.1, Фдуч (??), 17:49, 27/09/2004 [ответить]  
  • +/
    Сильно укороченная и написанная расхлябанным языком глава 2.3 из книги Advanced Linux Programming (mark Mitchell, Jeffrey Oldman, Alex Samuel) английский оригинал ISBN 0-7357-1043-0 русское издание 5-8459-0243-6

    Между прочим, на источники полезно ссылаться хоть бы для того, чтобы читатели могли расширить знания.

     
     
  • 2.8, JackOfShadow (?), 10:11, 27/03/2008 [^] [^^] [^^^] [ответить]  
  • +/
    Спасибо и на том. Есть "более грамотные" которые и того не написали. Но насчёт источников - согласен.
     

  • 1.2, ctaryxa (?), 18:31, 19/10/2004 [ответить]  
  • +/
    Диагноз: "слизано, сакс"

     
  • 1.4, X0R (?), 15:34, 28/03/2006 [ответить]  
  • +/
    что по русскому в школе было?
     
  • 1.5, Lion (??), 15:01, 16/05/2006 [ответить]  
  • +/
    Как откомпилировать шареную библиотеку которая использует другие библиотеки, чтобы
    1. ldd выдавал зависимости
    2. при линковке програмы с етой библиотекой автоматом подключались все зависимости
    А то у меня не получаеться
     
  • 1.6, Lion (??), 15:11, 16/05/2006 [ответить]  
  • +/
    Обясню детальней
    К примеру есть libfirst.so он использует потоки (-lpthread)
    Затем я использую со-шку в своей програме и во время линоковки выдает
    /usr/local/lib/libfirst.so: undefined reference to 'pthread_create'
    Пока я явно не укажу в линкере использовать -lpthread. Что я  не так делаю?
     
     
  • 2.7, pavlinux (ok), 22:55, 24/03/2008 [^] [^^] [^^^] [ответить]  
  • +/
    Явно не указываешь в линкере использовать -lpthread.
     

    лог модерирования

     Добавить комментарий
    Имя:
    E-Mail:
    Заголовок:
    Текст:




    Спонсоры:
    Слёрм
    Inferno Solutions
    Hosting by Hoster.ru
    Хостинг:

    Закладки на сайте
    Проследить за страницей
    Created 1996-2020 by Maxim Chirkov
    Добавить, Поддержать, Вебмастеру