about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog36
-rw-r--r--elf/get-dynamic-info.h3
-rw-r--r--include/libc-symbols.h15
-rw-r--r--malloc/Makefile14
-rw-r--r--malloc/tst-interpose-aux-nothread.c20
-rw-r--r--malloc/tst-interpose-aux-thread.c20
-rw-r--r--malloc/tst-interpose-aux.c270
-rw-r--r--malloc/tst-interpose-aux.h30
-rw-r--r--sysdeps/mach/hurd/fork.c6
-rw-r--r--sysdeps/nptl/fork.c6
-rw-r--r--sysdeps/powerpc/powerpc64/power8/fpu/math_private.h49
-rw-r--r--test-skeleton.c2
12 files changed, 462 insertions, 9 deletions
diff --git a/ChangeLog b/ChangeLog
index cd6b5a92e9..d96bbeccb6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -206,6 +206,32 @@
 	(reg_AVX512BW): Likewise.
 	(reg_AVX512VL): Likewise.
 
+2016-08-26  Florian Weimer  <fweimer@redhat.com>
+
+	[BZ #20432]
+	Avoid strong references to malloc-internal symbols when linking
+	statically, to support statically interposed mallocs.
+	* include/libc-symbols.h (call_function_static_weak): New macro.
+	* malloc/Makefile (extra-tests-objs): Add tst-interpose-aux-nothread.o,
+	tst-interpose-aux-thread.o.
+	(test-extras): Add tst-interpose-aux-nothread,
+	tst-interpose-aux-thread.
+	* malloc/tst-interpose-aux-nothread.c: New file.
+	* malloc/tst-interpose-aux-thread.c: Likewise.
+	* malloc/tst-interpose-aux.c: Likewise.
+	* malloc/tst-interpose-aux.h: Likewise.
+	* sysdeps/mach/hurd/fork.c (__fork): Only call
+	__malloc_fork_lock_parent, __malloc_fork_unlock_parent,
+	__malloc_fork_unlock_child if defined.
+	* sysdeps/nptl/fork.c (__libc_fork): Likewise.
+
+2016-08-02  Florian Weimer  <fweimer@redhat.com>
+
+	[BZ #19469]
+	* malloc/Makefile (CPPFLAGS): Compile tests with
+	-DTEST_NO_MALLOPT.
+	* test-skeleton.c (main): Only call mallopt if !TEST_NO_MALLOPT.
+
 2017-01-05  Joseph Myers  <joseph@codesourcery.com>
 
 	[BZ #21026]
@@ -426,6 +452,11 @@
 	* sysdeps/unix/sysv/linux/spawni.c (__spawni): Correctly enable and disable
 	asynchronous cancellation.
 
+2016-10-18  Tulio Magno Quites Machado Filho  <tuliom@linux.vnet.ibm.com>
+
+	* sysdeps/powerpc/powerpc64/power8/fpu/math_private.h (GET_FLOAT_WORD):
+	(SET_FLOAT_WORD): New macros.
+
 2016-09-19  Tulio Magno Quites Machado Filho  <tuliom@linux.vnet.ibm.com>
 
 	[BZ #20615]
@@ -539,6 +570,11 @@
 	(ifunc_one): Add "12" to the clobber list. Use "i" constraint instead
 	of "X".
 
+2015-08-06  Tulio Magno Quites Machado Filho  <tuliom@linux.vnet.ibm.com>
+
+	* elf/get-dynamic-info.h (elf_get_dynamic_info): Remove assert
+	if DT_RUNPATH and DT_RPATH flags are found in ld.so.
+
 2016-08-04  Carlos O'Donell  <carlos@redhat.com>
 
 	* po/de.po: Update from Translation Project.
diff --git a/elf/get-dynamic-info.h b/elf/get-dynamic-info.h
index 4cf18a46cc..06519b9bc5 100644
--- a/elf/get-dynamic-info.h
+++ b/elf/get-dynamic-info.h
@@ -138,9 +138,6 @@ elf_get_dynamic_info (struct link_map *l, ElfW(Dyn) *temp)
 	  || (info[VERSYMIDX (DT_FLAGS_1)]->d_un.d_val & ~DF_1_NOW) == 0);
   assert (info[DT_FLAGS] == NULL
 	  || (info[DT_FLAGS]->d_un.d_val & ~DF_BIND_NOW) == 0);
-  /* Flags must not be set for ld.so.  */
-  assert (info[DT_RUNPATH] == NULL);
-  assert (info[DT_RPATH] == NULL);
 #else
   if (info[DT_FLAGS] != NULL)
     {
diff --git a/include/libc-symbols.h b/include/libc-symbols.h
index 4548e097dc..8245885c9f 100644
--- a/include/libc-symbols.h
+++ b/include/libc-symbols.h
@@ -121,6 +121,21 @@
 # define weak_extern(symbol) _weak_extern (weak symbol)
 # define _weak_extern(expr) _Pragma (#expr)
 
+/* In shared builds, the expression call_function_static_weak
+   (FUNCTION-SYMBOL, ARGUMENTS) invokes FUNCTION-SYMBOL (an
+   identifier) unconditionally, with the (potentially empty) argument
+   list ARGUMENTS.  In static builds, if FUNCTION-SYMBOL has a
+   definition, the function is invoked as before; if FUNCTION-SYMBOL
+   is NULL, no call is performed.  */
+# ifdef SHARED
+#  define call_function_static_weak(func, ...) func (__VA_ARGS__)
+# else	/* !SHARED */
+#  define call_function_static_weak(func, ...)		\
+  ({							\
+    extern __typeof__ (func) func weak_function;	\
+    (func != NULL ? func (__VA_ARGS__) : (void)0);	\
+  })
+# endif
 
 #else /* __ASSEMBLER__ */
 
diff --git a/malloc/Makefile b/malloc/Makefile
index fa1730ecb7..e683daab35 100644
--- a/malloc/Makefile
+++ b/malloc/Makefile
@@ -31,6 +31,7 @@ tests := mallocbug tst-malloc tst-valloc tst-calloc tst-obstack \
 	 tst-malloc-backtrace tst-malloc-thread-exit \
 	 tst-malloc-thread-fail tst-malloc-fork-deadlock \
 	 tst-mallocfork2
+
 test-srcs = tst-mtrace
 
 routines = malloc morecore mcheck mtrace obstack \
@@ -44,6 +45,15 @@ non-lib.a := libmcheck.a
 extra-libs = libmemusage
 extra-libs-others = $(extra-libs)
 
+# Helper objects for some tests.
+extra-tests-objs += \
+  tst-interpose-aux-nothread.o \
+  tst-interpose-aux-thread.o \
+
+test-extras = \
+  tst-interpose-aux-nothread \
+  tst-interpose-aux-thread \
+
 libmemusage-routines = memusage
 libmemusage-inhibit-o = $(filter-out .os,$(object-suffixes))
 
@@ -166,3 +176,7 @@ $(objpfx)libmemusage.so: $(libdl)
 
 # Extra dependencies
 $(foreach o,$(all-object-suffixes),$(objpfx)malloc$(o)): arena.c hooks.c
+
+# Compile the tests with a flag which suppresses the mallopt call in
+# the test skeleton.
+$(tests:%=$(objpfx)%.o): CPPFLAGS += -DTEST_NO_MALLOPT
diff --git a/malloc/tst-interpose-aux-nothread.c b/malloc/tst-interpose-aux-nothread.c
new file mode 100644
index 0000000000..0eae66fa6c
--- /dev/null
+++ b/malloc/tst-interpose-aux-nothread.c
@@ -0,0 +1,20 @@
+/* Interposed malloc, version without threading support.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C 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.
+
+   The GNU C 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 the GNU C Library; see the file COPYING.LIB.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#define INTERPOSE_THREADS 0
+#include "tst-interpose-aux.c"
diff --git a/malloc/tst-interpose-aux-thread.c b/malloc/tst-interpose-aux-thread.c
new file mode 100644
index 0000000000..354e4d8ed1
--- /dev/null
+++ b/malloc/tst-interpose-aux-thread.c
@@ -0,0 +1,20 @@
+/* Interposed malloc, version with threading support.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C 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.
+
+   The GNU C 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 the GNU C Library; see the file COPYING.LIB.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#define INTERPOSE_THREADS 1
+#include "tst-interpose-aux.c"
diff --git a/malloc/tst-interpose-aux.c b/malloc/tst-interpose-aux.c
new file mode 100644
index 0000000000..77866b2e5d
--- /dev/null
+++ b/malloc/tst-interpose-aux.c
@@ -0,0 +1,270 @@
+/* Minimal malloc implementation for interposition tests.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C 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.
+
+   The GNU C 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 the GNU C Library; see the file COPYING.LIB.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#include "tst-interpose-aux.h"
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#if INTERPOSE_THREADS
+#include <pthread.h>
+#endif
+
+/* Print the error message and terminate the process with status 1.  */
+__attribute__ ((noreturn))
+__attribute__ ((format (printf, 1, 2)))
+static void *
+fail (const char *format, ...)
+{
+  /* This assumes that vsnprintf will not call malloc.  It does not do
+     so for the format strings we use.  */
+  char message[4096];
+  va_list ap;
+  va_start (ap, format);
+  vsnprintf (message, sizeof (message), format, ap);
+  va_end (ap);
+
+  enum { count = 3 };
+  struct iovec iov[count];
+
+  iov[0].iov_base = (char *) "error: ";
+  iov[1].iov_base = (char *) message;
+  iov[2].iov_base = (char *) "\n";
+
+  for (int i = 0; i < count; ++i)
+    iov[i].iov_len = strlen (iov[i].iov_base);
+
+  int unused __attribute__ ((unused));
+  unused = writev (STDOUT_FILENO, iov, count);
+  _exit (1);
+}
+
+#if INTERPOSE_THREADS
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+static void
+lock (void)
+{
+#if INTERPOSE_THREADS
+  int ret = pthread_mutex_lock (&mutex);
+  if (ret != 0)
+    {
+      errno = ret;
+      fail ("pthread_mutex_lock: %m");
+    }
+#endif
+}
+
+static void
+unlock (void)
+{
+#if INTERPOSE_THREADS
+  int ret = pthread_mutex_unlock (&mutex);
+  if (ret != 0)
+    {
+      errno = ret;
+      fail ("pthread_mutex_unlock: %m");
+    }
+#endif
+}
+
+struct __attribute__ ((aligned (__alignof__ (max_align_t)))) allocation_header
+{
+  size_t allocation_index;
+  size_t allocation_size;
+};
+
+/* Array of known allocations, to track invalid frees.  */
+enum { max_allocations = 65536 };
+static struct allocation_header *allocations[max_allocations];
+static size_t allocation_index;
+static size_t deallocation_count;
+
+/* Sanity check for successful malloc interposition.  */
+__attribute__ ((destructor))
+static void
+check_for_allocations (void)
+{
+  if (allocation_index == 0)
+    {
+      /* Make sure that malloc is called at least once from libc.  */
+      void *volatile ptr = strdup ("ptr");
+      free (ptr);
+      /* Compiler barrier.  The strdup function calls malloc, which
+         updates allocation_index, but strdup is marked __THROW, so
+         the compiler could optimize away the reload.  */
+      __asm__ volatile ("" ::: "memory");
+      /* If the allocation count is still zero, it means we did not
+         interpose malloc successfully.  */
+      if (allocation_index == 0)
+        fail ("malloc does not seem to have been interposed");
+    }
+}
+
+static struct allocation_header *get_header (const char *op, void *ptr)
+{
+  struct allocation_header *header = ((struct allocation_header *) ptr) - 1;
+  if (header->allocation_index >= allocation_index)
+    fail ("%s: %p: invalid allocation index: %zu (not less than %zu)",
+          op, ptr, header->allocation_index, allocation_index);
+  if (allocations[header->allocation_index] != header)
+    fail ("%s: %p: allocation pointer does not point to header, but %p",
+          op, ptr, allocations[header->allocation_index]);
+  return header;
+}
+
+/* Internal helper functions.  Those must be called while the lock is
+   acquired.  */
+
+static void *
+malloc_internal (size_t size)
+{
+  if (allocation_index == max_allocations)
+    {
+      errno = ENOMEM;
+      return NULL;
+    }
+  size_t allocation_size = size + sizeof (struct allocation_header);
+  if (allocation_size < size)
+    {
+      errno = ENOMEM;
+      return NULL;
+    }
+
+  size_t index = allocation_index++;
+  void *result = mmap (NULL, allocation_size, PROT_READ | PROT_WRITE,
+                       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  if (result == MAP_FAILED)
+    return NULL;
+  allocations[index] = result;
+  *allocations[index] = (struct allocation_header)
+    {
+      .allocation_index = index,
+      .allocation_size = allocation_size
+    };
+  return allocations[index] + 1;
+}
+
+static void
+free_internal (const char *op, struct allocation_header *header)
+{
+  size_t index = header->allocation_index;
+  int result = mprotect (header, header->allocation_size, PROT_NONE);
+  if (result != 0)
+    fail ("%s: mprotect (%p, %zu): %m", op, header, header->allocation_size);
+  /* Catch double-free issues.  */
+  allocations[index] = NULL;
+  ++deallocation_count;
+}
+
+static void *
+realloc_internal (void *ptr, size_t new_size)
+{
+  struct allocation_header *header = get_header ("realloc", ptr);
+  size_t old_size = header->allocation_size - sizeof (struct allocation_header);
+  if (old_size >= new_size)
+    return ptr;
+
+  void *newptr = malloc_internal (new_size);
+  if (newptr == NULL)
+    return NULL;
+  memcpy (newptr, ptr, old_size);
+  free_internal ("realloc", header);
+  return newptr;
+}
+
+/* Public interfaces.  These functions must perform locking.  */
+
+size_t
+malloc_allocation_count (void)
+{
+  lock ();
+  size_t count = allocation_index;
+  unlock ();
+  return count;
+}
+
+size_t
+malloc_deallocation_count (void)
+{
+  lock ();
+  size_t count = deallocation_count;
+  unlock ();
+  return count;
+}
+void *
+malloc (size_t size)
+{
+  lock ();
+  void *result = malloc_internal (size);
+  unlock ();
+  return result;
+}
+
+void
+free (void *ptr)
+{
+  if (ptr == NULL)
+    return;
+  lock ();
+  struct allocation_header *header = get_header ("free", ptr);
+  free_internal ("free", header);
+  unlock ();
+}
+
+void *
+calloc (size_t a, size_t b)
+{
+  if (b > 0 && a > SIZE_MAX / b)
+    {
+      errno = ENOMEM;
+      return NULL;
+    }
+  lock ();
+  /* malloc_internal uses mmap, so the memory is zeroed.  */
+  void *result = malloc_internal (a * b);
+  unlock ();
+  return result;
+}
+
+void *
+realloc (void *ptr, size_t n)
+{
+  if (n ==0)
+    {
+      free (ptr);
+      return NULL;
+    }
+  else if (ptr == NULL)
+    return malloc (n);
+  else
+    {
+      lock ();
+      void *result = realloc_internal (ptr, n);
+      unlock ();
+      return result;
+    }
+}
diff --git a/malloc/tst-interpose-aux.h b/malloc/tst-interpose-aux.h
new file mode 100644
index 0000000000..2fb22d312a
--- /dev/null
+++ b/malloc/tst-interpose-aux.h
@@ -0,0 +1,30 @@
+/* Statistics interface for the minimal malloc implementation.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C 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.
+
+   The GNU C 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 the GNU C Library; see the file COPYING.LIB.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef TST_INTERPOSE_AUX_H
+#define TST_INTERPOSE_AUX_H
+
+#include <stddef.h>
+
+/* Return the number of allocations performed.  */
+size_t malloc_allocation_count (void);
+
+/* Return the number of deallocations performed.  */
+size_t malloc_deallocation_count (void);
+
+#endif /* TST_INTERPOSE_AUX_H */
diff --git a/sysdeps/mach/hurd/fork.c b/sysdeps/mach/hurd/fork.c
index 2e8b59e7c0..9973b1bb9e 100644
--- a/sysdeps/mach/hurd/fork.c
+++ b/sysdeps/mach/hurd/fork.c
@@ -112,7 +112,7 @@ __fork (void)
 	 handlers may use malloc, and the libio list lock has an
 	 indirect malloc dependency as well (via the getdelim
 	 function).  */
-      __malloc_fork_lock_parent ();
+      call_function_static_weak (__malloc_fork_lock_parent);
 
       /* Lock things that want to be locked before we fork.  */
       {
@@ -612,7 +612,7 @@ __fork (void)
 	}
 
       /* Release malloc locks.  */
-      __malloc_fork_unlock_parent ();
+      call_function_static_weak (__malloc_fork_unlock_parent);
 
       /* Run things that want to run in the parent to restore it to
 	 normality.  Usually prepare hooks and parent hooks are
@@ -666,7 +666,7 @@ __fork (void)
       __sigemptyset (&_hurdsig_traced);
 
       /* Release malloc locks.  */
-      __malloc_fork_unlock_child ();
+      call_function_static_weak (__malloc_fork_unlock_child);
 
       /* Run things that want to run in the child task to set up.  */
       RUN_HOOK (_hurd_fork_child_hook, ());
diff --git a/sysdeps/nptl/fork.c b/sysdeps/nptl/fork.c
index a5d1e86d71..4ff13726a3 100644
--- a/sysdeps/nptl/fork.c
+++ b/sysdeps/nptl/fork.c
@@ -128,7 +128,7 @@ __libc_fork (void)
 	 handlers may use malloc, and the libio list lock has an
 	 indirect malloc dependency as well (via the getdelim
 	 function).  */
-      __malloc_fork_lock_parent ();
+      call_function_static_weak (__malloc_fork_lock_parent);
     }
 
 #ifdef ARCH_FORK
@@ -177,7 +177,7 @@ __libc_fork (void)
       if (multiple_threads)
 	{
 	  /* Release malloc locks.  */
-	  __malloc_fork_unlock_child ();
+	  call_function_static_weak (__malloc_fork_unlock_child);
 
 	  /* Reset the file list.  These are recursive mutexes.  */
 	  fresetlockfiles ();
@@ -220,7 +220,7 @@ __libc_fork (void)
       if (multiple_threads)
 	{
 	  /* Release malloc locks, parent process variant.  */
-	  __malloc_fork_unlock_parent ();
+	  call_function_static_weak (__malloc_fork_unlock_parent);
 
 	  /* We execute this even if the 'fork' call failed.  */
 	  _IO_list_unlock ();
diff --git a/sysdeps/powerpc/powerpc64/power8/fpu/math_private.h b/sysdeps/powerpc/powerpc64/power8/fpu/math_private.h
new file mode 100644
index 0000000000..fe00c738ae
--- /dev/null
+++ b/sysdeps/powerpc/powerpc64/power8/fpu/math_private.h
@@ -0,0 +1,49 @@
+/* Private inline math functions for POWER8.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C 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.
+
+   The GNU C 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 the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* The compiler isn't extracting this without going through memory, so
+   we use some VSX scalar instructions to convert to the 32b format
+   and move to a GPR.  */
+#define GET_FLOAT_WORD(i,d)			\
+  do {						\
+      float tmpd = d;				\
+      double tmp;				\
+      long tmpi;				\
+      __asm__ ("xscvdpspn %x1, %x2\n\t"		\
+	       "mfvsrd %0, %x1\n\t"		\
+	       : "=wr" (tmpi),			\
+		 "=wa" (tmp)			\
+	       : "wa" (tmpd) );			\
+      i = tmpi >> 32;				\
+    } while(0)
+
+/* To ensure that we don't go through memory, we use some VSX scalar
+   instructions to move VSR and to convert to the 32b format.  */
+#define SET_FLOAT_WORD(d,i)		\
+  do {					\
+    long tmpi = i;			\
+    float tmpd;				\
+    tmpi = tmpi << 32;			\
+    __asm__ ("mtvsrd %x0, %1\n\t"	\
+	     "xscvspdpn %x0, %x0\n\t"	\
+	     : "=wa" (tmpd)		\
+	     : "wr" (tmpi) );		\
+    d = tmpd;				\
+  } while(0)
+
+#include_next <math_private.h>
diff --git a/test-skeleton.c b/test-skeleton.c
index d9bf989fa8..5a90c65826 100644
--- a/test-skeleton.c
+++ b/test-skeleton.c
@@ -346,8 +346,10 @@ main (int argc, char *argv[])
   unsigned int timeoutfactor = 1;
   pid_t termpid;
 
+#ifndef TEST_NO_MALLOPT
   /* Make uses of freed and uninitialized memory known.  */
   mallopt (M_PERTURB, 42);
+#endif
 
 #ifdef STDOUT_UNBUFFERED
   setbuf (stdout, NULL);