From: Julian Seward Date: Sun, 13 Oct 2002 11:52:00 +0000 (+0000) Subject: merge revs X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7daaec3580499697e9c45fd05ddcf1a59d85f365;p=thirdparty%2Fvalgrind.git merge revs acconfig.h 1.4 configure.in 1.70 coregrind/vg_libpthread.c 1.95, 1.97 and 1.98 coregrind/vg_libpthread_unimp.c 1.33 coregrind/vg_scheduler.c 1.88 coregrind/vg_include.h 1.88 Merge support for Red Hat 8 from HEAD. That means principally the new thread-specific-data stuff. git-svn-id: svn://svn.valgrind.org/valgrind/branches/VALGRIND_1_0_BRANCH@1215 --- diff --git a/acconfig.h b/acconfig.h index aa5ffc3d3f..5e9821355d 100644 --- a/acconfig.h +++ b/acconfig.h @@ -6,6 +6,7 @@ #undef GLIBC_2_1 #undef GLIBC_2_2 +#undef GLIBC_2_3 #undef XFREE_3 #undef XFREE_4 diff --git a/configure.in b/configure.in index cb42b23178..903a5c6027 100644 --- a/configure.in +++ b/configure.in @@ -131,6 +131,16 @@ AC_EGREP_CPP([GLIBC_22], [ ], glibc="2.2") +AC_EGREP_CPP([GLIBC_23], [ +#include +#ifdef __GNU_LIBRARY__ + #if (__GLIBC__ == 2 && __GLIBC_MINOR__ == 3) + GLIBC_23 + #endif +#endif +], +glibc="2.3") + case "${glibc}" in 2.1) AC_MSG_RESULT(2.1 family) @@ -144,9 +154,18 @@ case "${glibc}" in DEFAULT_SUPP="${DEFAULT_SUPP} glibc-2.2.supp" ;; + 2.3) + AC_MSG_RESULT(2.3 family) + AC_DEFINE(GLIBC_2_3) + ## Use the same suppression for as for 2.2. + ## This is a horrible hack that should be gotten + ## rid of. JRS, 20021007. + DEFAULT_SUPP="${DEFAULT_SUPP} glibc-2.2.supp" + ;; + *) AC_MSG_RESULT(unsupported version) - AC_MSG_ERROR([Valgrind requires the glibc version 2.1 or 2.2]) + AC_MSG_ERROR([Valgrind requires the glibc version 2.1, 2.2 or 2.3]) ;; esac diff --git a/vg_include.h b/vg_include.h index e8ad84a185..36b295b4ae 100644 --- a/vg_include.h +++ b/vg_include.h @@ -498,13 +498,14 @@ extern Bool VG_(is_empty_arena) ( ArenaId aid ); #define VG_USERREQ__PTHREAD_COND_BROADCAST 0x3012 #define VG_USERREQ__PTHREAD_KEY_CREATE 0x3013 #define VG_USERREQ__PTHREAD_KEY_DELETE 0x3014 -#define VG_USERREQ__PTHREAD_SETSPECIFIC 0x3015 -#define VG_USERREQ__PTHREAD_GETSPECIFIC 0x3016 +#define VG_USERREQ__PTHREAD_SETSPECIFIC_PTR 0x3015 +#define VG_USERREQ__PTHREAD_GETSPECIFIC_PTR 0x3016 #define VG_USERREQ__READ_MILLISECOND_TIMER 0x3017 #define VG_USERREQ__PTHREAD_SIGMASK 0x3018 #define VG_USERREQ__SIGWAIT 0x3019 #define VG_USERREQ__PTHREAD_KILL 0x301A #define VG_USERREQ__PTHREAD_YIELD 0x301B +#define VG_USERREQ__PTHREAD_KEY_VALIDATE 0x301C #define VG_USERREQ__CLEANUP_PUSH 0x3020 #define VG_USERREQ__CLEANUP_POP 0x3021 @@ -660,8 +661,13 @@ typedef Int custack_used; CleanupEntry custack[VG_N_CLEANUPSTACK]; - /* thread-specific data */ - void* specifics[VG_N_THREAD_KEYS]; + /* A pointer to the thread's-specific-data. This is handled + almost entirely from vg_libpthread.c. We just provide hooks + to get and set this ptr. This is either NULL, indicating the + thread has read/written none of its specifics so far, OR + points to a void*[VG_N_THREAD_KEYS], allocated and + deallocated in vg_libpthread.c. */ + void** specifics_ptr; /* This thread's blocked-signals mask. Semantics is that for a signal to be delivered to this thread, the signal must not be diff --git a/vg_libpthread.c b/vg_libpthread.c index 5972dfa640..17ab75fb23 100644 --- a/vg_libpthread.c +++ b/vg_libpthread.c @@ -75,10 +75,33 @@ static void wait_for_fd_to_be_readable_or_erring ( int fd ); +static +int my_do_syscall1 ( int syscallno, int arg1 ); + static int my_do_syscall2 ( int syscallno, int arg1, int arg2 ); +static +int my_do_syscall3 ( int syscallno, + int arg1, int arg2, int arg3 ); + + +#ifdef GLIBC_2_3 + /* kludge by JRS (not from glibc) ... */ + typedef void* __locale_t; + + /* Copied from locale/locale.h in glibc-2.2.93 sources */ + /* This value can be passed to `uselocale' and may be returned by + it. Passing this value to any other function has undefined + behavior. */ +# define LC_GLOBAL_LOCALE ((__locale_t) -1L) + extern __locale_t __uselocale ( __locale_t ); +#endif + +static +void init_libc_tsd_keys ( void ); + /* --------------------------------------------------------------------- Helpers. We have to be pretty self-sufficient. @@ -99,20 +122,18 @@ int get_pt_trace_level ( void ) return res; } - static void my_exit ( int arg ) { - int __res; - __asm__ volatile ("movl %%ecx, %%ebx ; int $0x80" - : "=a" (__res) - : "0" (__NR_exit), - "c" (arg) ); - /* We don't bother to mention the fact that this asm trashes %ebx, - since it won't return. If you ever do let it return ... fix - this! */ + my_do_syscall1(__NR_exit, arg); + /*NOTREACHED*/ } +static +void my_write ( int fd, const void *buf, int count ) +{ + my_do_syscall3(__NR_write, fd, (int)buf, count ); +} /* We need this guy -- it's in valgrind.so. */ extern void VG_(startup) ( void ); @@ -143,7 +164,7 @@ void barf ( char* str ) strcat(buf, "\nvalgrind's libpthread.so: "); strcat(buf, str); strcat(buf, "\n\n"); - write(2, buf, strlen(buf)); + my_write(2, buf, strlen(buf)); my_exit(1); /* We have to persuade gcc into believing this doesn't return. */ while (1) { }; @@ -154,10 +175,10 @@ static void ignored ( char* msg ) { if (get_pt_trace_level() >= 0) { char* ig = "valgrind's libpthread.so: IGNORED call to: "; - write(2, ig, strlen(ig)); - write(2, msg, strlen(msg)); + my_write(2, ig, strlen(ig)); + my_write(2, msg, strlen(msg)); ig = "\n"; - write(2, ig, strlen(ig)); + my_write(2, ig, strlen(ig)); } } @@ -165,10 +186,10 @@ static void kludged ( char* msg ) { if (get_pt_trace_level() >= 0) { char* ig = "valgrind's libpthread.so: KLUDGED call to: "; - write(2, ig, strlen(ig)); - write(2, msg, strlen(msg)); + my_write(2, ig, strlen(ig)); + my_write(2, msg, strlen(msg)); ig = "\n"; - write(2, ig, strlen(ig)); + my_write(2, ig, strlen(ig)); } } @@ -181,10 +202,10 @@ __attribute__((noreturn)) void vgPlain_unimp ( char* what ) { char* ig = "valgrind's libpthread.so: UNIMPLEMENTED FUNCTION: "; - write(2, ig, strlen(ig)); - write(2, what, strlen(what)); + my_write(2, ig, strlen(ig)); + my_write(2, what, strlen(what)); ig = "\n"; - write(2, ig, strlen(ig)); + my_write(2, ig, strlen(ig)); barf("Please report this bug to me at: jseward@acm.org"); } @@ -211,6 +232,27 @@ void my_assert_fail ( Char* expr, Char* file, Int line, Char* fn ) __FILE__, __LINE__, \ __PRETTY_FUNCTION__), 0))) +static +void my_free ( void* ptr ) +{ + int res; + VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, + VG_USERREQ__FREE, ptr, 0, 0, 0); + my_assert(res == 0); +} + + +static +void* my_malloc ( int nbytes ) +{ + void* res; + VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, + VG_USERREQ__MALLOC, nbytes, 0, 0, 0); + my_assert(res != (void*)0); + return res; +} + + /* --------------------------------------------------------------------- Pass pthread_ calls to Valgrind's request mechanism. @@ -234,6 +276,24 @@ void pthread_error ( const char* msg ) } +/* --------------------------------------------------- + Here so it can be inlined without complaint. + ------------------------------------------------ */ + +__inline__ +pthread_t pthread_self(void) +{ + int tid; + ensure_valgrind("pthread_self"); + VALGRIND_MAGIC_SEQUENCE(tid, 0 /* default */, + VG_USERREQ__PTHREAD_GET_THREADID, + 0, 0, 0, 0); + if (tid < 1 || tid >= VG_N_THREADS) + barf("pthread_self: invalid ThreadId"); + return tid; +} + + /* --------------------------------------------------- THREAD ATTRIBUTES ------------------------------------------------ */ @@ -425,6 +485,7 @@ void thread_exit_wrapper ( void* ret_val ) int detached, res; CleanupEntry cu; pthread_key_t key; + void** specifics_ptr; /* Run this thread's cleanup handlers. */ while (1) { @@ -453,6 +514,15 @@ void thread_exit_wrapper ( void* ret_val ) my_assert(res == -1); } + /* Free up my specifics space, if any. */ + VALGRIND_MAGIC_SEQUENCE(specifics_ptr, 3 /* default */, + VG_USERREQ__PTHREAD_GETSPECIFIC_PTR, + pthread_self(), 0, 0, 0); + my_assert(specifics_ptr != (void**)3); + my_assert(specifics_ptr != (void**)1); /* 1 means invalid thread */ + if (specifics_ptr != NULL) + my_free(specifics_ptr); + /* Decide on my final disposition. */ VALGRIND_MAGIC_SEQUENCE(detached, (-1) /* default */, VG_USERREQ__SET_OR_GET_DETACH, @@ -497,7 +567,6 @@ static __attribute__((noreturn)) void thread_wrapper ( NewThreadInfo* info ) { - int res; int attr__detachstate; void* (*root_fn) ( void* ); void* arg; @@ -508,9 +577,7 @@ void thread_wrapper ( NewThreadInfo* info ) arg = info->arg; /* Free up the arg block that pthread_create malloced. */ - VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, - VG_USERREQ__FREE, info, 0, 0, 0); - my_assert(res == 0); + my_free(info); /* Minimally observe the attributes supplied. */ if (attr__detachstate != PTHREAD_CREATE_DETACHED @@ -519,6 +586,15 @@ void thread_wrapper ( NewThreadInfo* info ) if (attr__detachstate == PTHREAD_CREATE_DETACHED) pthread_detach(pthread_self()); +# ifdef GLIBC_2_3 + /* Set this thread's locale to the global (default) locale. A hack + in support of glibc-2.3. This does the biz for the all new + threads; the root thread is done with a horrible hack in + init_libc_tsd_keys() below. + */ + __uselocale(LC_GLOBAL_LOCALE); +# endif + /* The root function might not return. But if it does we simply move along to thread_exit_wrapper. All other ways out for the thread (cancellation, or calling pthread_exit) lead there @@ -563,11 +639,13 @@ pthread_create (pthread_t *__restrict __thredd, ensure_valgrind("pthread_create"); + /* make sure the tsd keys, and hence locale info, are initialised + before we get into complications making new threads. */ + init_libc_tsd_keys(); + /* Allocate space for the arg block. thread_wrapper will free it. */ - VALGRIND_MAGIC_SEQUENCE(info, NULL /* default */, - VG_USERREQ__MALLOC, - sizeof(NewThreadInfo), 0, 0, 0); + info = my_malloc(sizeof(NewThreadInfo)); my_assert(info != NULL); if (__attr) @@ -608,19 +686,6 @@ void pthread_exit(void *retval) } -pthread_t pthread_self(void) -{ - int tid; - ensure_valgrind("pthread_self"); - VALGRIND_MAGIC_SEQUENCE(tid, 1 /* default */, - VG_USERREQ__PTHREAD_GET_THREADID, - 0, 0, 0, 0); - if (tid < 1 || tid >= VG_N_THREADS) - barf("pthread_self: invalid ThreadId"); - return tid; -} - - int pthread_detach(pthread_t th) { int res; @@ -1201,43 +1266,140 @@ int pause ( void ) THREAD-SPECIFICs ------------------------------------------------ */ +static +int key_is_valid (pthread_key_t key) +{ + int res; + VALGRIND_MAGIC_SEQUENCE(res, 2 /* default */, + VG_USERREQ__PTHREAD_KEY_VALIDATE, + key, 0, 0, 0); + my_assert(res != 2); + return res; +} + + +/* Returns NULL if thread is invalid. Otherwise, if the thread + already has a specifics area, return that. Otherwise allocate it + one. */ +static +void** get_or_allocate_specifics_ptr ( pthread_t thread ) +{ + int res, i; + void** specifics_ptr; + ensure_valgrind("get_or_allocate_specifics_ptr"); + + /* Returns zero if the thread has no specific_ptr. One if thread + is invalid. Otherwise, the specific_ptr value. This is + allocated with my_malloc and so is aligned and cannot be + confused with 1 or 3. */ + VALGRIND_MAGIC_SEQUENCE(specifics_ptr, 3 /* default */, + VG_USERREQ__PTHREAD_GETSPECIFIC_PTR, + thread, 0, 0, 0); + my_assert(specifics_ptr != (void**)3); + + if (specifics_ptr == (void**)1) + return NULL; /* invalid thread */ + + if (specifics_ptr != NULL) + return specifics_ptr; /* already has a specifics ptr. */ + + /* None yet ... allocate a new one. Should never fail. */ + specifics_ptr = my_malloc( VG_N_THREAD_KEYS * sizeof(void*) ); + my_assert(specifics_ptr != NULL); + + VALGRIND_MAGIC_SEQUENCE(res, -1 /* default */, + VG_USERREQ__PTHREAD_SETSPECIFIC_PTR, + specifics_ptr, 0, 0, 0); + my_assert(res == 0); + + /* POSIX sez: "Upon thread creation, the value NULL shall be + associated with all defined keys in the new thread." This + allocation is in effect a delayed allocation of the specific + data for a thread, at its first-use. Hence we initialise it + here. */ + for (i = 0; i < VG_N_THREAD_KEYS; i++) { + specifics_ptr[i] = NULL; + } + + return specifics_ptr; +} + + int __pthread_key_create(pthread_key_t *key, void (*destr_function) (void *)) { - int res; + void** specifics_ptr; + int res, i; ensure_valgrind("pthread_key_create"); - VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, + + /* This writes *key if successful. It should never fail. */ + VALGRIND_MAGIC_SEQUENCE(res, 1 /* default */, VG_USERREQ__PTHREAD_KEY_CREATE, key, destr_function, 0, 0); + my_assert(res == 0); + + /* POSIX sez: "Upon key creation, the value NULL shall be + associated with the new key in all active threads." */ + for (i = 0; i < VG_N_THREADS; i++) { + specifics_ptr = get_or_allocate_specifics_ptr(i); + /* we get NULL if i is an invalid thread. */ + if (specifics_ptr != NULL) + specifics_ptr[*key] = NULL; + } + return res; } int pthread_key_delete(pthread_key_t key) { - static int moans = N_MOANS; - if (moans-- > 0) - ignored("pthread_key_delete"); + int res; + ensure_valgrind("pthread_key_create"); + if (!key_is_valid(key)) + return EINVAL; + VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, + VG_USERREQ__PTHREAD_KEY_DELETE, + key, 0, 0, 0); + my_assert(res == 0); return 0; } int __pthread_setspecific(pthread_key_t key, const void *pointer) { - int res; + void** specifics_ptr; ensure_valgrind("pthread_setspecific"); - VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, - VG_USERREQ__PTHREAD_SETSPECIFIC, - key, pointer, 0, 0); - return res; + + if (!key_is_valid(key)) + return EINVAL; + + specifics_ptr = get_or_allocate_specifics_ptr(pthread_self()); + specifics_ptr[key] = (void*)pointer; + return 0; } void * __pthread_getspecific(pthread_key_t key) { - int res; + void** specifics_ptr; ensure_valgrind("pthread_getspecific"); - VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, - VG_USERREQ__PTHREAD_GETSPECIFIC, - key, 0 , 0, 0); - return (void*)res; + + if (!key_is_valid(key)) + return NULL; + + specifics_ptr = get_or_allocate_specifics_ptr(pthread_self()); + return specifics_ptr[key]; +} + + +static +void ** __pthread_getspecific_addr(pthread_key_t key) +{ + void** specifics_ptr; + ensure_valgrind("pthread_getspecific_addr"); + + if (!key_is_valid(key)) + return NULL; + + specifics_ptr = get_or_allocate_specifics_ptr(pthread_self()); + return &(specifics_ptr[key]); } @@ -1391,11 +1553,14 @@ struct __res_state* __res_state ( void ) /* The allowable keys (indices) (all 3 of them). From sysdeps/pthread/bits/libc-tsd.h */ -#define N_LIBC_TSD_EXTRA_KEYS 0 - +/* as per glibc anoncvs HEAD of 20021001. */ enum __libc_tsd_key_t { _LIBC_TSD_KEY_MALLOC = 0, _LIBC_TSD_KEY_DL_ERROR, _LIBC_TSD_KEY_RPC_VARS, + _LIBC_TSD_KEY_LOCALE, + _LIBC_TSD_KEY_CTYPE_B, + _LIBC_TSD_KEY_CTYPE_TOLOWER, + _LIBC_TSD_KEY_CTYPE_TOUPPER, _LIBC_TSD_KEY_N }; /* Auto-initialising subsystem. libc_specifics_inited is set @@ -1403,31 +1568,60 @@ enum __libc_tsd_key_t { _LIBC_TSD_KEY_MALLOC = 0, static int libc_specifics_inited = 0; static pthread_mutex_t libc_specifics_inited_mx = PTHREAD_MUTEX_INITIALIZER; + /* These are the keys we must initialise the first time. */ -static pthread_key_t libc_specifics_keys[_LIBC_TSD_KEY_N - + N_LIBC_TSD_EXTRA_KEYS]; +static pthread_key_t libc_specifics_keys[_LIBC_TSD_KEY_N]; -/* Initialise the keys, if they are not already initialise. */ + +/* Initialise the keys, if they are not already initialised. */ static void init_libc_tsd_keys ( void ) { int res, i; pthread_key_t k; - res = pthread_mutex_lock(&libc_specifics_inited_mx); + /* Don't fall into deadlock if we get called again whilst we still + hold the lock, via the __uselocale() call herein. */ + if (libc_specifics_inited != 0) + return; + + /* Take the lock. */ + res = __pthread_mutex_lock(&libc_specifics_inited_mx); if (res != 0) barf("init_libc_tsd_keys: lock"); - if (libc_specifics_inited == 0) { - /* printf("INIT libc specifics\n"); */ - libc_specifics_inited = 1; - for (i = 0; i < _LIBC_TSD_KEY_N + N_LIBC_TSD_EXTRA_KEYS; i++) { - res = pthread_key_create(&k, NULL); - if (res != 0) barf("init_libc_tsd_keys: create"); - libc_specifics_keys[i] = k; - } + /* Now test again, to be sure there is no mistake. */ + if (libc_specifics_inited != 0) { + res = __pthread_mutex_unlock(&libc_specifics_inited_mx); + if (res != 0) barf("init_libc_tsd_keys: unlock(1)"); + return; + } + + /* Actually do the initialisation. */ + /* printf("INIT libc specifics\n"); */ + for (i = 0; i < _LIBC_TSD_KEY_N; i++) { + res = __pthread_key_create(&k, NULL); + if (res != 0) barf("init_libc_tsd_keys: create"); + libc_specifics_keys[i] = k; } - res = pthread_mutex_unlock(&libc_specifics_inited_mx); + /* Signify init done. */ + libc_specifics_inited = 1; + +# ifdef GLIBC_2_3 + /* Set the initialising thread's locale to the global (default) + locale. A hack in support of glibc-2.3. This does the biz for + the root thread. For all other threads we run this in + thread_wrapper(), which does the real work of + pthread_create(). */ + /* assert that we are the root thread. I don't know if this is + really a valid assertion to make; if it breaks I'll reconsider + it. */ + my_assert(pthread_self() == 1); + __uselocale(LC_GLOBAL_LOCALE); +# endif + + /* Unlock and return. */ + res = __pthread_mutex_unlock(&libc_specifics_inited_mx); if (res != 0) barf("init_libc_tsd_keys: unlock"); } @@ -1436,18 +1630,12 @@ static int libc_internal_tsd_set ( enum __libc_tsd_key_t key, const void * pointer ) { - int res; - static int moans = N_MOANS; + int res; /* printf("SET SET SET key %d ptr %p\n", key, pointer); */ - if (key < _LIBC_TSD_KEY_MALLOC - || key >= _LIBC_TSD_KEY_N + N_LIBC_TSD_EXTRA_KEYS) + if (key < _LIBC_TSD_KEY_MALLOC || key >= _LIBC_TSD_KEY_N) barf("libc_internal_tsd_set: invalid key"); - if (key >= _LIBC_TSD_KEY_N && moans-- > 0) - fprintf(stderr, - "valgrind's libpthread.so: libc_internal_tsd_set: " - "dubious key %d\n", key); init_libc_tsd_keys(); - res = pthread_setspecific(libc_specifics_keys[key], pointer); + res = __pthread_setspecific(libc_specifics_keys[key], pointer); if (res != 0) barf("libc_internal_tsd_set: setspecific failed"); return 0; } @@ -1455,25 +1643,17 @@ libc_internal_tsd_set ( enum __libc_tsd_key_t key, static void * libc_internal_tsd_get ( enum __libc_tsd_key_t key ) { - void* v; - static int moans = N_MOANS; + void* v; /* printf("GET GET GET key %d\n", key); */ - if (key < _LIBC_TSD_KEY_MALLOC - || key >= _LIBC_TSD_KEY_N + N_LIBC_TSD_EXTRA_KEYS) + if (key < _LIBC_TSD_KEY_MALLOC || key >= _LIBC_TSD_KEY_N) barf("libc_internal_tsd_get: invalid key"); - if (key >= _LIBC_TSD_KEY_N && moans-- > 0) - fprintf(stderr, - "valgrind's libpthread.so: libc_internal_tsd_get: " - "dubious key %d\n", key); init_libc_tsd_keys(); - v = pthread_getspecific(libc_specifics_keys[key]); + v = __pthread_getspecific(libc_specifics_keys[key]); /* if (v == NULL) barf("libc_internal_tsd_set: getspecific failed"); */ return v; } - - int (*__libc_internal_tsd_set) (enum __libc_tsd_key_t key, const void * pointer) = libc_internal_tsd_set; @@ -1483,6 +1663,26 @@ void* (*__libc_internal_tsd_get) = libc_internal_tsd_get; +#ifdef GLIBC_2_3 +/* This one was first spotted be me in the glibc-2.2.93 sources. */ +static void** +libc_internal_tsd_address ( enum __libc_tsd_key_t key ) +{ + void** v; + /* printf("ADDR ADDR ADDR key %d\n", key); */ + if (key < _LIBC_TSD_KEY_MALLOC || key >= _LIBC_TSD_KEY_N) + barf("libc_internal_tsd_address: invalid key"); + init_libc_tsd_keys(); + v = __pthread_getspecific_addr(libc_specifics_keys[key]); + return v; +} + +void ** (*__libc_internal_tsd_address) + (enum __libc_tsd_key_t key) + = libc_internal_tsd_address; +#endif + + /* --------------------------------------------------------------------- These are here (I think) because they are deemed cancellation points by POSIX. For the moment we'll simply pass the call along diff --git a/vg_libpthread_unimp.c b/vg_libpthread_unimp.c index f3938ec9e6..458c1a06f4 100644 --- a/vg_libpthread_unimp.c +++ b/vg_libpthread_unimp.c @@ -34,6 +34,8 @@ Give a binding for everything the real libpthread.so binds. ------------------------------------------------------------------ */ +#include "vg_include.h" /* For GLIBC_2_3, or not, as the case may be */ + extern void vgPlain_unimp ( char* ); #define unimp(str) vgPlain_unimp(str) @@ -163,6 +165,13 @@ void sem_unlink ( void ) { unimp("sem_unlink"); } void __pthread_clock_gettime ( void ) { unimp("__pthread_clock_gettime"); } void __pthread_clock_settime ( void ) { unimp("__pthread_clock_settime"); } +#if defined(GLIBC_2_2) || defined(GLIBC_2_3) +/* Needed for Red Hat 8.0 */ +__asm__(".symver __pthread_clock_gettime," + "__pthread_clock_gettime@GLIBC_PRIVATE"); +__asm__(".symver __pthread_clock_settime," + "__pthread_clock_settime@GLIBC_PRIVATE"); +#endif #if 0 void pthread_create@@GLIBC_2.1 ( void ) { unimp("pthread_create@@GLIBC_2.1"); } diff --git a/vg_scheduler.c b/vg_scheduler.c index b4fe6247eb..8690c1b376 100644 --- a/vg_scheduler.c +++ b/vg_scheduler.c @@ -528,7 +528,6 @@ void increment_epoch ( void ) static void mostly_clear_thread_record ( ThreadId tid ) { - Int j; vg_assert(tid >= 0 && tid < VG_N_THREADS); VG_(threads)[tid].tid = tid; VG_(threads)[tid].status = VgTs_Empty; @@ -546,8 +545,7 @@ void mostly_clear_thread_record ( ThreadId tid ) VG_(threads)[tid].n_signals_returned = 0; VG_(ksigemptyset)(&VG_(threads)[tid].sig_mask); VG_(ksigemptyset)(&VG_(threads)[tid].sigs_waited_for); - for (j = 0; j < VG_N_THREAD_KEYS; j++) - VG_(threads)[tid].specifics[j] = NULL; + VG_(threads)[tid].specifics_ptr = NULL; } @@ -2674,6 +2672,32 @@ Bool is_valid_key ( ThreadKey k ) return True; } + +/* Return in %EDX a value of 1 if the key is valid, else 0. */ +static +void do_pthread_key_validate ( ThreadId tid, + pthread_key_t key ) +{ + Char msg_buf[100]; + + if (VG_(clo_trace_pthread_level) >= 1) { + VG_(sprintf)(msg_buf, "pthread_key_validate key %p", + key ); + print_pthread_event(tid, msg_buf); + } + + vg_assert(sizeof(pthread_key_t) == sizeof(ThreadKey)); + vg_assert(VG_(is_valid_tid)(tid) + && VG_(threads)[tid].status == VgTs_Runnable); + + if (is_valid_key((ThreadKey)key)) { + SET_EDX(tid, 1); + } else { + SET_EDX(tid, 0); + } +} + + static void do_pthread_key_create ( ThreadId tid, pthread_key_t* key, @@ -2742,66 +2766,57 @@ void do_pthread_key_delete ( ThreadId tid, pthread_key_t key ) } vg_thread_keys[key].inuse = False; - - /* Optional. We're not required to do this, although it shouldn't - make any difference to programs which use the key/specifics - functions correctly. */ -# if 1 - for (tid = 1; tid < VG_N_THREADS; tid++) { - if (VG_(threads)[tid].status != VgTs_Empty) - VG_(threads)[tid].specifics[key] = NULL; - } -# endif + SET_EDX(tid, 0); } +/* Get the .specific_ptr for a thread. Return 1 if the thread-slot + isn't in use, so that client-space can scan all thread slots. 1 + cannot be confused with NULL or a legitimately-aligned specific_ptr + value. */ static -void do_pthread_getspecific ( ThreadId tid, pthread_key_t key ) +void do_pthread_getspecific_ptr ( ThreadId tid ) { - Char msg_buf[100]; + void** specifics_ptr; + Char msg_buf[100]; + if (VG_(clo_trace_pthread_level) >= 1) { - VG_(sprintf)(msg_buf, "pthread_getspecific key %d", - key ); + VG_(sprintf)(msg_buf, "pthread_getspecific_ptr" ); print_pthread_event(tid, msg_buf); } - vg_assert(VG_(is_valid_tid)(tid) - && VG_(threads)[tid].status == VgTs_Runnable); + vg_assert(VG_(is_valid_or_empty_tid)(tid)); - if (!is_valid_key(key)) { - VG_(record_pthread_err)( tid, - "pthread_getspecific: key is invalid"); - SET_EDX(tid, (UInt)NULL); + if (VG_(threads)[tid].status == VgTs_Empty) { + SET_EDX(tid, 1); return; } - SET_EDX(tid, (UInt)VG_(threads)[tid].specifics[key]); + specifics_ptr = VG_(threads)[tid].specifics_ptr; + +# define IS_ALIGNED4_ADDR(aaa_p) (0 == (((UInt)(aaa_p)) & 3)) + vg_assert(specifics_ptr == NULL + || IS_ALIGNED4_ADDR(specifics_ptr)); +# undef IS_ALIGNED4_ADDR + + SET_EDX(tid, (UInt)specifics_ptr); } static -void do_pthread_setspecific ( ThreadId tid, - pthread_key_t key, - void *pointer ) +void do_pthread_setspecific_ptr ( ThreadId tid, void** ptr ) { Char msg_buf[100]; if (VG_(clo_trace_pthread_level) >= 1) { - VG_(sprintf)(msg_buf, "pthread_setspecific key %d, ptr %p", - key, pointer ); + VG_(sprintf)(msg_buf, "pthread_setspecific_ptr ptr %p", + ptr ); print_pthread_event(tid, msg_buf); } vg_assert(VG_(is_valid_tid)(tid) && VG_(threads)[tid].status == VgTs_Runnable); - if (!is_valid_key(key)) { - VG_(record_pthread_err)( tid, - "pthread_setspecific: key is invalid"); - SET_EDX(tid, EINVAL); - return; - } - - VG_(threads)[tid].specifics[key] = pointer; + VG_(threads)[tid].specifics_ptr = ptr; SET_EDX(tid, 0); } @@ -2823,12 +2838,19 @@ void do__get_key_destr_and_spec ( ThreadId tid, } vg_assert(VG_(is_valid_tid)(tid)); vg_assert(key >= 0 && key < VG_N_THREAD_KEYS); + if (!vg_thread_keys[key].inuse) { SET_EDX(tid, -1); return; } + cu->fn = vg_thread_keys[key].destructor; - cu->arg = VG_(threads)[tid].specifics[key]; + if (VG_(threads)[tid].specifics_ptr == NULL) { + cu->arg = NULL; + } else { + cu->arg = VG_(threads)[tid].specifics_ptr[key]; + } + if (VG_(clo_instrument)) VGM_(make_readable)( (Addr)cu, sizeof(CleanupEntry) ); SET_EDX(tid, 0); @@ -3165,8 +3187,8 @@ void do_client_request ( ThreadId tid ) do_pthread_mutex_unlock( tid, (void *)(arg[1]) ); break; - case VG_USERREQ__PTHREAD_GETSPECIFIC: - do_pthread_getspecific ( tid, (UInt)(arg[1]) ); + case VG_USERREQ__PTHREAD_GETSPECIFIC_PTR: + do_pthread_getspecific_ptr ( tid ); break; case VG_USERREQ__SET_CANCELTYPE: @@ -3221,6 +3243,11 @@ void do_client_request ( ThreadId tid ) (pthread_cond_t *)(arg[1]) ); break; + case VG_USERREQ__PTHREAD_KEY_VALIDATE: + do_pthread_key_validate ( tid, + (pthread_key_t)(arg[1]) ); + break; + case VG_USERREQ__PTHREAD_KEY_CREATE: do_pthread_key_create ( tid, (pthread_key_t*)(arg[1]), @@ -3232,10 +3259,9 @@ void do_client_request ( ThreadId tid ) (pthread_key_t)(arg[1]) ); break; - case VG_USERREQ__PTHREAD_SETSPECIFIC: - do_pthread_setspecific ( tid, - (pthread_key_t)(arg[1]), - (void*)(arg[2]) ); + case VG_USERREQ__PTHREAD_SETSPECIFIC_PTR: + do_pthread_setspecific_ptr ( tid, + (void**)(arg[1]) ); break; case VG_USERREQ__PTHREAD_SIGMASK: