svn commit: branches/uClibc-nptl/ldso: include ldso

sjhill at uclibc.org sjhill at uclibc.org
Sat Oct 1 15:34:01 UTC 2005


Author: sjhill
Date: 2005-10-01 08:33:59 -0700 (Sat, 01 Oct 2005)
New Revision: 11719

Log:
Latest 'ldso' changes for TLS support dated 20050930. I just did not get them checked in last night.


Modified:
   branches/uClibc-nptl/ldso/include/dl-hash.h
   branches/uClibc-nptl/ldso/include/ldsodefs.h
   branches/uClibc-nptl/ldso/ldso/dl-elf.c
   branches/uClibc-nptl/ldso/ldso/dl-tls.c
   branches/uClibc-nptl/ldso/ldso/ldso.c


Changeset:
Modified: branches/uClibc-nptl/ldso/include/dl-hash.h
===================================================================
--- branches/uClibc-nptl/ldso/include/dl-hash.h	2005-10-01 10:16:02 UTC (rev 11718)
+++ branches/uClibc-nptl/ldso/include/dl-hash.h	2005-10-01 15:33:59 UTC (rev 11719)
@@ -121,9 +121,10 @@
 #define LD_ERROR_NOTDYN 5
 #define LD_ERROR_MMAP_FAILED 6
 #define LD_ERROR_NODYNAMIC 7
-#define LD_WRONG_RELOCS 8
-#define LD_BAD_HANDLE 9
-#define LD_NO_SYMBOL 10
+#define LD_ERROR_TLS_FAILED 8
+#define LD_WRONG_RELOCS 9
+#define LD_BAD_HANDLE 10
+#define LD_NO_SYMBOL 11
 
 
 

Modified: branches/uClibc-nptl/ldso/include/ldsodefs.h
===================================================================
--- branches/uClibc-nptl/ldso/include/ldsodefs.h	2005-10-01 10:16:02 UTC (rev 11718)
+++ branches/uClibc-nptl/ldso/include/ldsodefs.h	2005-10-01 15:33:59 UTC (rev 11719)
@@ -88,6 +88,8 @@
 EXTERN size_t _dl_tls_static_used;
 /* Alignment requirement of the static TLS block.  */
 EXTERN size_t _dl_tls_static_align;
