Blob Blame History Raw
This patch creates the contents of the support/ directory up to this
upstream commit on the master branch:

commit f8bf15febcaf137bbec5a61101e88cd5a9d56ca8
Author: Carlos O'Donell <carlos@redhat.com>
Date:   Sat Jan 28 19:13:34 2017 -0500

    Bug 20116: Fix use after free in pthread_create()
    
    The commit documents the ownership rules around 'struct pthread' and
    when a thread can read or write to the descriptor. With those ownership
    rules in place it becomes obvious that pd->stopped_start should not be
    touched in several of the paths during thread startup, particularly so
    for detached threads. In the case of detached threads, between the time
    the thread is created by the OS kernel and the creating thread checks
    pd->stopped_start, the detached thread might have already exited and the
    memory for pd unmapped. As a regression test we add a simple test which
    exercises this exact case by quickly creating detached threads with
    large enough stacks to ensure the thread stack cache is bypassed and the
    stacks are unmapped. Before the fix the testcase segfaults, after the
    fix it works correctly and completes without issue.
    
    For a detailed discussion see:
    https://www.sourceware.org/ml/libc-alpha/2017-01/msg00505.html

diff --git a/support/Makefile b/support/Makefile
new file mode 100644
index 0000000..2ace559
--- /dev/null
+++ b/support/Makefile
@@ -0,0 +1,128 @@
+# 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_become_root \
+  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_record_failure \
+  support_run_diff \
+  support_test_main \
+  support_test_verify_impl \
+  temp_file \
+  write_message \
+  xaccept \
+  xasprintf \
+  xbind \
+  xcalloc \
+  xconnect \
+  xfclose \
+  xfopen \
+  xfork \
+  xgetsockname \
+  xlisten \
+  xmalloc \
+  xmemstream \
+  xmmap \
+  xmunmap \
+  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_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
+
+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/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..6bc82d6
--- /dev/null
+++ b/support/namespace.h
@@ -0,0 +1,53 @@
+/* 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);
+
+/* 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);
+
+__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..2d0ea3c
--- /dev/null
+++ b/support/resolv_test.c
@@ -0,0 +1,1150 @@
+/* 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 <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;
+};
+
+/* Update *INFO from the specified DNS packet.  */
+static void
+parse_query (struct query_info *info,
+             const unsigned char *buffer, size_t length)
+{
+  if (length < 12)
+    FAIL_EXIT1 ("malformed DNS query: too short: %zu bytes", length);
+
+  int ret = dn_expand (buffer, buffer + length, buffer + 12,
+                       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);
+}
+
+
+/* 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,
+    };
+  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,
+        };
+      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;
+    }
+
+  close (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);
+          close (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)
+            {
+              close (server->socket_udp);
+              close (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)
+        {
+          close (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)
+        {
+          close (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));
+          close (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));
+          close (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);
+          close (obj->servers[server_index].socket_udp);
+        }
+      if (!obj->config.servers[server_index].disable_tcp)
+        {
+          xpthread_join (obj->servers[server_index].thread_tcp);
+          close (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..7a9f1f7
--- /dev/null
+++ b/support/resolv_test.h
@@ -0,0 +1,169 @@
+/* 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
+
+/* 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;
+};
+
+/* 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.h b/support/support.h
new file mode 100644
index 0000000..7292e2a
--- /dev/null
+++ b/support/support.h
@@ -0,0 +1,59 @@
+/* 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)));
+
+/* 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_enter_network_namespace.c b/support/support_enter_network_namespace.c
new file mode 100644
index 0000000..d2e78fe
--- /dev/null
+++ b/support/support_enter_network_namespace.c
@@ -0,0 +1,74 @@
+/* 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 <sys/ioctl.h>
+#include <unistd.h>
+#include <xsocket.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);
+        }
+      close (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..262e0df
--- /dev/null
+++ b/support/support_format_addrinfo.c
@@ -0,0 +1,202 @@
+/* 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 (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_one (FILE *out, struct addrinfo *ai, int *flags)
+{
+  /* ai_flags */
+  if (ai->ai_flags != *flags)
+    {
+      fprintf (out, "flags:");
+      int flags_printed = 0;
+#define FLAG(flag) format_ai_flags (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");
+      *flags = ai->ai_flags;
+    }
+
+  {
+    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));
+  }
+
+  /* ai_canonname */
+  if (ai->ai_canonname != NULL)
+    fprintf (out, "canonname: %s\n", ai->ai_canonname);
+}
+
+/* Format all the addresses in one address family.  */
+static void
+format_ai_family (FILE *out, struct addrinfo *ai, int family, int *flags)
+{
+  while (ai)
+    {
+      if (ai->ai_family == family)
+        format_ai_one (out, ai, flags);
+      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
+    {
+      int flags = 0;
+      format_ai_family (mem.out, ai, AF_INET, &flags);
+      format_ai_family (mem.out, ai, AF_INET6, &flags);
+    }
+
+  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..21fe7e5
--- /dev/null
+++ b/support/support_format_dns_packet.c
@@ -0,0 +1,215 @@
+/* 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 || 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:
+          {
+            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);
+          }
+          break;
+        case T_CNAME:
+        case T_PTR:
+          {
+            struct dname name;
+            if (extract_name (full, &in, &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_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..3085037
--- /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 <sys/wait.h>
+#include <xunistd.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));
+  TEST_VERIFY_EXIT (close (fd) == 0);
+  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_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/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..f06647a
--- /dev/null
+++ b/support/temp_file.c
@@ -0,0 +1,125 @@
+/* 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 <search.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* List of temporary files.  */
+static struct temp_name_list
+{
+  struct qelem q;
+  char *name;
+} *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;
+      if (temp_name_list == NULL)
+	temp_name_list = (struct temp_name_list *) &newp->q;
+      else
+	insque (newp, temp_name_list);
+    }
+  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)
+{
+  while (temp_name_list != NULL)
+    {
+      remove (temp_name_list->name);
+      free (temp_name_list->name);
+
+      struct temp_name_list *next
+	= (struct temp_name_list *) temp_name_list->q.q_forw;
+      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 = (struct temp_name_list *) n->q.q_forw)
+        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_record_failure-2.sh b/support/tst-support_record_failure-2.sh
new file mode 100644
index 0000000..1751377
--- /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 ouput: $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:108: not true: false
+error: 1 test failures" $direct --test-verify
+    run_test 2 "error: tst-support_record_failure.c:108: 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:115: 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:115: 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..62d8e1f
--- /dev/null
+++ b/support/tst-support_record_failure.c
@@ -0,0 +1,152 @@
+/* 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>
+
+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/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/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/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/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/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..0dbf13a
--- /dev/null
+++ b/support/xsocket.h
@@ -0,0 +1,38 @@
+/* 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 *);
+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..a83b1f4
--- /dev/null
+++ b/support/xunistd.h
@@ -0,0 +1,43 @@
+/* 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 <unistd.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+pid_t xfork (void);
+pid_t xwaitpid (pid_t, int *status, int flags);
+
+/* 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;
+    }
+}