mirror of https://gitee.com/openkylin/libvirt.git
160 lines
4.2 KiB
C
160 lines
4.2 KiB
C
|
/*
|
||
|
* Copyright (C) 2011, 2013 Red Hat, Inc.
|
||
|
*
|
||
|
* This library is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of the GNU Lesser General Public
|
||
|
* License as published by the Free Software Foundation; either
|
||
|
* version 2.1 of the License, or (at your option) any later version.
|
||
|
*
|
||
|
* This library is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
* Lesser General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU Lesser General Public
|
||
|
* License along with this library. If not, see
|
||
|
* <http://www.gnu.org/licenses/>.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* When libvirt initializes, it creates a thread local for storing
|
||
|
* the last virErrorPtr instance. It also registers a cleanup
|
||
|
* callback for the thread local that will be invoked whenever
|
||
|
* a thread exits.
|
||
|
*
|
||
|
* If the libvirt.so library was dlopen()'d and is dlclose()'d
|
||
|
* while there is still a thread present, then when that thread
|
||
|
* later exits, the libvirt cleanup callback will be invoked.
|
||
|
* Unfortunately libvirt.so will no longer be in memory so the
|
||
|
* callback SEGVs (if you're lucky), or invokes unlreated
|
||
|
* code at the same address as the old callback (if you're
|
||
|
* unlucky).
|
||
|
*
|
||
|
* To fix the problem libvirt is linked '-z nodelete' which
|
||
|
* prevents the code being removed from memory at dlclose().
|
||
|
*
|
||
|
* This test case demonstrates this SEGV scenario. If this
|
||
|
* test does not SEGV, then the '-z nodelete' fix is working
|
||
|
*/
|
||
|
|
||
|
#include <config.h>
|
||
|
|
||
|
#define VIR_NO_GLIB_STDIO /* This file intentionally does not link to libvirt/glib */
|
||
|
#include "testutils.h"
|
||
|
|
||
|
#ifdef linux
|
||
|
|
||
|
# include <dlfcn.h>
|
||
|
# include <pthread.h>
|
||
|
# include <unistd.h>
|
||
|
# include <signal.h>
|
||
|
|
||
|
# include "internal.h"
|
||
|
|
||
|
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
|
||
|
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
|
||
|
bool running = false;
|
||
|
bool failstart = false;
|
||
|
bool quit = false;
|
||
|
|
||
|
static void *threadMain(void *arg)
|
||
|
{
|
||
|
int (*startup)(void) = arg;
|
||
|
|
||
|
if (startup() < 0) {
|
||
|
pthread_mutex_lock(&lock);
|
||
|
failstart = true;
|
||
|
pthread_cond_signal(&cond);
|
||
|
} else {
|
||
|
pthread_mutex_lock(&lock);
|
||
|
running = true;
|
||
|
pthread_cond_signal(&cond);
|
||
|
}
|
||
|
|
||
|
while (!quit)
|
||
|
pthread_cond_wait(&cond, &lock);
|
||
|
pthread_mutex_unlock(&lock);
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static void sigHandler(int sig)
|
||
|
{
|
||
|
ignore_value(write(STDERR_FILENO, "FAIL\n", 5)); /* sc_avoid_write */
|
||
|
signal(sig, SIG_DFL);
|
||
|
raise(sig);
|
||
|
}
|
||
|
|
||
|
/* We're not using the testutils.c main() wrapper because
|
||
|
* we don't want 'shunloadtest' itself to link against
|
||
|
* libvirt.so. We need to test dlopen()'ing of libvirt.so
|
||
|
*/
|
||
|
int main(int argc G_GNUC_UNUSED, char **argv)
|
||
|
{
|
||
|
void (*startup)(void);
|
||
|
pthread_t t;
|
||
|
void *lib;
|
||
|
char *theprogname;
|
||
|
|
||
|
theprogname = argv[0];
|
||
|
if (STRPREFIX(theprogname, "./"))
|
||
|
theprogname += 2;
|
||
|
|
||
|
fprintf(stderr, "TEST: %s\n", theprogname);
|
||
|
fprintf(stderr, " .%*s 1 ", 39, "");
|
||
|
signal(SIGSEGV, sigHandler);
|
||
|
|
||
|
if (!(lib = dlopen(abs_builddir "/libshunload.so", RTLD_LAZY))) {
|
||
|
fprintf(stderr, "Cannot load ./libshunload.so %s\n", dlerror());
|
||
|
return 1;
|
||
|
}
|
||
|
if (!(startup = dlsym(lib, "shunloadStart"))) {
|
||
|
fprintf(stderr, "Cannot find shunloadStart %s\n", dlerror());
|
||
|
dlclose(lib);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Create a thread which is going to initialize libvirt
|
||
|
* and raise an error
|
||
|
*/
|
||
|
pthread_create(&t, NULL, threadMain, startup);
|
||
|
|
||
|
/* Wait for the thread to start and call libvirt */
|
||
|
pthread_mutex_lock(&lock);
|
||
|
while (!running && !failstart)
|
||
|
pthread_cond_wait(&cond, &lock);
|
||
|
|
||
|
/* Close the shared library (and thus make libvirt.so
|
||
|
* non-resident */
|
||
|
dlclose(lib);
|
||
|
|
||
|
/* Tell the thread to quit */
|
||
|
quit = true;
|
||
|
pthread_cond_signal(&cond);
|
||
|
pthread_mutex_unlock(&lock);
|
||
|
|
||
|
pthread_join(t, NULL);
|
||
|
|
||
|
/* If we got to here the thread successfully exited without
|
||
|
* causing a SEGV !
|
||
|
*/
|
||
|
|
||
|
if (failstart)
|
||
|
fprintf(stderr, "FAIL to initialize libvirt\n");
|
||
|
else
|
||
|
fprintf(stderr, "OK\n");
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#else
|
||
|
|
||
|
int main(void)
|
||
|
{
|
||
|
return EXIT_AM_SKIP;
|
||
|
}
|
||
|
|
||
|
#endif
|