+/* Function pointer for catching TLS errors.  */
+EXTERN void **(*_dl_error_catch_tsd) (void) __attribute__ ((const));
 
 /* Number of additional entries in the slotinfo array of each slotinfo
    list element.  A large number makes it almost certain take we never

Modified: branches/uClibc-nptl/ldso/ldso/dl-elf.c
===================================================================
--- branches/uClibc-nptl/ldso/ldso/dl-elf.c	2005-10-01 10:16:02 UTC (rev 11718)
+++ branches/uClibc-nptl/ldso/ldso/dl-elf.c	2005-10-01 15:33:59 UTC (rev 11719)
@@ -432,6 +432,87 @@
 				maxvma = ppnt->p_vaddr + ppnt->p_memsz;
 			}
 		}
+
+		if (ppnt->p_type == PT_TLS)
+		{
+#if USE_TLS
+			if (ppnt->p_memsz == 0)
+				/* Nothing to do for an empty segment.  */
+				continue;
+
+			tpnt->l_tls_blocksize = ppnt->p_memsz;
+			tpnt->l_tls_align = ppnt->p_align;
+			if (ppnt->p_align == 0)
+				tpnt->l_tls_firstbyte_offset = 0;
+			else
+				tpnt->l_tls_firstbyte_offset = ppnt->p_vaddr &
+					(ppnt->p_align - 1);
+			tpnt->l_tls_initimage_size = ppnt->p_filesz;
+			/* Since we don't know the load address yet only store the
+			   offset.  We will adjust it later.  */
+			tpnt->l_tls_initimage = (void *) ppnt->p_vaddr;
+
+			/* If _dl_tls_dtv_slotinfo_list == NULL, then ldso.c did
+			   not set up TLS data structures, so don't use them now.  */
+			if (__builtin_expect (_dl_tls_dtv_slotinfo_list != NULL, 1))
+			{
+				/* Assign the next available module ID.  */
+				tpnt->l_tls_modid = _dl_next_tls_modid ();
+				continue;
+			}
+
+# ifdef SHARED
+			if (tpnt->prev == NULL)
+				/* We are loading the executable itself when the dynamic linker
+				   was executed directly.  The setup will happen later.  */
+				continue;
+
+			/* In a static binary there is no way to tell if we dynamically
+			   loaded libpthread.  */
+			if (_dl_error_catch_tsd == &_dl_initial_error_catch_tsd)
+# endif
+			{
+				/* We have not yet loaded libpthread.
+				   We can do the TLS setup right now!  */
+
+				void *tcb;
+
+				/* The first call allocates TLS bookkeeping data structures.
+				   Then we allocate the TCB for the initial thread.  */
+				if (__builtin_expect (_dl_tls_setup (), 0)
+					|| __builtin_expect ((tcb = _dl_allocate_tls(NULL)) == NULL, 0))
+				{
+					_dl_dprintf(2, "%s: '%s' cannot allocate TLS data structures for initial thread\n", _dl_progname, libname);
+					goto tls_failed;	/* I'm using a goto, so shoot me. */
+				}
+
+				/* Now we install the TCB in the thread register.  */
+				if (__builtin_expect (TLS_INIT_TP (tcb, 0) == NULL, 1))
+				{
+					/* Now we are all good.  */
+					tpnt->l_tls_modid = ++_dl_tls_max_dtv_idx;
+					continue;
+				}
+
+				/* The kernel is too old or somesuch.  */
+				_dl_dprintf(2, "%s: '%s' unknown TLS error\n", _dl_progname, libname);
+				_dl_deallocate_tls (tcb, 1);
+			}
+tls_failed:
+#else
+			/*
+			 * Yup, the user was an idiot and tried to sneak in a library with
+			 * TLS in it and we don't support it. Let's fall on our own sword
+			 * and scream at the luser while we die.
+			 */
+			_dl_dprintf(2, "%s: '%s' library contains unsupported TLS\n",
+				_dl_progname, libname);
+#endif
+			_dl_internal_error_number = LD_ERROR_TLS_FAILED;
+			_dl_close(infile);
+			_dl_munmap(header, _dl_pagesize);
+			return NULL;
+		}
 		ppnt++;
 	};
 
@@ -585,6 +666,12 @@
 	tpnt->ppnt = (ElfW(Phdr) *)(intptr_t) (tpnt->loadaddr + epnt->e_phoff);
 	tpnt->n_phent = epnt->e_phnum;
 
+#if USE_TLS
+	/* Adjust the address of the TLS initialization image.  */
+	if (tpnt->l_tls_initimage != NULL)
+		tpnt->l_tls_initimage = (char *) tpnt->l_tls_initimage + tpnt->loadaddr;
+#endif
+
 	/*
 	 * Add this object into the symbol chain
 	 */
@@ -681,6 +768,13 @@
 					tpnt->dynamic_info[DT_PLTRELSZ]);
 		}
 	}
+
+#ifdef USE_TLS
+	/* Add object to slot information data if necessasy.  */
+	if (tpnt->l_tls_blocksize != 0 && tls_init_tp_called)
+		_dl_add_to_slotinfo ((struct link_map *) tpnt);
+#endif
+
 	return goof;
 }
 

Modified: branches/uClibc-nptl/ldso/ldso/dl-tls.c
===================================================================
--- branches/uClibc-nptl/ldso/ldso/dl-tls.c	2005-10-01 10:16:02 UTC (rev 11718)
+++ branches/uClibc-nptl/ldso/ldso/dl-tls.c	2005-10-01 15:33:59 UTC (rev 11719)
@@ -26,16 +26,79 @@
  * SUCH DAMAGE.
  */
 
+/*
+ * The big TODO list:
+ *
+ *    - Environment variables LD_TRACE_LOADED_OBJECTS and LD_PRELOAD
+ *      need to be supported.
+ *    - Config option LDSO_PRELOAD_FILE_SUPPORT needs to be supported.
+ *    - Support TLS information for 'ldd' command.
+ *
+ */
+
 #include <tls.h>
 #include <dl-tls.h>
 #include <ldsodefs.h>
 
