// foo.h #ifndef foo_h__ #define foo_h__ extern "C" __attribute__ ((visibility("default"))) void foo(void); #endif // foo_h__ // foo1.cpp
#include <stdio.h> #include "foo.h"__attribute__((constructor)) void foo_init() { puts("Init shared library 1"); } __attribute__((destructor)) void foo_deinit() { puts("Deinit shared library 1"); } void foo(void) { puts("Hello, I'm a shared library 1"); } // foo2.cpp
#include <stdio.h> #include "foo.h" __attribute__((constructor)) void foo_init() { puts("Init shared library 2"); } __attribute__((destructor)) void foo_deinit() { puts("Deinit shared library 2"); } void foo(void) { puts("Hello, I'm a shared library 2"); } |
#include <stdio.h> #include <dlfcn.h> //gcc -o main main.cpp -ldl int main() { void *h1, *h2; // define function prototype typedef void (*foo_fn_ptr)(void); // function pointers foo_fn_ptr foo1, foo2; char *error; // open first library h1 = dlopen ("./libfoo1.so", RTLD_LAZY); if(!h1) { fputs(dlerror(), stderr); puts(""); return 1; } // get function from first lib foo1 = (foo_fn_ptr) dlsym(h1, "foo"); error = dlerror(); if (error) { puts(error); return 1; } // use function foo1(); // load second lib h2 = dlopen ("./libfoo2.so", RTLD_LAZY); if(!h2) { fputs(dlerror(), stderr); puts(""); return 1; } // get function from second lib foo2 = (foo_fn_ptr) dlsym(h2, "foo"); error = dlerror(); if (error) { puts(error); return 1; } // use function foo2(); // unload libraries dlclose(h1); dlclose(h2); return 0; }
Как все это собирать. Сначала соберем наши shared objects:
gcc -Wall -Werror -o libfoo1.so -shared -fpic foo1.cpp
gcc -Wall -Werror -o libfoo2.so -shared -fpic foo2.cpp
Теперь соберем наш исполняемый файл, который загружает и использует эти библиотеки:gcc -o main main.cpp -ldl
У нас все собрано и готово, надо только добавить папку, где лежат наши библиотеки в список путей, откуда их можно загружать:
export LD_LIBRARY_PATH=$PWDТеперь можно запустить:
./main Hello world! Init shared library 1 Hello, I'm a shared library 1 Init shared library 2 Hello, I'm a shared library 2 Deinit shared library 1 Deinit shared library 2
Ниже пояснения.
Код библиотек.
Зачем нам нужен extern "C"? Строго говоря, не нужен, потому что по умолчанию GCC (нынешняя версия, по крайней мере), экспортирует все функции и они доступны для использования, но есть один нюанс. Можно закомментировать эту строчку, пересобрать библиотеку и вместо увидеть ошибку "./libfoo2.so: undefined symbol: foo". То есть функция dlsym не находит символа "foo" в библиотеке, хотя мы точно знаем, что он там есть. С помощью nm мы просмотрим таблицу экспорта нашей библиотеки:
$ nm -D ./libfoo2.so 0000000000201028 B __bss_start w __cxa_finalize 0000000000201028 D _edata 0000000000201030 B _end 00000000000006b4 T _fini w __gmon_start__ 0000000000000548 T _init w _ITM_deregisterTMCloneTable w _ITM_registerTMCloneTable w _Jv_RegisterClasses U puts
Вот эта последняя строчка "_Z3foov" и есть имя нашей экспортированной функции и это очевидно не то же самое, что "foo". Теперь раскомментируем строчку extern "C", пересоберем библиотеку и запустим nm еще раз:0000000000000733 T _Z10foo_deinitv 0000000000000720 T _Z8foo_initv00000000000006a0 T _Z3foov$
$ nm -D ./libfoo2.so 0000000000201028 B __bss_start w __cxa_finalize 0000000000201028 D _edata 0000000000201030 B _end 000000000000075c T _fini 0000000000000746 T foo w __gmon_start__ 00000000000005d0 T _init w _ITM_deregisterTMCloneTable w _ITM_registerTMCloneTable w _Jv_RegisterClasses U puts 0000000000000733 T _Z10foo_deinitv 0000000000000720 T _Z8foo_initv $Теперь мы видим "foo" и у нас все опять работает:
./main
Hello world!
Init shared library 1
Hello, I'm a shared library 1
Init shared library 2
Hello, I'm a shared library 2
Deinit shared library 1
Deinit shared library 2
Что же насчет __attribute__ ((visibility("default"))), то по умолчанию все функции и так имеют видимость default, но, во избежание всяких неожиданностей типа нестандартных флагов компилятора, мы добавляем этот флаг в каждую экспортируемую функцию.
Сборка библиотек.
Компилируем и создаем из объектного файла shared object двумя отдельными командами.
Компиляция:
gcc -c -Wall -Werror -fpic foo1.cpp
Все нюансы тут состоят в использовании флага pic (Position Independent Code), что необходимо для динамически загружаемых библиотек (статические библиотеки не нуждаются в таком флаге).
Сборка происходит так:
gcc -shared -o libfoo1.so foo1.o
gcc -Wall -Werror -o libfoo1.so -shared -fpic foo1.cpp
тут используется специальный флаг shared для создания динамических библиотек. То же самое одной командой:
gcc -Wall -Werror -o libfoo1.so -shared -fpic foo1.cpp
Сборка main.
Все как обычно, но линкуем библиотеку dl, которая содержит dlopen, dlsym, dlclose и прочие фукции:
gcc -o main main.cpp -ldl
Настройка окружения и запуск.
В linux библиотеки лежат в строго определенных местах и откуда-то еще их загрузить не получится (в отличии от Windows), поэтому, когда мы запустим программу в первый раз, мы увидим что-то такое:
$ ./main ./libfoo1.so: cannot open shared object file: No such file or directory $Чтобы добавить новый путь, делаем следующее:
export LD_LIBRARY_PATH=$PWD
В bash переменная окружения PWD хранит текущую папку. Теперь все работает.
Ссылки:
http://www.cprogramming.com/tutorial/shared-libraries-linux-gcc.html
http://www.tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html
http://www.keil.com/support/man/docs/armcc/armcc_chr1359124983511.htm
https://www.cyberciti.biz/faq/linux-list-all-environment-variables-env-command/
http://www.cprogramming.com/tutorial/function-pointers.html
http://www.microhowto.info/howto/build_a_shared_library_using_gcc.html
Ссылки:
http://www.cprogramming.com/tutorial/shared-libraries-linux-gcc.html
http://www.tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html
http://www.keil.com/support/man/docs/armcc/armcc_chr1359124983511.htm
https://www.cyberciti.biz/faq/linux-list-all-environment-variables-env-command/
http://www.cprogramming.com/tutorial/function-pointers.html
http://www.microhowto.info/howto/build_a_shared_library_using_gcc.html
Великолепно! Теперь бы заставить CODELITE компилировать и собирать, что бы каждый раз в командную строку не лазить)
ОтветитьУдалитьВсе, настроил, пришлось сначала выбрать статическую библиотеку при создании проекта, затем в настройках поменять на динамическую.
УдалитьВсе ключи компиляции для динамической библиотеки (-fpic -shared) автоматически вставляются, прописывать не надо. Во вкладке General только прописал желаемый выходной файл lib$(ProjectName).so