This is the mail archive of the glibc-cvs@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

GNU C Library master sources branch release/2.22/master updated. glibc-2.22-106-g017d97c


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU C Library master sources".

The branch, release/2.22/master has been updated
       via  017d97cd2ec0f626f8afb8c73ea3d612d8e844c3 (commit)
       via  436359fd41343c1db0616bd90e8a05bf188f237c (commit)
       via  407ec876262f0e6f55635ea0783f1f4a6c5d127f (commit)
       via  d2450a97c3df5527ea0fd49743bc354c979c185f (commit)
       via  c64d6bc3da8e61feab4117bcad53bd97e7a111cd (commit)
       via  d9c54360ca92a92ee8ee587f15a3cfc64fe4cb37 (commit)
       via  f87adbcaa47de2109e1c4561a2badf8aa82bc349 (commit)
       via  21c5d14bfb4e08bee86f94fd815535d3be2c3869 (commit)
       via  9d0aec236891576c7f12e935128364669b785233 (commit)
       via  89dc0372bb497b7d51bcf9999ce3f9684d450959 (commit)
       via  1be1845b280cfadff0cbd09170af554549849ffb (commit)
      from  771fb81f98a2be9e96f2a09056617ad93d64959f (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=017d97cd2ec0f626f8afb8c73ea3d612d8e844c3

commit 017d97cd2ec0f626f8afb8c73ea3d612d8e844c3
Author: Florian Weimer <fweimer@redhat.com>
Date:   Tue Feb 6 09:19:03 2018 +0100

    Record CVE-2018-6551 in NEWS and ChangeLog [BZ #22774]
    
    (cherry picked from commit 71aa429b029fdb6f9e65d44050388b51eca460d6)

diff --git a/ChangeLog b/ChangeLog
index e92c110..0b83802 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,7 +1,9 @@
 2018-01-18  Arjun Shankar  <arjun@redhat.com>
 
 	[BZ #22343]
+	[BZ #22774]
 	CVE-2018-6485
+	CVE-2018-6551
 	* malloc/malloc.c (checked_request2size): call REQUEST_OUT_OF_RANGE
 	after padding.
 	(_int_memalign): check for integer overflow before calling
diff --git a/NEWS b/NEWS
index ac8901e..db81e9e 100644
--- a/NEWS
+++ b/NEWS
@@ -91,6 +91,10 @@ Version 2.22.1
 * CVE-2018-6485: The posix_memalign and memalign functions, when called with
   an object size near the value of SIZE_MAX, would return a pointer to a
   buffer which is too small, instead of NULL.  Reported by Jakub Wilk.
+
+* CVE-2018-6551: The malloc function, when called with an object size near
+  the value of SIZE_MAX, would return a pointer to a buffer which is too
+  small, instead of NULL.
 
 Version 2.22
 

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=436359fd41343c1db0616bd90e8a05bf188f237c

commit 436359fd41343c1db0616bd90e8a05bf188f237c
Author: Florian Weimer <fweimer@redhat.com>
Date:   Thu Feb 1 15:00:44 2018 +0100

    Record CVE-2018-6485 in ChangeLog and NEWS [BZ #22343]
    
    (cherry picked from commit 4590634fd65162568b9f52fb4beb60aa25da37f2)

diff --git a/ChangeLog b/ChangeLog
index a01b406..e92c110 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,7 @@
 2018-01-18  Arjun Shankar  <arjun@redhat.com>
 
 	[BZ #22343]
+	CVE-2018-6485
 	* malloc/malloc.c (checked_request2size): call REQUEST_OUT_OF_RANGE
 	after padding.
 	(_int_memalign): check for integer overflow before calling
diff --git a/NEWS b/NEWS
index d7c016c..ac8901e 100644
--- a/NEWS
+++ b/NEWS
@@ -87,6 +87,10 @@ Version 2.22.1
   dynamic linker.  (The glibc project disputes that this is a security
   vulnerability; only trusted binaries must be examined using the ldd
   script.)
+
+* CVE-2018-6485: The posix_memalign and memalign functions, when called with
+  an object size near the value of SIZE_MAX, would return a pointer to a
+  buffer which is too small, instead of NULL.  Reported by Jakub Wilk.
 
 Version 2.22
 

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=407ec876262f0e6f55635ea0783f1f4a6c5d127f

commit 407ec876262f0e6f55635ea0783f1f4a6c5d127f
Author: Florian Weimer <fweimer@redhat.com>
Date:   Wed Aug 16 16:47:20 2017 +0200

    Add ChangeLog reference to bug 16750/CVE-2009-5064
    
    (cherry picked from commit 403143e1df85dadd374f304bd891be0cd7573e3b)

diff --git a/ChangeLog b/ChangeLog
index dfacabe..a01b406 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -48,6 +48,8 @@
 
 2017-08-16  Andreas Schwab  <schwab@suse.de>
 
+	[BZ #16750]
+	CVE-2009-5064
 	* elf/ldd.bash.in: Never run file directly.
 
 2016-10-14  Carlos Eduardo Seo  <cseo@linux.vnet.ibm.com>
diff --git a/NEWS b/NEWS
index ebebb40..d7c016c 100644
--- a/NEWS
+++ b/NEWS
@@ -81,6 +81,12 @@ Version 2.22.1
   to the allocation of too much memory.  (This is not a security bug per se,
   it is mentioned here only because of the CVE assignment.)  Reported by
   Qualys.
+
+* CVE-2009-5064: The ldd script would sometimes run the program under
+  examination directly, without preventing code execution through the
+  dynamic linker.  (The glibc project disputes that this is a security
+  vulnerability; only trusted binaries must be examined using the ldd
+  script.)
 
 Version 2.22
 

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=d2450a97c3df5527ea0fd49743bc354c979c185f

commit d2450a97c3df5527ea0fd49743bc354c979c185f
Author: Arjun Shankar <arjun.is@lostca.se>
Date:   Thu Jan 18 16:47:06 2018 +0000

    Fix integer overflows in internal memalign and malloc functions [BZ #22343]
    
    When posix_memalign is called with an alignment less than MALLOC_ALIGNMENT
    and a requested size close to SIZE_MAX, it falls back to malloc code
    (because the alignment of a block returned by malloc is sufficient to
    satisfy the call).  In this case, an integer overflow in _int_malloc leads
    to posix_memalign incorrectly returning successfully.
    
    Upon fixing this and writing a somewhat thorough regression test, it was
    discovered that when posix_memalign is called with an alignment larger than
    MALLOC_ALIGNMENT (so it uses _int_memalign instead) and a requested size
    close to SIZE_MAX, a different integer overflow in _int_memalign leads to
    posix_memalign incorrectly returning successfully.
    
    Both integer overflows affect other memory allocation functions that use
    _int_malloc (one affected malloc in x86) or _int_memalign as well.
    
    This commit fixes both integer overflows.  In addition to this, it adds a
    regression test to guard against false successful allocations by the
    following memory allocation functions when called with too-large allocation
    sizes and, where relevant, various valid alignments:
    malloc, realloc, calloc, reallocarray, memalign, posix_memalign,
    aligned_alloc, valloc, and pvalloc.
    
    (cherry picked from commit 8e448310d74b283c5cd02b9ed7fb997b47bf9b22)

diff --git a/ChangeLog b/ChangeLog
index abd3289..dfacabe 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2018-01-18  Arjun Shankar  <arjun@redhat.com>
+
+	[BZ #22343]
+	* malloc/malloc.c (checked_request2size): call REQUEST_OUT_OF_RANGE
+	after padding.
+	(_int_memalign): check for integer overflow before calling
+	_int_malloc.
+	* malloc/tst-malloc-too-large.c: New test.
+	* malloc/Makefile: Add tst-malloc-too-large.
+
 2017-11-02  Florian Weimer  <fweimer@redhat.com>
 
 	Add array_length and array_end macros.
diff --git a/malloc/Makefile b/malloc/Makefile
index aa0579c..8445b9e 100644
--- a/malloc/Makefile
+++ b/malloc/Makefile
@@ -28,7 +28,8 @@ tests := mallocbug tst-malloc tst-valloc tst-calloc tst-obstack \
 	 tst-mallocstate tst-mcheck tst-mallocfork tst-trim1 \
 	 tst-malloc-usable tst-realloc tst-posix_memalign \
 	 tst-pvalloc tst-memalign tst-mallopt tst-scratch_buffer \
-	 tst-malloc-backtrace tst-malloc-thread-exit
+	 tst-malloc-backtrace tst-malloc-thread-exit tst-malloc-too-large \
+
 test-srcs = tst-mtrace
 
 routines = malloc morecore mcheck mtrace obstack \
diff --git a/malloc/malloc.c b/malloc/malloc.c
index 5c84e62..a5334e5 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -1252,14 +1252,21 @@ nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    MINSIZE :                                                      \
    ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)
 
-/*  Same, except also perform argument check */
-
-#define checked_request2size(req, sz)                             \
-  if (REQUEST_OUT_OF_RANGE (req)) {					      \
-      __set_errno (ENOMEM);						      \
-      return 0;								      \
-    }									      \
-  (sz) = request2size (req);
+/* Same, except also perform an argument and result check.  First, we check
+   that the padding done by request2size didn't result in an integer
+   overflow.  Then we check (using REQUEST_OUT_OF_RANGE) that the resulting
+   size isn't so large that a later alignment would lead to another integer
+   overflow.  */
+#define checked_request2size(req, sz) \
+({				    \
+  (sz) = request2size (req);	    \
+  if (((sz) < (req))		    \
+      || REQUEST_OUT_OF_RANGE (sz)) \
+    {				    \
+      __set_errno (ENOMEM);	    \
+      return 0;			    \
+    }				    \
+})
 
 /*
    --------------- Physical chunk operations ---------------
@@ -4417,6 +4424,13 @@ _int_memalign (mstate av, size_t alignment, size_t bytes)
    */
 
 
+  /* Check for overflow.  */
+  if (nb > SIZE_MAX - alignment - MINSIZE)
+    {
+      __set_errno (ENOMEM);
+      return 0;
+    }
+
   /* Call malloc with worst case padding to hit alignment. */
 
   m = (char *) (_int_malloc (av, nb + alignment + MINSIZE));
diff --git a/malloc/tst-malloc-too-large.c b/malloc/tst-malloc-too-large.c
new file mode 100644
index 0000000..1f7bf29
--- /dev/null
+++ b/malloc/tst-malloc-too-large.c
@@ -0,0 +1,237 @@
+/* Test and verify that too-large memory allocations fail with ENOMEM.
+   Copyright (C) 2018 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/>.  */
+
+/* Bug 22375 reported a regression in malloc where if after malloc'ing then
+   free'ing a small block of memory, malloc is then called with a really
+   large size argument (close to SIZE_MAX): instead of returning NULL and
+   setting errno to ENOMEM, malloc incorrectly returns the previously
+   allocated block instead.  Bug 22343 reported a similar case where
+   posix_memalign incorrectly returns successfully when called with an with
+   a really large size argument.
+
+   Both of these were caused by integer overflows in the allocator when it
+   was trying to pad the requested size to allow for book-keeping or
+   alignment.  This test guards against such bugs by repeatedly allocating
+   and freeing small blocks of memory then trying to allocate various block
+   sizes larger than the memory bus width of 64-bit targets, or almost
+   as large as SIZE_MAX on 32-bit targets supported by glibc.  In each case,
+   it verifies that such impossibly large allocations correctly fail.  */
+
+
+#include <stdlib.h>
+#include <malloc.h>
+#include <errno.h>
+#include <stdint.h>
+#include <sys/resource.h>
+#include <libc-internal.h>
+#include <support/check.h>
+#include <unistd.h>
+#include <sys/param.h>
+
+
+/* This function prepares for each 'too-large memory allocation' test by
+   performing a small successful malloc/free and resetting errno prior to
+   the actual test.  */
+static void
+test_setup (void)
+{
+  void *volatile ptr = malloc (16);
+  TEST_VERIFY_EXIT (ptr != NULL);
+  free (ptr);
+  errno = 0;
+}
+
+
+/* This function tests each of:
+   - malloc (SIZE)
+   - realloc (PTR_FOR_REALLOC, SIZE)
+   - for various values of NMEMB:
+    - calloc (NMEMB, SIZE/NMEMB)
+    - calloc (SIZE/NMEMB, NMEMB)
+   and precedes each of these tests with a small malloc/free before it.  */
+static void
+test_large_allocations (size_t size)
+{
+  void * ptr_to_realloc;
+
+  test_setup ();
+  TEST_VERIFY (malloc (size) == NULL);
+  TEST_VERIFY (errno == ENOMEM);
+
+  ptr_to_realloc = malloc (16);
+  TEST_VERIFY_EXIT (ptr_to_realloc != NULL);
+  test_setup ();
+  TEST_VERIFY (realloc (ptr_to_realloc, size) == NULL);
+  TEST_VERIFY (errno == ENOMEM);
+  free (ptr_to_realloc);
+
+  for (size_t nmemb = 1; nmemb <= 8; nmemb *= 2)
+    if ((size % nmemb) == 0)
+      {
+        test_setup ();
+        TEST_VERIFY (calloc (nmemb, size / nmemb) == NULL);
+        TEST_VERIFY (errno == ENOMEM);
+
+        test_setup ();
+        TEST_VERIFY (calloc (size / nmemb, nmemb) == NULL);
+        TEST_VERIFY (errno == ENOMEM);
+      }
+    else
+      break;
+}
+
+
+static long pagesize;
+
+/* This function tests the following aligned memory allocation functions
+   using several valid alignments and precedes each allocation test with a
+   small malloc/free before it:
+   memalign, posix_memalign, aligned_alloc, valloc, pvalloc.  */
+static void
+test_large_aligned_allocations (size_t size)
+{
+  /* ptr stores the result of posix_memalign but since all those calls
+     should fail, posix_memalign should never change ptr.  We set it to
+     NULL here and later on we check that it remains NULL after each
+     posix_memalign call.  */
+  void * ptr = NULL;
+
+  size_t align;
+
+  /* All aligned memory allocation functions expect an alignment that is a
+     power of 2.  Given this, we test each of them with every valid
+     alignment from 1 thru PAGESIZE.  */
+  for (align = 1; align <= pagesize; align *= 2)
+    {
+      test_setup ();
+      TEST_VERIFY (memalign (align, size) == NULL);
+      TEST_VERIFY (errno == ENOMEM);
+
+      /* posix_memalign expects an alignment that is a power of 2 *and* a
+         multiple of sizeof (void *).  */
+      if ((align % sizeof (void *)) == 0)
+        {
+          test_setup ();
+          TEST_VERIFY (posix_memalign (&ptr, align, size) == ENOMEM);
+          TEST_VERIFY (ptr == NULL);
+        }
+
+      /* aligned_alloc expects a size that is a multiple of alignment.  */
+      if ((size % align) == 0)
+        {
+          test_setup ();
+          TEST_VERIFY (aligned_alloc (align, size) == NULL);
+          TEST_VERIFY (errno == ENOMEM);
+        }
+    }
+
+  /* Both valloc and pvalloc return page-aligned memory.  */
+
+  test_setup ();
+  TEST_VERIFY (valloc (size) == NULL);
+  TEST_VERIFY (errno == ENOMEM);
+
+  test_setup ();
+  TEST_VERIFY (pvalloc (size) == NULL);
+  TEST_VERIFY (errno == ENOMEM);
+}
+
+
+#define FOURTEEN_ON_BITS ((1UL << 14) - 1)
+#define FIFTY_ON_BITS ((1UL << 50) - 1)
+
+
+static int
+do_test (void)
+{
+
+#if __WORDSIZE >= 64
+
+  /* This test assumes that none of the supported targets have an address
+     bus wider than 50 bits, and that therefore allocations for sizes wider
+     than 50 bits will fail.  Here, we ensure that the assumption continues
+     to be true in the future when we might have address buses wider than 50
+     bits.  */
+
+  struct rlimit alloc_size_limit
+    = {
+        .rlim_cur = FIFTY_ON_BITS,
+        .rlim_max = FIFTY_ON_BITS
+      };
+
+  setrlimit (RLIMIT_AS, &alloc_size_limit);
+
+#endif /* __WORDSIZE >= 64 */
+
+  DIAG_PUSH_NEEDS_COMMENT;
+#if __GNUC_PREREQ (7, 0)
+  /* GCC 7 warns about too-large allocations; here we want to test
+     that they fail.  */
+  DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
+#endif
+
+  /* Aligned memory allocation functions need to be tested up to alignment
+     size equivalent to page size, which should be a power of 2.  */
+  pagesize = sysconf (_SC_PAGESIZE);
+  TEST_VERIFY_EXIT (powerof2 (pagesize));
+
+  /* Loop 1: Ensure that all allocations with SIZE close to SIZE_MAX, i.e.
+     in the range (SIZE_MAX - 2^14, SIZE_MAX], fail.
+
+     We can expect that this range of allocation sizes will always lead to
+     an allocation failure on both 64 and 32 bit targets, because:
+
+     1. no currently supported 64-bit target has an address bus wider than
+     50 bits -- and (2^64 - 2^14) is much wider than that;
+
+     2. on 32-bit targets, even though 2^32 is only 4 GB and potentially
+     addressable, glibc itself is more than 2^14 bytes in size, and
+     therefore once glibc is loaded, less than (2^32 - 2^14) bytes remain
+     available.  */
+
+  for (size_t i = 0; i <= FOURTEEN_ON_BITS; i++)
+    {
+      test_large_allocations (SIZE_MAX - i);
+      test_large_aligned_allocations (SIZE_MAX - i);
+    }
+
+#if __WORDSIZE >= 64
+  /* On 64-bit targets, we need to test a much wider range of too-large
+     sizes, so we test at intervals of (1 << 50) that allocation sizes
+     ranging from SIZE_MAX down to (1 << 50) fail:
+     The 14 MSBs are decremented starting from "all ON" going down to 1,
+     the 50 LSBs are "all ON" and then "all OFF" during every iteration.  */
+  for (size_t msbs = FOURTEEN_ON_BITS; msbs >= 1; msbs--)
+    {
+      size_t size = (msbs << 50) | FIFTY_ON_BITS;
+      test_large_allocations (size);
+      test_large_aligned_allocations (size);
+
+      size = msbs << 50;
+      test_large_allocations (size);
+      test_large_aligned_allocations (size);
+    }
+#endif /* __WORDSIZE >= 64 */
+
+  DIAG_POP_NEEDS_COMMENT;
+
+  return 0;
+}
+
+
+#include <support/test-driver.c>

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=c64d6bc3da8e61feab4117bcad53bd97e7a111cd

commit c64d6bc3da8e61feab4117bcad53bd97e7a111cd
Author: Florian Weimer <fweimer@redhat.com>
Date:   Thu Dec 14 15:18:38 2017 +0100

    elf: Compute correct array size in _dl_init_paths [BZ #22606]
    
    (cherry picked from commit 8a0b17e48b83e933960dfeb8fa08b259f03f310e)

diff --git a/ChangeLog b/ChangeLog
index 59d00db..abd3289 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -5,6 +5,14 @@
 
 2017-12-14  Florian Weimer  <fweimer@redhat.com>
 
+	[BZ #22606]
+	CVE-2017-1000408
+	* elf/dl-load.c (system_dirs): Update comment.
+	(nsystem_dirs_len): Use array_length.
+	(_dl_init_paths): Use nsystem_dirs_len to compute the array size.
+
+2017-12-14  Florian Weimer  <fweimer@redhat.com>
+
 	[BZ #22607]
 	CVE-2017-1000409
 	* elf/dl-load.c (_dl_init_paths): Compute number of components in
diff --git a/NEWS b/NEWS
index 7b2e078..ebebb40 100644
--- a/NEWS
+++ b/NEWS
@@ -76,6 +76,11 @@ Version 2.22.1
   vulnerability per se because no trust boundary is crossed if the fix for
   CVE-2017-1000366 has been applied, but it is mentioned here only because
   of the CVE assignment.)  Reported by Qualys.
+
+* CVE-2017-1000408: Incorrect array size computation in _dl_init_paths leads
+  to the allocation of too much memory.  (This is not a security bug per se,
+  it is mentioned here only because of the CVE assignment.)  Reported by
+  Qualys.
 
 Version 2.22
 
diff --git a/elf/dl-load.c b/elf/dl-load.c
index f22fd0d..32c3159 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -36,6 +36,7 @@
 #include <caller.h>
 #include <sysdep.h>
 #include <stap-probe.h>
+#include <array_length.h>
 
 #include <dl-dst.h>
 #include <dl-load.h>
@@ -102,7 +103,9 @@ static size_t ncapstr attribute_relro;
 static size_t max_capstrlen attribute_relro;
 
 
-/* Get the generated information about the trusted directories.  */
+/* Get the generated information about the trusted directories.  Use
+   an array of concatenated strings to avoid relocations.  See
+   gen-trusted-dirs.awk.  */
 #include "trusted-dirs.h"
 
 static const char system_dirs[] = SYSTEM_DIRS;
@@ -110,9 +113,7 @@ static const size_t system_dirs_len[] =
 {
   SYSTEM_DIRS_LEN
 };
-#define nsystem_dirs_len \
-  (sizeof (system_dirs_len) / sizeof (system_dirs_len[0]))
-
+#define nsystem_dirs_len array_length (system_dirs_len)
 
 static bool
 is_trusted_path (const char *path, size_t len)
@@ -704,9 +705,8 @@ _dl_init_paths (const char *llp)
 		 + ncapstr * sizeof (enum r_dir_status))
 		/ sizeof (struct r_search_path_elem));
 
-  rtld_search_dirs.dirs[0] = (struct r_search_path_elem *)
-    malloc ((sizeof (system_dirs) / sizeof (system_dirs[0]))
-	    * round_size * sizeof (struct r_search_path_elem));
+  rtld_search_dirs.dirs[0] = malloc (nsystem_dirs_len * round_size
+				     * sizeof (*rtld_search_dirs.dirs[0]));
   if (rtld_search_dirs.dirs[0] == NULL)
     {
       errstring = N_("cannot create cache for search path");

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=d9c54360ca92a92ee8ee587f15a3cfc64fe4cb37

commit d9c54360ca92a92ee8ee587f15a3cfc64fe4cb37
Author: Florian Weimer <fweimer@redhat.com>
Date:   Thu Nov 2 12:14:01 2017 +0100

    <array_length.h>: New array_length and array_end macros
    
    (cherry picked from commit c94a5688fb1228a862b2d4a3f1239cdc0e3349e5)

diff --git a/ChangeLog b/ChangeLog
index 3c8fd97..59d00db 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2017-11-02  Florian Weimer  <fweimer@redhat.com>
+
+	Add array_length and array_end macros.
+	* include/array_length.h: New file.
+
 2017-12-14  Florian Weimer  <fweimer@redhat.com>
 
 	[BZ #22607]
diff --git a/include/array_length.h b/include/array_length.h
new file mode 100644
index 0000000..cb4a8b2
--- /dev/null
+++ b/include/array_length.h
@@ -0,0 +1,36 @@
+/* The array_length and array_end macros.
+   Copyright (C) 2017 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/>.  */
+
+#ifndef _ARRAY_LENGTH_H
+#define _ARRAY_LENGTH_H
+
+/* array_length (VAR) is the number of elements in the array VAR.  VAR
+   must evaluate to an array, not a pointer.  */
+#define array_length(var)                                               \
+  __extension__ ({                                                      \
+    _Static_assert (!__builtin_types_compatible_p                       \
+                    (__typeof (var), __typeof (&(var)[0])),             \
+                    "argument must be an array");                       \
+    sizeof (var) / sizeof ((var)[0]);                                   \
+  })
+
+/* array_end (VAR) is a pointer one past the end of the array VAR.
+   VAR must evaluate to an array, not a pointer.  */
+#define array_end(var) (&(var)[array_length (var)])
+
+#endif /* _ARRAY_LENGTH_H */

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=f87adbcaa47de2109e1c4561a2badf8aa82bc349

commit f87adbcaa47de2109e1c4561a2badf8aa82bc349
Author: Florian Weimer <fweimer@redhat.com>
Date:   Thu Dec 14 15:05:57 2017 +0100

    elf: Count components of the expanded path in _dl_init_path [BZ #22607]
    
    (cherry picked from commit 3ff3dfa5af313a6ea33f3393916f30eece4f0171)

diff --git a/ChangeLog b/ChangeLog
index 066b0d2..3c8fd97 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2017-12-14  Florian Weimer  <fweimer@redhat.com>
+
+	[BZ #22607]
+	CVE-2017-1000409
+	* elf/dl-load.c (_dl_init_paths): Compute number of components in
+	the expanded path string.
+
 2017-12-30  Aurelien Jarno  <aurelien@aurel32.net>
 	    Dmitry V. Levin  <ldv@altlinux.org>
 
diff --git a/NEWS b/NEWS
index 3a33aee..7b2e078 100644
--- a/NEWS
+++ b/NEWS
@@ -70,6 +70,12 @@ Version 2.22.1
 * CVE-2017-16997: Incorrect handling of RPATH or RUNPATH containing $ORIGIN
   for AT_SECURE or SUID binaries could be used to load libraries from the
   current directory.
+
+* CVE-2017-1000409: Buffer overflow in _dl_init_paths due to miscomputation
+  of the number of search path components.  (This is not a security
+  vulnerability per se because no trust boundary is crossed if the fix for
+  CVE-2017-1000366 has been applied, but it is mentioned here only because
+  of the CVE assignment.)  Reported by Qualys.
 
 Version 2.22
 
diff --git a/elf/dl-load.c b/elf/dl-load.c
index d0ac65e..f22fd0d 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -792,8 +792,6 @@ _dl_init_paths (const char *llp)
 
   if (llp != NULL && *llp != '\0')
     {
-      size_t nllp;
-      const char *cp = llp;
       char *llp_tmp;
 
 #ifdef SHARED
@@ -816,13 +814,10 @@ _dl_init_paths (const char *llp)
 
       /* Decompose the LD_LIBRARY_PATH contents.  First determine how many
 	 elements it has.  */
-      nllp = 1;
-      while (*cp)
-	{
-	  if (*cp == ':' || *cp == ';')
-	    ++nllp;
-	  ++cp;
-	}
+      size_t nllp = 1;
+      for (const char *cp = llp_tmp; *cp != '\0'; ++cp)
+	if (*cp == ':' || *cp == ';')
+	  ++nllp;
 
       env_path_list.dirs = (struct r_search_path_elem **)
 	malloc ((nllp + 1) * sizeof (struct r_search_path_elem *));

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=21c5d14bfb4e08bee86f94fd815535d3be2c3869

commit 21c5d14bfb4e08bee86f94fd815535d3be2c3869
Author: Aurelien Jarno <aurelien@aurel32.net>
Date:   Sat Dec 30 10:54:23 2017 +0100

    elf: Check for empty tokens before dynamic string token expansion [BZ #22625]
    
    The fillin_rpath function in elf/dl-load.c loops over each RPATH or
    RUNPATH tokens and interprets empty tokens as the current directory
    ("./"). In practice the check for empty token is done *after* the
    dynamic string token expansion. The expansion process can return an
    empty string for the $ORIGIN token if __libc_enable_secure is set
    or if the path of the binary can not be determined (/proc not mounted).
    
    Fix that by moving the check for empty tokens before the dynamic string
    token expansion. In addition, check for NULL pointer or empty strings
    return by expand_dynamic_string_token.
    
    The above changes highlighted a bug in decompose_rpath, an empty array
    is represented by the first element being NULL at the fillin_rpath
    level, but by using a -1 pointer in decompose_rpath and other functions.
    
    Changelog:
    	[BZ #22625]
    	* elf/dl-load.c (fillin_rpath): Check for empty tokens before dynamic
    	string token expansion. Check for NULL pointer or empty string possibly
    	returned by expand_dynamic_string_token.
    	(decompose_rpath): Check for empty path after dynamic string
    	token expansion.
    
    (cherry picked from commit 3e3c904daef69b8bf7d5cc07f793c9f07c3553ef)

diff --git a/ChangeLog b/ChangeLog
index f7e2089..066b0d2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2017-12-30  Aurelien Jarno  <aurelien@aurel32.net>
+	    Dmitry V. Levin  <ldv@altlinux.org>
+
+	[BZ #22625]
+	* elf/dl-load.c (fillin_rpath): Check for empty tokens before dynamic
+	string token expansion. Check for NULL pointer or empty string possibly
+	returned by expand_dynamic_string_token.
+	(decompose_rpath): Check for empty path after dynamic string
+	token expansion.
+
 2017-02-27  Florian Weimer  <fweimer@redhat.com>
 
 	[BZ #21115]
diff --git a/NEWS b/NEWS
index fc6ee52..3a33aee 100644
--- a/NEWS
+++ b/NEWS
@@ -66,6 +66,10 @@ Version 2.22.1
 
 * A use-after-free vulnerability in clntudp_call in the Sun RPC system has been
   fixed (CVE-2017-12133).
+
+* CVE-2017-16997: Incorrect handling of RPATH or RUNPATH containing $ORIGIN
+  for AT_SECURE or SUID binaries could be used to load libraries from the
+  current directory.
 
 Version 2.22
 
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 7e6f4c5..d0ac65e 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -432,31 +432,40 @@ fillin_rpath (char *rpath, struct r_search_path_elem **result, const char *sep,
 {
   char *cp;
   size_t nelems = 0;
-  char *to_free;
 
   while ((cp = __strsep (&rpath, sep)) != NULL)
     {
       struct r_search_path_elem *dirp;
+      char *to_free = NULL;
+      size_t len = 0;
 
-      to_free = cp = expand_dynamic_string_token (l, cp, 1);
+      /* `strsep' can pass an empty string.  */
+      if (*cp != '\0')
+	{
+	  to_free = cp = expand_dynamic_string_token (l, cp, 1);
 
-      size_t len = strlen (cp);
+	  /* expand_dynamic_string_token can return NULL in case of empty
+	     path or memory allocation failure.  */
+	  if (cp == NULL)
+	    continue;
 
-      /* `strsep' can pass an empty string.  This has to be
-	 interpreted as `use the current directory'. */
-      if (len == 0)
-	{
-	  static const char curwd[] = "./";
-	  cp = (char *) curwd;
-	}
+	  /* Compute the length after dynamic string token expansion and
+	     ignore empty paths.  */
+	  len = strlen (cp);
+	  if (len == 0)
+	    {
+	      free (to_free);
+	      continue;
+	    }
 
-      /* Remove trailing slashes (except for "/").  */
-      while (len > 1 && cp[len - 1] == '/')
-	--len;
+	  /* Remove trailing slashes (except for "/").  */
+	  while (len > 1 && cp[len - 1] == '/')
+	    --len;
 
-      /* Now add one if there is none so far.  */
-      if (len > 0 && cp[len - 1] != '/')
-	cp[len++] = '/';
+	  /* Now add one if there is none so far.  */
+	  if (len > 0 && cp[len - 1] != '/')
+	    cp[len++] = '/';
+	}
 
       /* Make sure we don't use untrusted directories if we run SUID.  */
       if (__glibc_unlikely (check_trusted) && !is_trusted_path (cp, len))
@@ -620,6 +629,14 @@ decompose_rpath (struct r_search_path_struct *sps,
      necessary.  */
   free (copy);
 
+  /* There is no path after expansion.  */
+  if (result[0] == NULL)
+    {
+      free (result);
+      sps->dirs = (struct r_search_path_elem **) -1;
+      return false;
+    }
+
   sps->dirs = result;
   /* The caller will change this value if we haven't used a real malloc.  */
   sps->malloced = 1;

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=9d0aec236891576c7f12e935128364669b785233

commit 9d0aec236891576c7f12e935128364669b785233
Author: Florian Weimer <fweimer@redhat.com>
Date:   Thu Apr 13 13:09:38 2017 +0200

    sunrpc: Avoid use-after-free read access in clntudp_call [BZ #21115]
    
    After commit bc779a1a5b3035133024b21e2f339fe4219fb11c
    (CVE-2016-4429: sunrpc: Do not use alloca in clntudp_call
    [BZ #20112]), ancillary data is stored on the heap,
    but it is accessed after it has been freed.
    
    The test case must be run under a heap debugger such as valgrind
    to observe the invalid access.  A malloc implementation which
    immediately calls munmap on free would catch this bug as well.
    
    (cherry picked from commit d42eed4a044e5e10dfb885cf9891c2518a72a491)

diff --git a/ChangeLog b/ChangeLog
index 643aeb9..f7e2089 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2017-02-27  Florian Weimer  <fweimer@redhat.com>
+
+	[BZ #21115]
+	* sunrpc/clnt_udp.c (clntudp_call): Free ancillary data later.
+	* sunrpc/Makefile (tests): Add tst-udp-error.
+	(tst-udp-error): Link against libc.so explicitly.
+	* sunrpc/tst-udp-error: New file.
+
 2017-08-16  Andreas Schwab  <schwab@suse.de>
 
 	* elf/ldd.bash.in: Never run file directly.
diff --git a/NEWS b/NEWS
index a19f656..fc6ee52 100644
--- a/NEWS
+++ b/NEWS
@@ -63,6 +63,9 @@ Version 2.22.1
 * The Sun RPC UDP client could exhaust all available stack space when
   flooded with crafted ICMP and UDP messages.  Reported by Aldy Hernandez'
   alloca plugin for GCC.  (CVE-2016-4429)
+
+* A use-after-free vulnerability in clntudp_call in the Sun RPC system has been
+  fixed (CVE-2017-12133).
 
 Version 2.22
 
diff --git a/sunrpc/Makefile b/sunrpc/Makefile
index 60caa0a..4f74f27 100644
--- a/sunrpc/Makefile
+++ b/sunrpc/Makefile
@@ -96,7 +96,7 @@ rpcgen-objs = rpc_main.o rpc_hout.o rpc_cout.o rpc_parse.o \
 extra-objs = $(rpcgen-objs) $(addprefix cross-,$(rpcgen-objs))
 others += rpcgen
 
-tests = tst-xdrmem tst-xdrmem2 test-rpcent
+tests = tst-xdrmem tst-xdrmem2 test-rpcent tst-udp-error
 xtests := tst-getmyaddr
 
 ifeq ($(have-thread-library),yes)
@@ -153,6 +153,7 @@ BUILD_CPPFLAGS += $(sunrpc-CPPFLAGS)
 $(objpfx)tst-getmyaddr: $(common-objpfx)linkobj/libc.so
 $(objpfx)tst-xdrmem: $(common-objpfx)linkobj/libc.so
 $(objpfx)tst-xdrmem2: $(common-objpfx)linkobj/libc.so
+$(objpfx)tst-udp-error: $(common-objpfx)linkobj/libc.so
 
 $(objpfx)rpcgen: $(addprefix $(objpfx),$(rpcgen-objs))
 
diff --git a/sunrpc/clnt_udp.c b/sunrpc/clnt_udp.c
index c818caf..30613e3 100644
--- a/sunrpc/clnt_udp.c
+++ b/sunrpc/clnt_udp.c
@@ -453,9 +453,9 @@ send_again:
 		 cmsg = CMSG_NXTHDR (&msg, cmsg))
 	      if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR)
 		{
-		  free (cbuf);
 		  e = (struct sock_extended_err *) CMSG_DATA(cmsg);
 		  cu->cu_error.re_errno = e->ee_errno;
+		  free (cbuf);
 		  return (cu->cu_error.re_status = RPC_CANTRECV);
 		}
 	  free (cbuf);
diff --git a/sunrpc/tst-udp-error.c b/sunrpc/tst-udp-error.c
new file mode 100644
index 0000000..1efc02f
--- /dev/null
+++ b/sunrpc/tst-udp-error.c
@@ -0,0 +1,62 @@
+/* Check for use-after-free in clntudp_call (bug 21115).
+   Copyright (C) 2017 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/>.  */
+
+#include <netinet/in.h>
+#include <rpc/clnt.h>
+#include <rpc/svc.h>
+#include <support/check.h>
+#include <support/namespace.h>
+#include <support/xsocket.h>
+#include <unistd.h>
+
+static int
+do_test (void)
+{
+  support_become_root ();
+  support_enter_network_namespace ();
+
+  /* Obtain a likely-unused port number.  */
+  struct sockaddr_in sin =
+    {
+      .sin_family = AF_INET,
+      .sin_addr.s_addr = htonl (INADDR_LOOPBACK),
+    };
+  {
+    int fd = xsocket (AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+    xbind (fd, (struct sockaddr *) &sin, sizeof (sin));
+    socklen_t sinlen = sizeof (sin);
+    xgetsockname (fd, (struct sockaddr *) &sin, &sinlen);
+    /* Close the socket, so that we will receive an error below.  */
+    close (fd);
+  }
+
+  int sock = RPC_ANYSOCK;
+  CLIENT *clnt = clntudp_create
+    (&sin, 1, 2, (struct timeval) { 1, 0 }, &sock);
+  TEST_VERIFY_EXIT (clnt != NULL);
+  TEST_VERIFY (clnt_call (clnt, 3,
+                          (xdrproc_t) xdr_void, NULL,
+                          (xdrproc_t) xdr_void, NULL,
+                          ((struct timeval) { 3, 0 }))
+               == RPC_CANTRECV);
+  clnt_destroy (clnt);
+
+  return 0;
+}
+
+#include <support/test-driver.c>

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=89dc0372bb497b7d51bcf9999ce3f9684d450959

commit 89dc0372bb497b7d51bcf9999ce3f9684d450959
Author: Andreas Schwab <schwab@suse.de>
Date:   Wed Aug 16 15:59:55 2017 +0200

    ldd: never run file directly
    
    (cherry picked from commit eedca9772e99c72ab4c3c34e43cc764250aa3e3c)

diff --git a/ChangeLog b/ChangeLog
index c5389fa..643aeb9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2017-08-16  Andreas Schwab  <schwab@suse.de>
+
+	* elf/ldd.bash.in: Never run file directly.
+
 2016-10-14  Carlos Eduardo Seo  <cseo@linux.vnet.ibm.com>
 
 	* sysdeps/powerpc/bits/hwcap.h: Add PPC_FEATURE2_HTM_NOSC.
diff --git a/elf/ldd.bash.in b/elf/ldd.bash.in
index 7fb6c15..9cb1636 100644
--- a/elf/ldd.bash.in
+++ b/elf/ldd.bash.in
@@ -164,18 +164,6 @@ warning: you do not have execution permission for" "\`$file'" >&2
       fi
     done
     case $ret in
-    0)
-      # If the program exits with exit code 5, it means the process has been
-      # invoked with __libc_enable_secure.  Fall back to running it through
-      # the dynamic linker.
-      try_trace "$file"
-      rc=$?
-      if [ $rc = 5 ]; then
-	try_trace "$RTLD" "$file"
-	rc=$?
-      fi
-      [ $rc = 0 ] || result=1
-      ;;
     1)
       # This can be a non-ELF binary or no binary at all.
       nonelf "$file" || {
@@ -183,7 +171,7 @@ warning: you do not have execution permission for" "\`$file'" >&2
 	result=1
       }
       ;;
-    2)
+    0|2)
       try_trace "$RTLD" "$file" || result=1
       ;;
     *)

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=1be1845b280cfadff0cbd09170af554549849ffb

commit 1be1845b280cfadff0cbd09170af554549849ffb
Author: Arjun Shankar <arjun.is@lostca.se>
Date:   Wed Jun 7 11:46:24 2017 +0200

    Synchronize support/ infrastructure with master
    
    This commit updates the support/ subdirectory to
    commit 2714c5f3c95f90977167c1d21326d907fb76b419
    on the master branch and modifies Makeconfig,
    Rules, and extra-lib.mk accordingly.
    
    (cherry picked from commit 4c5785aa129a5d195fc1cd5c7fcd6f62c2b0ff0c)
    
    Reviewed-by: Carlos O'Donell <carlos@redhat.com>

diff --git a/Makeconfig b/Makeconfig
index f136b88..b5d100b 100644
--- a/Makeconfig
+++ b/Makeconfig
@@ -395,6 +395,9 @@ ifndef after-link
 after-link =
 endif
 
+# Additional libraries to link into every test.
+link-extra-libs-tests = $(libsupport)
+
 # Command for linking PIE programs with the C library.
 ifndef +link-pie
 +link-pie-before-libc = $(CC) -pie -Wl,-O1 -nostdlib -nostartfiles -o $@ \
@@ -504,7 +507,7 @@ link-libc = $(link-libc-rpath-link) $(link-libc-before-gnulib) $(gnulib)
 link-libc-tests = $(link-libc-tests-rpath-link) \
 		  $(link-libc-before-gnulib) $(gnulib-tests)
 # This is how to find at build-time things that will be installed there.
-rpath-dirs = math elf dlfcn nss nis rt resolv crypt mathvec
+rpath-dirs = math elf dlfcn nss nis rt resolv crypt mathvec support
 rpath-link = \
 $(common-objdir):$(subst $(empty) ,:,$(patsubst ../$(subdir),.,$(rpath-dirs:%=$(common-objpfx)%)))
 else
@@ -851,7 +854,7 @@ libio-include = -I$(..)libio
 # List of non-library modules that we build.
 built-modules = iconvprogs iconvdata ldconfig lddlibc4 libmemusage \
 		libSegFault libpcprofile librpcsvc locale-programs \
-		memusagestat nonlib nscd extramodules libnldbl
+		memusagestat nonlib nscd extramodules libnldbl libsupport
 
 in-module = $(subst -,_,$(firstword $(libof-$(basename $(@F))) \
 				    $(libof-$(<F)) \
@@ -1089,6 +1092,12 @@ libm = $(common-objpfx)math/libm.a
 libmvec = $(common-objpfx)mathvec/libmvec.a
 endif
 
+ifeq ($(build-shared),yes)
+libsupport = $(common-objpfx)support/libsupport_nonshared.a
+else
+libsupport = $(common-objpfx)support/libsupport.a
+endif
+
 # These are the subdirectories containing the library source.  The order
 # is more or less arbitrary.  The sorting step will take care of the
 # dependencies.
@@ -1096,7 +1105,7 @@ all-subdirs = csu assert ctype locale intl catgets math setjmp signal	    \
 	      stdlib stdio-common libio malloc string wcsmbs time dirent    \
 	      grp pwd posix io termios resource misc socket sysvipc gmon    \
 	      gnulib iconv iconvdata wctype manual shadow gshadow po argp   \
-	      crypt localedata timezone rt conform debug mathvec	    \
+	      crypt localedata timezone rt conform debug mathvec support    \
 	      $(add-on-subdirs) dlfcn elf
 
 ifndef avoid-generated
diff --git a/Rules b/Rules
index e237d03..7c5777c 100644
--- a/Rules
+++ b/Rules
@@ -149,6 +149,7 @@ endif
 
 ifneq "$(strip $(binaries-shared-tests))" ""
 $(addprefix $(objpfx),$(binaries-shared-tests)): %: %.o \
+  $(link-extra-libs-tests) \
   $(sort $(filter $(common-objpfx)lib%,$(link-libc))) \
   $(addprefix $(csu-objpfx),start.o) $(+preinit) $(+postinit)
 	$(+link-tests)
@@ -156,6 +157,7 @@ endif
 
 ifneq "$(strip $(binaries-pie-tests))" ""
 $(addprefix $(objpfx),$(binaries-pie-tests)): %: %.o \
+  $(link-extra-libs-tests) \
   $(sort $(filter $(common-objpfx)lib%,$(link-libc))) \
   $(addprefix $(csu-objpfx),start.o) $(+preinit) $(+postinit)
 	$(+link-pie-tests)
@@ -177,6 +179,7 @@ endif
 
 ifneq "$(strip $(binaries-static-tests))" ""
 $(addprefix $(objpfx),$(binaries-static-tests)): %: %.o \
+  $(link-extra-libs-tests) \
   $(sort $(filter $(common-objpfx)lib%,$(link-libc-static-tests))) \
   $(addprefix $(csu-objpfx),start.o) $(+preinit) $(+postinit)
 	$(+link-static-tests)
diff --git a/extra-lib.mk b/extra-lib.mk
index b10748d..2552049 100644
--- a/extra-lib.mk
+++ b/extra-lib.mk
@@ -5,6 +5,9 @@
 # The variable $($(lib)-routines) defines the list of modules
 # to be included in that library.  A sysdep Makefile can add to
 # $(lib)-sysdep_routines to include additional modules.
+#
+# Libraries listed in $(extra-libs-noinstall) are built, but not
+# installed.
 
 lib := $(firstword $(extra-libs-left))
 extra-libs-left := $(filter-out $(lib),$(extra-libs-left))
@@ -28,7 +31,9 @@ extra-objs := $(extra-objs)
 all-$(lib)-routines := $($(lib)-routines) $($(lib)-sysdep_routines)
 
 # Add each flavor of library to the lists of things to build and install.
+ifeq (,$(filter $(lib), $(extra-libs-noinstall)))
 install-lib += $(foreach o,$(object-suffixes-$(lib)),$(lib:lib%=$(libtype$o)))
+endif
 extra-objs += $(foreach o,$(filter-out .os .oS,$(object-suffixes-$(lib))),\
 			$(patsubst %,%$o,$(filter-out \
 					   $($(lib)-shared-only-routines),\
diff --git a/scripts/backport-support.sh b/scripts/backport-support.sh
new file mode 100644
index 0000000..2ece7ce
--- /dev/null
+++ b/scripts/backport-support.sh
@@ -0,0 +1,110 @@
+#!/bin/bash
+# Create a patch which backports the support/ subdirectory.
+# Copyright (C) 2017 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/>.
+
+# This script does not backport the Makefile tweaks outside the
+# support/ directory (which need to be backported separately), or the
+# changes to test-skeleton.c (which should not be backported).
+
+set -e
+
+export LC_ALL=C
+export GIT_CONFIG=/dev/null
+export GTT_CONFIG_NOSYSTEM=0
+export GIT_PAGER=
+
+usage () {
+    cat >&2 <<EOF
+usage: $0 {patch|commit}
+EOF
+    exit 1
+}
+
+if test $# -ne 1 ; then
+    usage
+fi
+
+command="$1"
+
+case "$command" in
+    patch|commit)
+    ;;
+    *)
+	usage
+	;;
+esac
+
+# The upstream branch to work on.
+branch=origin/master
+
+# The commit which added the support/ directory.
+initial_commit=c23de0aacbeaa7a091609b35764bed931475a16d
+
+# We backport the support directory and this script.  Directories need
+# to end in a /.
+patch_targets="support/ scripts/backport-support.sh"
+
+latest_commit="$(git log --max-count=1 --pretty=format:%H "$branch" -- \
+  $patch_targets)"
+
+# Simplify the branch name somewhat for reporting.
+branch_name="$(echo "$branch" | sed s,^origin/,,)"
+
+command_patch () {
+    cat <<EOF
+This patch creates the contents of the support/ directory up to this
+upstream commit on the $branch_name branch:
+
+EOF
+    git log --max-count=1 "$latest_commit"
+    echo
+    git diff "$initial_commit"^.."$latest_commit" $patch_targets
+    echo "# Before applying the patch, run this command:" >&2
+    echo "# rm -rf $patch_targets" >&2
+}
+
+command_commit () {
+    git status --porcelain | while read line ; do
+	echo "error: working copy is not clean, cannot commit" >&2
+	exit 1
+    done
+    for path in $patch_targets; do
+	echo "# Processing $path" >&2
+	case "$path" in
+	    [a-zA-Z0-9]*/)
+		# Directory.
+		git rm --cached --ignore-unmatch -r "$path"
+		rm -rf "$path"
+		git read-tree --prefix="$path" "$latest_commit":"$path"
+		git checkout "$path"
+		;;
+	    *)
+		# File.
+		git show "$latest_commit":"$path" > "$path"
+		git add "$path"
+	esac
+    done
+    git commit -m "Synchronize support/ infrastructure with $branch_name
+
+This commit updates the support/ subdirectory to
+commit $latest_commit
+on the $branch_name branch.
+"
+}
+
+command_$command
diff --git a/support/Makefile b/support/Makefile
new file mode 100644
index 0000000..20b0343
--- /dev/null
+++ b/support/Makefile
@@ -0,0 +1,146 @@
+# Makefile for support library, used only at build and test time
+# Copyright (C) 2016-2017 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/>.
+
+subdir := support
+
+include ../Makeconfig
+
+extra-libs := libsupport
+extra-libs-others = $(extra-libs)
+extra-libs-noinstall := $(extra-libs)
+
+libsupport-routines = \
+  check \
+  check_addrinfo \
+  check_dns_packet \
+  check_hostent \
+  check_netent \
+  delayed_exit \
+  ignore_stderr \
+  oom_error \
+  resolv_test \
+  set_fortify_handler \
+  support-xstat \
+  support_become_root \
+  support_can_chroot \
+  support_capture_subprocess \
+  support_capture_subprocess_check \
+  support_enter_network_namespace \
+  support_format_address_family \
+  support_format_addrinfo \
+  support_format_dns_packet \
+  support_format_herrno \
+  support_format_hostent \
+  support_format_netent \
+  support_isolate_in_subprocess \
+  support_record_failure \
+  support_run_diff \
+  support_shared_allocate \
+  support_write_file_string \
+  support_test_main \
+  support_test_verify_impl \
+  temp_file \
+  write_message \
+  xaccept \
+  xaccept4 \
+  xasprintf \
+  xbind \
+  xcalloc \
+  xchroot \
+  xclose \
+  xconnect \
+  xdup2 \
+  xfclose \
+  xfopen \
+  xfork \
+  xgetsockname \
+  xlisten \
+  xmalloc \
+  xmemstream \
+  xmkdir \
+  xmmap \
+  xmunmap \
+  xopen \
+  xpipe \
+  xpoll \
+  xpthread_attr_destroy \
+  xpthread_attr_init \
+  xpthread_attr_setdetachstate \
+  xpthread_attr_setstacksize \
+  xpthread_barrier_destroy \
+  xpthread_barrier_init \
+  xpthread_barrier_wait \
+  xpthread_cancel \
+  xpthread_check_return \
+  xpthread_cond_wait \
+  xpthread_create \
+  xpthread_detach \
+  xpthread_join \
+  xpthread_mutex_consistent \
+  xpthread_mutex_destroy \
+  xpthread_mutex_init \
+  xpthread_mutex_lock \
+  xpthread_mutex_unlock \
+  xpthread_mutexattr_destroy \
+  xpthread_mutexattr_init \
+  xpthread_mutexattr_setprotocol \
+  xpthread_mutexattr_setpshared \
+  xpthread_mutexattr_setrobust \
+  xpthread_mutexattr_settype \
+  xpthread_once \
+  xpthread_sigmask \
+  xpthread_spin_lock \
+  xpthread_spin_unlock \
+  xrealloc \
+  xrecvfrom \
+  xsendto \
+  xsetsockopt \
+  xsocket \
+  xstrdup \
+  xwaitpid \
+  xwrite \
+
+libsupport-static-only-routines := $(libsupport-routines)
+# Only build one variant of the library.
+libsupport-inhibit-o := .os
+ifeq ($(build-shared),yes)
+libsupport-inhibit-o += .o
+endif
+
+tests = \
+  README-testing \
+  tst-support-namespace \
+  tst-support_capture_subprocess \
+  tst-support_format_dns_packet \
+  tst-support_record_failure \
+
+ifeq ($(run-built-tests),yes)
+tests-special = \
+  $(objpfx)tst-support_record_failure-2.out
+
+$(objpfx)tst-support_record_failure-2.out: tst-support_record_failure-2.sh \
+  $(objpfx)tst-support_record_failure
+	$(SHELL) $< $(common-objpfx) '$(test-program-prefix-before-env)' \
+	  '$(run-program-env)' '$(test-program-prefix-after-env)' \
+	  > $@; \
+	$(evaluate-test)
+endif
+
+$(objpfx)tst-support_format_dns_packet: $(common-objpfx)resolv/libresolv.so
+
+include ../Rules
diff --git a/support/README b/support/README
new file mode 100644
index 0000000..476cfcd
--- /dev/null
+++ b/support/README
@@ -0,0 +1,29 @@
+This subdirectory contains infrastructure which is not put into
+installed libraries, but may be linked into programs (installed or
+not) and tests.
+
+# Error-checking wrappers
+
+These wrappers test for error return codes an terminate the process on
+error.  They are declared in these header files:
+
+* support.h
+* xsignal.h
+* xthread.h
+
+In general, new wrappers should be added to support.h if possible.
+However, support.h must remain fully compatible with C90 and therefore
+cannot include headers which use identifers not reserved in C90.  If
+the wrappers need additional types, additional headers such as
+signal.h need to be introduced.
+
+# Test framework
+
+The test framework provides a main program for tests, including a
+timeout for hanging tests.  See README-testing.c for a minimal
+example, and test-driver.c for details how to use it.  The following
+header files provide related declarations:
+
+* check.h
+* temp_file.h
+* test-driver.h
diff --git a/support/README-testing.c b/support/README-testing.c
new file mode 100644
index 0000000..9d289c3
--- /dev/null
+++ b/support/README-testing.c
@@ -0,0 +1,19 @@
+/* This file contains an example test case which shows minimal use of
+   the test framework.  Additional testing hooks are described in
+   <support/test-driver.c>.  */
+
+/* This function will be called from the test driver.  */
+static int
+do_test (void)
+{
+  if (3 == 5)
+    /* Indicate failure.  */
+    return 1;
+  else
+    /* Indicate success.  */
+    return 0;
+}
+
+/* This file references do_test above and contains the definition of
+   the main function.  */
+#include <support/test-driver.c>
diff --git a/support/capture_subprocess.h b/support/capture_subprocess.h
new file mode 100644
index 0000000..43caf9b
--- /dev/null
+++ b/support/capture_subprocess.h
@@ -0,0 +1,61 @@
+/* Capture output from a subprocess.
+   Copyright (C) 2017 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/>.  */
+
+#ifndef SUPPORT_CAPTURE_SUBPROCESS_H
+#define SUPPORT_CAPTURE_SUBPROCESS_H
+
+#include <support/xmemstream.h>
+
+struct support_capture_subprocess
+{
+  struct xmemstream out;
+  struct xmemstream err;
+  int status;
+};
+
+/* Invoke CALLBACK (CLOSURE) in a subprocess and capture standard
+   output, standard error, and the exit status.  The out.buffer and
+   err.buffer members in the result are null-terminated strings which
+   can be examined by the caller (out.out and err.out are NULL).  */
+struct support_capture_subprocess support_capture_subprocess
+  (void (*callback) (void *), void *closure);
+
+/* Deallocate the subprocess data captured by
+   support_capture_subprocess.  */
+void support_capture_subprocess_free (struct support_capture_subprocess *);
+
+enum support_capture_allow
+{
+  /* No output is allowed.  */
+  sc_allow_none = 0x01,
+  /* Output to stdout is permitted.  */
+  sc_allow_stdout = 0x02,
+  /* Output to standard error is permitted.  */
+  sc_allow_stderr = 0x04,
+};
+
+/* Check that the subprocess exited with STATUS and that only the
+   allowed outputs happened.  ALLOWED is a combination of
+   support_capture_allow flags.  Report errors under the CONTEXT
+   message.  */
+void support_capture_subprocess_check (struct support_capture_subprocess *,
+                                       const char *context, int status,
+                                       int allowed)
+  __attribute__ ((nonnull (1, 2)));
+
+#endif /* SUPPORT_CAPTURE_SUBPROCESS_H */
diff --git a/support/check.c b/support/check.c
new file mode 100644
index 0000000..592f2bc
--- /dev/null
+++ b/support/check.c
@@ -0,0 +1,57 @@
+/* Support code for reporting test results.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/check.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/test-driver.h>
+
+static void
+print_failure (const char *file, int line, const char *format, va_list ap)
+{
+  printf ("error: %s:%d: ", file, line);
+  vprintf (format, ap);
+  puts ("");
+}
+
+int
+support_print_failure_impl (const char *file, int line,
+                            const char *format, ...)
+{
+  support_record_failure ();
+  va_list ap;
+  va_start (ap, format);
+  print_failure (file, line, format, ap);
+  va_end (ap);
+  return 1;
+}
+
+void
+support_exit_failure_impl (int status, const char *file, int line,
+                           const char *format, ...)
+{
+  if (status != EXIT_SUCCESS && status != EXIT_UNSUPPORTED)
+    support_record_failure ();
+  va_list ap;
+  va_start (ap, format);
+  print_failure (file, line, format, ap);
+  va_end (ap);
+  exit (status);
+}
diff --git a/support/check.h b/support/check.h
new file mode 100644
index 0000000..1d244a3
--- /dev/null
+++ b/support/check.h
@@ -0,0 +1,94 @@
+/* Functionality for reporting test results.
+   Copyright (C) 2016-2017 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/>.  */
+
+#ifndef SUPPORT_CHECK_H
+#define SUPPORT_CHECK_H
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/* Record a test failure, print the failure message to standard output
+   and return 1.  */
+#define FAIL_RET(...) \
+  return support_print_failure_impl (__FILE__, __LINE__, __VA_ARGS__)
+
+/* Print the failure message and terminate the process with STATUS.
+   Record a the process as failed if STATUS is neither EXIT_SUCCESS
+   nor EXIT_UNSUPPORTED.  */
+#define FAIL_EXIT(status, ...) \
+  support_exit_failure_impl (status, __FILE__, __LINE__, __VA_ARGS__)
+
+/* Record a test failure, print the failure message and terminate with
+   exit status 1.  */
+#define FAIL_EXIT1(...) \
+  support_exit_failure_impl (1, __FILE__, __LINE__, __VA_ARGS__)
+
+/* Print failure message and terminate with as unsupported test (exit
+   status of 77).  */
+#define FAIL_UNSUPPORTED(...) \
+  support_exit_failure_impl (77, __FILE__, __LINE__, __VA_ARGS__)
+
+/* Record a test failure (but continue executing) if EXPR evaluates to
+   false.  */
+#define TEST_VERIFY(expr)                                       \
+  ({                                                            \
+    if (expr)                                                   \
+      ;                                                         \
+    else                                                        \
+      support_test_verify_impl (-1, __FILE__, __LINE__, #expr); \
+  })
+
+/* Record a test failure and exit if EXPR evaluates to false.  */
+#define TEST_VERIFY_EXIT(expr)                                  \
+  ({                                                            \
+    if (expr)                                                   \
+      ;                                                         \
+    else                                                        \
+      support_test_verify_impl (1, __FILE__, __LINE__, #expr);  \
+  })
+
+int support_print_failure_impl (const char *file, int line,
+                                const char *format, ...)
+  __attribute__ ((nonnull (1), format (printf, 3, 4)));
+void support_exit_failure_impl (int exit_status,
+                                const char *file, int line,
+                                const char *format, ...)
+  __attribute__ ((noreturn, nonnull (2), format (printf, 4, 5)));
+void support_test_verify_impl (int status, const char *file, int line,
+                               const char *expr);
+
+/* Record a test failure.  This function returns and does not
+   terminate the process.  The failure counter is stored in a shared
+   memory mapping, so that failures reported in child processes are
+   visible to the parent process and test driver.  This function
+   depends on initialization by an ELF constructor, so it can only be
+   invoked after the test driver has run.  Note that this function
+   does not support reporting failures from a DSO.  */
+void support_record_failure (void);
+
+/* Internal function called by the test driver.  */
+int support_report_failure (int status)
+  __attribute__ ((weak, warn_unused_result));
+
+/* Internal function used to test the failure recording framework.  */
+void support_record_failure_reset (void);
+
+__END_DECLS
+
+#endif /* SUPPORT_CHECK_H */
diff --git a/support/check_addrinfo.c b/support/check_addrinfo.c
new file mode 100644
index 0000000..55895ac
--- /dev/null
+++ b/support/check_addrinfo.c
@@ -0,0 +1,42 @@
+/* Compare struct addrinfo values against a formatted string.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/check_nss.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/format_nss.h>
+#include <support/run_diff.h>
+
+void
+check_addrinfo (const char *query_description, struct addrinfo *ai, int ret,
+                const char *expected)
+{
+  char *formatted = support_format_addrinfo (ai, ret);
+  if (strcmp (formatted, expected) != 0)
+    {
+      support_record_failure ();
+      printf ("error: addrinfo comparison failure\n");
+      if (query_description != NULL)
+        printf ("query: %s\n", query_description);
+      support_run_diff ("expected", expected,
+                        "actual", formatted);
+    }
+  free (formatted);
+}
diff --git a/support/check_dns_packet.c b/support/check_dns_packet.c
new file mode 100644
index 0000000..d2a31be
--- /dev/null
+++ b/support/check_dns_packet.c
@@ -0,0 +1,42 @@
+/* Check that a DNS packet buffer has the expected contents.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/check_nss.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/format_nss.h>
+#include <support/run_diff.h>
+
+void
+check_dns_packet (const char *query_description,
+                  const unsigned char *buffer, size_t length,
+                  const char *expected)
+{
+  char *formatted = support_format_dns_packet (buffer, length);
+  if (strcmp (formatted, expected) != 0)
+    {
+      support_record_failure ();
+      printf ("error: packet comparison failure\n");
+      if (query_description != NULL)
+        printf ("query: %s\n", query_description);
+      support_run_diff ("expected", expected, "actual", formatted);
+    }
+  free (formatted);
+}
diff --git a/support/check_hostent.c b/support/check_hostent.c
new file mode 100644
index 0000000..890d672
--- /dev/null
+++ b/support/check_hostent.c
@@ -0,0 +1,42 @@
+/* Compare struct hostent values against a formatted string.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/check_nss.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/format_nss.h>
+#include <support/run_diff.h>
+
+void
+check_hostent (const char *query_description, struct hostent *h,
+               const char *expected)
+{
+  char *formatted = support_format_hostent (h);
+  if (strcmp (formatted, expected) != 0)
+    {
+      support_record_failure ();
+      printf ("error: hostent comparison failure\n");
+      if (query_description != NULL)
+        printf ("query: %s\n", query_description);
+      support_run_diff ("expected", expected,
+                        "actual", formatted);
+    }
+  free (formatted);
+}
diff --git a/support/check_netent.c b/support/check_netent.c
new file mode 100644
index 0000000..daa3083
--- /dev/null
+++ b/support/check_netent.c
@@ -0,0 +1,42 @@
+/* Compare struct netent values against a formatted string.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/check_nss.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/format_nss.h>
+#include <support/run_diff.h>
+
+void
+check_netent (const char *query_description, struct netent *e,
+              const char *expected)
+{
+  char *formatted = support_format_netent (e);
+  if (strcmp (formatted, expected) != 0)
+    {
+      support_record_failure ();
+      printf ("error: netent comparison failure\n");
+      if (query_description != NULL)
+        printf ("query: %s\n", query_description);
+      support_run_diff ("expected", expected,
+                        "actual", formatted);
+    }
+  free (formatted);
+}
diff --git a/support/check_nss.h b/support/check_nss.h
new file mode 100644
index 0000000..2893f2c
--- /dev/null
+++ b/support/check_nss.h
@@ -0,0 +1,42 @@
+/* Test verification functions for NSS- and DNS-related data.
+   Copyright (C) 2016-2017 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/>.  */
+
+#ifndef SUPPORT_CHECK_NSS_H
+#define SUPPORT_CHECK_NSS_H
+
+#include <netdb.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/* Compare the data structures against the expected values (which have
+   to be formatted according to the support_format_* functions in
+   <support/format_nss.h>).  If there is a difference, a delayed test
+   failure is recorded, and a diff is written to standard output.  */
+void check_addrinfo (const char *query_description,
+                     struct addrinfo *, int ret, const char *expected);
+void check_dns_packet (const char *query_description,
+                       const unsigned char *, size_t, const char *expected);
+void check_hostent (const char *query_description,
+                    struct hostent *, const char *expected);
+void check_netent (const char *query_description,
+                   struct netent *, const char *expected);
+
+__END_DECLS
+
+#endif  /* SUPPORT_CHECK_NSS_H */
diff --git a/support/delayed_exit.c b/support/delayed_exit.c
new file mode 100644
index 0000000..67442f9
--- /dev/null
+++ b/support/delayed_exit.c
@@ -0,0 +1,55 @@
+/* Time-triggered process termination.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xthread.h>
+#include <support/xsignal.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <time.h>
+
+static void *
+delayed_exit_thread (void *seconds_as_ptr)
+{
+  int seconds = (uintptr_t) seconds_as_ptr;
+  struct timespec delay = { seconds, 0 };
+  struct timespec remaining = { 0 };
+  if (nanosleep (&delay, &remaining) != 0)
+    FAIL_EXIT1 ("nanosleep: %m");
+  /* Exit the process sucessfully.  */
+  exit (0);
+  return NULL;
+}
+
+void
+delayed_exit (int seconds)
+{
+  /* Create the new thread with all signals blocked.  */
+  sigset_t all_blocked;
+  sigfillset (&all_blocked);
+  sigset_t old_set;
+  xpthread_sigmask (SIG_SETMASK, &all_blocked, &old_set);
+  /* Create a detached thread. */
+  pthread_t thr = xpthread_create
+    (NULL, delayed_exit_thread, (void *) (uintptr_t) seconds);
+  xpthread_detach (thr);
+  /* Restore the original signal mask.  */
+  xpthread_sigmask (SIG_SETMASK, &old_set, NULL);
+}
diff --git a/support/format_nss.h b/support/format_nss.h
new file mode 100644
index 0000000..fb4597c
--- /dev/null
+++ b/support/format_nss.h
@@ -0,0 +1,41 @@
+/* String formatting functions for NSS- and DNS-related data.
+   Copyright (C) 2016-2017 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/>.  */
+
+#ifndef SUPPORT_FORMAT_NSS_H
+#define SUPPORT_FORMAT_NSS_H
+
+#include <netdb.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/* The following functions format their arguments as human-readable
+   strings (which can span multiple lines).  The caller must free the
+   returned buffer.  For NULL pointers or failure status arguments,
+   error variables such as h_errno and errno are included in the
+   result.  */
+char *support_format_address_family (int);
+char *support_format_addrinfo (struct addrinfo *, int ret);
+char *support_format_dns_packet (const unsigned char *buffer, size_t length);
+char *support_format_herrno (int);
+char *support_format_hostent (struct hostent *);
+char *support_format_netent (struct netent *);
+
+__END_DECLS
+
+#endif  /* SUPPORT_FORMAT_NSS_H */
diff --git a/support/ignore_stderr.c b/support/ignore_stderr.c
new file mode 100644
index 0000000..7b77a2c
--- /dev/null
+++ b/support/ignore_stderr.c
@@ -0,0 +1,38 @@
+/* Avoid all the buffer overflow messages on stderr.
+   Copyright (C) 2015-2017 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/>.  */
+
+#include <support/support.h>
+
+#include <fcntl.h>
+#include <paths.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+void
+ignore_stderr (void)
+{
+  int fd = open (_PATH_DEVNULL, O_WRONLY);
+  if (fd == -1)
+    close (STDERR_FILENO);
+  else
+    {
+      dup2 (fd, STDERR_FILENO);
+      close (fd);
+    }
+  setenv ("LIBC_FATAL_STDERR_", "1", 1);
+}
diff --git a/support/namespace.h b/support/namespace.h
new file mode 100644
index 0000000..e1ccaa1
--- /dev/null
+++ b/support/namespace.h
@@ -0,0 +1,65 @@
+/* Entering namespaces for test case isolation.
+   Copyright (C) 2016-2017 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/>.  */
+
+#ifndef SUPPORT_NAMESPACE_H
+#define SUPPORT_NAMESPACE_H
+
+#include <stdbool.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/* Attempts to become root (or acquire root-like privileges), possibly
+   with the help of user namespaces.  Return true if (restricted) root
+   privileges could be attained in some way.  Print diagnostics to
+   standard output.
+
+   Note that this function generally has to be called before a process
+   becomes multi-threaded, otherwise it may fail with insufficient
+   privileges on systems which would support this operation for
+   single-threaded processes.  */
+bool support_become_root (void);
+
+/* Return true if this process can perform a chroot operation.  In
+   general, this is only possible if support_become_root has been
+   called.  Note that the actual test is performed in a subprocess,
+   after fork, so that the file system root of the original process is
+   not changed.  */
+bool support_can_chroot (void);
+
+/* Enter a network namespace (and a UTS namespace if possible) and
+   configure the loopback interface.  Return true if a network
+   namespace could be created.  Print diagnostics to standard output.
+   If a network namespace could be created, but networking in it could
+   not be configured, terminate the process.  It is recommended to
+   call support_become_root before this function so that the process
+   has sufficient privileges.  */
+bool support_enter_network_namespace (void);
+
+/* Return true if support_enter_network_namespace managed to enter a
+   UTS namespace.  */
+bool support_in_uts_namespace (void);
+
+/* Invoke CALLBACK (CLOSURE) in a subprocess created using fork.
+   Terminate the calling process if the subprocess exits with a
+   non-zero exit status.  */
+void support_isolate_in_subprocess (void (*callback) (void *), void *closure);
+
+__END_DECLS
+
+#endif
diff --git a/support/oom_error.c b/support/oom_error.c
new file mode 100644
index 0000000..7816978
--- /dev/null
+++ b/support/oom_error.c
@@ -0,0 +1,29 @@
+/* Reporting out-of-memory errors.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/support.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+void
+oom_error (const char *function, size_t size)
+{
+  printf ("%s: unable to allocate %zu bytes: %m\n", function, size);
+  exit (1);
+}
diff --git a/support/resolv_test.c b/support/resolv_test.c
new file mode 100644
index 0000000..050cd71
--- /dev/null
+++ b/support/resolv_test.c
@@ -0,0 +1,1202 @@
+/* DNS test framework and libresolv redirection.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/resolv_test.h>
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <nss.h>
+#include <resolv.h>
+#include <search.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/namespace.h>
+#include <support/support.h>
+#include <support/test-driver.h>
+#include <support/xsocket.h>
+#include <support/xthread.h>
+#include <support/xunistd.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+/* Response builder.  */
+
+enum
+  {
+    max_response_length = 65536
+  };
+
+/* List of pointers to be freed.  The hash table implementation
+   (struct hsearch_data) does not provide a way to deallocate all
+   objects, so this approach is used to avoid memory leaks.  */
+struct to_be_freed
+{
+  struct to_be_freed *next;
+  void *ptr;
+};
+
+struct resolv_response_builder
+{
+  const unsigned char *query_buffer;
+  size_t query_length;
+
+  size_t offset;                /* Bytes written so far in buffer.  */
+  ns_sect section;              /* Current section in the DNS packet.  */
+  unsigned int truncate_bytes;  /* Bytes to remove at end of response. */
+  bool drop;                    /* Discard generated response.  */
+  bool close;                   /* Close TCP client connection.  */
+
+  /* Offset of the two-byte RDATA length field in the currently
+     written RDATA sub-structure.  0 if no RDATA is being written.  */
+  size_t current_rdata_offset;
+
+  /* Hash table for locating targets for label compression.  */
+  struct hsearch_data compression_offsets;
+  /* List of pointers which need to be freed.  Used for domain names
+     involved in label compression.  */
+  struct to_be_freed *to_be_freed;
+
+  /* Must be last.  Not zeroed for performance reasons.  */
+  unsigned char buffer[max_response_length];
+};
+
+/* Response builder. */
+
+/* Add a pointer to the list of pointers to be freed when B is
+   deallocated.  */
+static void
+response_push_pointer_to_free (struct resolv_response_builder *b, void *ptr)
+{
+  if (ptr == NULL)
+    return;
+  struct to_be_freed *e = xmalloc (sizeof (*e));
+  *e = (struct to_be_freed) {b->to_be_freed, ptr};
+  b->to_be_freed = e;
+}
+
+void
+resolv_response_init (struct resolv_response_builder *b,
+                      struct resolv_response_flags flags)
+{
+  if (b->offset > 0)
+    FAIL_EXIT1 ("response_init: called at offset %zu", b->offset);
+  if (b->query_length < 12)
+    FAIL_EXIT1 ("response_init called for a query of size %zu",
+                b->query_length);
+  if (flags.rcode > 15)
+    FAIL_EXIT1 ("response_init: invalid RCODE %u", flags.rcode);
+
+  /* Copy the transaction ID.  */
+  b->buffer[0] = b->query_buffer[0];
+  b->buffer[1] = b->query_buffer[1];
+
+  /* Initialize the flags.  */
+  b->buffer[2] = 0x80;                       /* Mark as response.   */
+  b->buffer[2] |= b->query_buffer[2] & 0x01; /* Copy the RD bit.  */
+  if (flags.tc)
+    b->buffer[2] |= 0x02;
+  b->buffer[3] = 0x80 | flags.rcode; /* Always set RA.  */
+
+  /* Fill in the initial section count values.  */
+  b->buffer[4] = flags.qdcount >> 8;
+  b->buffer[5] = flags.qdcount;
+  b->buffer[6] = flags.ancount >> 8;
+  b->buffer[7] = flags.ancount;
+  b->buffer[8] = flags.nscount >> 8;
+  b->buffer[9] = flags.nscount;
+  b->buffer[10] = flags.adcount >> 8;
+  b->buffer[11] = flags.adcount;
+
+  b->offset = 12;
+}
+
+void
+resolv_response_section (struct resolv_response_builder *b, ns_sect section)
+{
+  if (b->offset == 0)
+    FAIL_EXIT1 ("resolv_response_section: response_init not called before");
+  if (section < b->section)
+    FAIL_EXIT1 ("resolv_response_section: cannot go back to previous section");
+  b->section = section;
+}
+
+/* Add a single byte to B.  */
+static inline void
+response_add_byte (struct resolv_response_builder *b, unsigned char ch)
+{
+  if (b->offset == max_response_length)
+    FAIL_EXIT1 ("DNS response exceeds 64 KiB limit");
+  b->buffer[b->offset] = ch;
+  ++b->offset;
+}
+
+/* Add a 16-bit word VAL to B, in big-endian format.  */
+static void
+response_add_16 (struct resolv_response_builder *b, uint16_t val)
+{
+  response_add_byte (b, val >> 8);
+  response_add_byte (b, val);
+}
+
+/* Increment the pers-section record counter in the packet header.  */
+static void
+response_count_increment (struct resolv_response_builder *b)
+{
+  unsigned int offset = b->section;
+  offset = 4 + 2 * offset;
+  ++b->buffer[offset + 1];
+  if (b->buffer[offset + 1] == 0)
+    {
+      /* Carry.  */
+      ++b->buffer[offset];
+      if (b->buffer[offset] == 0)
+        /* Overflow.  */
+        FAIL_EXIT1 ("too many records in section");
+    }
+}
+
+void
+resolv_response_add_question (struct resolv_response_builder *b,
+                              const char *name, uint16_t class, uint16_t type)
+{
+  if (b->offset == 0)
+    FAIL_EXIT1 ("resolv_response_add_question: "
+                "resolv_response_init not called");
+  if (b->section != ns_s_qd)
+    FAIL_EXIT1 ("resolv_response_add_question: "
+                "must be called in the question section");
+
+  resolv_response_add_name (b, name);
+  response_add_16 (b, type);
+  response_add_16 (b, class);
+
+  response_count_increment (b);
+}
+
+void
+resolv_response_add_name (struct resolv_response_builder *b,
+                          const char *const origname)
+{
+  /* Normalized name.  */
+  char *name;
+  /* Normalized name with case preserved.  */
+  char *name_case;
+  {
+    size_t namelen = strlen (origname);
+    /* Remove trailing dots.  FIXME: Handle trailing quoted dots.  */
+    while (namelen > 0 && origname[namelen - 1] == '.')
+      --namelen;
+    name = xmalloc (namelen + 1);
+    name_case = xmalloc (namelen + 1);
+    /* Copy and convert to lowercase.  FIXME: This needs to normalize
+       escaping as well.  */
+    for (size_t i = 0; i < namelen; ++i)
+      {
+        char ch = origname[i];
+        name_case[i] = ch;
+        if ('A' <= ch && ch <= 'Z')
+          ch = ch - 'A' + 'a';
+        name[i] = ch;
+      }
+    name[namelen] = 0;
+    name_case[namelen] = 0;
+  }
+  char *name_start = name;
+  char *name_case_start = name_case;
+
+  bool compression = false;
+  while (*name)
+    {
+      /* Search for a previous name we can reference.  */
+      ENTRY new_entry =
+        {
+          .key = name,
+          .data = (void *) (uintptr_t) b->offset,
+        };
+
+      /* If the label can be a compression target because it is at a
+         reachable offset, add it to the hash table.  */
+      ACTION action;
+      if (b->offset < (1 << 12))
+        action = ENTER;
+      else
+        action = FIND;
+
+      /* Search for known compression offsets in the hash table.  */
+      ENTRY *e;
+      if (hsearch_r (new_entry, action, &e, &b->compression_offsets) == 0)
+        {
+          if (action == FIND && errno == ESRCH)
+            /* Fall through.  */
+            e = NULL;
+          else
+            FAIL_EXIT1 ("hsearch_r failure in name compression: %m");
+        }
+
+      /* The name is known.  Reference the previous location.  */
+      if (e != NULL && e->data != new_entry.data)
+        {
+          size_t old_offset = (uintptr_t) e->data;
+          response_add_byte (b, 0xC0 | (old_offset >> 8));
+          response_add_byte (b, old_offset);
+          compression = true;
+          break;
+        }
+
+      /* The name does not exist yet.  Write one label.  First, add
+         room for the label length.  */
+      size_t buffer_label_offset = b->offset;
+      response_add_byte (b, 0);
+
+      /* Copy the label.  */
+      while (true)
+        {
+          char ch = *name_case;
+          if (ch == '\0')
+            break;
+          ++name;
+          ++name_case;
+          if (ch == '.')
+            break;
+          /* FIXME: Handle escaping.  */
+          response_add_byte (b, ch);
+        }
+
+      /* Patch in the label length.  */
+      size_t label_length = b->offset - buffer_label_offset - 1;
+      if (label_length == 0)
+        FAIL_EXIT1 ("empty label in name compression: %s", origname);
+      if (label_length > 63)
+        FAIL_EXIT1 ("label too long in name compression: %s", origname);
+      b->buffer[buffer_label_offset] = label_length;
+
+      /* Continue with the tail of the name and the next label.  */
+    }
+
+  if (compression)
+    {
+      /* If we found an immediate match for the name, we have not put
+         it into the hash table, and can free it immediately.  */
+      if (name == name_start)
+        free (name_start);
+      else
+        response_push_pointer_to_free (b, name_start);
+    }
+  else
+    {
+      /* Terminate the sequence of labels.  With compression, this is
+         implicit in the compression reference.  */
+      response_add_byte (b, 0);
+      response_push_pointer_to_free (b, name_start);
+    }
+
+  free (name_case_start);
+}
+
+void
+resolv_response_open_record (struct resolv_response_builder *b,
+                             const char *name,
+                             uint16_t class, uint16_t type, uint32_t ttl)
+{
+  if (b->section == ns_s_qd)
+    FAIL_EXIT1 ("resolv_response_open_record called in question section");
+  if (b->current_rdata_offset != 0)
+    FAIL_EXIT1 ("resolv_response_open_record called with open record");
+
+  resolv_response_add_name (b, name);
+  response_add_16 (b, type);
+  response_add_16 (b, class);
+  response_add_16 (b, ttl >> 16);
+  response_add_16 (b, ttl);
+
+  b->current_rdata_offset = b->offset;
+  /* Add room for the RDATA length.  */
+  response_add_16 (b, 0);
+}
+
+
+void
+resolv_response_close_record (struct resolv_response_builder *b)
+{
+  size_t rdata_offset = b->current_rdata_offset;
+  if (rdata_offset == 0)
+    FAIL_EXIT1 ("response_close_record called without open record");
+  size_t rdata_length = b->offset - rdata_offset - 2;
+  if (rdata_length > 65535)
+    FAIL_EXIT1 ("RDATA length %zu exceeds limit", rdata_length);
+  b->buffer[rdata_offset] = rdata_length >> 8;
+  b->buffer[rdata_offset + 1] = rdata_length;
+  response_count_increment (b);
+  b->current_rdata_offset = 0;
+}
+
+void
+resolv_response_add_data (struct resolv_response_builder *b,
+                          const void *data, size_t length)
+{
+  size_t remaining = max_response_length - b->offset;
+  if (remaining < length)
+    FAIL_EXIT1 ("resolv_response_add_data: not enough room for %zu bytes",
+                length);
+  memcpy (b->buffer + b->offset, data, length);
+  b->offset += length;
+}
+
+void
+resolv_response_drop (struct resolv_response_builder *b)
+{
+  b->drop = true;
+}
+
+void
+resolv_response_close (struct resolv_response_builder *b)
+{
+  b->close = true;
+}
+
+void
+resolv_response_truncate_data (struct resolv_response_builder *b, size_t count)
+{
+  if (count > 65535)
+    FAIL_EXIT1 ("resolv_response_truncate_data: argument too large: %zu",
+                count);
+  b->truncate_bytes = count;
+}
+
+
+size_t
+resolv_response_length (const struct resolv_response_builder *b)
+{
+  return b->offset;
+}
+
+unsigned char *
+resolv_response_buffer (const struct resolv_response_builder *b)
+{
+  unsigned char *result = xmalloc (b->offset);
+  memcpy (result, b->buffer, b->offset);
+  return result;
+}
+
+static struct resolv_response_builder *
+response_builder_allocate
+  (const unsigned char *query_buffer, size_t query_length)
+{
+  struct resolv_response_builder *b = xmalloc (sizeof (*b));
+  memset (b, 0, offsetof (struct resolv_response_builder, buffer));
+  b->query_buffer = query_buffer;
+  b->query_length = query_length;
+  TEST_VERIFY_EXIT (hcreate_r (10000, &b->compression_offsets) != 0);
+  return b;
+}
+
+static void
+response_builder_free (struct resolv_response_builder *b)
+{
+  struct to_be_freed *current = b->to_be_freed;
+  while (current != NULL)
+    {
+      struct to_be_freed *next = current->next;
+      free (current->ptr);
+      free (current);
+      current = next;
+    }
+  hdestroy_r (&b->compression_offsets);
+  free (b);
+}
+
+/* DNS query processing. */
+
+/* Data extracted from the question section of a DNS packet.  */
+struct query_info
+{
+  char qname[MAXDNAME];
+  uint16_t qclass;
+  uint16_t qtype;
+  struct resolv_edns_info edns;
+};
+
+/* Update *INFO from the specified DNS packet.  */
+static void
+parse_query (struct query_info *info,
+             const unsigned char *buffer, size_t length)
+{
+  HEADER hd;
+  _Static_assert (sizeof (hd) == 12, "DNS header size");
+  if (length < sizeof (hd))
+    FAIL_EXIT1 ("malformed DNS query: too short: %zu bytes", length);
+  memcpy (&hd, buffer, sizeof (hd));
+
+  if (ntohs (hd.qdcount) != 1)
+    FAIL_EXIT1 ("malformed DNS query: wrong question count: %d",
+                (int) ntohs (hd.qdcount));
+  if (ntohs (hd.ancount) != 0)
+    FAIL_EXIT1 ("malformed DNS query: wrong answer count: %d",
+                (int) ntohs (hd.ancount));
+  if (ntohs (hd.nscount) != 0)
+    FAIL_EXIT1 ("malformed DNS query: wrong authority count: %d",
+                (int) ntohs (hd.nscount));
+  if (ntohs (hd.arcount) > 1)
+    FAIL_EXIT1 ("malformed DNS query: wrong additional count: %d",
+                (int) ntohs (hd.arcount));
+
+  int ret = dn_expand (buffer, buffer + length, buffer + sizeof (hd),
+                       info->qname, sizeof (info->qname));
+  if (ret < 0)
+    FAIL_EXIT1 ("malformed DNS query: cannot uncompress QNAME");
+
+  /* Obtain QTYPE and QCLASS.  */
+  size_t remaining = length - (12 + ret);
+  struct
+  {
+    uint16_t qtype;
+    uint16_t qclass;
+  } qtype_qclass;
+  if (remaining < sizeof (qtype_qclass))
+    FAIL_EXIT1 ("malformed DNS query: "
+                "query lacks QCLASS/QTYPE, QNAME: %s", info->qname);
+  memcpy (&qtype_qclass, buffer + 12 + ret, sizeof (qtype_qclass));
+  info->qclass = ntohs (qtype_qclass.qclass);
+  info->qtype = ntohs (qtype_qclass.qtype);
+
+  memset (&info->edns, 0, sizeof (info->edns));
+  if (ntohs (hd.arcount) > 0)
+    {
+      /* Parse EDNS record.  */
+      struct __attribute__ ((packed, aligned (1)))
+      {
+        uint8_t root;
+        uint16_t rtype;
+        uint16_t payload;
+        uint8_t edns_extended_rcode;
+        uint8_t edns_version;
+        uint16_t flags;
+        uint16_t rdatalen;
+      } rr;
+      _Static_assert (sizeof (rr) == 11, "EDNS record size");
+
+      if (remaining < 4 + sizeof (rr))
+        FAIL_EXIT1 ("mailformed DNS query: no room for EDNS record");
+      memcpy (&rr, buffer + 12 + ret + 4, sizeof (rr));
+      if (rr.root != 0)
+        FAIL_EXIT1 ("malformed DNS query: invalid OPT RNAME: %d\n", rr.root);
+      if (rr.rtype != htons (41))
+        FAIL_EXIT1 ("malformed DNS query: invalid OPT type: %d\n",
+                    ntohs (rr.rtype));
+      info->edns.active = true;
+      info->edns.extended_rcode = rr.edns_extended_rcode;
+      info->edns.version = rr.edns_version;
+      info->edns.flags = ntohs (rr.flags);
+      info->edns.payload_size = ntohs (rr.payload);
+    }
+}
+
+
+/* Main testing framework.  */
+
+/* Per-server information.  One struct is allocated for each test
+   server.  */
+struct resolv_test_server
+{
+  /* Local address of the server.  UDP and TCP use the same port.  */
+  struct sockaddr_in address;
+
+  /* File descriptor of the UDP server, or -1 if this server is
+     disabled.  */
+  int socket_udp;
+
+  /* File descriptor of the TCP server, or -1 if this server is
+     disabled.  */
+  int socket_tcp;
+
+  /* Counter of the number of responses processed so far.  */
+  size_t response_number;
+
+  /* Thread handles for the server threads (if not disabled in the
+     configuration).  */
+  pthread_t thread_udp;
+  pthread_t thread_tcp;
+};
+
+/* Main struct for keeping track of libresolv redirection and
+   testing.  */
+struct resolv_test
+{
+  /* After initialization, any access to the struct must be performed
+     while this lock is acquired.  */
+  pthread_mutex_t lock;
+
+  /* Data for each test server. */
+  struct resolv_test_server servers[resolv_max_test_servers];
+
+  /* Used if config.single_thread_udp is true.  */
+  pthread_t thread_udp_single;
+
+  struct resolv_redirect_config config;
+  bool termination_requested;
+};
+
+/* Function implementing a server thread.  */
+typedef void (*thread_callback) (struct resolv_test *, int server_index);
+
+/* Storage for thread-specific data, for passing to the
+   thread_callback function.  */
+struct thread_closure
+{
+  struct resolv_test *obj;      /* Current test object.  */
+  thread_callback callback;     /* Function to call.  */
+  int server_index;             /* Index of the implemented server.  */
+};
+
+/* Wrap response_callback as a function which can be passed to
+   pthread_create.  */
+static void *
+thread_callback_wrapper (void *arg)
+{
+  struct thread_closure *closure = arg;
+  closure->callback (closure->obj, closure->server_index);
+  free (closure);
+  return NULL;
+}
+
+/* Start a server thread for the specified SERVER_INDEX, implemented
+   by CALLBACK.  */
+static pthread_t
+start_server_thread (struct resolv_test *obj, int server_index,
+                     thread_callback callback)
+{
+  struct thread_closure *closure = xmalloc (sizeof (*closure));
+  *closure = (struct thread_closure)
+    {
+      .obj = obj,
+      .callback = callback,
+      .server_index = server_index,
+    };
+  return xpthread_create (NULL, thread_callback_wrapper, closure);
+}
+
+/* Process one UDP query.  Return false if a termination requested has
+   been detected.  */
+static bool
+server_thread_udp_process_one (struct resolv_test *obj, int server_index)
+{
+  unsigned char query[512];
+  struct sockaddr_storage peer;
+  socklen_t peerlen = sizeof (peer);
+  size_t length = xrecvfrom (obj->servers[server_index].socket_udp,
+                             query, sizeof (query), 0,
+                             (struct sockaddr *) &peer, &peerlen);
+  /* Check for termination.  */
+  {
+    bool termination_requested;
+    xpthread_mutex_lock (&obj->lock);
+    termination_requested = obj->termination_requested;
+    xpthread_mutex_unlock (&obj->lock);
+    if (termination_requested)
+      return false;
+  }
+
+
+  struct query_info qinfo;
+  parse_query (&qinfo, query, length);
+  if (test_verbose > 0)
+    {
+      if (test_verbose > 1)
+        printf ("info: UDP server %d: incoming query:"
+                " %zd bytes, %s/%u/%u, tnxid=0x%02x%02x\n",
+                server_index, length, qinfo.qname, qinfo.qclass, qinfo.qtype,
+                query[0], query[1]);
+      else
+        printf ("info: UDP server %d: incoming query:"
+                " %zd bytes, %s/%u/%u\n",
+                server_index, length, qinfo.qname, qinfo.qclass, qinfo.qtype);
+    }
+
+  struct resolv_response_context ctx =
+    {
+      .query_buffer = query,
+      .query_length = length,
+      .server_index = server_index,
+      .tcp = false,
+      .edns = qinfo.edns,
+    };
+  struct resolv_response_builder *b = response_builder_allocate (query, length);
+  obj->config.response_callback
+    (&ctx, b, qinfo.qname, qinfo.qclass, qinfo.qtype);
+
+  if (b->drop)
+    {
+      if (test_verbose)
+        printf ("info: UDP server %d: dropping response to %s/%u/%u\n",
+                server_index, qinfo.qname, qinfo.qclass, qinfo.qtype);
+    }
+  else
+    {
+      if (test_verbose)
+        {
+          if (b->offset >= 12)
+            printf ("info: UDP server %d: sending response:"
+                    " %zu bytes, RCODE %d (for %s/%u/%u)\n",
+                    server_index, b->offset, b->buffer[3] & 0x0f,
+                    qinfo.qname, qinfo.qclass, qinfo.qtype);
+          else
+            printf ("info: UDP server %d: sending response: %zu bytes"
+                    " (for %s/%u/%u)\n",
+                    server_index, b->offset,
+                    qinfo.qname, qinfo.qclass, qinfo.qtype);
+          if (b->truncate_bytes > 0)
+            printf ("info:    truncated by %u bytes\n", b->truncate_bytes);
+        }
+      size_t to_send = b->offset;
+      if (to_send < b->truncate_bytes)
+        to_send = 0;
+      else
+        to_send -= b->truncate_bytes;
+
+      /* Ignore most errors here because the other end may have closed
+         the socket. */
+      if (sendto (obj->servers[server_index].socket_udp,
+                  b->buffer, to_send, 0,
+                  (struct sockaddr *) &peer, peerlen) < 0)
+        TEST_VERIFY_EXIT (errno != EBADF);
+    }
+  response_builder_free (b);
+  return true;
+}
+
+/* UDP thread_callback function.  Variant for one thread per
+   server.  */
+static void
+server_thread_udp (struct resolv_test *obj, int server_index)
+{
+  while (server_thread_udp_process_one (obj, server_index))
+    ;
+}
+
+/* Single-threaded UDP processing function, for the single_thread_udp
+   case.  */
+static void *
+server_thread_udp_single (void *closure)
+{
+  struct resolv_test *obj = closure;
+
+  struct pollfd fds[resolv_max_test_servers];
+  for (int server_index = 0; server_index < resolv_max_test_servers;
+       ++server_index)
+    if (obj->config.servers[server_index].disable_udp)
+      fds[server_index] = (struct pollfd) {.fd = -1};
+    else
+      {
+        fds[server_index] = (struct pollfd)
+          {
+            .fd = obj->servers[server_index].socket_udp,
+            .events = POLLIN
+          };
+
+        /* Make the socket non-blocking.  */
+        int flags = fcntl (obj->servers[server_index].socket_udp, F_GETFL, 0);
+        if (flags < 0)
+          FAIL_EXIT1 ("fcntl (F_GETFL): %m");
+        flags |= O_NONBLOCK;
+        if (fcntl (obj->servers[server_index].socket_udp, F_SETFL, flags) < 0)
+          FAIL_EXIT1 ("fcntl (F_SETFL): %m");
+      }
+
+  while (true)
+    {
+      xpoll (fds, resolv_max_test_servers, -1);
+      for (int server_index = 0; server_index < resolv_max_test_servers;
+           ++server_index)
+        if (fds[server_index].revents != 0)
+          {
+            if (!server_thread_udp_process_one (obj, server_index))
+              goto out;
+            fds[server_index].revents = 0;
+          }
+    }
+
+ out:
+  return NULL;
+}
+
+/* Start the single UDP handler thread (for the single_thread_udp
+   case).  */
+static void
+start_server_thread_udp_single (struct resolv_test *obj)
+{
+  obj->thread_udp_single
+    = xpthread_create (NULL, server_thread_udp_single, obj);
+}
+
+/* Data describing a TCP client connect.  */
+struct tcp_thread_closure
+{
+  struct resolv_test *obj;
+  int server_index;
+  int client_socket;
+};
+
+/* Read a complete DNS query packet.  If EOF_OK, an immediate
+   end-of-file condition is acceptable.  */
+static bool
+read_fully (int fd, void *buf, size_t len, bool eof_ok)
+{
+  const void *const end = buf + len;
+  while (buf < end)
+    {
+      ssize_t ret = read (fd, buf, end - buf);
+      if (ret == 0)
+        {
+          if (!eof_ok)
+            {
+              support_record_failure ();
+              printf ("error: unexpected EOF on TCP connection\n");
+            }
+          return false;
+        }
+      else if (ret < 0)
+        {
+          if (!eof_ok || errno != ECONNRESET)
+            {
+              support_record_failure ();
+              printf ("error: TCP read: %m\n");
+            }
+          return false;
+        }
+      buf += ret;
+      eof_ok = false;
+    }
+  return true;
+}
+
+/* Write an array of iovecs.  Terminate the process on failure.  */
+static void
+writev_fully (int fd, struct iovec *buffers, size_t count)
+{
+  while (count > 0)
+    {
+      /* Skip zero-length write requests.  */
+      if (buffers->iov_len == 0)
+        {
+          ++buffers;
+          --count;
+          continue;
+        }
+      /* Try to rewrite the remaing buffers.  */
+      ssize_t ret = writev (fd, buffers, count);
+      if (ret < 0)
+        FAIL_EXIT1 ("writev: %m");
+      if (ret == 0)
+        FAIL_EXIT1 ("writev: invalid return value zero");
+      /* Find the buffers that were successfully written.  */
+      while (ret > 0)
+        {
+          if (count == 0)
+            FAIL_EXIT1 ("internal writev consistency failure");
+          /* Current buffer was partially written.  */
+          if (buffers->iov_len > (size_t) ret)
+            {
+              buffers->iov_base += ret;
+              buffers->iov_len -= ret;
+              ret = 0;
+            }
+          else
+            {
+              ret -= buffers->iov_len;
+              buffers->iov_len = 0;
+              ++buffers;
+              --count;
+            }
+        }
+    }
+}
+
+/* Thread callback for handling a single established TCP connection to
+   a client.  */
+static void *
+server_thread_tcp_client (void *arg)
+{
+  struct tcp_thread_closure *closure = arg;
+
+  while (true)
+    {
+      /* Read packet length.  */
+      uint16_t query_length;
+      if (!read_fully (closure->client_socket,
+                       &query_length, sizeof (query_length), true))
+        break;
+      query_length = ntohs (query_length);
+
+      /* Read the packet.  */
+      unsigned char *query_buffer = xmalloc (query_length);
+      read_fully (closure->client_socket, query_buffer, query_length, false);
+
+      struct query_info qinfo;
+      parse_query (&qinfo, query_buffer, query_length);
+      if (test_verbose > 0)
+        {
+          if (test_verbose > 1)
+            printf ("info: UDP server %d: incoming query:"
+                    " %d bytes, %s/%u/%u, tnxid=0x%02x%02x\n",
+                    closure->server_index, query_length,
+                    qinfo.qname, qinfo.qclass, qinfo.qtype,
+                    query_buffer[0], query_buffer[1]);
+          else
+            printf ("info: TCP server %d: incoming query:"
+                    " %u bytes, %s/%u/%u\n",
+                    closure->server_index, query_length,
+                    qinfo.qname, qinfo.qclass, qinfo.qtype);
+        }
+
+      struct resolv_response_context ctx =
+        {
+          .query_buffer = query_buffer,
+          .query_length = query_length,
+          .server_index = closure->server_index,
+          .tcp = true,
+          .edns = qinfo.edns,
+        };
+      struct resolv_response_builder *b = response_builder_allocate
+        (query_buffer, query_length);
+      closure->obj->config.response_callback
+        (&ctx, b, qinfo.qname, qinfo.qclass, qinfo.qtype);
+
+      if (b->drop)
+        {
+          if (test_verbose)
+            printf ("info: TCP server %d: dropping response to %s/%u/%u\n",
+                    closure->server_index,
+                    qinfo.qname, qinfo.qclass, qinfo.qtype);
+        }
+      else
+        {
+          if (test_verbose)
+            printf ("info: TCP server %d: sending response: %zu bytes"
+                    " (for %s/%u/%u)\n",
+                    closure->server_index, b->offset,
+                    qinfo.qname, qinfo.qclass, qinfo.qtype);
+          uint16_t length = htons (b->offset);
+          size_t to_send = b->offset;
+          if (to_send < b->truncate_bytes)
+            to_send = 0;
+          else
+            to_send -= b->truncate_bytes;
+          struct iovec buffers[2] =
+            {
+              {&length, sizeof (length)},
+              {b->buffer, to_send}
+            };
+          writev_fully (closure->client_socket, buffers, 2);
+        }
+      bool close_flag = b->close;
+      response_builder_free (b);
+      free (query_buffer);
+      if (close_flag)
+        break;
+    }
+
+  xclose (closure->client_socket);
+  free (closure);
+  return NULL;
+}
+
+/* thread_callback for the TCP case.  Accept connections and create a
+   new thread for each client.  */
+static void
+server_thread_tcp (struct resolv_test *obj, int server_index)
+{
+  while (true)
+    {
+      /* Get the client conenction.  */
+      int client_socket = xaccept
+        (obj->servers[server_index].socket_tcp, NULL, NULL);
+
+      /* Check for termination.  */
+      xpthread_mutex_lock (&obj->lock);
+      if (obj->termination_requested)
+        {
+          xpthread_mutex_unlock (&obj->lock);
+          xclose (client_socket);
+          break;
+        }
+      xpthread_mutex_unlock (&obj->lock);
+
+      /* Spawn a new thread for handling this connection.  */
+      struct tcp_thread_closure *closure = xmalloc (sizeof (*closure));
+      *closure = (struct tcp_thread_closure)
+        {
+          .obj = obj,
+          .server_index = server_index,
+          .client_socket = client_socket,
+        };
+
+      pthread_t thr
+        = xpthread_create (NULL, server_thread_tcp_client, closure);
+      /* TODO: We should keep track of this thread so that we can
+         block in resolv_test_end until it has exited.  */
+      xpthread_detach (thr);
+    }
+}
+
+/* Create UDP and TCP server sockets.  */
+static void
+make_server_sockets (struct resolv_test_server *server)
+{
+  while (true)
+    {
+      server->socket_udp = xsocket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+      server->socket_tcp = xsocket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+      /* Pick the address for the UDP socket.  */
+      server->address = (struct sockaddr_in)
+        {
+          .sin_family = AF_INET,
+          .sin_addr = {.s_addr = htonl (INADDR_LOOPBACK)}
+        };
+      xbind (server->socket_udp,
+             (struct sockaddr *)&server->address, sizeof (server->address));
+
+      /* Retrieve the address. */
+      socklen_t addrlen = sizeof (server->address);
+      xgetsockname (server->socket_udp,
+                    (struct sockaddr *)&server->address, &addrlen);
+
+      /* Bind the TCP socket to the same address.  */
+      {
+        int on = 1;
+        xsetsockopt (server->socket_tcp, SOL_SOCKET, SO_REUSEADDR,
+                     &on, sizeof (on));
+      }
+      if (bind (server->socket_tcp,
+                (struct sockaddr *)&server->address,
+                sizeof (server->address)) != 0)
+        {
+          /* Port collision.  The UDP bind succeeded, but the TCP BIND
+             failed.  We assume here that the kernel will pick the
+             next local UDP address randomly.  */
+          if (errno == EADDRINUSE)
+            {
+              xclose (server->socket_udp);
+              xclose (server->socket_tcp);
+              continue;
+            }
+          FAIL_EXIT1 ("TCP bind: %m");
+        }
+      xlisten (server->socket_tcp, 5);
+      break;
+    }
+}
+
+/* One-time initialization of NSS.  */
+static void
+resolv_redirect_once (void)
+{
+  /* Only use nss_dns.  */
+  __nss_configure_lookup ("hosts", "dns");
+  __nss_configure_lookup ("networks", "dns");
+  /* Enter a network namespace for isolation and firewall state
+     cleanup.  The tests will still work if these steps fail, but they
+     may be less reliable.  */
+  support_become_root ();
+  support_enter_network_namespace ();
+}
+pthread_once_t resolv_redirect_once_var = PTHREAD_ONCE_INIT;
+
+void
+resolv_test_init (void)
+{
+  /* Perform one-time initialization of NSS.  */
+  xpthread_once (&resolv_redirect_once_var, resolv_redirect_once);
+}
+
+/* Copy the search path from CONFIG.search to the _res object.  */
+static void
+set_search_path (struct resolv_redirect_config config)
+{
+  memset (_res.defdname, 0, sizeof (_res.defdname));
+  memset (_res.dnsrch, 0, sizeof (_res.dnsrch));
+
+  char *current = _res.defdname;
+  char *end = current + sizeof (_res.defdname);
+
+  for (unsigned int i = 0;
+       i < sizeof (config.search) / sizeof (config.search[0]); ++i)
+    {
+      if (config.search[i] == NULL)
+        continue;
+
+      size_t length = strlen (config.search[i]) + 1;
+      size_t remaining = end - current;
+      TEST_VERIFY_EXIT (length <= remaining);
+      memcpy (current, config.search[i], length);
+      _res.dnsrch[i] = current;
+      current += length;
+    }
+}
+
+struct resolv_test *
+resolv_test_start (struct resolv_redirect_config config)
+{
+  /* Apply configuration defaults.  */
+  if (config.nscount == 0)
+    config.nscount = resolv_max_test_servers;
+
+  struct resolv_test *obj = xmalloc (sizeof (*obj));
+  *obj = (struct resolv_test) {
+    .config = config,
+    .lock = PTHREAD_MUTEX_INITIALIZER,
+  };
+
+  resolv_test_init ();
+
+  /* Create all the servers, to reserve the necessary ports.  */
+  for (int server_index = 0; server_index < config.nscount; ++server_index)
+    make_server_sockets (obj->servers + server_index);
+
+  /* Start server threads.  Disable the server ports, as
+     requested.  */
+  for (int server_index = 0; server_index < config.nscount; ++server_index)
+    {
+      struct resolv_test_server *server = obj->servers + server_index;
+      if (config.servers[server_index].disable_udp)
+        {
+          xclose (server->socket_udp);
+          server->socket_udp = -1;
+        }
+      else if (!config.single_thread_udp)
+        server->thread_udp = start_server_thread (obj, server_index,
+                                                  server_thread_udp);
+      if (config.servers[server_index].disable_tcp)
+        {
+          xclose (server->socket_tcp);
+          server->socket_tcp = -1;
+        }
+      else
+        server->thread_tcp = start_server_thread (obj, server_index,
+                                                  server_thread_tcp);
+    }
+  if (config.single_thread_udp)
+    start_server_thread_udp_single (obj);
+
+  int timeout = 1;
+
+  /* Initialize libresolv.  */
+  TEST_VERIFY_EXIT (res_init () == 0);
+
+  /* Disable IPv6 name server addresses.  The code below only
+     overrides the IPv4 addresses.  */
+  __res_iclose (&_res, true);
+  _res._u._ext.nscount = 0;
+
+  /* Redirect queries to the server socket.  */
+  if (test_verbose)
+    {
+      printf ("info: old timeout value: %d\n", _res.retrans);
+      printf ("info: old retry attempt value: %d\n", _res.retry);
+      printf ("info: old _res.options: 0x%lx\n", _res.options);
+      printf ("info: old _res.nscount value: %d\n", _res.nscount);
+      printf ("info: old _res.ndots value: %d\n", _res.ndots);
+    }
+  _res.retrans = timeout;
+  _res.retry = 4;
+  _res.nscount = config.nscount;
+  _res.options = RES_INIT | RES_RECURSE | RES_DEFNAMES | RES_DNSRCH;
+  _res.ndots = 1;
+  if (test_verbose)
+    {
+      printf ("info: new timeout value: %d\n", _res.retrans);
+      printf ("info: new retry attempt value: %d\n", _res.retry);
+      printf ("info: new _res.options: 0x%lx\n", _res.options);
+      printf ("info: new _res.nscount value: %d\n", _res.nscount);
+      printf ("info: new _res.ndots value: %d\n", _res.ndots);
+    }
+  for (int server_index = 0; server_index < config.nscount; ++server_index)
+    {
+      _res.nsaddr_list[server_index] = obj->servers[server_index].address;
+      if (test_verbose)
+        {
+          char buf[256];
+          TEST_VERIFY_EXIT
+            (inet_ntop (AF_INET, &obj->servers[server_index].address.sin_addr,
+                        buf, sizeof (buf)) != NULL);
+          printf ("info: server %d: %s/%u\n",
+                  server_index, buf,
+                  htons (obj->servers[server_index].address.sin_port));
+        }
+    }
+
+  set_search_path (config);
+
+  return obj;
+}
+
+void
+resolv_test_end (struct resolv_test *obj)
+{
+  res_close ();
+
+  xpthread_mutex_lock (&obj->lock);
+  obj->termination_requested = true;
+  xpthread_mutex_unlock (&obj->lock);
+
+  /* Send trigger packets to unblock the server threads.  */
+  for (int server_index = 0; server_index < obj->config.nscount;
+       ++server_index)
+    {
+      if (!obj->config.servers[server_index].disable_udp)
+        {
+          int sock = xsocket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+          xsendto (sock, "", 1, 0,
+                   (struct sockaddr *) &obj->servers[server_index].address,
+                   sizeof (obj->servers[server_index].address));
+          xclose (sock);
+        }
+      if (!obj->config.servers[server_index].disable_tcp)
+        {
+          int sock = xsocket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
+          xconnect (sock,
+                    (struct sockaddr *) &obj->servers[server_index].address,
+                    sizeof (obj->servers[server_index].address));
+          xclose (sock);
+        }
+    }
+
+  if (obj->config.single_thread_udp)
+    xpthread_join (obj->thread_udp_single);
+
+  /* Wait for the server threads to terminate.  */
+  for (int server_index = 0; server_index < obj->config.nscount;
+       ++server_index)
+    {
+      if (!obj->config.servers[server_index].disable_udp)
+        {
+          if (!obj->config.single_thread_udp)
+            xpthread_join (obj->servers[server_index].thread_udp);
+          xclose (obj->servers[server_index].socket_udp);
+        }
+      if (!obj->config.servers[server_index].disable_tcp)
+        {
+          xpthread_join (obj->servers[server_index].thread_tcp);
+          xclose (obj->servers[server_index].socket_tcp);
+        }
+    }
+
+  free (obj);
+}
diff --git a/support/resolv_test.h b/support/resolv_test.h
new file mode 100644
index 0000000..6498751
--- /dev/null
+++ b/support/resolv_test.h
@@ -0,0 +1,180 @@
+/* DNS test framework and libresolv redirection.
+   Copyright (C) 2016-2017 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/>.  */
+
+#ifndef SUPPORT_RESOLV_TEST_H
+#define SUPPORT_RESOLV_TEST_H
+
+#include <arpa/nameser.h>
+#include <stdbool.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/* Information about EDNS properties of a DNS query.  */
+struct resolv_edns_info
+{
+  bool active;
+  uint8_t extended_rcode;
+  uint8_t version;
+  uint16_t flags;
+  uint16_t payload_size;
+};
+
+/* This struct provides context information when the response callback
+   specified in struct resolv_redirect_config is invoked. */
+struct resolv_response_context
+{
+  const unsigned char *query_buffer;
+  size_t query_length;
+  int server_index;
+  bool tcp;
+  struct resolv_edns_info edns;
+};
+
+/* This opaque struct is used to construct responses from within the
+   response callback function.  */
+struct resolv_response_builder;
+
+/* This opaque struct collects information about the resolver testing
+   currently in progress.  */
+struct resolv_test;
+
+enum
+  {
+    /* Maximum number of test servers supported by the framework.  */
+    resolv_max_test_servers = 3,
+  };
+
+/* Configuration settings specific to individual test servers.  */
+struct resolv_redirect_server_config
+{
+  bool disable_tcp;             /* If true, no TCP server is listening.  */
+  bool disable_udp;             /* If true, no UDP server is listening.  */
+};
+
+/* Instructions for setting up the libresolv redirection.  */
+struct resolv_redirect_config
+{
+  /* The response_callback function is called for every incoming DNS
+     packet, over UDP or TCP.  It must be specified, the other
+     configuration settings are optional.  */
+  void (*response_callback) (const struct resolv_response_context *,
+                             struct resolv_response_builder *,
+                             const char *qname,
+                             uint16_t qclass, uint16_t qtype);
+
+  /* Per-server configuration.  */
+  struct resolv_redirect_server_config servers[resolv_max_test_servers];
+
+  /* Search path entries.  The first entry serves as the default
+     domain name as well.  */
+  const char *search[7];
+
+  /* Number of servers to activate in resolv.  0 means the default,
+     resolv_max_test_servers.  */
+  int nscount;
+
+  /* If true, use a single thread to process all UDP queries.  This
+     may results in more predictable ordering of queries and
+     responses.  */
+  bool single_thread_udp;
+};
+
+/* Configure NSS to use, nss_dns only for aplicable databases, and try
+   to put the process into a network namespace for better isolation.
+   This may have to be called before resolv_test_start, before the
+   process creates any threads.  Otherwise, initialization is
+   performed by resolv_test_start implicitly.  */
+void resolv_test_init (void);
+
+/* Initiate resolver testing.  This updates the _res variable as
+   needed.  As a side effect, NSS is reconfigured to use nss_dns only
+   for aplicable databases, and the process may enter a network
+   namespace for better isolation.  */
+struct resolv_test *resolv_test_start (struct resolv_redirect_config);
+
+/* Call this function at the end of resolver testing, to free
+   resources and report pending errors (if any).  */
+void resolv_test_end (struct resolv_test *);
+
+/* The remaining facilities in this file are used for constructing
+   response packets from the response_callback function.  */
+
+/* Special settings for constructing responses from the callback.  */
+struct resolv_response_flags
+{
+  /* 4-bit response code to incorporate into the response. */
+  unsigned char rcode;
+
+  /* If true, the TC (truncation) flag will be set.  */
+  bool tc;
+
+  /* Initial section count values.  Can be used to artificially
+     increase the counts, for malformed packet testing.*/
+  unsigned short qdcount;
+  unsigned short ancount;
+  unsigned short nscount;
+  unsigned short adcount;
+};
+
+/* Begin a new response with the requested flags.  Must be called
+   first.  */
+void resolv_response_init (struct resolv_response_builder *,
+                           struct resolv_response_flags);
+
+/* Switches to the section in the response packet.  Only forward
+   movement is supported.  */
+void resolv_response_section (struct resolv_response_builder *, ns_sect);
+
+/* Add a question record to the question section.  */
+void resolv_response_add_question (struct resolv_response_builder *,
+                                   const char *name, uint16_t class,
+                                   uint16_t type);
+/* Starts a new resource record with the specified owner name, class,
+   type, and TTL.  Data is supplied with resolv_response_add_data or
+   resolv_response_add_name.  */
+void resolv_response_open_record (struct resolv_response_builder *,
+                                  const char *name, uint16_t class,
+                                  uint16_t type, uint32_t ttl);
+
+/* Add unstructed bytes to the RDATA part of a resource record.  */
+void resolv_response_add_data (struct resolv_response_builder *,
+                               const void *, size_t);
+
+/* Add a compressed domain name to the RDATA part of a resource
+   record.  */
+void resolv_response_add_name (struct resolv_response_builder *,
+                               const char *name);
+
+/* Mark the end of the constructed record.  Must be called last.  */
+void resolv_response_close_record (struct resolv_response_builder *);
+
+/* Drop this query packet (that is, do not send a response, not even
+   an empty packet).  */
+void resolv_response_drop (struct resolv_response_builder *);
+
+/* In TCP mode, close the connection after this packet (if a response
+   is sent).  */
+void resolv_response_close (struct resolv_response_builder *);
+
+/* The size of the response packet built so far.  */
+size_t resolv_response_length (const struct resolv_response_builder *);
+
+__END_DECLS
+
+#endif /* SUPPORT_RESOLV_TEST_H */
diff --git a/support/run_diff.h b/support/run_diff.h
new file mode 100644
index 0000000..f65b5dd
--- /dev/null
+++ b/support/run_diff.h
@@ -0,0 +1,31 @@
+/* Invoke the system diff tool to compare two strings.
+   Copyright (C) 2016-2017 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/>.  */
+
+#ifndef SUPPORT_RUN_DIFF_H
+#define SUPPORT_RUN_DIFF_H
+
+/* Compare the two NUL-terminated strings LEFT and RIGHT using the
+   diff tool.  Label the sides of the diff with LEFT_LABEL and
+   RIGHT_LABEL, respectively.
+
+   This function assumes that LEFT and RIGHT are different
+   strings.  */
+void support_run_diff (const char *left_label, const char *left,
+                       const char *right_label, const char *right);
+
+#endif /* SUPPORT_RUN_DIFF_H */
diff --git a/support/set_fortify_handler.c b/support/set_fortify_handler.c
new file mode 100644
index 0000000..f434a80
--- /dev/null
+++ b/support/set_fortify_handler.c
@@ -0,0 +1,34 @@
+/* Set signal handler for use in fortify tests.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/support.h>
+
+#include <signal.h>
+
+void
+set_fortify_handler (void (*handler) (int sig))
+{
+  struct sigaction sa;
+
+  sa.sa_handler = handler;
+  sa.sa_flags = 0;
+  sigemptyset (&sa.sa_mask);
+
+  sigaction (SIGABRT, &sa, NULL);
+  ignore_stderr ();
+}
diff --git a/support/support-xstat.c b/support/support-xstat.c
new file mode 100644
index 0000000..86a81ec
--- /dev/null
+++ b/support/support-xstat.c
@@ -0,0 +1,30 @@
+/* stat64 with error checking.
+   Copyright (C) 2017 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/>.  */
+
+/* NB: Non-standard file name to avoid sysdeps override for xstat.  */
+
+#include <support/check.h>
+#include <support/xunistd.h>
+#include <sys/stat.h>
+
+void
+xstat (const char *path, struct stat64 *result)
+{
+  if (stat64 (path, result) != 0)
+    FAIL_EXIT1 ("stat64 (\"%s\"): %m", path);
+}
diff --git a/support/support.h b/support/support.h
new file mode 100644
index 0000000..4b5f04c
--- /dev/null
+++ b/support/support.h
@@ -0,0 +1,74 @@
+/* Common extra functions.
+   Copyright (C) 2016-2017 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/>.  */
+
+/* This header file should only contain definitions compatible with
+   C90.  (Using __attribute__ is fine because <features.h> provides a
+   fallback.)  */
+
+#ifndef SUPPORT_H
+#define SUPPORT_H
+
+#include <stddef.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/* Write a message to standard output.  Can be used in signal
+   handlers.  */
+void write_message (const char *message) __attribute__ ((nonnull (1)));
+
+/* Avoid all the buffer overflow messages on stderr.  */
+void ignore_stderr (void);
+
+/* Set fortification error handler.  Used when tests want to verify that bad
+   code is caught by the library.  */
+void set_fortify_handler (void (*handler) (int sig));
+
+/* Report an out-of-memory error for the allocation of SIZE bytes in
+   FUNCTION, terminating the process.  */
+void oom_error (const char *function, size_t size)
+  __attribute__ ((nonnull (1)));
+
+/* Return a pointer to a memory region of SIZE bytes.  The memory is
+   initialized to zero and will be shared with subprocesses (across
+   fork).  The returned pointer must be freed using
+   support_shared_free; it is not compatible with the malloc
+   functions.  */
+void *support_shared_allocate (size_t size);
+
+/* Deallocate a pointer returned by support_shared_allocate.  */
+void support_shared_free (void *);
+
+/* Write CONTENTS to the file PATH.  Create or truncate the file as
+   needed.  The file mode is 0666 masked by the umask.  Terminate the
+   process on error.  */
+void support_write_file_string (const char *path, const char *contents);
+
+/* Error-checking wrapper functions which terminate the process on
+   error.  */
+
+void *xmalloc (size_t) __attribute__ ((malloc));
+void *xcalloc (size_t n, size_t s) __attribute__ ((malloc));
+void *xrealloc (void *p, size_t n);
+char *xasprintf (const char *format, ...)
+  __attribute__ ((format (printf, 1, 2), malloc));
+char *xstrdup (const char *);
+
+__END_DECLS
+
+#endif /* SUPPORT_H */
diff --git a/support/support_become_root.c b/support/support_become_root.c
new file mode 100644
index 0000000..3fa0bd4
--- /dev/null
+++ b/support/support_become_root.c
@@ -0,0 +1,40 @@
+/* Acquire root privileges.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/namespace.h>
+
+#include <sched.h>
+#include <stdio.h>
+#include <unistd.h>
+
+bool
+support_become_root (void)
+{
+#ifdef CLONE_NEWUSER
+  if (unshare (CLONE_NEWUSER | CLONE_NEWNS) == 0)
+    /* Even if we do not have UID zero, we have extended privileges at
+       this point.  */
+    return true;
+#endif
+  if (setuid (0) != 0)
+    {
+      printf ("warning: could not become root outside namespace (%m)\n");
+      return false;
+    }
+  return true;
+}
diff --git a/support/support_can_chroot.c b/support/support_can_chroot.c
new file mode 100644
index 0000000..0dfd2de
--- /dev/null
+++ b/support/support_can_chroot.c
@@ -0,0 +1,65 @@
+/* Return true if the process can perform a chroot operation.
+   Copyright (C) 2017 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/>.  */
+
+#include <errno.h>
+#include <stdio.h>
+#include <support/check.h>
+#include <support/namespace.h>
+#include <support/support.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <xunistd.h>
+
+static void
+callback (void *closure)
+{
+  int *result = closure;
+  struct stat64 before;
+  xstat ("/dev", &before);
+  if (chroot ("/dev") != 0)
+    {
+      *result = errno;
+      return;
+    }
+  struct stat64 after;
+  xstat ("/", &after);
+  TEST_VERIFY (before.st_dev == after.st_dev);
+  TEST_VERIFY (before.st_ino == after.st_ino);
+  *result = 0;
+}
+
+bool
+support_can_chroot (void)
+{
+  int *result = support_shared_allocate (sizeof (*result));
+  *result = 0;
+  support_isolate_in_subprocess (callback, result);
+  bool ok = *result == 0;
+  if (!ok)
+    {
+      static bool already_warned;
+      if (!already_warned)
+        {
+          already_warned = true;
+          errno = *result;
+          printf ("warning: this process does not support chroot: %m\n");
+        }
+    }
+  support_shared_free (result);
+  return ok;
+}
diff --git a/support/support_capture_subprocess.c b/support/support_capture_subprocess.c
new file mode 100644
index 0000000..030f124
--- /dev/null
+++ b/support/support_capture_subprocess.c
@@ -0,0 +1,108 @@
+/* Capture output from a subprocess.
+   Copyright (C) 2017 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/>.  */
+
+#include <support/capture_subprocess.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/xunistd.h>
+#include <support/xsocket.h>
+
+static void
+transfer (const char *what, struct pollfd *pfd, struct xmemstream *stream)
+{
+  if (pfd->revents != 0)
+    {
+      char buf[1024];
+      ssize_t ret = TEMP_FAILURE_RETRY (read (pfd->fd, buf, sizeof (buf)));
+      if (ret < 0)
+        {
+          support_record_failure ();
+          printf ("error: reading from subprocess %s: %m", what);
+          pfd->events = 0;
+          pfd->revents = 0;
+        }
+      else if (ret == 0)
+        {
+          /* EOF reached.  Stop listening.  */
+          pfd->events = 0;
+          pfd->revents = 0;
+        }
+      else
+        /* Store the data just read.   */
+        TEST_VERIFY (fwrite (buf, ret, 1, stream->out) == 1);
+    }
+}
+
+struct support_capture_subprocess
+support_capture_subprocess (void (*callback) (void *), void *closure)
+{
+  struct support_capture_subprocess result;
+  xopen_memstream (&result.out);
+  xopen_memstream (&result.err);
+
+  int stdout_pipe[2];
+  xpipe (stdout_pipe);
+  int stderr_pipe[2];
+  xpipe (stderr_pipe);
+
+  TEST_VERIFY (fflush (stdout) == 0);
+  TEST_VERIFY (fflush (stderr) == 0);
+
+  pid_t pid = xfork ();
+  if (pid == 0)
+    {
+      xclose (stdout_pipe[0]);
+      xclose (stderr_pipe[0]);
+      xdup2 (stdout_pipe[1], STDOUT_FILENO);
+      xdup2 (stderr_pipe[1], STDERR_FILENO);
+      callback (closure);
+      _exit (0);
+    }
+  xclose (stdout_pipe[1]);
+  xclose (stderr_pipe[1]);
+
+  struct pollfd fds[2] =
+    {
+      { .fd = stdout_pipe[0], .events = POLLIN },
+      { .fd = stderr_pipe[0], .events = POLLIN },
+    };
+
+  do
+    {
+      xpoll (fds, 2, -1);
+      transfer ("stdout", &fds[0], &result.out);
+      transfer ("stderr", &fds[1], &result.err);
+    }
+  while (fds[0].events != 0 || fds[1].events != 0);
+  xclose (stdout_pipe[0]);
+  xclose (stderr_pipe[0]);
+
+  xfclose_memstream (&result.out);
+  xfclose_memstream (&result.err);
+  xwaitpid (pid, &result.status, 0);
+  return result;
+}
+
+void
+support_capture_subprocess_free (struct support_capture_subprocess *p)
+{
+  free (p->out.buffer);
+  free (p->err.buffer);
+}
diff --git a/support/support_capture_subprocess_check.c b/support/support_capture_subprocess_check.c
new file mode 100644
index 0000000..708c89f
--- /dev/null
+++ b/support/support_capture_subprocess_check.c
@@ -0,0 +1,67 @@
+/* Verify capture output from a subprocess.
+   Copyright (C) 2017 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/>.  */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <support/capture_subprocess.h>
+#include <support/check.h>
+
+static void
+print_context (const char *context, bool *failed)
+{
+  if (*failed)
+    /* Do not duplicate message.  */
+    return;
+  support_record_failure ();
+  printf ("error: subprocess failed: %s\n", context);
+}
+
+void
+support_capture_subprocess_check (struct support_capture_subprocess *proc,
+                                  const char *context, int status,
+                                  int allowed)
+{
+  TEST_VERIFY ((allowed & sc_allow_none)
+               || (allowed & sc_allow_stdout)
+               || (allowed & sc_allow_stderr));
+  TEST_VERIFY (!((allowed & sc_allow_none)
+                 && ((allowed & sc_allow_stdout)
+                     || (allowed & sc_allow_stderr))));
+
+  bool failed = false;
+  if (proc->status != status)
+    {
+      print_context (context, &failed);
+      printf ("error:   expected exit status: %d\n", status);
+      printf ("error:   actual exit status:   %d\n", status);
+    }
+  if (!(allowed & sc_allow_stdout) && proc->out.length != 0)
+    {
+      print_context (context, &failed);
+      printf ("error:   unexpected output from subprocess\n");
+      fwrite (proc->out.buffer, proc->out.length, 1, stdout);
+      puts ("\n");
+    }
+  if (!(allowed & sc_allow_stderr) && proc->err.length != 0)
+    {
+      print_context (context, &failed);
+      printf ("error:   unexpected error output from subprocess\n");
+      fwrite (proc->err.buffer, proc->err.length, 1, stdout);
+      puts ("\n");
+    }
+}
diff --git a/support/support_enter_network_namespace.c b/support/support_enter_network_namespace.c
new file mode 100644
index 0000000..28b0ee2
--- /dev/null
+++ b/support/support_enter_network_namespace.c
@@ -0,0 +1,75 @@
+/* Enter a network namespace.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/namespace.h>
+
+#include <net/if.h>
+#include <sched.h>
+#include <stdio.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/xsocket.h>
+#include <support/xunistd.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+static bool in_uts_namespace;
+
+bool
+support_enter_network_namespace (void)
+{
+#ifdef CLONE_NEWUTS
+  if (unshare (CLONE_NEWUTS) == 0)
+    in_uts_namespace = true;
+  else
+    printf ("warning: unshare (CLONE_NEWUTS) failed: %m\n");
+#endif
+
+#ifdef CLONE_NEWNET
+  if (unshare (CLONE_NEWNET) == 0)
+    {
+      /* Bring up the loopback interface.  */
+      int fd = xsocket (AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+      struct ifreq req;
+      strcpy (req.ifr_name, "lo");
+      TEST_VERIFY_EXIT (ioctl (fd, SIOCGIFFLAGS, &req) == 0);
+      bool already_up = req.ifr_flags & IFF_UP;
+      if (already_up)
+        /* This means that we likely have not achieved isolation from
+           the parent namespace.  */
+        printf ("warning: loopback interface already exists"
+                " in new network namespace\n");
+      else
+        {
+          req.ifr_flags |= IFF_UP | IFF_RUNNING;
+          TEST_VERIFY_EXIT (ioctl (fd, SIOCSIFFLAGS, &req) == 0);
+        }
+      xclose (fd);
+
+      return !already_up;
+    }
+#endif
+  printf ("warning: could not enter network namespace\n");
+  return false;
+}
+
+bool
+support_in_uts_namespace (void)
+{
+  return in_uts_namespace;
+}
diff --git a/support/support_format_address_family.c b/support/support_format_address_family.c
new file mode 100644
index 0000000..5d42c42
--- /dev/null
+++ b/support/support_format_address_family.c
@@ -0,0 +1,35 @@
+/* Convert an address family to a string.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/format_nss.h>
+
+#include <support/support.h>
+
+char *
+support_format_address_family (int family)
+{
+  switch (family)
+    {
+    case AF_INET:
+      return xstrdup ("INET");
+    case AF_INET6:
+      return xstrdup ("INET6");
+    default:
+      return xasprintf ("<unknown address family %d>", family);
+    }
+}
diff --git a/support/support_format_addrinfo.c b/support/support_format_addrinfo.c
new file mode 100644
index 0000000..eedb030
--- /dev/null
+++ b/support/support_format_addrinfo.c
@@ -0,0 +1,239 @@
+/* Convert struct addrinfo values to a string.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/format_nss.h>
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <stdio.h>
+#include <support/support.h>
+#include <support/xmemstream.h>
+
+static size_t
+socket_address_length (int family)
+{
+  switch (family)
+    {
+    case AF_INET:
+      return sizeof (struct sockaddr_in);
+    case AF_INET6:
+      return sizeof (struct sockaddr_in6);
+    default:
+      return -1;
+    }
+}
+
+static void
+format_ai_flags_1 (FILE *out, struct addrinfo *ai, int flag, const char *name,
+                   int * flags_printed)
+{
+  if ((ai->ai_flags & flag) != 0)
+    fprintf (out, " %s", name);
+  *flags_printed |= flag;
+}
+
+static void
+format_ai_flags (FILE *out, struct addrinfo *ai)
+{
+  if (ai == NULL)
+    return;
+
+  if (ai->ai_flags != 0)
+    {
+      fprintf (out, "flags:");
+      int flags_printed = 0;
+#define FLAG(flag) format_ai_flags_1 (out, ai, flag, #flag, &flags_printed)
+      FLAG (AI_PASSIVE);
+      FLAG (AI_CANONNAME);
+      FLAG (AI_NUMERICHOST);
+      FLAG (AI_V4MAPPED);
+      FLAG (AI_ALL);
+      FLAG (AI_ADDRCONFIG);
+      FLAG (AI_IDN);
+      FLAG (AI_CANONIDN);
+      FLAG (AI_IDN_ALLOW_UNASSIGNED);
+      FLAG (AI_IDN_USE_STD3_ASCII_RULES);
+      FLAG (AI_NUMERICSERV);
+#undef FLAG
+      int remaining = ai->ai_flags & ~flags_printed;
+      if (remaining != 0)
+        fprintf (out, " %08x", remaining);
+      fprintf (out, "\n");
+    }
+
+  /* Report flag mismatches within the list.  */
+  int flags = ai->ai_flags;
+  int index = 1;
+  ai = ai->ai_next;
+  while (ai != NULL)
+    {
+      if (ai->ai_flags != flags)
+        fprintf (out, "error: flags at %d: 0x%x expected, 0x%x actual\n",
+                 index, flags, ai->ai_flags);
+      ai = ai->ai_next;
+      ++index;
+    }
+}
+
+static void
+format_ai_canonname (FILE *out, struct addrinfo *ai)
+{
+  if (ai == NULL)
+    return;
+  if (ai->ai_canonname != NULL)
+    fprintf (out, "canonname: %s\n", ai->ai_canonname);
+
+  /* Report incorrectly set ai_canonname fields on subsequent list
+     entries.  */
+  int index = 1;
+  ai = ai->ai_next;
+  while (ai != NULL)
+    {
+      if (ai->ai_canonname != NULL)
+        fprintf (out, "error: canonname set at %d: %s\n",
+                 index, ai->ai_canonname);
+      ai = ai->ai_next;
+      ++index;
+    }
+}
+
+static void
+format_ai_one (FILE *out, struct addrinfo *ai)
+{
+  {
+    char type_buf[32];
+    const char *type_str;
+    char proto_buf[32];
+    const char *proto_str;
+
+    /* ai_socktype */
+    switch (ai->ai_socktype)
+      {
+      case SOCK_RAW:
+        type_str = "RAW";
+        break;
+      case SOCK_DGRAM:
+        type_str = "DGRAM";
+        break;
+      case SOCK_STREAM:
+        type_str = "STREAM";
+        break;
+      default:
+        snprintf (type_buf, sizeof (type_buf), "%d", ai->ai_socktype);
+        type_str = type_buf;
+      }
+
+    /* ai_protocol */
+    switch (ai->ai_protocol)
+      {
+      case IPPROTO_IP:
+        proto_str = "IP";
+        break;
+      case IPPROTO_UDP:
+        proto_str = "UDP";
+        break;
+      case IPPROTO_TCP:
+        proto_str = "TCP";
+        break;
+      default:
+        snprintf (proto_buf, sizeof (proto_buf), "%d", ai->ai_protocol);
+        proto_str = proto_buf;
+      }
+    fprintf (out, "address: %s/%s", type_str, proto_str);
+  }
+
+  /* ai_addrlen */
+  if (ai->ai_addrlen != socket_address_length (ai->ai_family))
+    {
+      char *family = support_format_address_family (ai->ai_family);
+      fprintf (out, "error: invalid address length %d for %s\n",
+               ai->ai_addrlen, family);
+      free (family);
+    }
+
+  /* ai_addr */
+  {
+    char buf[128];
+    uint16_t port;
+    const char *ret;
+    switch (ai->ai_family)
+      {
+      case AF_INET:
+        {
+          struct sockaddr_in *sin = (struct sockaddr_in *) ai->ai_addr;
+          ret = inet_ntop (AF_INET, &sin->sin_addr, buf, sizeof (buf));
+          port = sin->sin_port;
+        }
+        break;
+      case AF_INET6:
+        {
+          struct sockaddr_in6 *sin = (struct sockaddr_in6 *) ai->ai_addr;
+          ret = inet_ntop (AF_INET6, &sin->sin6_addr, buf, sizeof (buf));
+          port = sin->sin6_port;
+        }
+        break;
+      default:
+        errno = EAFNOSUPPORT;
+        ret = NULL;
+      }
+    if (ret == NULL)
+        fprintf (out, "error: inet_top failed: %m\n");
+    else
+      fprintf (out, " %s %u\n", buf, ntohs (port));
+  }
+}
+
+/* Format all the addresses in one address family.  */
+static void
+format_ai_family (FILE *out, struct addrinfo *ai, int family)
+{
+  while (ai)
+    {
+      if (ai->ai_family == family)
+        format_ai_one (out, ai);
+      ai = ai->ai_next;
+    }
+}
+
+char *
+support_format_addrinfo (struct addrinfo *ai, int ret)
+{
+  int errno_copy = errno;
+
+  struct xmemstream mem;
+  xopen_memstream (&mem);
+  if (ret != 0)
+    {
+      fprintf (mem.out, "error: %s\n", gai_strerror (ret));
+      if (ret == EAI_SYSTEM)
+        {
+          errno = errno_copy;
+          fprintf (mem.out, "error: %m\n");
+        }
+    }
+  else
+    {
+      format_ai_flags (mem.out, ai);
+      format_ai_canonname (mem.out, ai);
+      format_ai_family (mem.out, ai, AF_INET);
+      format_ai_family (mem.out, ai, AF_INET6);
+    }
+
+  xfclose_memstream (&mem);
+  return mem.buffer;
+}
diff --git a/support/support_format_dns_packet.c b/support/support_format_dns_packet.c
new file mode 100644
index 0000000..2992c57
--- /dev/null
+++ b/support/support_format_dns_packet.c
@@ -0,0 +1,222 @@
+/* Convert a DNS packet to a human-readable representation.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/format_nss.h>
+
+#include <arpa/inet.h>
+#include <resolv.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/xmemstream.h>
+
+struct in_buffer
+{
+  const unsigned char *data;
+  size_t size;
+};
+
+static inline bool
+extract_8 (struct in_buffer *in, unsigned char *value)
+{
+  if (in->size == 0)
+    return false;
+  *value = in->data[0];
+  ++in->data;
+  --in->size;
+  return true;
+}
+
+static inline bool
+extract_16 (struct in_buffer *in, unsigned short *value)
+{
+  if (in->size < 2)
+    return false;
+  *value = (in->data[0] << 8) | in->data[1];
+  in->data += 2;
+  in->size -= 2;
+  return true;
+}
+
+static inline bool
+extract_32 (struct in_buffer *in, unsigned *value)
+{
+  if (in->size < 4)
+    return false;
+  unsigned a = in->data[0];
+  unsigned b = in->data[1];
+  unsigned c = in->data[2];
+  unsigned d = in->data[3];
+  *value = (a << 24) | (b << 16) | (c << 8) | d;
+  in->data += 4;
+  in->size -= 4;
+  return true;
+}
+
+static inline bool
+extract_bytes (struct in_buffer *in, size_t length, struct in_buffer *value)
+{
+  if (in->size < length)
+    return false;
+  *value = (struct in_buffer) {in->data, length};
+  in->data += length;
+  in->size -= length;
+  return true;
+}
+
+struct dname
+{
+  char name[MAXDNAME + 1];
+};
+
+static bool
+extract_name (struct in_buffer full, struct in_buffer *in, struct dname *value)
+{
+  const unsigned char *full_end = full.data + full.size;
+  /* Sanity checks; these indicate buffer misuse.  */
+  TEST_VERIFY_EXIT
+    (!(in->data < full.data || in->data > full_end
+       || in->size > (size_t) (full_end - in->data)));
+  int ret = dn_expand (full.data, full_end, in->data,
+                       value->name, sizeof (value->name));
+  if (ret < 0)
+    return false;
+  in->data += ret;
+  in->size -= ret;
+  return true;
+}
+
+char *
+support_format_dns_packet (const unsigned char *buffer, size_t length)
+{
+  struct in_buffer full = { buffer, length };
+  struct in_buffer in = full;
+  struct xmemstream mem;
+  xopen_memstream (&mem);
+
+  unsigned short txnid;
+  unsigned short flags;
+  unsigned short qdcount;
+  unsigned short ancount;
+  unsigned short nscount;
+  unsigned short adcount;
+  if (!(extract_16 (&in, &txnid)
+        && extract_16 (&in, &flags)
+        && extract_16 (&in, &qdcount)
+        && extract_16 (&in, &ancount)
+        && extract_16 (&in, &nscount)
+        && extract_16 (&in, &adcount)))
+    {
+      fprintf (mem.out, "error: could not parse DNS header\n");
+      goto out;
+    }
+  if (qdcount != 1)
+    {
+      fprintf (mem.out, "error: question count is %d, not 1\n", qdcount);
+      goto out;
+    }
+  struct dname qname;
+  if (!extract_name (full, &in, &qname))
+    {
+      fprintf (mem.out, "error: malformed QNAME\n");
+      goto out;
+    }
+  unsigned short qtype;
+  unsigned short qclass;
+  if (!(extract_16 (&in, &qtype)
+        && extract_16 (&in, &qclass)))
+    {
+      fprintf (mem.out, "error: malformed question\n");
+      goto out;
+    }
+  if (qtype != T_A && qtype != T_AAAA && qtype != T_PTR)
+    {
+      fprintf (mem.out, "error: unsupported QTYPE %d\n", qtype);
+      goto out;
+    }
+
+  fprintf (mem.out, "name: %s\n", qname.name);
+
+  for (int i = 0; i < ancount; ++i)
+    {
+      struct dname rname;
+      if (!extract_name (full, &in, &rname))
+        {
+          fprintf (mem.out, "error: malformed record name\n");
+          goto out;
+        }
+      unsigned short rtype;
+      unsigned short rclass;
+      unsigned ttl;
+      unsigned short rdlen;
+      struct in_buffer rdata;
+      if (!(extract_16 (&in, &rtype)
+            && extract_16 (&in, &rclass)
+            && extract_32 (&in, &ttl)
+            && extract_16 (&in, &rdlen)
+            && extract_bytes (&in, rdlen, &rdata)))
+        {
+          fprintf (mem.out, "error: malformed record header\n");
+          goto out;
+        }
+      /* Skip non-matching record types.  */
+      if ((rtype != qtype && rtype != T_CNAME) || rclass != qclass)
+        continue;
+      switch (rtype)
+        {
+        case T_A:
+          if (rdlen == 4)
+              fprintf (mem.out, "address: %d.%d.%d.%d\n",
+                       rdata.data[0],
+                       rdata.data[1],
+                       rdata.data[2],
+                       rdata.data[3]);
+          else
+            fprintf (mem.out, "error: A record of size %d: %s\n",
+                     rdlen, rname.name);
+          break;
+        case T_AAAA:
+          {
+            if (rdlen == 16)
+              {
+                char buf[100];
+                if (inet_ntop (AF_INET6, rdata.data, buf, sizeof (buf)) == NULL)
+                  fprintf (mem.out, "error: AAAA record decoding failed: %m\n");
+                else
+                  fprintf (mem.out, "address: %s\n", buf);
+              }
+            else
+              fprintf (mem.out, "error: AAAA record of size %d: %s\n",
+                       rdlen, rname.name);
+          }
+          break;
+        case T_CNAME:
+        case T_PTR:
+          {
+            struct dname name;
+            if (extract_name (full, &rdata, &name))
+              fprintf (mem.out, "name: %s\n", name.name);
+            else
+              fprintf (mem.out, "error: malformed CNAME/PTR record\n");
+          }
+        }
+    }
+
+ out:
+  xfclose_memstream (&mem);
+  return mem.buffer;
+}
diff --git a/support/support_format_herrno.c b/support/support_format_herrno.c
new file mode 100644
index 0000000..493d6ae
--- /dev/null
+++ b/support/support_format_herrno.c
@@ -0,0 +1,45 @@
+/* Convert a h_errno error code to a string.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/format_nss.h>
+
+#include <support/support.h>
+
+char *
+support_format_herrno (int code)
+{
+  const char *errstr;
+  switch (code)
+    {
+    case HOST_NOT_FOUND:
+      errstr = "HOST_NOT_FOUND";
+      break;
+    case NO_ADDRESS:
+      errstr = "NO_ADDRESS";
+      break;
+    case NO_RECOVERY:
+      errstr = "NO_RECOVERY";
+      break;
+    case TRY_AGAIN:
+      errstr = "TRY_AGAIN";
+      break;
+    default:
+      return xasprintf ("<invalid h_errno value %d>\n", code);
+    }
+  return xstrdup (errstr);
+}
diff --git a/support/support_format_hostent.c b/support/support_format_hostent.c
new file mode 100644
index 0000000..5b5f260
--- /dev/null
+++ b/support/support_format_hostent.c
@@ -0,0 +1,75 @@
+/* Convert a struct hostent object to a string.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/format_nss.h>
+
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <support/support.h>
+#include <support/xmemstream.h>
+
+static int
+address_length (int family)
+{
+  switch (family)
+    {
+    case AF_INET:
+      return 4;
+    case AF_INET6:
+      return 16;
+    }
+  return -1;
+}
+
+char *
+support_format_hostent (struct hostent *h)
+{
+  if (h == NULL)
+    {
+      char *value = support_format_herrno (h_errno);
+      char *result = xasprintf ("error: %s\n", value);
+      free (value);
+      return result;
+    }
+
+  struct xmemstream mem;
+  xopen_memstream (&mem);
+
+  fprintf (mem.out, "name: %s\n", h->h_name);
+  for (char **alias = h->h_aliases; *alias != NULL; ++alias)
+    fprintf (mem.out, "alias: %s\n", *alias);
+  for (unsigned i = 0; h->h_addr_list[i] != NULL; ++i)
+    {
+      char buf[128];
+      if (inet_ntop (h->h_addrtype, h->h_addr_list[i],
+                     buf, sizeof (buf)) == NULL)
+        fprintf (mem.out, "error: inet_ntop failed: %m\n");
+      else
+        fprintf (mem.out, "address: %s\n", buf);
+    }
+  if (h->h_length != address_length (h->h_addrtype))
+    {
+      char *family = support_format_address_family (h->h_addrtype);
+      fprintf (mem.out, "error: invalid address length %d for %s\n",
+               h->h_length, family);
+      free (family);
+    }
+
+  xfclose_memstream (&mem);
+  return mem.buffer;
+}
diff --git a/support/support_format_netent.c b/support/support_format_netent.c
new file mode 100644
index 0000000..020f572
--- /dev/null
+++ b/support/support_format_netent.c
@@ -0,0 +1,52 @@
+/* Convert a struct netent object to a string.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/format_nss.h>
+
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <support/support.h>
+#include <support/xmemstream.h>
+
+char *
+support_format_netent (struct netent *e)
+{
+  if (e == NULL)
+    {
+      char *value = support_format_herrno (h_errno);
+      char *result = xasprintf ("error: %s\n", value);
+      free (value);
+      return result;
+    }
+
+  struct xmemstream mem;
+  xopen_memstream (&mem);
+
+  if (e->n_name != NULL)
+    fprintf (mem.out, "name: %s\n", e->n_name);
+  for (char **ap = e->n_aliases; *ap != NULL; ++ap)
+    fprintf (mem.out, "alias: %s\n", *ap);
+  if (e->n_addrtype != AF_INET)
+    fprintf (mem.out, "addrtype: %d\n", e->n_addrtype);
+  /* On alpha, e->n_net is an unsigned long.  */
+  unsigned int n_net = e->n_net;
+  fprintf (mem.out, "net: 0x%08x\n", n_net);
+
+  xfclose_memstream (&mem);
+  return mem.buffer;
+}
diff --git a/support/support_isolate_in_subprocess.c b/support/support_isolate_in_subprocess.c
new file mode 100644
index 0000000..cf48614
--- /dev/null
+++ b/support/support_isolate_in_subprocess.c
@@ -0,0 +1,38 @@
+/* Run a function in a subprocess.
+   Copyright (C) 2017 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/>.  */
+
+#include <support/check.h>
+#include <support/xunistd.h>
+
+void
+support_isolate_in_subprocess (void (*callback) (void *), void *closure)
+{
+  pid_t pid = xfork ();
+  if (pid == 0)
+    {
+      /* Child process.  */
+      callback (closure);
+      _exit (0);
+    }
+
+  /* Parent process.  */
+  int status;
+  xwaitpid (pid, &status, 0);
+  if (status != 0)
+    FAIL_EXIT1 ("child process exited with status %d", status);
+}
diff --git a/support/support_record_failure.c b/support/support_record_failure.c
new file mode 100644
index 0000000..684055c
--- /dev/null
+++ b/support/support_record_failure.c
@@ -0,0 +1,106 @@
+/* Global test failure counter.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/check.h>
+#include <support/support.h>
+#include <support/test-driver.h>
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+/* This structure keeps track of test failures.  The counter is
+   incremented on each failure.  The failed member is set to true if a
+   failure is detected, so that even if the counter wraps around to
+   zero, the failure of a test can be detected.
+
+   The init constructor function below puts *state on a shared
+   annonymous mapping, so that failure reports from subprocesses
+   propagate to the parent process.  */
+struct test_failures
+{
+  unsigned int counter;
+  unsigned int failed;
+};
+static struct test_failures *state;
+
+static __attribute__ ((constructor)) void
+init (void)
+{
+  void *ptr = mmap (NULL, sizeof (*state), PROT_READ | PROT_WRITE,
+                    MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+  if (ptr == MAP_FAILED)
+    {
+      printf ("error: could not map %zu bytes: %m\n", sizeof (*state));
+      exit (1);
+    }
+  /* Zero-initialization of the struct is sufficient.  */
+  state = ptr;
+}
+
+void
+support_record_failure (void)
+{
+  if (state == NULL)
+    {
+      write_message
+        ("error: support_record_failure called without initialization\n");
+      _exit (1);
+    }
+  /* Relaxed MO is sufficient because we are only interested in the
+     values themselves, in isolation.  */
+  __atomic_store_n (&state->failed, 1, __ATOMIC_RELEASE);
+  __atomic_add_fetch (&state->counter, 1, __ATOMIC_RELEASE);
+}
+
+int
+support_report_failure (int status)
+{
+  if (state == NULL)
+    {
+      write_message
+        ("error: support_report_failure called without initialization\n");
+      return 1;
+    }
+
+  /* Relaxed MO is sufficient because acquire test result reporting
+     assumes that exiting from the main thread happens before the
+     error reporting via support_record_failure, which requires some
+     form of external synchronization.  */
+  bool failed = __atomic_load_n (&state->failed, __ATOMIC_RELAXED);
+  if (failed)
+    printf ("error: %u test failures\n",
+            __atomic_load_n (&state->counter, __ATOMIC_RELAXED));
+
+  if ((status == 0 || status == EXIT_UNSUPPORTED) && failed)
+    /* If we have a recorded failure, it overrides a non-failure
+       report from the test function.  */
+    status = 1;
+  return status;
+}
+
+void
+support_record_failure_reset (void)
+{
+  /* Only used for testing the test framework, with external
+     synchronization, but use release MO for consistency.  */
+  __atomic_store_n (&state->failed, 0, __ATOMIC_RELAXED);
+  __atomic_add_fetch (&state->counter, 0, __ATOMIC_RELAXED);
+}
diff --git a/support/support_run_diff.c b/support/support_run_diff.c
new file mode 100644
index 0000000..f5155de
--- /dev/null
+++ b/support/support_run_diff.c
@@ -0,0 +1,76 @@
+/* Invoke the system diff tool to compare two strings.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/run_diff.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/temp_file.h>
+#include <support/xunistd.h>
+#include <sys/wait.h>
+
+static char *
+write_to_temp_file (const char *prefix, const char *str)
+{
+  char *template = xasprintf ("run_diff-%s", prefix);
+  char *name = NULL;
+  int fd = create_temp_file (template, &name);
+  TEST_VERIFY_EXIT (fd >= 0);
+  free (template);
+  xwrite (fd, str, strlen (str));
+  xclose (fd);
+  return name;
+}
+
+void
+support_run_diff (const char *left_label, const char *left,
+                  const char *right_label, const char *right)
+{
+  /* Ensure that the diff command output is ordered properly with
+     standard output.  */
+  TEST_VERIFY_EXIT (fflush (stdout) == 0);
+
+  char *left_path = write_to_temp_file ("left-diff", left);
+  char *right_path = write_to_temp_file ("right-diff", right);
+
+  pid_t pid = xfork ();
+  if (pid == 0)
+    {
+      execlp ("diff", "diff", "-u",
+              "--label", left_label, "--label", right_label,
+              "--", left_path, right_path,
+              NULL);
+      _exit (17);
+    }
+  else
+    {
+      int status;
+      xwaitpid (pid, &status, 0);
+      if (!WIFEXITED (status) || WEXITSTATUS (status) != 1)
+        printf ("warning: could not run diff, exit status: %d\n"
+                "*** %s ***\n%s\n"
+                "*** %s ***\n%s\n",
+                status, left_label, left, right_label, right);
+    }
+
+  free (right_path);
+  free (left_path);
+}
diff --git a/support/support_shared_allocate.c b/support/support_shared_allocate.c
new file mode 100644
index 0000000..c2ca7c2
--- /dev/null
+++ b/support/support_shared_allocate.c
@@ -0,0 +1,59 @@
+/* Allocate a memory region shared across processes.
+   Copyright (C) 2017 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/>.  */
+
+#include <errno.h>
+#include <stddef.h>
+#include <support/support.h>
+#include <support/xunistd.h>
+#include <sys/mman.h>
+
+#if defined ISO11
+/* Header for the allocation.  It contains the size of the allocation
+   for subsequent unmapping.  */
+struct header
+{
+  size_t total_size;
+  char data[] __attribute__ ((aligned (__alignof__ (max_align_t))));
+};
+
+void *
+support_shared_allocate (size_t size)
+{
+  size_t total_size = size + offsetof (struct header, data);
+  if (total_size < size)
+    {
+      errno = ENOMEM;
+      oom_error (__func__, size);
+      return NULL;
+    }
+  else
+    {
+      struct header *result = xmmap (NULL, total_size, PROT_READ | PROT_WRITE,
+                                     MAP_ANONYMOUS | MAP_SHARED, -1);
+      result->total_size = total_size;
+      return &result->data;
+    }
+}
+
+void
+support_shared_free (void *data)
+{
+  struct header *header = data - offsetof (struct header, data);
+  xmunmap (header, header->total_size);
+}
+#endif
diff --git a/support/support_test_main.c b/support/support_test_main.c
new file mode 100644
index 0000000..914d64f
--- /dev/null
+++ b/support/support_test_main.c
@@ -0,0 +1,423 @@
+/* Main worker function for the test driver.
+   Copyright (C) 1998-2017 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/>.  */
+
+#include <support/test-driver.h>
+#include <support/check.h>
+#include <support/temp_file-internal.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <getopt.h>
+#include <malloc.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+static const struct option default_options[] =
+{
+  TEST_DEFAULT_OPTIONS
+  { NULL, 0, NULL, 0 }
+};
+
+/* Show people how to run the program.  */
+static void
+usage (const struct option *options)
+{
+  size_t i;
+
+  printf ("Usage: %s [options]\n"
+          "\n"
+          "Environment Variables:\n"
+          "  TIMEOUTFACTOR          An integer used to scale the timeout\n"
+          "  TMPDIR                 Where to place temporary files\n"
+          "  TEST_COREDUMPS         Do not disable coredumps if set\n"
+          "\n",
+          program_invocation_short_name);
+  printf ("Options:\n");
+  for (i = 0; options[i].name; ++i)
+    {
+      int indent;
+
+      indent = printf ("  --%s", options[i].name);
+      if (options[i].has_arg == required_argument)
+        indent += printf (" <arg>");
+      printf ("%*s", 25 - indent, "");
+      switch (options[i].val)
+        {
+        case 'v':
+          printf ("Increase the output verbosity");
+          break;
+        case OPT_DIRECT:
+          printf ("Run the test directly (instead of forking & monitoring)");
+          break;
+        case OPT_TESTDIR:
+          printf ("Override the TMPDIR env var");
+          break;
+        }
+      printf ("\n");
+    }
+}
+
+/* The PID of the test process.  */
+static pid_t test_pid;
+
+/* The cleanup handler passed to test_main.  */
+static void (*cleanup_function) (void);
+
+/* Timeout handler.  We kill the child and exit with an error.  */
+static void
+__attribute__ ((noreturn))
+signal_handler (int sig)
+{
+  int killed;
+  int status;
+
+  assert (test_pid > 1);
+  /* Kill the whole process group.  */
+  kill (-test_pid, SIGKILL);
+  /* In case setpgid failed in the child, kill it individually too.  */
+  kill (test_pid, SIGKILL);
+
+  /* Wait for it to terminate.  */
+  int i;
+  for (i = 0; i < 5; ++i)
+    {
+      killed = waitpid (test_pid, &status, WNOHANG|WUNTRACED);
+      if (killed != 0)
+        break;
+
+      /* Delay, give the system time to process the kill.  If the
+         nanosleep() call return prematurely, all the better.  We
+         won't restart it since this probably means the child process
+         finally died.  */
+      struct timespec ts;
+      ts.tv_sec = 0;
+      ts.tv_nsec = 100000000;
+      nanosleep (&ts, NULL);
+    }
+  if (killed != 0 && killed != test_pid)
+    {
+      printf ("Failed to kill test process: %m\n");
+      exit (1);
+    }
+
+  if (cleanup_function != NULL)
+    cleanup_function ();
+
+  if (sig == SIGINT)
+    {
+      signal (sig, SIG_DFL);
+      raise (sig);
+    }
+
+  if (killed == 0 || (WIFSIGNALED (status) && WTERMSIG (status) == SIGKILL))
+    puts ("Timed out: killed the child process");
+  else if (WIFSTOPPED (status))
+    printf ("Timed out: the child process was %s\n",
+            strsignal (WSTOPSIG (status)));
+  else if (WIFSIGNALED (status))
+    printf ("Timed out: the child process got signal %s\n",
+            strsignal (WTERMSIG (status)));
+  else
+    printf ("Timed out: killed the child process but it exited %d\n",
+            WEXITSTATUS (status));
+
+  /* Exit with an error.  */
+  exit (1);
+}
+
+/* Run test_function or test_function_argv.  */
+static int
+run_test_function (int argc, char **argv, const struct test_config *config)
+{
+  if (config->test_function != NULL)
+    return config->test_function ();
+  else if (config->test_function_argv != NULL)
+    return config->test_function_argv (argc, argv);
+  else
+    {
+      printf ("error: no test function defined\n");
+      exit (1);
+    }
+}
+
+static bool test_main_called;
+
+const char *test_dir = NULL;
+unsigned int test_verbose = 0;
+
+/* If test failure reporting has been linked in, it may contribute
+   additional test failures.  */
+static int
+adjust_exit_status (int status)
+{
+  if (support_report_failure != NULL)
+    return support_report_failure (status);
+  return status;
+}
+
+int
+support_test_main (int argc, char **argv, const struct test_config *config)
+{
+  if (test_main_called)
+    {
+      printf ("error: test_main called for a second time\n");
+      exit (1);
+    }
+  test_main_called = true;
+  const struct option *options;
+  if (config->options != NULL)
+    options = config->options;
+  else
+    options = default_options;
+
+  cleanup_function = config->cleanup_function;
+
+  int direct = 0;       /* Directly call the test function?  */
+  int status;
+  int opt;
+  unsigned int timeoutfactor = 1;
+  pid_t termpid;
+
+  if (!config->no_mallopt)
+    {
+      /* Make uses of freed and uninitialized memory known.  Do not
+         pull in a definition for mallopt if it has not been defined
+         already.  */
+      extern __typeof__ (mallopt) mallopt __attribute__ ((weak));
+      if (mallopt != NULL)
+        mallopt (M_PERTURB, 42);
+    }
+
+  while ((opt = getopt_long (argc, argv, "+", options, NULL)) != -1)
+    switch (opt)
+      {
+      case '?':
+        usage (options);
+        exit (1);
+      case 'v':
+        ++test_verbose;
+        break;
+      case OPT_DIRECT:
+        direct = 1;
+        break;
+      case OPT_TESTDIR:
+        test_dir = optarg;
+        break;
+      default:
+        if (config->cmdline_function != NULL)
+          config->cmdline_function (opt);
+      }
+
+  /* If set, read the test TIMEOUTFACTOR value from the environment.
+     This value is used to scale the default test timeout values. */
+  char *envstr_timeoutfactor = getenv ("TIMEOUTFACTOR");
+  if (envstr_timeoutfactor != NULL)
+    {
+      char *envstr_conv = envstr_timeoutfactor;
+      unsigned long int env_fact;
+
+      env_fact = strtoul (envstr_timeoutfactor, &envstr_conv, 0);
+      if (*envstr_conv == '\0' && envstr_conv != envstr_timeoutfactor)
+        timeoutfactor = MAX (env_fact, 1);
+    }
+
+  /* Set TMPDIR to specified test directory.  */
+  if (test_dir != NULL)
+    {
+      setenv ("TMPDIR", test_dir, 1);
+
+      if (chdir (test_dir) < 0)
+        {
+          printf ("chdir: %m\n");
+          exit (1);
+        }
+    }
+  else
+    {
+      test_dir = getenv ("TMPDIR");
+      if (test_dir == NULL || test_dir[0] == '\0')
+        test_dir = "/tmp";
+    }
+  if (support_set_test_dir != NULL)
+    support_set_test_dir (test_dir);
+
+  int timeout = config->timeout;
+  if (timeout == 0)
+    timeout =  DEFAULT_TIMEOUT;
+
+  /* Make sure we see all message, even those on stdout.  */
+  setvbuf (stdout, NULL, _IONBF, 0);
+
+  /* Make sure temporary files are deleted.  */
+  if (support_delete_temp_files != NULL)
+      atexit (support_delete_temp_files);
+
+  /* Correct for the possible parameters.  */
+  argv[optind - 1] = argv[0];
+  argv += optind - 1;
+  argc -= optind - 1;
+
+  /* Call the initializing function, if one is available.  */
+  if (config->prepare_function != NULL)
+    config->prepare_function (argc, argv);
+
+  const char *envstr_direct = getenv ("TEST_DIRECT");
+  if (envstr_direct != NULL)
+    {
+      FILE *f = fopen (envstr_direct, "w");
+      if (f == NULL)
+        {
+          printf ("cannot open TEST_DIRECT output file '%s': %m\n",
+                  envstr_direct);
+          exit (1);
+        }
+
+      fprintf (f, "timeout=%u\ntimeoutfactor=%u\n",
+               config->timeout, timeoutfactor);
+      if (config->expected_status != 0)
+        fprintf (f, "exit=%u\n", config->expected_status);
+      if (config->expected_signal != 0)
+        fprintf (f, "signal=%s\n", strsignal (config->expected_signal));
+
+      if (support_print_temp_files != NULL)
+        support_print_temp_files (f);
+
+      fclose (f);
+      direct = 1;
+    }
+
+  bool disable_coredumps;
+  {
+    const char *coredumps = getenv ("TEST_COREDUMPS");
+    disable_coredumps = coredumps == NULL || coredumps[0] == '\0';
+  }
+
+  /* If we are not expected to fork run the function immediately.  */
+  if (direct)
+    return adjust_exit_status (run_test_function (argc, argv, config));
+
+  /* Set up the test environment:
+     - prevent core dumps
+     - set up the timer
+     - fork and execute the function.  */
+
+  test_pid = fork ();
+  if (test_pid == 0)
+    {
+      /* This is the child.  */
+      if (disable_coredumps)
+        {
+          /* Try to avoid dumping core.  This is necessary because we
+             run the test from the source tree, and the coredumps
+             would end up there (and not in the build tree).  */
+          struct rlimit core_limit;
+          core_limit.rlim_cur = 0;
+          core_limit.rlim_max = 0;
+          setrlimit (RLIMIT_CORE, &core_limit);
+        }
+
+      /* We put the test process in its own pgrp so that if it bogusly
+         generates any job control signals, they won't hit the whole build.  */
+      if (setpgid (0, 0) != 0)
+        printf ("Failed to set the process group ID: %m\n");
+
+      /* Execute the test function and exit with the return value.   */
+      exit (run_test_function (argc, argv, config));
+    }
+  else if (test_pid < 0)
+    {
+      printf ("Cannot fork test program: %m\n");
+      exit (1);
+    }
+
+  /* Set timeout.  */
+  signal (SIGALRM, signal_handler);
+  alarm (timeout * timeoutfactor);
+
+  /* Make sure we clean up if the wrapper gets interrupted.  */
+  signal (SIGINT, signal_handler);
+
+  /* Wait for the regular termination.  */
+  termpid = TEMP_FAILURE_RETRY (waitpid (test_pid, &status, 0));
+  if (termpid == -1)
+    {
+      printf ("Waiting for test program failed: %m\n");
+      exit (1);
+    }
+  if (termpid != test_pid)
+    {
+      printf ("Oops, wrong test program terminated: expected %ld, got %ld\n",
+              (long int) test_pid, (long int) termpid);
+      exit (1);
+    }
+
+  /* Process terminated normaly without timeout etc.  */
+  if (WIFEXITED (status))
+    {
+      if (config->expected_status == 0)
+        {
+          if (config->expected_signal == 0)
+            /* Exit with the return value of the test.  */
+            return adjust_exit_status (WEXITSTATUS (status));
+          else
+            {
+              printf ("Expected signal '%s' from child, got none\n",
+                      strsignal (config->expected_signal));
+              exit (1);
+            }
+        }
+      else
+        {
+          /* Non-zero exit status is expected */
+          if (WEXITSTATUS (status) != config->expected_status)
+            {
+              printf ("Expected status %d, got %d\n",
+                      config->expected_status, WEXITSTATUS (status));
+              exit (1);
+            }
+        }
+      return adjust_exit_status (0);
+    }
+  /* Process was killed by timer or other signal.  */
+  else
+    {
+      if (config->expected_signal == 0)
+        {
+          printf ("Didn't expect signal from child: got `%s'\n",
+                  strsignal (WTERMSIG (status)));
+          exit (1);
+        }
+      else if (WTERMSIG (status) != config->expected_signal)
+        {
+          printf ("Incorrect signal from child: got `%s', need `%s'\n",
+                  strsignal (WTERMSIG (status)),
+                  strsignal (config->expected_signal));
+          exit (1);
+        }
+
+      return adjust_exit_status (0);
+    }
+}
diff --git a/support/support_test_verify_impl.c b/support/support_test_verify_impl.c
new file mode 100644
index 0000000..5bae38f
--- /dev/null
+++ b/support/support_test_verify_impl.c
@@ -0,0 +1,33 @@
+/* Implementation of the TEST_VERIFY and TEST_VERIFY_EXIT macros.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/check.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+void
+support_test_verify_impl (int status, const char *file, int line,
+                          const char *expr)
+{
+  support_record_failure ();
+  printf ("error: %s:%d: not true: %s\n", file, line, expr);
+  if (status >= 0)
+    exit (status);
+
+}
diff --git a/support/support_write_file_string.c b/support/support_write_file_string.c
new file mode 100644
index 0000000..48e8959
--- /dev/null
+++ b/support/support_write_file_string.c
@@ -0,0 +1,39 @@
+/* Write a string to a file.
+   Copyright (C) 2017 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/>.  */
+
+#include <fcntl.h>
+#include <string.h>
+#include <support/check.h>
+#include <xunistd.h>
+
+void
+support_write_file_string (const char *path, const char *contents)
+{
+  int fd = xopen (path, O_CREAT | O_TRUNC | O_WRONLY, 0666);
+  const char *end = contents + strlen (contents);
+  for (const char *p = contents; p < end; )
+    {
+      ssize_t ret = write (fd, p, end - p);
+      if (ret < 0)
+        FAIL_EXIT1 ("cannot write to \"%s\": %m", path);
+      if (ret == 0)
+        FAIL_EXIT1 ("zero-length write to \"%s\"", path);
+      p += ret;
+    }
+  xclose (fd);
+}
diff --git a/support/temp_file-internal.h b/support/temp_file-internal.h
new file mode 100644
index 0000000..fb6cceb
--- /dev/null
+++ b/support/temp_file-internal.h
@@ -0,0 +1,31 @@
+/* Internal weak declarations for temporary file handling.
+   Copyright (C) 2016-2017 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/>.  */
+
+#ifndef SUPPORT_TEMP_FILE_INTERNAL_H
+#define SUPPORT_TEMP_FILE_INTERNAL_H
+
+/* These functions are called by the test driver if they are
+   defined.  Tests should not call them directly.  */
+
+#include <stdio.h>
+
+void support_set_test_dir (const char *name) __attribute__ ((weak));
+void support_delete_temp_files (void) __attribute__ ((weak));
+void support_print_temp_files (FILE *) __attribute__ ((weak));
+
+#endif /* SUPPORT_TEMP_FILE_INTERNAL_H */
diff --git a/support/temp_file.c b/support/temp_file.c
new file mode 100644
index 0000000..fdb2477
--- /dev/null
+++ b/support/temp_file.c
@@ -0,0 +1,132 @@
+/* Temporary file handling for tests.
+   Copyright (C) 1998-2017 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/>.  */
+
+/* This is required to get an mkstemp which can create large files on
+   some 32-bit platforms. */
+#define _FILE_OFFSET_BITS 64
+
+#include <support/temp_file.h>
+#include <support/temp_file-internal.h>
+#include <support/support.h>
+
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* List of temporary files.  */
+static struct temp_name_list
+{
+  struct temp_name_list *next;
+  char *name;
+  pid_t owner;
+} *temp_name_list;
+
+/* Location of the temporary files.  Set by the test skeleton via
+   support_set_test_dir.  The string is not be freed.  */
+static const char *test_dir = _PATH_TMP;
+
+void
+add_temp_file (const char *name)
+{
+  struct temp_name_list *newp
+    = (struct temp_name_list *) xcalloc (sizeof (*newp), 1);
+  char *newname = strdup (name);
+  if (newname != NULL)
+    {
+      newp->name = newname;
+      newp->next = temp_name_list;
+      newp->owner = getpid ();
+      temp_name_list = newp;
+    }
+  else
+    free (newp);
+}
+
+int
+create_temp_file (const char *base, char **filename)
+{
+  char *fname;
+  int fd;
+
+  fname = (char *) xmalloc (strlen (test_dir) + 1 + strlen (base)
+			    + sizeof ("XXXXXX"));
+  strcpy (stpcpy (stpcpy (stpcpy (fname, test_dir), "/"), base), "XXXXXX");
+
+  fd = mkstemp (fname);
+  if (fd == -1)
+    {
+      printf ("cannot open temporary file '%s': %m\n", fname);
+      free (fname);
+      return -1;
+    }
+
+  add_temp_file (fname);
+  if (filename != NULL)
+    *filename = fname;
+  else
+    free (fname);
+
+  return fd;
+}
+
+/* Helper functions called by the test skeleton follow.  */
+
+void
+support_set_test_dir (const char *path)
+{
+  test_dir = path;
+}
+
+void
+support_delete_temp_files (void)
+{
+  pid_t pid = getpid ();
+  while (temp_name_list != NULL)
+    {
+      /* Only perform the removal if the path was registed in the same
+	 process, as identified by the PID.  (This assumes that the
+	 parent process which registered the temporary file sticks
+	 around, to prevent PID reuse.)  */
+      if (temp_name_list->owner == pid)
+	{
+	  if (remove (temp_name_list->name) != 0)
+	    printf ("warning: could not remove temporary file: %s: %m\n",
+		    temp_name_list->name);
+	}
+      free (temp_name_list->name);
+
+      struct temp_name_list *next = temp_name_list->next;
+      free (temp_name_list);
+      temp_name_list = next;
+    }
+}
+
+void
+support_print_temp_files (FILE *f)
+{
+  if (temp_name_list != NULL)
+    {
+      struct temp_name_list *n;
+      fprintf (f, "temp_files=(\n");
+      for (n = temp_name_list; n != NULL; n = n->next)
+        fprintf (f, "  '%s'\n", n->name);
+      fprintf (f, ")\n");
+    }
+}
diff --git a/support/temp_file.h b/support/temp_file.h
new file mode 100644
index 0000000..6fed8df
--- /dev/null
+++ b/support/temp_file.h
@@ -0,0 +1,37 @@
+/* Declarations for temporary file handling.
+   Copyright (C) 2016-2017 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/>.  */
+
+#ifndef SUPPORT_TEMP_FILE_H
+#define SUPPORT_TEMP_FILE_H
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/* Schedule a temporary file for deletion on exit.  */
+void add_temp_file (const char *name);
+
+/* Create a temporary file.  Return the opened file descriptor on
+   success, or -1 on failure.  Write the file name to *FILENAME if
+   FILENAME is not NULL.  In this case, the caller is expected to free
+   *FILENAME.  */
+int create_temp_file (const char *base, char **filename);
+
+__END_DECLS
+
+#endif /* SUPPORT_TEMP_FILE_H */
diff --git a/support/test-driver.c b/support/test-driver.c
new file mode 100644
index 0000000..482066d
--- /dev/null
+++ b/support/test-driver.c
@@ -0,0 +1,156 @@
+/* Main function for test programs.
+   Copyright (C) 2016-2017 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/>.  */
+
+/* This file should be included from test cases.  It will define a
+   main function which provides the test wrapper.
+
+   It assumes that the test case defines a function
+
+     int do_test (void);
+
+   and arranges for that function being called under the test wrapper.
+   The do_test function should return 0 to indicate a passing test, 1
+   to indicate a failing test, or 77 to indicate an unsupported test.
+   Other result values could be used to indicate a failing test, but
+   the result of the expression is passed to exit and exit only
+   returns the lower 8 bits of its input.  A non-zero return with some
+   values could cause a test to incorrectly be considered passing when
+   it really failed.  For this reason, the function should always
+   return 0 (EXIT_SUCCESS), 1 (EXIT_FAILURE), or 77
+   (EXIT_UNSUPPORTED).
+
+   The test function may print out diagnostic or warning messages as well
+   as messages about failures.  These messages should be printed to stdout
+   and not stderr so that the output is properly ordered with respect to
+   the rest of the glibc testsuite run output.
+
+   Several preprocessors macros can be defined before including this
+   file.
+
+   The name of the do_test function can be changed with the
+   TEST_FUNCTION macro.  It must expand to the desired function name.
+
+   If the test case needs access to command line parameters, it must
+   define the TEST_FUNCTION_ARGV macro with the name of the test
+   function.  It must have the following type:
+
+     int TEST_FUNCTION_ARGV (int argc, char **argv);
+
+   This overrides the do_test default function and is incompatible
+   with the TEST_FUNCTION macro.
+
+   If PREPARE is defined, it must expand to the name of a function of
+   the type
+
+     void PREPARE (int argc, char **);
+
+   This function will be called early, after parsing the command line,
+   but before running the test, in the parent process which acts as
+   the test supervisor.
+
+   If CLEANUP_HANDLER is defined, it must expand to the name of a
+   function of the type
+
+     void CLEANUP_HANDLER (void);
+
+   This function will be called from the timeout (SIGALRM) signal
+   handler.
+
+   If EXPECTED_SIGNAL is defined, it must expanded to a constant which
+   denotes the expected signal number.
+
+   If EXPECTED_STATUS is defined, it must expand to the expected exit
+   status.
+
+   If TIMEOUT is defined, it must be positive constant.  It overrides
+   the default test timeout and is measured in seconds.
+
+   If TEST_NO_MALLOPT is defined, the test wrapper will not call
+   mallopt.
+
+   Custom command line handling can be implemented by defining the
+   CMDLINE_OPTION macro (after including the <getopt.h> header; this
+   requires _GNU_SOURCE to be defined).  This macro must expand to a
+   to a comma-separated list of braced initializers for struct option
+   from <getopt.h>, with a trailing comma.  CMDLINE_PROCESS can be
+   defined as the name of a function which is called to process these
+   options.  The function is passed the option character/number and
+   has this type:
+
+     void CMDLINE_PROCESS (int);
+*/
+
+#include <support/test-driver.h>
+
+#include <string.h>
+
+int
+main (int argc, char **argv)
+{
+  struct test_config test_config;
+  memset (&test_config, 0, sizeof (test_config));
+
+#ifdef PREPARE
+  test_config.prepare_function = (PREPARE);
+#endif
+
+#if defined (TEST_FUNCTION) && defined (TEST_FUNCTON_ARGV)
+# error TEST_FUNCTION and TEST_FUNCTION_ARGV cannot be defined at the same time
+#endif
+#if defined (TEST_FUNCTION)
+  test_config.test_function = TEST_FUNCTION;
+#elif defined (TEST_FUNCTION_ARGV)
+  test_config.test_function_argv = TEST_FUNCTION_ARGV;
+#else
+  test_config.test_function = do_test;
+#endif
+
+#ifdef CLEANUP_HANDLER
+  test_config.cleanup_function = CLEANUP_HANDLER;
+#endif
+
+#ifdef EXPECTED_SIGNAL
+  test_config.expected_signal = (EXPECTED_SIGNAL);
+#endif
+
+#ifdef EXPECTED_STATUS
+  test_config.expected_status = (EXPECTED_STATUS);
+#endif
+
+#ifdef TEST_NO_MALLOPT
+  test_config.no_mallopt = 1;
+#endif
+
+#ifdef TIMEOUT
+  test_config.timeout = TIMEOUT;
+#endif
+
+#ifdef CMDLINE_OPTIONS
+  struct option options[] =
+    {
+      CMDLINE_OPTIONS
+      TEST_DEFAULT_OPTIONS
+    };
+  test_config.options = &options;
+#endif
+#ifdef CMDLINE_PROCESS
+  test_config.cmdline_function = CMDLINE_PROCESS;
+#endif
+
+  return support_test_main (argc, argv, &test_config);
+}
diff --git a/support/test-driver.h b/support/test-driver.h
new file mode 100644
index 0000000..af1971a
--- /dev/null
+++ b/support/test-driver.h
@@ -0,0 +1,74 @@
+/* Interfaces for the test driver.
+   Copyright (C) 2016-2017 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/>.  */
+
+#ifndef SUPPORT_TEST_DRIVER_H
+#define SUPPORT_TEST_DRIVER_H
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+struct test_config
+{
+  void (*prepare_function) (int argc, char **argv);
+  int (*test_function) (void);
+  int (*test_function_argv) (int argc, char **argv);
+  void (*cleanup_function) (void);
+  void (*cmdline_function) (int);
+  const void *options;   /* Custom options if not NULL.  */
+  int timeout;           /* Test timeout in seconds.  */
+  int expected_status;   /* Expected exit status.  */
+  int expected_signal;   /* If non-zero, expect termination by signal.  */
+  char no_mallopt;       /* Boolean flag to disable mallopt.  */
+};
+
+enum
+  {
+    /* Test exit status which indicates that the feature is
+       unsupported. */
+    EXIT_UNSUPPORTED = 77,
+
+    /* Default timeout is twenty seconds.  Tests should normally
+       complete faster than this, but if they don't, that's abnormal
+       (a bug) anyways.  */
+    DEFAULT_TIMEOUT = 20,
+
+    /* Used for command line argument parsing.  */
+    OPT_DIRECT = 1000,
+    OPT_TESTDIR,
+  };
+
+/* Options provided by the test driver.  */
+#define TEST_DEFAULT_OPTIONS                            \
+  { "verbose", no_argument, NULL, 'v' },                \
+  { "direct", no_argument, NULL, OPT_DIRECT },          \
+  { "test-dir", required_argument, NULL, OPT_TESTDIR }, \
+
+/* The directory the test should use for temporary files.  */
+extern const char *test_dir;
+
+/* The number of --verbose arguments specified during program
+   invocation.  This variable can be used to control the verbosity of
+   tests.  */
+extern unsigned int test_verbose;
+
+int support_test_main (int argc, char **argv, const struct test_config *);
+
+__END_DECLS
+
+#endif /* SUPPORT_TEST_DRIVER_H */
diff --git a/support/tst-support-namespace.c b/support/tst-support-namespace.c
new file mode 100644
index 0000000..a50b074
--- /dev/null
+++ b/support/tst-support-namespace.c
@@ -0,0 +1,34 @@
+/* Test entering namespaces.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <stdio.h>
+#include <support/namespace.h>
+
+static int
+do_test (void)
+{
+  if (support_become_root ())
+    printf ("info: acquired root-like privileges\n");
+  if (support_enter_network_namespace ())
+    printf ("info: entered network namespace\n");
+  if (support_in_uts_namespace ())
+    printf ("info: also entered UTS namespace\n");
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/support/tst-support_capture_subprocess.c b/support/tst-support_capture_subprocess.c
new file mode 100644
index 0000000..5672fba
--- /dev/null
+++ b/support/tst-support_capture_subprocess.c
@@ -0,0 +1,188 @@
+/* Test capturing output from a subprocess.
+   Copyright (C) 2017 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/>.  */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/capture_subprocess.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+/* Write one byte at *P to FD and advance *P.  Do nothing if *P is
+   '\0'.  */
+static void
+transfer (const unsigned char **p, int fd)
+{
+  if (**p != '\0')
+    {
+      TEST_VERIFY (write (fd, *p, 1) == 1);
+      ++*p;
+    }
+}
+
+/* Determine the order in which stdout and stderr are written.  */
+enum write_mode { out_first, err_first, interleave,
+                  write_mode_last =  interleave };
+
+/* Describe what to write in the subprocess.  */
+struct test
+{
+  char *out;
+  char *err;
+  enum write_mode write_mode;
+  int signal;
+  int status;
+};
+
+/* For use with support_capture_subprocess.  */
+static void
+callback (void *closure)
+{
+  const struct test *test = closure;
+  bool mode_ok = false;
+  switch (test->write_mode)
+    {
+    case out_first:
+      TEST_VERIFY (fputs (test->out, stdout) >= 0);
+      TEST_VERIFY (fflush (stdout) == 0);
+      TEST_VERIFY (fputs (test->err, stderr) >= 0);
+      TEST_VERIFY (fflush (stderr) == 0);
+      mode_ok = true;
+      break;
+    case err_first:
+      TEST_VERIFY (fputs (test->err, stderr) >= 0);
+      TEST_VERIFY (fflush (stderr) == 0);
+      TEST_VERIFY (fputs (test->out, stdout) >= 0);
+      TEST_VERIFY (fflush (stdout) == 0);
+      mode_ok = true;
+      break;
+    case interleave:
+      {
+        const unsigned char *pout = (const unsigned char *) test->out;
+        const unsigned char *perr = (const unsigned char *) test->err;
+        do
+          {
+            transfer (&pout, STDOUT_FILENO);
+            transfer (&perr, STDERR_FILENO);
+          }
+        while (*pout != '\0' || *perr != '\0');
+      }
+      mode_ok = true;
+      break;
+    }
+  TEST_VERIFY (mode_ok);
+
+  if (test->signal != 0)
+    raise (test->signal);
+  exit (test->status);
+}
+
+/* Create a heap-allocated random string of letters.  */
+static char *
+random_string (size_t length)
+{
+  char *result = xmalloc (length + 1);
+  for (size_t i = 0; i < length; ++i)
+    result[i] = 'a' + (rand () % 26);
+  result[length] = '\0';
+  return result;
+}
+
+/* Check that the specific stream from the captured subprocess matches
+   expectations.  */
+static void
+check_stream (const char *what, const struct xmemstream *stream,
+              const char *expected)
+{
+  if (strcmp (stream->buffer, expected) != 0)
+    {
+      support_record_failure ();
+      printf ("error: captured %s data incorrect\n"
+              "  expected: %s\n"
+              "  actual:   %s\n",
+              what, expected, stream->buffer);
+    }
+  if (stream->length != strlen (expected))
+    {
+      support_record_failure ();
+      printf ("error: captured %s data length incorrect\n"
+              "  expected: %zu\n"
+              "  actual:   %zu\n",
+              what, strlen (expected), stream->length);
+    }
+}
+
+static int
+do_test (void)
+{
+  const int lengths[] = {0, 1, 17, 512, 20000, -1};
+
+  /* Test multiple combinations of support_capture_subprocess.
+
+     length_idx_stdout: Index into the lengths array above,
+       controls how many bytes are written by the subprocess to
+       standard output.
+     length_idx_stderr: Same for standard error.
+     write_mode: How standard output and standard error writes are
+       ordered.
+     signal: Exit with no signal if zero, with SIGTERM if one.
+     status: Process exit status: 0 if zero, 3 if one.  */
+  for (int length_idx_stdout = 0; lengths[length_idx_stdout] >= 0;
+       ++length_idx_stdout)
+    for (int length_idx_stderr = 0; lengths[length_idx_stderr] >= 0;
+         ++length_idx_stderr)
+      for (int write_mode = 0; write_mode < write_mode_last; ++write_mode)
+        for (int signal = 0; signal < 2; ++signal)
+          for (int status = 0; status < 2; ++status)
+            {
+              struct test test =
+                {
+                  .out = random_string (lengths[length_idx_stdout]),
+                  .err = random_string (lengths[length_idx_stderr]),
+                  .write_mode = write_mode,
+                  .signal = signal * SIGTERM, /* 0 or SIGTERM.  */
+                  .status = status * 3,       /* 0 or 3.  */
+                };
+              TEST_VERIFY (strlen (test.out) == lengths[length_idx_stdout]);
+              TEST_VERIFY (strlen (test.err) == lengths[length_idx_stderr]);
+
+              struct support_capture_subprocess result
+                = support_capture_subprocess (callback, &test);
+              check_stream ("stdout", &result.out, test.out);
+              check_stream ("stderr", &result.err, test.err);
+              if (test.signal != 0)
+                {
+                  TEST_VERIFY (WIFSIGNALED (result.status));
+                  TEST_VERIFY (WTERMSIG (result.status) == test.signal);
+                }
+              else
+                {
+                  TEST_VERIFY (WIFEXITED (result.status));
+                  TEST_VERIFY (WEXITSTATUS (result.status) == test.status);
+                }
+              support_capture_subprocess_free (&result);
+              free (test.out);
+              free (test.err);
+            }
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/support/tst-support_format_dns_packet.c b/support/tst-support_format_dns_packet.c
new file mode 100644
index 0000000..9c8589c
--- /dev/null
+++ b/support/tst-support_format_dns_packet.c
@@ -0,0 +1,101 @@
+/* Tests for the support_format_dns_packet function.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/check.h>
+#include <support/format_nss.h>
+#include <support/run_diff.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void
+check_packet (const void *buffer, size_t length,
+              const char *name, const char *expected)
+{
+  char *actual = support_format_dns_packet (buffer, length);
+  if (strcmp (actual, expected) != 0)
+    {
+      support_record_failure ();
+      printf ("error: formatted packet does not match: %s\n", name);
+      support_run_diff ("expected", expected,
+                        "actual", actual);
+    }
+  free (actual);
+}
+
+static void
+test_aaaa_length (void)
+{
+  static const char packet[] =
+    /* Header: Response with two records.  */
+    "\x12\x34\x80\x00\x00\x01\x00\x02\x00\x00\x00\x00"
+    /* Question section.  www.example/IN/AAAA.  */
+    "\x03www\x07""example\x00\x00\x1c\x00\x01"
+    /* Answer section.  www.example AAAA [corrupted].  */
+    "\xc0\x0c"
+    "\x00\x1c\x00\x01\x00\x00\x00\x00\x00\x10"
+    "\x20\x01\x0d\xb8\x05\x06\x07\x08"
+    "\x11\x12\x13\x14\x15\x16\x17\x18"
+    /* www.example AAAA [corrupted].  */
+    "\xc0\x0c"
+    "\x00\x1c\x00\x01\x00\x00\x00\x00\x00\x11"
+    "\x01\x02\x03\x04\x05\x06\x07\x08"
+    "\x11\x12\x13\x14\x15\x16\x17\x18" "\xff";
+  check_packet (packet, sizeof (packet) - 1, __func__,
+                "name: www.example\n"
+                "address: 2001:db8:506:708:1112:1314:1516:1718\n"
+                "error: AAAA record of size 17: www.example\n");
+}
+
+static void
+test_multiple_cnames (void)
+{
+  static const char packet[] =
+    /* Header: Response with three records.  */
+    "\x12\x34\x80\x00\x00\x01\x00\x03\x00\x00\x00\x00"
+    /* Question section.  www.example/IN/A.  */
+    "\x03www\x07""example\x00\x00\x01\x00\x01"
+    /* Answer section.  www.example CNAME www1.example.  */
+    "\xc0\x0c"
+    "\x00\x05\x00\x01\x00\x00\x00\x00\x00\x07"
+    "\x04www1\xc0\x10"
+    /* www1 CNAME www2.  */
+    "\x04www1\xc0\x10"
+    "\x00\x05\x00\x01\x00\x00\x00\x00\x00\x07"
+    "\x04www2\xc0\x10"
+    /* www2 A 192.0.2.1.  */
+    "\x04www2\xc0\x10"
+    "\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04"
+    "\xc0\x00\x02\x01";
+  check_packet (packet, sizeof (packet) - 1, __func__,
+                "name: www.example\n"
+                "name: www1.example\n"
+                "name: www2.example\n"
+                "address: 192.0.2.1\n");
+}
+
+static int
+do_test (void)
+{
+  test_aaaa_length ();
+  test_multiple_cnames ();
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/support/tst-support_record_failure-2.sh b/support/tst-support_record_failure-2.sh
new file mode 100644
index 0000000..2c9372c
--- /dev/null
+++ b/support/tst-support_record_failure-2.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+# Test failure recording (with and without --direct).
+# Copyright (C) 2016-2017 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/>.  */
+
+set -e
+
+common_objpfx=$1; shift
+test_program_prefix_before_env=$1; shift
+run_program_env=$1; shift
+test_program_prefix_after_env=$1; shift
+
+run_test () {
+    expected_status="$1"
+    expected_output="$2"
+    shift 2
+    args="${common_objpfx}support/tst-support_record_failure $*"
+    echo "running: $args"
+    set +e
+    output="$(${test_program_prefix_before_env} \
+		 ${run_program} ${test_program_prefix_after_env} $args)"
+    status=$?
+    set -e
+    echo "  exit status: $status"
+    if test "$output" != "$expected_output" ; then
+	echo "error: unexpected output: $output"
+	exit 1
+    fi
+    if test "$status" -ne "$expected_status" ; then
+	echo "error: exit status $expected_status expected"
+	exit 1
+    fi
+}
+
+different_status () {
+    direct="$1"
+    run_test 1 "error: 1 test failures" $direct --status=0
+    run_test 1 "error: 1 test failures" $direct --status=1
+    run_test 2 "error: 1 test failures" $direct --status=2
+    run_test 1 "error: 1 test failures" $direct --status=77
+    run_test 2 "error: tst-support_record_failure.c:109: not true: false
+error: 1 test failures" $direct --test-verify
+    run_test 2 "error: tst-support_record_failure.c:109: not true: false
+info: execution passed failed TEST_VERIFY
+error: 1 test failures" $direct --test-verify --verbose
+}
+
+different_status
+different_status --direct
+
+run_test 1 "error: tst-support_record_failure.c:116: not true: false
+error: 1 test failures" --test-verify-exit
+# --direct does not print the summary error message if exit is called.
+run_test 1 "error: tst-support_record_failure.c:116: not true: false" \
+	 --direct --test-verify-exit
diff --git a/support/tst-support_record_failure.c b/support/tst-support_record_failure.c
new file mode 100644
index 0000000..e739e73
--- /dev/null
+++ b/support/tst-support_record_failure.c
@@ -0,0 +1,153 @@
+/* Test support_record_failure state sharing.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/check.h>
+#include <support/support.h>
+#include <support/test-driver.h>
+#include <support/xunistd.h>
+
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+static int exit_status_with_failure = -1;
+static bool test_verify;
+static bool test_verify_exit;
+enum
+  {
+    OPT_STATUS = 10001,
+    OPT_TEST_VERIFY,
+    OPT_TEST_VERIFY_EXIT,
+  };
+#define CMDLINE_OPTIONS                                                 \
+  { "status", required_argument, NULL, OPT_STATUS },                    \
+  { "test-verify", no_argument, NULL, OPT_TEST_VERIFY },                \
+  { "test-verify-exit", no_argument, NULL, OPT_TEST_VERIFY_EXIT },
+static void
+cmdline_process (int c)
+{
+  switch (c)
+    {
+    case OPT_STATUS:
+      exit_status_with_failure = atoi (optarg);
+      break;
+    case OPT_TEST_VERIFY:
+      test_verify = true;
+      break;
+    case OPT_TEST_VERIFY_EXIT:
+      test_verify_exit = true;
+      break;
+    }
+}
+#define CMDLINE_PROCESS cmdline_process
+
+static void
+check_failure_reporting (int phase, int zero, int unsupported)
+{
+  int status = support_report_failure (0);
+  if (status != zero)
+    {
+      printf ("real-error (phase %d): support_report_failure (0) == %d\n",
+              phase, status);
+      exit (1);
+    }
+  status = support_report_failure (1);
+  if (status != 1)
+    {
+      printf ("real-error (phase %d): support_report_failure (1) == %d\n",
+              phase, status);
+      exit (1);
+    }
+  status = support_report_failure (2);
+  if (status != 2)
+    {
+      printf ("real-error (phase %d): support_report_failure (2) == %d\n",
+              phase, status);
+      exit (1);
+    }
+  status = support_report_failure (EXIT_UNSUPPORTED);
+  if (status != unsupported)
+    {
+      printf ("real-error (phase %d): "
+              "support_report_failure (EXIT_UNSUPPORTED) == %d\n",
+              phase, status);
+      exit (1);
+    }
+}
+
+static int
+do_test (void)
+{
+  if (exit_status_with_failure >= 0)
+    {
+      /* External invocation with requested error status.  Used by
+         tst-support_report_failure-2.sh.  */
+      support_record_failure ();
+      return exit_status_with_failure;
+    }
+  TEST_VERIFY (true);
+  TEST_VERIFY_EXIT (true);
+  if (test_verify)
+    {
+      TEST_VERIFY (false);
+      if (test_verbose)
+        printf ("info: execution passed failed TEST_VERIFY\n");
+      return 2; /* Expected exit status.  */
+    }
+  if (test_verify_exit)
+    {
+      TEST_VERIFY_EXIT (false);
+      return 3; /* Not reached.  Expected exit status is 1.  */
+    }
+
+  printf ("info: This test tests the test framework.\n"
+          "info: It reports some expected errors on stdout.\n");
+
+  /* Check that the status is passed through unchanged.  */
+  check_failure_reporting (1, 0, EXIT_UNSUPPORTED);
+
+  /* Check state propagation from a subprocess.  */
+  pid_t pid = xfork ();
+  if (pid == 0)
+    {
+      support_record_failure ();
+      _exit (0);
+    }
+  int status;
+  xwaitpid (pid, &status, 0);
+  if (status != 0)
+    {
+      printf ("real-error: incorrect status from subprocess: %d\n", status);
+      return 1;
+    }
+  check_failure_reporting (2, 1, 1);
+
+  /* Also test directly in the parent process.  */
+  support_record_failure_reset ();
+  check_failure_reporting (3, 0, EXIT_UNSUPPORTED);
+  support_record_failure ();
+  check_failure_reporting (4, 1, 1);
+
+  /* We need to mask the failure above.  */
+  support_record_failure_reset ();
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/support/write_message.c b/support/write_message.c
new file mode 100644
index 0000000..f03ed93
--- /dev/null
+++ b/support/write_message.c
@@ -0,0 +1,29 @@
+/* Write a message to standard output.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/support.h>
+
+#include <string.h>
+#include <unistd.h>
+
+void
+write_message (const char *message)
+{
+  ssize_t unused __attribute__ ((unused));
+  unused = write (STDOUT_FILENO, message, strlen (message));
+}
diff --git a/support/xaccept.c b/support/xaccept.c
new file mode 100644
index 0000000..7b25af3
--- /dev/null
+++ b/support/xaccept.c
@@ -0,0 +1,32 @@
+/* accept with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xsocket.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+
+int
+xaccept (int fd, struct sockaddr *sa, socklen_t *salen)
+{
+  int clientfd = accept (fd, sa, salen);
+  if (clientfd < 0)
+    FAIL_EXIT1 ("accept (%d): %m", fd);
+  return clientfd;
+}
diff --git a/support/xaccept4.c b/support/xaccept4.c
new file mode 100644
index 0000000..67dd95e
--- /dev/null
+++ b/support/xaccept4.c
@@ -0,0 +1,32 @@
+/* accept4 with error checking.
+   Copyright (C) 2017 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/>.  */
+
+#include <support/xsocket.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+
+int
+xaccept4 (int fd, struct sockaddr *sa, socklen_t *salen, int flags)
+{
+  int clientfd = accept4 (fd, sa, salen, flags);
+  if (clientfd < 0)
+    FAIL_EXIT1 ("accept4 (%d, 0x%x): %m", fd, flags);
+  return clientfd;
+}
diff --git a/support/xasprintf.c b/support/xasprintf.c
new file mode 100644
index 0000000..5157680
--- /dev/null
+++ b/support/xasprintf.c
@@ -0,0 +1,36 @@
+/* Error-checking wrapper for asprintf.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/support.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+
+char *
+xasprintf (const char *format, ...)
+{
+  va_list ap;
+  va_start (ap, format);
+  char *result;
+  if (vasprintf (&result, format, ap) < 0)
+    FAIL_EXIT1 ("asprintf: %m");
+  va_end (ap);
+  return result;
+}
diff --git a/support/xbind.c b/support/xbind.c
new file mode 100644
index 0000000..cfc6dd8
--- /dev/null
+++ b/support/xbind.c
@@ -0,0 +1,30 @@
+/* bind with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xsocket.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+
+void
+xbind (int fd, const struct sockaddr *sa, socklen_t sa_len)
+{
+  if (bind (fd, sa, sa_len) != 0)
+    FAIL_EXIT1 ("bind (%d), family %d: %m", fd, sa->sa_family);
+}
diff --git a/support/xcalloc.c b/support/xcalloc.c
new file mode 100644
index 0000000..135f42d
--- /dev/null
+++ b/support/xcalloc.c
@@ -0,0 +1,34 @@
+/* Error-checking wrapper for calloc.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/support.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+void *
+xcalloc (size_t n, size_t s)
+{
+  void *p;
+
+  p = calloc (n, s);
+  if (p == NULL)
+    oom_error ("calloc", n * s);
+  return p;
+}
diff --git a/support/xchroot.c b/support/xchroot.c
new file mode 100644
index 0000000..abcc299
--- /dev/null
+++ b/support/xchroot.c
@@ -0,0 +1,28 @@
+/* chroot with error checking.
+   Copyright (C) 2017 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/>.  */
+
+#include <support/check.h>
+#include <support/xunistd.h>
+#include <sys/stat.h>
+
+void
+xchroot (const char *path)
+{
+  if (chroot (path) != 0)
+    FAIL_EXIT1 ("chroot (\"%s\"): %m", path);
+}
diff --git a/support/xclose.c b/support/xclose.c
new file mode 100644
index 0000000..c931e08
--- /dev/null
+++ b/support/xclose.c
@@ -0,0 +1,28 @@
+/* close with error checking.
+   Copyright (C) 2017 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/>.  */
+
+#include <support/xunistd.h>
+#include <support/check.h>
+#include <errno.h>
+
+void
+xclose (int fd)
+{
+  if (close (fd) < 0 && errno != EINTR)
+    FAIL_EXIT1 ("close of descriptor %d failed: %m", fd);
+}
diff --git a/support/xconnect.c b/support/xconnect.c
new file mode 100644
index 0000000..0266dbc
--- /dev/null
+++ b/support/xconnect.c
@@ -0,0 +1,30 @@
+/* connect with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xsocket.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+
+void
+xconnect (int fd, const struct sockaddr *sa, socklen_t sa_len)
+{
+  if (connect (fd, sa, sa_len) != 0)
+    FAIL_EXIT1 ("connect (%d), family %d: %m", fd, sa->sa_family);
+}
diff --git a/support/xdup2.c b/support/xdup2.c
new file mode 100644
index 0000000..dc08c94
--- /dev/null
+++ b/support/xdup2.c
@@ -0,0 +1,28 @@
+/* dup2 with error checking.
+   Copyright (C) 2017 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/>.  */
+
+#include <support/xunistd.h>
+
+#include <support/check.h>
+
+void
+xdup2 (int from, int to)
+{
+  if (dup2 (from, to) < 0)
+    FAIL_EXIT1 ("dup2 (%d, %d): %m", from, to);
+}
diff --git a/support/xfclose.c b/support/xfclose.c
new file mode 100644
index 0000000..2737f05
--- /dev/null
+++ b/support/xfclose.c
@@ -0,0 +1,33 @@
+/* fclose with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xstdio.h>
+
+#include <support/check.h>
+#include <stdlib.h>
+
+void
+xfclose (FILE *fp)
+{
+  if (ferror (fp))
+    FAIL_EXIT1 ("stdio stream closed with pending errors");
+  if (fflush (fp) != 0)
+    FAIL_EXIT1 ("fflush: %m");
+  if (fclose (fp) != 0)
+    FAIL_EXIT1 ("fclose: %m");
+}
diff --git a/support/xfopen.c b/support/xfopen.c
new file mode 100644
index 0000000..14532a0
--- /dev/null
+++ b/support/xfopen.c
@@ -0,0 +1,31 @@
+/* fopen with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xstdio.h>
+
+#include <support/check.h>
+#include <stdlib.h>
+
+FILE *
+xfopen (const char *path, const char *mode)
+{
+  FILE *fp = fopen (path, mode);
+  if (fp == NULL)
+    FAIL_EXIT1 ("could not open %s (mode \"%s\"): %m", path, mode);
+  return fp;
+}
diff --git a/support/xfork.c b/support/xfork.c
new file mode 100644
index 0000000..aa52ba6
--- /dev/null
+++ b/support/xfork.c
@@ -0,0 +1,32 @@
+/* fork with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xunistd.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+
+pid_t
+xfork (void)
+{
+  pid_t result = fork ();
+  if (result < 0)
+    FAIL_EXIT1 ("fork: %m");
+  return result;
+}
diff --git a/support/xgetsockname.c b/support/xgetsockname.c
new file mode 100644
index 0000000..c3bd884
--- /dev/null
+++ b/support/xgetsockname.c
@@ -0,0 +1,30 @@
+/* getsockname with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xsocket.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+
+void
+xgetsockname (int fd, struct sockaddr *sa, socklen_t *plen)
+{
+  if (getsockname (fd, sa, plen) != 0)
+    FAIL_EXIT1 ("setsockopt (%d): %m", fd);
+}
diff --git a/support/xlisten.c b/support/xlisten.c
new file mode 100644
index 0000000..1953e59
--- /dev/null
+++ b/support/xlisten.c
@@ -0,0 +1,30 @@
+/* listen with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xsocket.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+
+void
+xlisten (int fd, int backlog)
+{
+  if (listen (fd, backlog) != 0)
+    FAIL_EXIT1 ("listen (%d, %d): %m", fd, backlog);
+}
diff --git a/support/xmalloc.c b/support/xmalloc.c
new file mode 100644
index 0000000..450f699
--- /dev/null
+++ b/support/xmalloc.c
@@ -0,0 +1,34 @@
+/* Error-checking wrapper for malloc.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/support.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+void *
+xmalloc (size_t n)
+{
+  void *p;
+
+  p = malloc (n);
+  if (p == NULL)
+    oom_error ("malloc", n);
+  return p;
+}
diff --git a/support/xmemstream.c b/support/xmemstream.c
new file mode 100644
index 0000000..bce6dc9
--- /dev/null
+++ b/support/xmemstream.c
@@ -0,0 +1,42 @@
+/* Error-checking wrappers for memstream functions.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xmemstream.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/xstdio.h>
+
+void
+xopen_memstream (struct xmemstream *stream)
+{
+  int old_errno = errno;
+  *stream = (struct xmemstream) {};
+  stream->out = open_memstream (&stream->buffer, &stream->length);
+  if (stream->out == NULL)
+    FAIL_EXIT1 ("open_memstream: %m");
+  errno = old_errno;
+}
+
+void
+xfclose_memstream (struct xmemstream *stream)
+{
+  xfclose (stream->out);
+  stream->out = NULL;
+}
diff --git a/support/xmemstream.h b/support/xmemstream.h
new file mode 100644
index 0000000..e5ba231
--- /dev/null
+++ b/support/xmemstream.h
@@ -0,0 +1,49 @@
+/* Error-checking wrappers for memstream functions.
+   Copyright (C) 2016-2017 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/>.  */
+
+#ifndef SUPPORT_XMEMSTREAM_H
+#define SUPPORT_XMEMSTREAM_H
+
+#include <stdio.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/* Wrappers for other libc functions.  */
+struct xmemstream
+{
+  FILE *out;
+  char *buffer;
+  size_t length;
+};
+
+/* Create a new in-memory stream.  Initializes *STREAM.  After this
+   function returns, STREAM->out is a file descriptor open for
+   writing.  errno is preserved, so that the %m format specifier can
+   be used for writing to STREAM->out.  */
+void xopen_memstream (struct xmemstream *stream);
+
+/* Closes STREAM->OUT.  After this function returns, STREAM->buffer
+   and STREAM->length denote a memory range which contains the bytes
+   written to the output stream.  The caller should free
+   STREAM->buffer.  */
+void xfclose_memstream (struct xmemstream *stream);
+
+__END_DECLS
+
+#endif /* SUPPORT_XMEMSTREAM_H */
diff --git a/support/xmkdir.c b/support/xmkdir.c
new file mode 100644
index 0000000..ea17d49
--- /dev/null
+++ b/support/xmkdir.c
@@ -0,0 +1,28 @@
+/* mkdir with error checking.
+   Copyright (C) 2017 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/>.  */
+
+#include <support/check.h>
+#include <support/xunistd.h>
+#include <sys/stat.h>
+
+void
+xmkdir (const char *path, mode_t mode)
+{
+  if (mkdir (path, mode) != 0)
+    FAIL_EXIT1 ("mkdir (\"%s\", 0%o): %m", path, mode);
+}
diff --git a/support/xmmap.c b/support/xmmap.c
new file mode 100644
index 0000000..435b1eb
--- /dev/null
+++ b/support/xmmap.c
@@ -0,0 +1,31 @@
+/* mmap with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/check.h>
+#include <support/xunistd.h>
+#include <sys/mman.h>
+
+void *
+xmmap (void *addr, size_t length, int prot, int flags, int fd)
+{
+  void *result = mmap (addr, length, prot, flags, fd, 0);
+  if (result == MAP_FAILED)
+    FAIL_EXIT1 ("mmap of %zu bytes, prot=0x%x, flags=0x%x: %m",
+                length, prot, flags);
+  return result;
+}
diff --git a/support/xmunmap.c b/support/xmunmap.c
new file mode 100644
index 0000000..6ef5a4a
--- /dev/null
+++ b/support/xmunmap.c
@@ -0,0 +1,28 @@
+/* munmap with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/check.h>
+#include <support/xunistd.h>
+#include <sys/mman.h>
+
+void
+xmunmap (void *addr, size_t length)
+{
+  if (munmap (addr, length) != 0)
+    FAIL_EXIT1 ("munmap of %zu bytes: %m", length);
+}
diff --git a/support/xopen.c b/support/xopen.c
new file mode 100644
index 0000000..7f033a0
--- /dev/null
+++ b/support/xopen.c
@@ -0,0 +1,30 @@
+/* open64 with error checking.
+   Copyright (C) 2017 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/>.  */
+
+#include <support/check.h>
+#include <support/xunistd.h>
+#include <fcntl.h>
+
+int
+xopen (const char *path, int flags, mode_t mode)
+{
+  int ret = open64 (path, flags, mode);
+  if (ret < 0)
+    FAIL_EXIT1 ("open64 (\"%s\", 0x%x, 0%o): %m", path, flags, mode);
+  return ret;
+}
diff --git a/support/xpipe.c b/support/xpipe.c
new file mode 100644
index 0000000..89a64a5
--- /dev/null
+++ b/support/xpipe.c
@@ -0,0 +1,28 @@
+/* pipe with error checking.
+   Copyright (C) 2017 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/>.  */
+
+#include <support/xunistd.h>
+
+#include <support/check.h>
+
+void
+xpipe (int fds[2])
+{
+  if (pipe (fds) < 0)
+    FAIL_EXIT1 ("pipe: %m");
+}
diff --git a/support/xpoll.c b/support/xpoll.c
new file mode 100644
index 0000000..bec2521
--- /dev/null
+++ b/support/xpoll.c
@@ -0,0 +1,32 @@
+/* poll with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xsocket.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+
+int
+xpoll (struct pollfd *fds, nfds_t nfds, int timeout)
+{
+  int ret = poll (fds, nfds, timeout);
+  if (ret < 0)
+    FAIL_EXIT1 ("poll: %m");
+  return ret;
+}
diff --git a/support/xpthread_attr_destroy.c b/support/xpthread_attr_destroy.c
new file mode 100644
index 0000000..664c809
--- /dev/null
+++ b/support/xpthread_attr_destroy.c
@@ -0,0 +1,26 @@
+/* pthread_attr_destroy with error checking.
+   Copyright (C) 2017 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/>.  */
+
+#include <support/xthread.h>
+
+void
+xpthread_attr_destroy (pthread_attr_t *attr)
+{
+  xpthread_check_return ("pthread_attr_destroy",
+			 pthread_attr_destroy (attr));
+}
diff --git a/support/xpthread_attr_init.c b/support/xpthread_attr_init.c
new file mode 100644
index 0000000..2e30ade
--- /dev/null
+++ b/support/xpthread_attr_init.c
@@ -0,0 +1,25 @@
+/* pthread_attr_init with error checking.
+   Copyright (C) 2017 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/>.  */
+
+#include <support/xthread.h>
+
+void
+xpthread_attr_init (pthread_attr_t *attr)
+{
+  xpthread_check_return ("pthread_attr_init", pthread_attr_init (attr));
+}
diff --git a/support/xpthread_attr_setdetachstate.c b/support/xpthread_attr_setdetachstate.c
new file mode 100644
index 0000000..b544dba
--- /dev/null
+++ b/support/xpthread_attr_setdetachstate.c
@@ -0,0 +1,27 @@
+/* pthread_attr_setdetachstate with error checking.
+   Copyright (C) 2017 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/>.  */
+
+#include <support/xthread.h>
+
+void
+xpthread_attr_setdetachstate (pthread_attr_t *attr, int detachstate)
+{
+  xpthread_check_return ("pthread_attr_setdetachstate",
+			 pthread_attr_setdetachstate (attr,
+						      detachstate));
+}
diff --git a/support/xpthread_attr_setstacksize.c b/support/xpthread_attr_setstacksize.c
new file mode 100644
index 0000000..02d0631
--- /dev/null
+++ b/support/xpthread_attr_setstacksize.c
@@ -0,0 +1,26 @@
+/* pthread_attr_setstacksize with error checking.
+   Copyright (C) 2017 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/>.  */
+
+#include <support/xthread.h>
+
+void
+xpthread_attr_setstacksize (pthread_attr_t *attr, size_t stacksize)
+{
+  xpthread_check_return ("pthread_attr_setstacksize",
+			 pthread_attr_setstacksize (attr, stacksize));
+}
diff --git a/support/xpthread_barrier_destroy.c b/support/xpthread_barrier_destroy.c
new file mode 100644
index 0000000..efc0719
--- /dev/null
+++ b/support/xpthread_barrier_destroy.c
@@ -0,0 +1,26 @@
+/* pthread_barrier_destroy with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xthread.h>
+
+void
+xpthread_barrier_destroy (pthread_barrier_t *barrier)
+{
+  xpthread_check_return ("pthread_barrier_destroy",
+                         pthread_barrier_destroy (barrier));
+}
diff --git a/support/xpthread_barrier_init.c b/support/xpthread_barrier_init.c
new file mode 100644
index 0000000..b32dad1
--- /dev/null
+++ b/support/xpthread_barrier_init.c
@@ -0,0 +1,27 @@
+/* pthread_barrier_init with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xthread.h>
+
+void
+xpthread_barrier_init (pthread_barrier_t *barrier,
+                       pthread_barrierattr_t *attr, unsigned int count)
+{
+  xpthread_check_return ("pthread_barrier_init",
+                         pthread_barrier_init (barrier, attr, count));
+}
diff --git a/support/xpthread_barrier_wait.c b/support/xpthread_barrier_wait.c
new file mode 100644
index 0000000..7cee44d
--- /dev/null
+++ b/support/xpthread_barrier_wait.c
@@ -0,0 +1,28 @@
+/* pthread_barrier_wait with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xthread.h>
+
+int
+xpthread_barrier_wait (pthread_barrier_t *barrier)
+{
+  int ret = pthread_barrier_wait (barrier);
+  if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD)
+    xpthread_check_return ("pthread_barrier_wait", ret);
+  return ret == PTHREAD_BARRIER_SERIAL_THREAD;
+}
diff --git a/support/xpthread_cancel.c b/support/xpthread_cancel.c
new file mode 100644
index 0000000..3af16f9
--- /dev/null
+++ b/support/xpthread_cancel.c
@@ -0,0 +1,25 @@
+/* pthread_cancel with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xthread.h>
+
+void
+xpthread_cancel (pthread_t thr)
+{
+  xpthread_check_return ("pthread_cancel", pthread_cancel (thr));
+}
diff --git a/support/xpthread_check_return.c b/support/xpthread_check_return.c
new file mode 100644
index 0000000..3094d82
--- /dev/null
+++ b/support/xpthread_check_return.c
@@ -0,0 +1,34 @@
+/* Return value checking for pthread functions, exit variant.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xthread.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+
+void
+xpthread_check_return (const char *function, int value)
+{
+  if (value != 0)
+    {
+      errno = value;
+      FAIL_EXIT1 ("%s: %m", function);
+    }
+}
diff --git a/support/xpthread_cond_wait.c b/support/xpthread_cond_wait.c
new file mode 100644
index 0000000..b0e9b2a
--- /dev/null
+++ b/support/xpthread_cond_wait.c
@@ -0,0 +1,26 @@
+/* pthread_cond_wait with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xthread.h>
+
+void
+xpthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex)
+{
+  xpthread_check_return
+    ("pthread_cond_wait", pthread_cond_wait (cond, mutex));
+}
diff --git a/support/xpthread_create.c b/support/xpthread_create.c
new file mode 100644
index 0000000..98c63e5
--- /dev/null
+++ b/support/xpthread_create.c
@@ -0,0 +1,29 @@
+/* pthread_create with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xthread.h>
+
+pthread_t
+xpthread_create (pthread_attr_t *attr,
+                 void *(*thread_func) (void *), void *closure)
+{
+  pthread_t thr;
+  xpthread_check_return
+    ("pthread_create", pthread_create (&thr, attr, thread_func, closure));
+  return thr;
+}
diff --git a/support/xpthread_detach.c b/support/xpthread_detach.c
new file mode 100644
index 0000000..2088af2
--- /dev/null
+++ b/support/xpthread_detach.c
@@ -0,0 +1,25 @@
+/* pthread_detach with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xthread.h>
+
+void
+xpthread_detach (pthread_t thr)
+{
+  xpthread_check_return ("pthread_detach", pthread_detach (thr));
+}
diff --git a/support/xpthread_join.c b/support/xpthread_join.c
new file mode 100644
index 0000000..f23bb9a
--- /dev/null
+++ b/support/xpthread_join.c
@@ -0,0 +1,27 @@
+/* pthread_join with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xthread.h>
+
+void *
+xpthread_join (pthread_t thr)
+{
+  void *result;
+  xpthread_check_return ("pthread_join", pthread_join (thr, &result));
+  return result;
+}
diff --git a/support/xpthread_mutex_consistent.c b/support/xpthread_mutex_consistent.c
new file mode 100644
index 0000000..52364be
--- /dev/null
+++ b/support/xpthread_mutex_consistent.c
@@ -0,0 +1,26 @@
+/* pthread_mutex_consistent with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xthread.h>
+
+void
+xpthread_mutex_consistent (pthread_mutex_t *mutex)
+{
+  xpthread_check_return ("pthread_mutex_consistent",
+                         pthread_mutex_consistent (mutex));
+}
diff --git a/support/xpthread_mutex_destroy.c b/support/xpthread_mutex_destroy.c
new file mode 100644
index 0000000..f11f8f0
--- /dev/null
+++ b/support/xpthread_mutex_destroy.c
@@ -0,0 +1,26 @@
+/* pthread_mutex_destroy with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xthread.h>
+
+void
+xpthread_mutex_destroy (pthread_mutex_t *mutex)
+{
+  xpthread_check_return ("pthread_mutex_destroy",
+                         pthread_mutex_destroy (mutex));
+}
diff --git a/support/xpthread_mutex_init.c b/support/xpthread_mutex_init.c
new file mode 100644
index 0000000..2d16d1b
--- /dev/null
+++ b/support/xpthread_mutex_init.c
@@ -0,0 +1,26 @@
+/* pthread_mutex_init with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xthread.h>
+
+void
+xpthread_mutex_init (pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)
+{
+  xpthread_check_return ("pthread_mutex_init",
+                         pthread_mutex_init (mutex, attr));
+}
diff --git a/support/xpthread_mutex_lock.c b/support/xpthread_mutex_lock.c
new file mode 100644
index 0000000..af727b4
--- /dev/null
+++ b/support/xpthread_mutex_lock.c
@@ -0,0 +1,25 @@
+/* pthread_mutex_lock with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xthread.h>
+
+void
+xpthread_mutex_lock (pthread_mutex_t *mutex)
+{
+  xpthread_check_return ("pthread_mutex_lock", pthread_mutex_lock (mutex));
+}
diff --git a/support/xpthread_mutex_unlock.c b/support/xpthread_mutex_unlock.c
new file mode 100644
index 0000000..161b41e
--- /dev/null
+++ b/support/xpthread_mutex_unlock.c
@@ -0,0 +1,25 @@
+/* pthread_mutex_unlock with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xthread.h>
+
+void
+xpthread_mutex_unlock (pthread_mutex_t *mutex)
+{
+  xpthread_check_return ("pthread_mutex_unlock", pthread_mutex_unlock (mutex));
+}
diff --git a/support/xpthread_mutexattr_destroy.c b/support/xpthread_mutexattr_destroy.c
new file mode 100644
index 0000000..c699e32
--- /dev/null
+++ b/support/xpthread_mutexattr_destroy.c
@@ -0,0 +1,26 @@
+/* pthread_mutexattr_destroy with error checking.
+   Copyright (C) 2017 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/>.  */
+
+#include <support/xthread.h>
+
+void
+xpthread_mutexattr_destroy (pthread_mutexattr_t *attr)
+{
+  xpthread_check_return ("pthread_mutexattr_destroy",
+                         pthread_mutexattr_destroy (attr));
+}
diff --git a/support/xpthread_mutexattr_init.c b/support/xpthread_mutexattr_init.c
new file mode 100644
index 0000000..fa93fab
--- /dev/null
+++ b/support/xpthread_mutexattr_init.c
@@ -0,0 +1,25 @@
+/* pthread_mutexattr_init with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xthread.h>
+
+void
+xpthread_mutexattr_init (pthread_mutexattr_t *attr)
+{
+  xpthread_check_return ("pthread_mutexattr_init", pthread_mutexattr_init (attr));
+}
diff --git a/support/xpthread_mutexattr_setprotocol.c b/support/xpthread_mutexattr_setprotocol.c
new file mode 100644
index 0000000..353f75e
--- /dev/null
+++ b/support/xpthread_mutexattr_setprotocol.c
@@ -0,0 +1,26 @@
+/* pthread_mutexattr_setprotocol with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xthread.h>
+
+void
+xpthread_mutexattr_setprotocol (pthread_mutexattr_t *attr, int flag)
+{
+  xpthread_check_return ("pthread_mutexattr_setprotocol",
+                         pthread_mutexattr_setprotocol (attr, flag));
+}
diff --git a/support/xpthread_mutexattr_setpshared.c b/support/xpthread_mutexattr_setpshared.c
new file mode 100644
index 0000000..242da1a
--- /dev/null
+++ b/support/xpthread_mutexattr_setpshared.c
@@ -0,0 +1,26 @@
+/* pthread_mutexattr_setpshared with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xthread.h>
+
+void
+xpthread_mutexattr_setpshared (pthread_mutexattr_t *attr, int flag)
+{
+  xpthread_check_return ("pthread_mutexattr_setpshared",
+                         pthread_mutexattr_setpshared (attr, flag));
+}
diff --git a/support/xpthread_mutexattr_setrobust.c b/support/xpthread_mutexattr_setrobust.c
new file mode 100644
index 0000000..d7d6fa8
--- /dev/null
+++ b/support/xpthread_mutexattr_setrobust.c
@@ -0,0 +1,26 @@
+/* pthread_mutexattr_setrobust with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xthread.h>
+
+void
+xpthread_mutexattr_setrobust (pthread_mutexattr_t *attr, int flag)
+{
+  xpthread_check_return ("pthread_mutexattr_setrobust",
+                         pthread_mutexattr_setrobust (attr, flag));
+}
diff --git a/support/xpthread_mutexattr_settype.c b/support/xpthread_mutexattr_settype.c
new file mode 100644
index 0000000..cf22170
--- /dev/null
+++ b/support/xpthread_mutexattr_settype.c
@@ -0,0 +1,26 @@
+/* pthread_mutexattr_settype with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xthread.h>
+
+void
+xpthread_mutexattr_settype (pthread_mutexattr_t *attr, int flag)
+{
+  xpthread_check_return ("pthread_mutexattr_settype",
+                         pthread_mutexattr_settype (attr, flag));
+}
diff --git a/support/xpthread_once.c b/support/xpthread_once.c
new file mode 100644
index 0000000..70d58db
--- /dev/null
+++ b/support/xpthread_once.c
@@ -0,0 +1,25 @@
+/* pthread_once with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xthread.h>
+
+void
+xpthread_once (pthread_once_t *guard, void (*func) (void))
+{
+  xpthread_check_return ("pthread_once", pthread_once (guard, func));
+}
diff --git a/support/xpthread_sigmask.c b/support/xpthread_sigmask.c
new file mode 100644
index 0000000..0ba9ca0
--- /dev/null
+++ b/support/xpthread_sigmask.c
@@ -0,0 +1,34 @@
+/* pthread_sigmask with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xsignal.h>
+#include <support/support.h>
+
+#include <unistd.h>
+
+void
+xpthread_sigmask (int how, const sigset_t *set, sigset_t *oldset)
+{
+  if (pthread_sigmask (how, set, oldset) != 0)
+    {
+      write_message ("error: pthread_setmask failed\n");
+      /* Do not use exit because pthread_sigmask can be called from a
+         signal handler.  */
+      _exit (1);
+    }
+}
diff --git a/support/xpthread_spin_lock.c b/support/xpthread_spin_lock.c
new file mode 100644
index 0000000..6975215
--- /dev/null
+++ b/support/xpthread_spin_lock.c
@@ -0,0 +1,25 @@
+/* pthread_spin_lock with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xthread.h>
+
+void
+xpthread_spin_lock (pthread_spinlock_t *lock)
+{
+  xpthread_check_return ("pthread_spin_lock", pthread_spin_lock (lock));
+}
diff --git a/support/xpthread_spin_unlock.c b/support/xpthread_spin_unlock.c
new file mode 100644
index 0000000..4f19a44
--- /dev/null
+++ b/support/xpthread_spin_unlock.c
@@ -0,0 +1,25 @@
+/* pthread_spin_unlock with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xthread.h>
+
+void
+xpthread_spin_unlock (pthread_spinlock_t *lock)
+{
+  xpthread_check_return ("pthread_spin_unlock", pthread_spin_unlock (lock));
+}
diff --git a/support/xrealloc.c b/support/xrealloc.c
new file mode 100644
index 0000000..00c3138
--- /dev/null
+++ b/support/xrealloc.c
@@ -0,0 +1,32 @@
+/* Error-checking wrapper for realloc.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/support.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+void *
+xrealloc (void *p, size_t n)
+{
+  void *result = realloc (p, n);
+  if (result == NULL && (n > 0 || p == NULL))
+    oom_error ("realloc", n);
+  return result;
+}
diff --git a/support/xrecvfrom.c b/support/xrecvfrom.c
new file mode 100644
index 0000000..17809c4
--- /dev/null
+++ b/support/xrecvfrom.c
@@ -0,0 +1,33 @@
+/* recvfrom with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xsocket.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+
+size_t
+xrecvfrom (int fd, void *buf, size_t buflen, int flags,
+           struct sockaddr *sa, socklen_t *salen)
+{
+  ssize_t ret = recvfrom (fd, buf, buflen, flags, sa, salen);
+  if (ret < 0)
+    FAIL_EXIT1 ("error: recvfrom (%d), %zu bytes buffer: %m", fd, buflen);
+  return ret;
+}
diff --git a/support/xsendto.c b/support/xsendto.c
new file mode 100644
index 0000000..20bddf6
--- /dev/null
+++ b/support/xsendto.c
@@ -0,0 +1,35 @@
+/* sendto with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xsocket.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+
+void
+xsendto (int fd, const void *buf, size_t buflen, int flags,
+         const struct sockaddr *sa, socklen_t salen)
+{
+  ssize_t ret = sendto (fd, buf, buflen, flags, sa, salen);
+  if (ret < 0)
+    FAIL_EXIT1 ("sendto (%d), %zu bytes, family %d: %m",
+                fd, buflen, sa->sa_family);
+  if (ret != buflen)
+    FAIL_EXIT1 ("sendto (%d) sent %zd bytes instead of %zu", fd, ret, buflen);
+}
diff --git a/support/xsetsockopt.c b/support/xsetsockopt.c
new file mode 100644
index 0000000..9931882
--- /dev/null
+++ b/support/xsetsockopt.c
@@ -0,0 +1,31 @@
+/* setsockopt with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xsocket.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+
+void
+xsetsockopt (int fd, int level, int name, const void *val, socklen_t vallen)
+{
+  if (setsockopt (fd, level, name, val, vallen) != 0)
+    FAIL_EXIT1 ("setsockopt (%d, %d, %d), %zu bytes: %m",
+                fd, level, name, (size_t) vallen);
+}
diff --git a/support/xsignal.h b/support/xsignal.h
new file mode 100644
index 0000000..3dc0d9d
--- /dev/null
+++ b/support/xsignal.h
@@ -0,0 +1,34 @@
+/* Support functionality for using signals.
+   Copyright (C) 2016-2017 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/>.  */
+
+#ifndef SUPPORT_SIGNAL_H
+#define SUPPORT_SIGNAL_H
+
+#include <signal.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/* The following functions call the corresponding libpthread functions
+   and terminate the process on error.  */
+
+void xpthread_sigmask (int how, const sigset_t *set, sigset_t *oldset);
+
+__END_DECLS
+
+#endif /* SUPPORT_SIGNAL_H */
diff --git a/support/xsocket.c b/support/xsocket.c
new file mode 100644
index 0000000..c1deaee
--- /dev/null
+++ b/support/xsocket.c
@@ -0,0 +1,32 @@
+/* socket with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xsocket.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+
+int
+xsocket (int domain, int type, int protocol)
+{
+  int fd = socket (domain, type, protocol);
+  if (fd < 0)
+    FAIL_EXIT1 ("socket (%d, %d, %d): %m\n", domain, type, protocol);
+  return fd;
+}
diff --git a/support/xsocket.h b/support/xsocket.h
new file mode 100644
index 0000000..d672494
--- /dev/null
+++ b/support/xsocket.h
@@ -0,0 +1,39 @@
+/* Error-checking wrappers for socket functions.
+   Copyright (C) 2016-2017 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/>.  */
+
+#ifndef SUPPORT_XSOCKET_H
+#define SUPPORT_XSOCKET_H
+
+#include <poll.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+int xsocket (int, int, int);
+void xsetsockopt (int, int, int, const void *, socklen_t);
+void xgetsockname (int, struct sockaddr *, socklen_t *);
+void xconnect (int, const struct sockaddr *, socklen_t);
+void xbind (int, const struct sockaddr *, socklen_t);
+void xlisten (int, int);
+int xaccept (int, struct sockaddr *, socklen_t *);
+int xaccept4 (int, struct sockaddr *, socklen_t *, int);
+void xsendto (int, const void *, size_t, int,
+              const struct sockaddr *, socklen_t);
+size_t xrecvfrom (int, void *, size_t, int, struct sockaddr *, socklen_t *);
+int xpoll (struct pollfd *, nfds_t, int);
+
+#endif /* SUPPORT_XSOCKET_H */
diff --git a/support/xstdio.h b/support/xstdio.h
new file mode 100644
index 0000000..bcc2e86
--- /dev/null
+++ b/support/xstdio.h
@@ -0,0 +1,32 @@
+/* Error-checking wrappers for stdio functions.
+   Copyright (C) 2016-2017 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/>.  */
+
+#ifndef SUPPORT_XSTDIO_H
+#define SUPPORT_XSTDIO_H
+
+#include <stdio.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+FILE *xfopen (const char *path, const char *mode);
+void xfclose (FILE *);
+
+__END_DECLS
+
+#endif /* SUPPORT_XSTDIO_H */
diff --git a/support/xstrdup.c b/support/xstrdup.c
new file mode 100644
index 0000000..d6a8c04
--- /dev/null
+++ b/support/xstrdup.c
@@ -0,0 +1,30 @@
+/* strdup with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/support.h>
+
+#include <string.h>
+
+char *
+xstrdup (const char *s)
+{
+  char *p = strdup (s);
+  if (p == NULL)
+    oom_error ("strdup", strlen (s));
+  return p;
+}
diff --git a/support/xthread.h b/support/xthread.h
new file mode 100644
index 0000000..6dd7e70
--- /dev/null
+++ b/support/xthread.h
@@ -0,0 +1,77 @@
+/* Support functionality for using threads.
+   Copyright (C) 2016-2017 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/>.  */
+
+#ifndef SUPPORT_THREAD_H
+#define SUPPORT_THREAD_H
+
+#include <pthread.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/* Terminate the process (with exit status 0) after SECONDS have
+   elapsed, from a helper thread.  The process is terminated with the
+   exit function, so atexit handlers are executed.  */
+void delayed_exit (int seconds);
+
+/* Terminate the process (with exit status 1) if VALUE is not zero.
+   In that case, print a failure message to standard output mentioning
+   FUNCTION.  The process is terminated with the exit function, so
+   atexit handlers are executed.  */
+void xpthread_check_return (const char *function, int value);
+
+/* The following functions call the corresponding libpthread functions
+   and terminate the process on error.  */
+
+void xpthread_barrier_init (pthread_barrier_t *barrier,
+                            pthread_barrierattr_t *attr, unsigned int count);
+void xpthread_barrier_destroy (pthread_barrier_t *barrier);
+void xpthread_mutexattr_destroy (pthread_mutexattr_t *);
+void xpthread_mutexattr_init (pthread_mutexattr_t *);
+void xpthread_mutexattr_setprotocol (pthread_mutexattr_t *, int);
+void xpthread_mutexattr_setpshared (pthread_mutexattr_t *, int);
+void xpthread_mutexattr_setrobust (pthread_mutexattr_t *, int);
+void xpthread_mutexattr_settype (pthread_mutexattr_t *, int);
+void xpthread_mutex_init (pthread_mutex_t *, const pthread_mutexattr_t *);
+void xpthread_mutex_destroy (pthread_mutex_t *);
+void xpthread_mutex_lock (pthread_mutex_t *mutex);
+void xpthread_mutex_unlock (pthread_mutex_t *mutex);
+void xpthread_mutex_consistent (pthread_mutex_t *);
+void xpthread_spin_lock (pthread_spinlock_t *lock);
+void xpthread_spin_unlock (pthread_spinlock_t *lock);
+void xpthread_cond_wait (pthread_cond_t * cond, pthread_mutex_t * mutex);
+pthread_t xpthread_create (pthread_attr_t *attr,
+                           void *(*thread_func) (void *), void *closure);
+void xpthread_detach (pthread_t thr);
+void xpthread_cancel (pthread_t thr);
+void *xpthread_join (pthread_t thr);
+void xpthread_once (pthread_once_t *guard, void (*func) (void));
+void xpthread_attr_destroy (pthread_attr_t *attr);
+void xpthread_attr_init (pthread_attr_t *attr);
+void xpthread_attr_setdetachstate (pthread_attr_t *attr,
+				   int detachstate);
+void xpthread_attr_setstacksize (pthread_attr_t *attr,
+				 size_t stacksize);
+
+/* This function returns non-zero if pthread_barrier_wait returned
+   PTHREAD_BARRIER_SERIAL_THREAD.  */
+int xpthread_barrier_wait (pthread_barrier_t *barrier);
+
+__END_DECLS
+
+#endif /* SUPPORT_THREAD_H */
diff --git a/support/xunistd.h b/support/xunistd.h
new file mode 100644
index 0000000..151d743
--- /dev/null
+++ b/support/xunistd.h
@@ -0,0 +1,56 @@
+/* POSIX-specific extra functions.
+   Copyright (C) 2016-2017 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/>.  */
+
+/* These wrapper functions use POSIX types and therefore cannot be
+   declared in <support/support.h>.  */
+
+#ifndef SUPPORT_XUNISTD_H
+#define SUPPORT_XUNISTD_H
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+__BEGIN_DECLS
+
+struct stat64;
+
+pid_t xfork (void);
+pid_t xwaitpid (pid_t, int *status, int flags);
+void xpipe (int[2]);
+void xdup2 (int, int);
+int xopen (const char *path, int flags, mode_t);
+void xstat (const char *path, struct stat64 *);
+void xmkdir (const char *path, mode_t);
+void xchroot (const char *path);
+
+/* Close the file descriptor.  Ignore EINTR errors, but terminate the
+   process on other errors.  */
+void xclose (int);
+
+/* Write the buffer.  Retry on short writes.  */
+void xwrite (int, const void *, size_t);
+
+/* Invoke mmap with a zero file offset.  */
+void *xmmap (void *addr, size_t length, int prot, int flags, int fd);
+
+void xmunmap (void *addr, size_t length);
+
+__END_DECLS
+
+#endif /* SUPPORT_XUNISTD_H */
diff --git a/support/xwaitpid.c b/support/xwaitpid.c
new file mode 100644
index 0000000..204795e
--- /dev/null
+++ b/support/xwaitpid.c
@@ -0,0 +1,33 @@
+/* waitpid with error checking.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xunistd.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <sys/wait.h>
+
+int
+xwaitpid (int pid, int *status, int flags)
+{
+  pid_t result = waitpid (pid, status, flags);
+  if (result < 0)
+    FAIL_EXIT1 ("waitpid: %m\n");
+  return result;
+}
diff --git a/support/xwrite.c b/support/xwrite.c
new file mode 100644
index 0000000..134e8ee
--- /dev/null
+++ b/support/xwrite.c
@@ -0,0 +1,39 @@
+/* write with error checking and retries.
+   Copyright (C) 2016-2017 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/>.  */
+
+#include <support/xunistd.h>
+
+#include <support/check.h>
+
+void
+xwrite (int fd, const void *buffer, size_t length)
+{
+  const char *p = buffer;
+  const char *end = p + length;
+  while (p < end)
+    {
+      ssize_t ret = write (fd, p, end - p);
+      if (ret < 0)
+        FAIL_EXIT1 ("write of %zu bytes failed after %td: %m",
+                    length, p - (const char *) buffer);
+      if (ret == 0)
+        FAIL_EXIT1 ("write return 0 after writing %td bytes of %zu",
+                    p - (const char *) buffer, length);
+      p += ret;
+    }
+}

-----------------------------------------------------------------------

Summary of changes:
 ChangeLog                                  |   57 ++
 Makeconfig                                 |   15 +-
 NEWS                                       |   32 +
 Rules                                      |    3 +
 elf/dl-load.c                              |   76 +-
 elf/ldd.bash.in                            |   14 +-
 extra-lib.mk                               |    5 +
 include/array_length.h                     |   36 +
 malloc/Makefile                            |    3 +-
 malloc/malloc.c                            |   30 +-
 malloc/tst-malloc-too-large.c              |  237 ++++++
 scripts/backport-support.sh                |  110 +++
 sunrpc/Makefile                            |    3 +-
 sunrpc/clnt_udp.c                          |    2 +-
 sunrpc/tst-udp-error.c                     |   62 ++
 support/Makefile                           |  146 ++++
 support/README                             |   29 +
 support/README-testing.c                   |   19 +
 support/capture_subprocess.h               |   61 ++
 support/check.c                            |   57 ++
 support/check.h                            |   94 +++
 support/check_addrinfo.c                   |   42 +
 support/check_dns_packet.c                 |   42 +
 support/check_hostent.c                    |   42 +
 support/check_netent.c                     |   42 +
 support/check_nss.h                        |   42 +
 support/delayed_exit.c                     |   55 ++
 support/format_nss.h                       |   41 +
 support/ignore_stderr.c                    |   38 +
 support/namespace.h                        |   65 ++
 support/oom_error.c                        |   29 +
 support/resolv_test.c                      | 1202 ++++++++++++++++++++++++++++
 support/resolv_test.h                      |  180 +++++
 support/run_diff.h                         |   31 +
 support/set_fortify_handler.c              |   34 +
 support/support-xstat.c                    |   30 +
 support/support.h                          |   74 ++
 support/support_become_root.c              |   40 +
 support/support_can_chroot.c               |   65 ++
 support/support_capture_subprocess.c       |  108 +++
 support/support_capture_subprocess_check.c |   67 ++
 support/support_enter_network_namespace.c  |   75 ++
 support/support_format_address_family.c    |   35 +
 support/support_format_addrinfo.c          |  239 ++++++
 support/support_format_dns_packet.c        |  222 +++++
 support/support_format_herrno.c            |   45 +
 support/support_format_hostent.c           |   75 ++
 support/support_format_netent.c            |   52 ++
 support/support_isolate_in_subprocess.c    |   38 +
 support/support_record_failure.c           |  106 +++
 support/support_run_diff.c                 |   76 ++
 support/support_shared_allocate.c          |   59 ++
 support/support_test_main.c                |  423 ++++++++++
 support/support_test_verify_impl.c         |   33 +
 support/support_write_file_string.c        |   39 +
 support/temp_file-internal.h               |   31 +
 support/temp_file.c                        |  132 +++
 support/temp_file.h                        |   37 +
 support/test-driver.c                      |  156 ++++
 support/test-driver.h                      |   74 ++
 support/tst-support-namespace.c            |   34 +
 support/tst-support_capture_subprocess.c   |  188 +++++
 support/tst-support_format_dns_packet.c    |  101 +++
 support/tst-support_record_failure-2.sh    |   69 ++
 support/tst-support_record_failure.c       |  153 ++++
 support/write_message.c                    |   29 +
 support/xaccept.c                          |   32 +
 support/xaccept4.c                         |   32 +
 support/xasprintf.c                        |   36 +
 support/xbind.c                            |   30 +
 support/xcalloc.c                          |   34 +
 support/xchroot.c                          |   28 +
 support/xclose.c                           |   28 +
 support/xconnect.c                         |   30 +
 support/xdup2.c                            |   28 +
 support/xfclose.c                          |   33 +
 support/xfopen.c                           |   31 +
 support/xfork.c                            |   32 +
 support/xgetsockname.c                     |   30 +
 support/xlisten.c                          |   30 +
 support/xmalloc.c                          |   34 +
 support/xmemstream.c                       |   42 +
 support/xmemstream.h                       |   49 ++
 support/xmkdir.c                           |   28 +
 support/xmmap.c                            |   31 +
 support/xmunmap.c                          |   28 +
 support/xopen.c                            |   30 +
 support/xpipe.c                            |   28 +
 support/xpoll.c                            |   32 +
 support/xpthread_attr_destroy.c            |   26 +
 support/xpthread_attr_init.c               |   25 +
 support/xpthread_attr_setdetachstate.c     |   27 +
 support/xpthread_attr_setstacksize.c       |   26 +
 support/xpthread_barrier_destroy.c         |   26 +
 support/xpthread_barrier_init.c            |   27 +
 support/xpthread_barrier_wait.c            |   28 +
 support/xpthread_cancel.c                  |   25 +
 support/xpthread_check_return.c            |   34 +
 support/xpthread_cond_wait.c               |   26 +
 support/xpthread_create.c                  |   29 +
 support/xpthread_detach.c                  |   25 +
 support/xpthread_join.c                    |   27 +
 support/xpthread_mutex_consistent.c        |   26 +
 support/xpthread_mutex_destroy.c           |   26 +
 support/xpthread_mutex_init.c              |   26 +
 support/xpthread_mutex_lock.c              |   25 +
 support/xpthread_mutex_unlock.c            |   25 +
 support/xpthread_mutexattr_destroy.c       |   26 +
 support/xpthread_mutexattr_init.c          |   25 +
 support/xpthread_mutexattr_setprotocol.c   |   26 +
 support/xpthread_mutexattr_setpshared.c    |   26 +
 support/xpthread_mutexattr_setrobust.c     |   26 +
 support/xpthread_mutexattr_settype.c       |   26 +
 support/xpthread_once.c                    |   25 +
 support/xpthread_sigmask.c                 |   34 +
 support/xpthread_spin_lock.c               |   25 +
 support/xpthread_spin_unlock.c             |   25 +
 support/xrealloc.c                         |   32 +
 support/xrecvfrom.c                        |   33 +
 support/xsendto.c                          |   35 +
 support/xsetsockopt.c                      |   31 +
 support/xsignal.h                          |   34 +
 support/xsocket.c                          |   32 +
 support/xsocket.h                          |   39 +
 support/xstdio.h                           |   32 +
 support/xstrdup.c                          |   30 +
 support/xthread.h                          |   77 ++
 support/xunistd.h                          |   56 ++
 support/xwaitpid.c                         |   33 +
 support/xwrite.c                           |   39 +
 130 files changed, 7804 insertions(+), 59 deletions(-)
 create mode 100644 include/array_length.h
 create mode 100644 malloc/tst-malloc-too-large.c
 create mode 100644 scripts/backport-support.sh
 create mode 100644 sunrpc/tst-udp-error.c
 create mode 100644 support/Makefile
 create mode 100644 support/README
 create mode 100644 support/README-testing.c
 create mode 100644 support/capture_subprocess.h
 create mode 100644 support/check.c
 create mode 100644 support/check.h
 create mode 100644 support/check_addrinfo.c
 create mode 100644 support/check_dns_packet.c
 create mode 100644 support/check_hostent.c
 create mode 100644 support/check_netent.c
 create mode 100644 support/check_nss.h
 create mode 100644 support/delayed_exit.c
 create mode 100644 support/format_nss.h
 create mode 100644 support/ignore_stderr.c
 create mode 100644 support/namespace.h
 create mode 100644 support/oom_error.c
 create mode 100644 support/resolv_test.c
 create mode 100644 support/resolv_test.h
 create mode 100644 support/run_diff.h
 create mode 100644 support/set_fortify_handler.c
 create mode 100644 support/support-xstat.c
 create mode 100644 support/support.h
 create mode 100644 support/support_become_root.c
 create mode 100644 support/support_can_chroot.c
 create mode 100644 support/support_capture_subprocess.c
 create mode 100644 support/support_capture_subprocess_check.c
 create mode 100644 support/support_enter_network_namespace.c
 create mode 100644 support/support_format_address_family.c
 create mode 100644 support/support_format_addrinfo.c
 create mode 100644 support/support_format_dns_packet.c
 create mode 100644 support/support_format_herrno.c
 create mode 100644 support/support_format_hostent.c
 create mode 100644 support/support_format_netent.c
 create mode 100644 support/support_isolate_in_subprocess.c
 create mode 100644 support/support_record_failure.c
 create mode 100644 support/support_run_diff.c
 create mode 100644 support/support_shared_allocate.c
 create mode 100644 support/support_test_main.c
 create mode 100644 support/support_test_verify_impl.c
 create mode 100644 support/support_write_file_string.c
 create mode 100644 support/temp_file-internal.h
 create mode 100644 support/temp_file.c
 create mode 100644 support/temp_file.h
 create mode 100644 support/test-driver.c
 create mode 100644 support/test-driver.h
 create mode 100644 support/tst-support-namespace.c
 create mode 100644 support/tst-support_capture_subprocess.c
 create mode 100644 support/tst-support_format_dns_packet.c
 create mode 100644 support/tst-support_record_failure-2.sh
 create mode 100644 support/tst-support_record_failure.c
 create mode 100644 support/write_message.c
 create mode 100644 support/xaccept.c
 create mode 100644 support/xaccept4.c
 create mode 100644 support/xasprintf.c
 create mode 100644 support/xbind.c
 create mode 100644 support/xcalloc.c
 create mode 100644 support/xchroot.c
 create mode 100644 support/xclose.c
 create mode 100644 support/xconnect.c
 create mode 100644 support/xdup2.c
 create mode 100644 support/xfclose.c
 create mode 100644 support/xfopen.c
 create mode 100644 support/xfork.c
 create mode 100644 support/xgetsockname.c
 create mode 100644 support/xlisten.c
 create mode 100644 support/xmalloc.c
 create mode 100644 support/xmemstream.c
 create mode 100644 support/xmemstream.h
 create mode 100644 support/xmkdir.c
 create mode 100644 support/xmmap.c
 create mode 100644 support/xmunmap.c
 create mode 100644 support/xopen.c
 create mode 100644 support/xpipe.c
 create mode 100644 support/xpoll.c
 create mode 100644 support/xpthread_attr_destroy.c
 create mode 100644 support/xpthread_attr_init.c
 create mode 100644 support/xpthread_attr_setdetachstate.c
 create mode 100644 support/xpthread_attr_setstacksize.c
 create mode 100644 support/xpthread_barrier_destroy.c
 create mode 100644 support/xpthread_barrier_init.c
 create mode 100644 support/xpthread_barrier_wait.c
 create mode 100644 support/xpthread_cancel.c
 create mode 100644 support/xpthread_check_return.c
 create mode 100644 support/xpthread_cond_wait.c
 create mode 100644 support/xpthread_create.c
 create mode 100644 support/xpthread_detach.c
 create mode 100644 support/xpthread_join.c
 create mode 100644 support/xpthread_mutex_consistent.c
 create mode 100644 support/xpthread_mutex_destroy.c
 create mode 100644 support/xpthread_mutex_init.c
 create mode 100644 support/xpthread_mutex_lock.c
 create mode 100644 support/xpthread_mutex_unlock.c
 create mode 100644 support/xpthread_mutexattr_destroy.c
 create mode 100644 support/xpthread_mutexattr_init.c
 create mode 100644 support/xpthread_mutexattr_setprotocol.c
 create mode 100644 support/xpthread_mutexattr_setpshared.c
 create mode 100644 support/xpthread_mutexattr_setrobust.c
 create mode 100644 support/xpthread_mutexattr_settype.c
 create mode 100644 support/xpthread_once.c
 create mode 100644 support/xpthread_sigmask.c
 create mode 100644 support/xpthread_spin_lock.c
 create mode 100644 support/xpthread_spin_unlock.c
 create mode 100644 support/xrealloc.c
 create mode 100644 support/xrecvfrom.c
 create mode 100644 support/xsendto.c
 create mode 100644 support/xsetsockopt.c
 create mode 100644 support/xsignal.h
 create mode 100644 support/xsocket.c
 create mode 100644 support/xsocket.h
 create mode 100644 support/xstdio.h
 create mode 100644 support/xstrdup.c
 create mode 100644 support/xthread.h
 create mode 100644 support/xunistd.h
 create mode 100644 support/xwaitpid.c
 create mode 100644 support/xwrite.c


hooks/post-receive
-- 
GNU C Library master sources


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]