Предположим есть такая разделяемая библиотека, которая внутри себя использует треды. Для примера
#include <pthread.h>
#include <assert.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *_thread(void *arg) {
int i;
struct addrinfo *res;
for (i=0; i<1; i++) {
if (getaddrinfo("localhost", NULL, NULL, &res) == 0) {
if (res) freeaddrinfo(res);
}
}
pthread_mutex_lock(&mutex);
printf("Just another thread message!\n");
pthread_mutex_unlock(&mutex);
return NULL;
}
void make_thread() {
pthread_t tid[10];
int i, rc;
for (i=0; i<10; i++) {
rc = pthread_create(&tid[i], NULL, _thread, NULL);
assert(rc == 0);
}
void *rv;
for (i=0; i<10; i++) {
rc = pthread_join(tid[i], &rv);
assert(rc == 0);
}
}
И такая главная программа, которая использует библиотеку
#include <stdio.h>
#include <dlfcn.h>
#include <netdb.h>
int main() {
void *mylib_hdl;
void (*make_thread)();
mylib_hdl = dlopen("./libmy.so", RTLD_NOW|RTLD_GLOBAL);
if (mylib_hdl == NULL) {
printf("dlopen: %s\n", dlerror());
return 1;
}
make_thread = (void (*)()) dlsym(mylib_hdl, "make_thread");
if (make_thread == NULL) {
printf("dlsym: %s\n", dlerror());
return 1;
}
(*make_thread)();
return 0;
}
И такой Makefile
all:
cc -pthread -g -fPIC -c mylib.c
cc -pthread -g -shared -o libmy.so mylib.o
cc -g -o main main.c -ldl
clean:
rm *.o *.so main
Заметьте главная программа скомпилирована без линковки с pthread, но при этом всё работает хорошо за счёт флага RTLD_GLOBAL для dlopen(). Если оставить только RTLD_NOW то будут сегфолты, т.к. внутри нашей библиотеки будут использованы не потокобезопасные версии функций libc, загруженные главной программой.
Но теперь поменяем главную программу немного
#include <stdio.h>
#include <dlfcn.h>
#include <netdb.h>
int main() {
void *mylib_hdl;
void (*make_thread)();
struct protoent *proto = getprotobyname("tcp");
mylib_hdl = dlopen("./libmy.so", RTLD_NOW|RTLD_GLOBAL);
if (mylib_hdl == NULL) {
printf("dlopen: %s\n", dlerror());
return 1;
}
make_thread = (void (*)()) dlsym(mylib_hdl, "make_thread");
if (make_thread == NULL) {
printf("dlsym: %s\n", dlerror());
return 1;
}
(*make_thread)();
return 0;
}
В главной программе появился вызов getprotobyname() и теперь флаг RTLD_GLOBAL не помогает. В программу приходит сегфолт.
Как я понял этот вызов окончательно и безповоротно подгрузил непотокобезопасную часть libc и внутри библиотеки уже нет возможности использовать потокобезопасную версию.
Вопрос: можно ли таки победить сегфолт в последнем случаи? А если нельзя, то есть ли возможность внутри библиотеки как-то проверить, что будут использованы потокобезопасные функции и если нет, то завершиться, имея возможность оставить запись в лог или совершить другие предварительные действия?