-#define calloc(a, b) NULL
-#define malloc(a) NULL
-#define realloc(a, b) NULL
-#define free(a)
-#define _dl_memalign(a, b) NULL
+void *(*_dl_calloc_function) (size_t __nmemb, size_t __size) = NULL;
+void *(*_dl_realloc_function) (void *__ptr, size_t __size) = NULL;
+void *(*_dl_memalign_function) (size_t __boundary, size_t __size) = NULL;
+void (*_dl_free_function) (void *__ptr) = NULL;
 
+void *_dl_memalign (size_t __boundary, size_t __size);
+
+
+void *
+_dl_calloc (size_t __nmemb, size_t __size)
+{
+	void *result;
+	size_t size = (__size * __nmemb); 
+
+	if (_dl_calloc_function) {
+#if 1 
+		_dl_debug_early("Calling libc version\n");
+#endif
+		return (*_dl_calloc_function) (__nmemb, __size);
+	}
+
+	_dl_debug_early("allocating memory and zeroing\n");
+	if (__nmemb && __size != (size / __nmemb)) {
+			_dl_dprintf(2, "%si:%i: unaligned structures\n",
+				__FUNCTION__, __LINE__);
+			_dl_exit(1);
+	}
+	if ((result = _dl_malloc(__size)) != NULL) {
+		_dl_memset(result, 0, size);
+	}
+	return result;
+}
+
+void *
+_dl_realloc (void * __ptr, size_t __size)
+{
+	_dl_debug_early("NOT IMPLEMENTED PROPERLY!!!\n");
+	if (_dl_realloc_function) {
+#if 1 
+		_dl_debug_early("Calling libc version\n");
+#endif
+		return (*_dl_realloc_function) (__ptr, __size);
+	}
+	return NULL;
+}
+
+void
+_dl_free (void *__ptr)
+{
+	_dl_debug_early("NOT IMPLEMENTED PROPERLY!!!\n");
+	if (_dl_free_function) {
+#if 1 
+		_dl_debug_early("Calling libc version\n");
+#endif
+		(*_dl_free_function) (__ptr);
+	}
+}
+
+
 /* The __tls_get_addr function has two basic forms which differ in the
    arguments.  The IA-64 form takes two parameters, the module ID and
    offset.  The form used, among others, on IA-32 takes a reference to
@@ -62,7 +125,16 @@
 /* Value used for dtv entries for which the allocation is delayed. */
 #define TLS_DTV_UNALLOCATED	((void *) -1l)
 
+/* Thread control block pointer. */
+void *tcbp = NULL;
 
