Библиотека сайта rus-linux.net
Цилюрик О.И. Linux-инструменты для Windows-программистов | ||
Назад | Компиляция и сборка приложений | Вперед |
Конструктор и деструктор
Посмотрим внешние имена (для связывания) созданной в предыдущем примере разделяемой библиотеки:
$ nm libhello.so ... 00000498 T _fini 000002ec T _init ... 00000430 T put_my_msg ...
Мы видим ряд имён (и как раз типа T для внешнего
связывания), одно из которых put_my_msg
— это имя нашей функции. Два других имени — присутствуют
в любой разделяемой библиотеке: функция _init()
вызывается при загрузке библиотеки в память (например, для
инициализации некоторых структур данных), а _fini()
- при выгрузке библиотеки (деструктор).
Примечание: Наличие функций конструктора и
деструктора (_init()
и _fini()
)
является общим свойством всех файлов формата ELF, к которому
относятся и исполнимые файлы Linux, и файлы динамических разделяемых библиотек.
Эти функции (конструктор и деструктор библиотеки)
могут быть переопределены из вашего пользовательского кода создания
библиотеки. Для этого перепишем динамическую библиотеку из наших
предыдущих примеров (каталог init
архива libraries.tgz
):
hello_child.c : #include "../hello_child.h" #include <sys/time.h> static mark_time( void ) { struct timeval t; gettimeofday( &t, NULL ); printf("%02d:%06d : ", t.tv_sec % 100, t.tv_usec ); } static mark_func( const char *f ) { mark_time(); printf( "%s\n", f ); } void _init( void ) { mark_func( __FUNCTION__ ); } void _fini( void ) { mark_func( __FUNCTION__ ); } int put_my_msg( char *messg ) { mark_time(); printf( "%s\n", messg ); return -1; }
Но просто собрать такую библиотеку не получится:
$ gcc -c -fpic -fPIC -shared hello_child.c -o hello_child.o $ gcc -shared -o libhello.so hello_child.o ... hello_child.c:(.text+0x0): multiple definition of `_init' /usr/lib/gcc/i686-redhat-linux/4.4.4/../../../crti.o:(.init+0x0): first defined here hello_child.o: In function `_fini': hello_child.c:(.text+0x26): multiple definition of `_fini' /usr/lib/gcc/i686-redhat-linux/4.4.4/../../../crti.o:(.fini+0x0): first defined here ...
Совершенно естественно, так как мы пытались повторно переопределить имена, уже определенные в стартовом объектном коде. Желаемого результата мы достигнем сборкой с опциями, как показано в следующем сценарии сборки:
Makefile : TARGET = hello CHILD = $(TARGET)_child LIB = lib$(TARGET) TARGET1 = $(TARGET)_d TARGET2 = $(TARGET)_a all: $(LIB) $(TARGET1) $(TARGET2) $(LIB): $(CHILD).c ../$(CHILD).h gcc -c -fpic -fPIC -shared $(CHILD).c -o $(CHILD).o gcc -shared -nostartfiles -o $(LIB).so $(CHILD).o rm -f *.o $(TARGET1): $(TARGET1).c $(LIB) gcc $< -Bdynamic -ldl -L./ -l$(TARGET) -o $@ $(TARGET2): $(TARGET2).c $(LIB) gcc $< -Bdynamic -L./ -l$(TARGET) -o $@
- здесь как TARGET1
(файл hello_d
)
собирается приложение, самостоятельно подгружающее динамическую
библиотеку libhello.so
по требованию, а как TARGET2
(файл hello_a
) - приложение, опирающееся на автоматическую загрузку библиотек
средствами системы. Сами исходные коды приложений теперь имеют вид:
hello_a.c : #include "../hello_child.h" int main( int argc, char *argv[] ) { int res = put_my_msg( (char*)__FUNCTION__ ); return res; }; hello_d.c : #include <dlfcn.h> #include "../hello_child.h" typedef int (*my_func)( char* ); int main( int argc, char *argv[] ) { // Открываем совместно используемую библиотеку void *dl_handle = dlopen( "./libhello.so", RTLD_LAZY ); if( !dl_handle ) { printf( "ERROR: %s\n", dlerror() ); return 3; } // Находим адрес функции в библиотеке my_func func = dlsym( dl_handle, "put_my_msg" ); char *error = dlerror(); if( error != NULL ) { printf( "ERROR: %s\n", dlerror() ); return 4; } // Вызываем функцию по найденному адресу int res = (*func)( (char*)__FUNCTION__ ); // Закрываем библиотеку dlclose( dl_handle ); return res; };
Теперь собираем всё это вместе и приступаем к опробованию:
$ make gcc -c -fpic -fPIC -shared hello_child.c -o hello_child.o gcc -shared -nostartfiles -o libhello.so hello_child.o rm -f *.o gcc hello_d.c -Bdynamic -ldl -L./ -lhello -o hello_d gcc hello_a.c -Bdynamic -L./ -lhello -o hello_a $ export LD_LIBRARY_PATH=`pwd` $ ./hello_a 65:074290 : _init 65:074387 : main 65:074400 : _fini $ ./hello_d 68:034516 : _init 68:034782 : main 68:034805 : _fini
Смысл приложений в том, что каждая из функций, в порядке их вызова, выводи своё имя и метку времени, когда она вызвана (число после двоеточия — микросекунды, перед — секунды).
Предыдущий раздел: | Оглавление | Следующий раздел: |
Как это всё работает? | Подмена имён |