+#define _dl_fatal_printf(fmt, args...)									\
+{																		\
+	do {																\
+		_dl_dprintf(2, "%s:%i: " fmt, __FUNCTION__, __LINE__, ## args);	\
+	} while (1);														\
+}
+
 /* Taken from glibc/elf/dl-reloc.c */
 #define CHECK_STATIC_TLS(sym_map)											\
 	do {																	\
@@ -114,11 +186,7 @@
 __attribute__ ((__noreturn__))
 oom (void)
 {
-	do {
-		_dl_dprintf (_dl_debug_file,
-			"cannot allocate thread-local memory: ABORT\n");
-		_dl_exit (127);
-	} while (1);
+	_dl_fatal_printf("cannot allocate thread-local memory: ABORT\n");
 }
 
 size_t
@@ -338,7 +406,7 @@
   const size_t nelem = 2 + TLS_SLOTINFO_SURPLUS;
 
   _dl_tls_dtv_slotinfo_list
-    = calloc (1, (sizeof (struct dtv_slotinfo_list)
+    = _dl_calloc (1, (sizeof (struct dtv_slotinfo_list)
 		  + nelem * sizeof (struct dtv_slotinfo)));
   if (_dl_tls_dtv_slotinfo_list == NULL)
     return -1;
@@ -367,7 +435,11 @@
      initial set of modules.  This should avoid in most cases expansions
      of the dtv.  */
   dtv_length = _dl_tls_max_dtv_idx + DTV_SURPLUS;
+#ifndef __UCLIBC__
   dtv = calloc (dtv_length + 2, sizeof (dtv_t));
+#else
+  dtv = _dl_calloc (dtv_length + 2, sizeof (dtv_t));
+#endif
   if (dtv != NULL)
     {
       /* This is the initial length of the dtv.  */
@@ -435,7 +507,7 @@
 
       result = allocate_dtv (result);
       if (result == NULL)
-	free (allocated);
+	_dl_free (allocated);
     }
 
   return result;
@@ -545,11 +617,11 @@
   for (cnt = 0; cnt < dtv[-1].counter; ++cnt)
     if (! dtv[1 + cnt].pointer.is_static
 	&& dtv[1 + cnt].pointer.val != TLS_DTV_UNALLOCATED)
-      free (dtv[1 + cnt].pointer.val);
+      _dl_free (dtv[1 + cnt].pointer.val);
 
   /* The array starts with dtv[-1].  */
   if (dtv != _dl_initial_dtv)
-    free (dtv - 1);
+    _dl_free (dtv - 1);
 
   if (dealloc_tcb)
     {
@@ -561,7 +633,7 @@
       tcb -= (TLS_PRE_TCB_SIZE + _dl_tls_static_align - 1)
 	     & ~(_dl_tls_static_align - 1);
 # endif
-      free (tcb);
+      _dl_free (tcb);
     }
 }
 rtld_hidden_def (_dl_deallocate_tls)
@@ -655,7 +727,7 @@
 		  if (! dtv[total + cnt].pointer.is_static
 		      && dtv[total + cnt].pointer.val != TLS_DTV_UNALLOCATED)
 		    {
-		      free (dtv[total + cnt].pointer.val);
+		      _dl_free (dtv[total + cnt].pointer.val);
 		      dtv[total + cnt].pointer.val = TLS_DTV_UNALLOCATED;
 		    }
 
@@ -681,14 +753,14 @@
 			 malloc instead of the real malloc.  We can't
 			 free it, we have to abandon the old storage.  */
 
-		      newp = malloc ((2 + newsize) * sizeof (dtv_t));
+		      newp = _dl_malloc ((2 + newsize) * sizeof (dtv_t));
 		      if (newp == NULL)
 			oom ();
 		      _dl_memcpy (newp, &dtv[-1], oldsize * sizeof (dtv_t));
 		    }
 		  else
 		    {
-		      newp = realloc (&dtv[-1],
+		      newp = _dl_realloc (&dtv[-1],
 				      (2 + newsize) * sizeof (dtv_t));
 		      if (newp == NULL)
 			oom ();
@@ -718,7 +790,7 @@
 		   deallocate even if it is this dtv entry we are
 		   supposed to load.  The reason is that we call
 		   memalign and not malloc.  */
-		free (dtv[modid].pointer.val);
+		_dl_free (dtv[modid].pointer.val);
 
 	      /* This module is loaded dynamically- We defer memory
 		 allocation.  */
@@ -812,7 +884,7 @@
       assert (idx == 0);
 
       listp = prevp->next = (struct dtv_slotinfo_list *)
-	malloc (sizeof (struct dtv_slotinfo_list)
+	_dl_malloc (sizeof (struct dtv_slotinfo_list)
 		+ TLS_SLOTINFO_SURPLUS * sizeof (struct dtv_slotinfo));
       if (listp == NULL)
 	{
@@ -841,3 +913,98 @@
   listp->slotinfo[idx].map = l;
   listp->slotinfo[idx].gen = _dl_tls_generation + 1;
 }
+
+/* Taken from glibc/elf/rtld.c */
+static bool tls_init_tp_called;
+
+/* _dl_error_catch_tsd points to this for the single-threaded case.
+   It's reset by the thread library for multithreaded programs.  */
+void ** __attribute__ ((const))
+_dl_initial_error_catch_tsd (void)
+{
+	static void *data;
+	return &data;
+}
+
+static void *
+init_tls (void)
+{
+	/* Number of elements in the static TLS block.  */
+	_dl_tls_static_nelem = _dl_tls_max_dtv_idx;
+
+	/* Do not do this twice.  The audit interface might have required
+	   the DTV interfaces to be set up early.  */
+	if (_dl_initial_dtv != NULL)
+		return NULL;
+
+	/* Allocate the array which contains the information about the
+	   dtv slots.  We allocate a few entries more than needed to
+	   avoid the need for reallocation.  */
+	size_t nelem = _dl_tls_max_dtv_idx + 1 + TLS_SLOTINFO_SURPLUS;
+
+	/* Allocate.  */
+	_dl_tls_dtv_slotinfo_list = (struct dtv_slotinfo_list *)
+		_dl_calloc (sizeof (struct dtv_slotinfo_list)
+			+ nelem * sizeof (struct dtv_slotinfo), 1);
+	/* No need to check the return value.  If memory allocation failed
+	   the program would have been terminated.  */
+
+	struct dtv_slotinfo *slotinfo = _dl_tls_dtv_slotinfo_list->slotinfo;
+	_dl_tls_dtv_slotinfo_list->len = nelem;
+	_dl_tls_dtv_slotinfo_list->next = NULL;
+
+	/* Fill in the information from the loaded modules.  No namespace
+	   but the base one can be filled at this time.  */
+#ifndef __UCLIBC__
+	assert (_dl_ns[LM_ID_BASE + 1]._ns_loaded == NULL);
+	int i = 0;
+	struct link_map *l;
+	for (l = _dl_ns[LM_ID_BASE]._ns_loaded; l != NULL; l = l->l_next)
+		if (l->l_tls_blocksize != 0)
+		{
+			/* This is a module with TLS data.  Store the map reference.
+			   The generation counter is zero.  */
+			slotinfo[i].map = l;
+			/* slotinfo[i].gen = 0; */
+		++i;
+		}
+#else
+	int i = 0;
+	struct link_map *l;
+	for (l =  (struct link_map *) _dl_loaded_modules; l != NULL; l = l->l_next)
+		if (l->l_tls_blocksize != 0)
+		{
+			/* This is a module with TLS data.  Store the map reference.
+			   The generation counter is zero.  */
+			slotinfo[i].map = l;
+			/* slotinfo[i].gen = 0; */
+		++i;
+		}
+#endif
+	assert (i == _dl_tls_max_dtv_idx);
+
+	/* Compute the TLS offsets for the various blocks.  */
+	_dl_determine_tlsoffset ();
+
+	/* Construct the static TLS block and the dtv for the initial
+	   thread.  For some platforms this will include allocating memory
+	   for the thread descriptor.  The memory for the TLS block will
+	   never be freed.  It should be allocated accordingly.  The dtv
+	   array can be changed if dynamic loading requires it.  */
+	void *tcbp = _dl_allocate_tls_storage ();
+	if (tcbp == NULL)
+		_dl_fatal_printf ("\ncannot allocate TLS data structures for initial thread");
+
+	/* Store for detection of the special case by __tls_get_addr
+	   so it knows not to pass this dtv to the normal realloc.  */
+	_dl_initial_dtv = GET_DTV (tcbp);
+
+	/* And finally install it for the main thread.  If ld.so itself uses
+	   TLS we know the thread pointer was initialized earlier.  */
+	const char *lossage = TLS_INIT_TP (tcbp, USE___THREAD);
+	if (__builtin_expect (lossage != NULL, 0))
+		_dl_fatal_printf ("cannot set up thread-local storage: %s\n", lossage);
+	tls_init_tp_called = true;
+
+	return tcbp;
+}

Modified: branches/uClibc-nptl/ldso/ldso/ldso.c
===================================================================
--- branches/uClibc-nptl/ldso/ldso/ldso.c	2005-10-01 10:16:02 UTC (rev 11718)
+++ branches/uClibc-nptl/ldso/ldso/ldso.c	2005-10-01 15:33:59 UTC (rev 11719)
@@ -228,12 +228,22 @@
 					"app_tpnt->loadaddr=%x\n", app_tpnt->loadaddr);
 	}
 
+#if USE_TLS
 	/*
-	 * This adds another loop in the non-NPTL case, but we have to
-	 * catch the stupid user who tries to run a binary with TLS data
-	 * in it. For NPTL, we fill in the TLS data for the application
-	 * like we are supposed to.
+	 * Adjust the address of the TLS initialization image in case
+	 * the executable is actually an ET_DYN object.
 	 */
+	if (app_tpnt->l_tls_initimage != NULL)
+		app_tpnt->l_tls_initimage =
+			(char *) app_tpnt->l_tls_initimage + app_tpnt->loadaddr;
+#endif
+
+	/*
+	 * This adds another loop, but we have to catch the stupid user who
+	 * tries to run a binary with TLS data and the linker does not support
+	 * it. Otherwise, we fill in the TLS data for the application like we
+	 * are supposed to.
+	 */
 	{
 		int i;
 		ElfW(Phdr) *ppnt = (ElfW(Phdr) *) auxvt[AT_PHDR].a_un.a_val;
@@ -263,12 +273,24 @@
 				}
 				break;
 #else
-				_dl_dprintf(_dl_debug_file, "Program uses TLS, but ld-uClibc.so does not support it!\n");
-				_dl_exit(1);
+				_dl_fatal_printf("Program uses TLS, but ld-uClibc.so does not support it!\n");
 #endif
 			}
 	}
 
+#if USE_TLS
+	/* We do not initialize any of the TLS functionality unless any of the
+	 * initial modules uses TLS.  This makes dynamic loading of modules with
+	 * TLS impossible, but to support it requires either eagerly doing setup
+	 * now or lazily doing it later.  Doing it now makes us incompatible with
+	 * an old kernel that can't perform TLS_INIT_TP, even if no TLS is ever
+	 * used.  Trying to do it lazily is too hairy to try when there could be
+	 * multiple threads (from a non-TLS-using libpthread).  */
+	bool was_tls_init_tp_called = tls_init_tp_called;
+	if (tcbp == NULL)
+		tcbp = init_tls ();
+#endif
+
 	/*
 	 * This is used by gdb to locate the chain of shared libraries that are
 	 * currently loaded.
@@ -817,6 +839,36 @@
 	 _dl_malloc_function = (void* (*)(size_t)) (intptr_t) _dl_find_hash("malloc",
 			 _dl_symbol_tables, NULL, ELF_RTYPE_CLASS_PLT);
 
+#if USE_TLS
+	/* Find the real functions and make ldso functions use them from now on */
+	_dl_calloc_function = (void* (*)(size_t, size_t)) (intptr_t)
+		_dl_find_hash("calloc", _dl_symbol_tables, NULL, ELF_RTYPE_CLASS_PLT);
+	_dl_realloc_function = (void* (*)(void *, size_t)) (intptr_t)
+		_dl_find_hash("recalloc", _dl_symbol_tables, NULL, ELF_RTYPE_CLASS_PLT);
+	_dl_free_function = (void (*)(void *)) (intptr_t)
+		_dl_find_hash("free", _dl_symbol_tables, NULL, ELF_RTYPE_CLASS_PLT);
+	_dl_memalign_function = (void* (*)(size_t, size_t)) (intptr_t)
+		_dl_find_hash("memalign", _dl_symbol_tables, NULL, ELF_RTYPE_CLASS_PLT);
+
+	if (!was_tls_init_tp_called && _dl_tls_max_dtv_idx > 0)
+		++_dl_tls_generation;
+
+	/* Now that we have completed relocation, the initializer data
+	   for the TLS blocks has its final values and we can copy them
+	   into the main thread's TLS area, which we allocated above.  */
+	_dl_allocate_tls_init (tcbp);
+
+	/* And finally install it for the main thread.  If ld.so itself uses
+	   TLS we know the thread pointer was initialized earlier.  */
+	if (! tls_init_tp_called)
+	{
+		const char *lossage = TLS_INIT_TP (tcbp, USE___THREAD);
+		if (__builtin_expect (lossage != NULL, 0))
+			_dl_fatal_printf ("cannot set up thread-local storage: %s\n",
+				lossage);
+	}
+#endif
+
 	/* Notify the debugger that all objects are now mapped in.  */
 	_dl_debug_addr->r_state = RT_CONSISTENT;
 	_dl_debug_state();
@@ -902,5 +954,32 @@
 	return retval;
 }
 
+#if USE_TLS
+void * _dl_memalign (size_t __boundary, size_t __size)
+{
+	void *result;
+	int i = 0;
+	size_t delta;
+	size_t rounded = 0;
+
+	if (_dl_memalign_function) {
+#if 1
+		_dl_debug_early("Calling libc version\n");
+#endif
+		return (*_dl_memalign_function) (__boundary, __size);
+	}
+
+	_dl_debug_early("allocating aligned memory\n");
+	while (rounded < __boundary) {
+		rounded = (1 << i++);
+	}
+	delta = (((size_t) _dl_malloc_addr + __size) % rounded);
+	if ((result = _dl_malloc(rounded - delta)) == NULL)
+		return result;
+	result = _dl_malloc(__size);
+	return result;
+}
+#endif
+
 #include "dl-hash.c"
 #include "dl-elf.c"




More information about the uClibc-cvs mailing list