public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
* V8 test-in-container patch
@ 2018-08-16 17:59 DJ Delorie
  2018-08-16 18:54 ` Florian Weimer
                   ` (2 more replies)
  0 siblings, 3 replies; 25+ messages in thread
From: DJ Delorie @ 2018-08-16 17:59 UTC (permalink / raw)
  To: libc-alpha; +Cc: fweimer, Joseph Myers, carlos


This still uses CLONE_NEWPID.  I recall spending a lot of time trying to
get the "su-less sudo" working correctly, and it's fragile but works
as-is.

* Makefile (testroot.pristine): New rules to initialize the
test-in-container "testroot".
* Makerules (all-testsuite): Add tests-container.
* Rules (tests): Add tests-container.
(binaries-all-tests): Likewise.
(tests-container): New, run these tests in the testroot container.
* support/Makefile (others): Add *-container, support_paths.c,
xmkdirp, and links-dso-program.
* support/links-dso-program-c.c: New.
* support/links-dso-program.cc: New.
* support/test-container.c: New.
* support/shell-container.c: New.
* support/echo-container.c: New.
* support/true-container.c: New.
* support/xmkdirp.c: New.
* support/xsymlink.c: New.
* support/support_paths.c: New.
* support/support.h: Add support paths prototypes.
* support/xunistd.h: Add xmkdirp () and xsymlink ().

* nss/tst-nss-test3.c: Convert to test-in-container.
* nss/tst-nss-test3.root/: New.

diff --git a/Makefile b/Makefile
index d3f25a525a..5290434f26 100644
--- a/Makefile
+++ b/Makefile
@@ -340,6 +340,52 @@ define summarize-tests
 @! egrep -q -v '^(X?PASS|XFAIL|UNSUPPORTED):' $(objpfx)$1
 endef
 
+# The intention here is to do ONE install of our build into the
+# testroot.pristine/ directory, then rsync (internal to
+# support/test-container) that to testroot.root/ at the start of each
+# test.  That way we can promise each test a "clean" install, without
+# having to do the install for each test.
+#
+# In addition, we have to copy some files (which we build) into this
+# root in addition to what glibc installs.  For example, many tests
+# require /bin/sh be present, and any shared objects that /bin/sh
+# depends on.  We also build a "test" program in either C or (if
+# available) C++ just so we can copy in any shared objects (which we
+# do not build) that GCC-compiled programs depend on.
+
+$(tests-container) $(addsuffix /tests,$(subdirs)) : \
+		$(objpfx)testroot.pristine/install.stamp
+$(objpfx)testroot.pristine/install.stamp :
+	test -d $(objpfx)testroot.pristine || \
+	  mkdir $(objpfx)testroot.pristine
+	# We need a working /bin/sh for some of the tests.
+	test -d $(objpfx)testroot.pristine/bin || \
+	  mkdir $(objpfx)testroot.pristine/bin
+	cp $(objpfx)support/shell-container $(objpfx)testroot.pristine/bin/sh
+	cp $(objpfx)support/echo-container $(objpfx)testroot.pristine/bin/echo
+	cp $(objpfx)support/true-container $(objpfx)testroot.pristine/bin/true
+	# Copy these DSOs first so we can overwrite them with our own.
+	for dso in `$(test-wrapper-env) LD_TRACE_LOADED_OBJECTS=1  \
+		$(objpfx)elf/$(rtld-installed-name) \
+		$(objpfx)testroot.pristine/bin/sh \
+	        | grep / | sed 's/^[^/]*//' | sed 's/ .*//'` ;\
+	  do \
+	    test -d `dirname $(objpfx)testroot.pristine$$dso` || \
+	      mkdir -p `dirname $(objpfx)testroot.pristine$$dso` ;\
+	    $(test-wrapper) cp $$dso $(objpfx)testroot.pristine$$dso ;\
+	  done
+	for dso in `$(test-wrapper-env) LD_TRACE_LOADED_OBJECTS=1  \
+		$(objpfx)elf/$(rtld-installed-name) \
+		$(objpfx)support/links-dso-program \
+	        | grep / | sed 's/^[^/]*//' | sed 's/ .*//'` ;\
+	  do \
+	    test -d `dirname $(objpfx)testroot.pristine$$dso` || \
+	      mkdir -p `dirname $(objpfx)testroot.pristine$$dso` ;\
+	    $(test-wrapper) cp $$dso $(objpfx)testroot.pristine$$dso ;\
+	  done
+	$(MAKE) install DESTDIR=$(objpfx)testroot.pristine
+	touch $(objpfx)testroot.pristine/install.stamp
+
 tests-special-notdir = $(patsubst $(objpfx)%, %, $(tests-special))
 tests: $(tests-special)
 	$(..)scripts/merge-test-results.sh -s $(objpfx) "" \
diff --git a/Makerules b/Makerules
index a10a0b4d70..5d6434c74b 100644
--- a/Makerules
+++ b/Makerules
@@ -1369,7 +1369,8 @@ xcheck: xtests
 # The only difference between MODULE_NAME=testsuite and MODULE_NAME=nonlib is
 # that almost all internal declarations from config.h, libc-symbols.h, and
 # include/*.h are not available to 'testsuite' code, but are to 'nonlib' code.
-all-testsuite := $(strip $(tests) $(xtests) $(test-srcs) $(test-extras))
+all-testsuite := $(strip $(tests) $(xtests) $(test-srcs) $(test-extras) \
+		 $(tests-container))
 ifneq (,$(all-testsuite))
 cpp-srcs-left = $(all-testsuite)
 lib := testsuite
diff --git a/Rules b/Rules
index 706c8a749d..d4dc2b6f45 100644
--- a/Rules
+++ b/Rules
@@ -130,12 +130,14 @@ others: $(py-const)
 
 ifeq ($(run-built-tests),no)
 tests: $(addprefix $(objpfx),$(filter-out $(tests-unsupported), \
-                                          $(tests) $(tests-internal)) \
+                                          $(tests) $(tests-internal) \
+					  $(tests-container)) \
 			     $(test-srcs)) $(tests-special) \
 			     $(tests-printers-programs)
 xtests: tests $(xtests-special)
 else
 tests: $(tests:%=$(objpfx)%.out) $(tests-internal:%=$(objpfx)%.out) \
+       $(tests-container:%=$(objpfx)%.out) \
        $(tests-special) $(tests-printers-out)
 xtests: tests $(xtests:%=$(objpfx)%.out) $(xtests-special)
 endif
@@ -149,7 +151,8 @@ tests-expected = $(tests) $(tests-internal) $(tests-printers)
 endif
 tests:
 	$(..)scripts/merge-test-results.sh -s $(objpfx) $(subdir) \
-	  $(sort $(tests-expected) $(tests-special-notdir:.out=)) \
+	  $(sort $(tests-expected) $(tests-special-notdir:.out=) \
+		 $(tests-container)) \
 	  > $(objpfx)subdir-tests.sum
 xtests:
 	$(..)scripts/merge-test-results.sh -s $(objpfx) $(subdir) \
@@ -158,7 +161,8 @@ xtests:
 
 ifeq ($(build-programs),yes)
 binaries-all-notests = $(others) $(sysdep-others)
-binaries-all-tests = $(tests) $(tests-internal) $(xtests) $(test-srcs)
+binaries-all-tests = $(tests) $(tests-internal) $(xtests) $(test-srcs) \
+		     $(tests-container)
 binaries-all = $(binaries-all-notests) $(binaries-all-tests)
 binaries-static-notests = $(others-static)
 binaries-static-tests = $(tests-static) $(xtests-static)
@@ -248,6 +252,16 @@ $(objpfx)%.out: /dev/null $(objpfx)%	# Make it 2nd arg for canned sequence.
 	$(make-test-out) > $@; \
 	$(evaluate-test)
 
+
+# Any tests that require an isolated container (chroot) in which to
+# run, should be added to tests-container.
+$(tests-container:%=$(objpfx)%.out): $(objpfx)%.out : $(if $(wildcard $(objpfx)%.files),$(objpfx)%.files,/dev/null) $(objpfx)%
+	$(test-wrapper-env) $(run-program-env) $(run-via-rtld-prefix) \
+	  $(common-objpfx)support/test-container env $(run-program-env) $($*-ENV) \
+	  $(host-test-program-cmd) $($*-ARGS) > $@; \
+	$(evaluate-test)
+
+
 # tests-unsupported lists tests that we will not try to build at all in
 # this configuration.  Note this runs every time because it does not
 # actually create its target.  The dependency on Makefile is meant to
diff --git a/nss/Makefile b/nss/Makefile
index 5209fc0456..e00a4f768f 100644
--- a/nss/Makefile
+++ b/nss/Makefile
@@ -55,11 +55,13 @@ tests-internal		= tst-field
 tests			= test-netdb test-digits-dots tst-nss-getpwent bug17079 \
 			  tst-nss-test1 \
 			  tst-nss-test2 \
-			  tst-nss-test3 \
 			  tst-nss-test4 \
 			  tst-nss-test5
 xtests			= bug-erange
 
+tests-container = \
+			  tst-nss-test3
+
 # Tests which need libdl
 ifeq (yes,$(build-shared))
 tests += tst-nss-files-hosts-erange
diff --git a/nss/tst-nss-test3.c b/nss/tst-nss-test3.c
index d9d708ae7b..4112231778 100644
--- a/nss/tst-nss-test3.c
+++ b/nss/tst-nss-test3.c
@@ -107,7 +107,11 @@ do_test (void)
   int i;
   struct group *g = NULL;
 
-  __nss_configure_lookup ("group", "test1");
+/* Previously we used __nss_configure_lookup to isolate the test
+   from the host environment and to get it to lookup from our new
+   test1 NSS service module, but now this test is run in a different
+   root filesystem via the test-container support and we directly
+   configure the use of the test1 NSS service.  */
 
   setgrent ();
 
diff --git a/nss/tst-nss-test3.root/etc/nsswitch.conf b/nss/tst-nss-test3.root/etc/nsswitch.conf
new file mode 100644
index 0000000000..5e08fe5eea
--- /dev/null
+++ b/nss/tst-nss-test3.root/etc/nsswitch.conf
@@ -0,0 +1 @@
+group	test1
diff --git a/nss/tst-nss-test3.root/tst-nss-test3.script b/nss/tst-nss-test3.root/tst-nss-test3.script
new file mode 100644
index 0000000000..a10beb1e6c
--- /dev/null
+++ b/nss/tst-nss-test3.root/tst-nss-test3.script
@@ -0,0 +1,2 @@
+cp $B/nss/libnss_test1.so $L/libnss_test1.so.2
+cp $B/nss/libnss_test2.so $L/libnss_test2.so.2
diff --git a/support/Makefile b/support/Makefile
index 652d2cdf69..2c937761ab 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -53,6 +53,7 @@ libsupport-routines = \
   support_format_netent \
   support_isolate_in_subprocess \
   support_openpty \
+  support_paths \
   support_quote_blob \
   support_record_failure \
   support_run_diff \
@@ -84,6 +85,7 @@ libsupport-routines = \
   xmalloc \
   xmemstream \
   xmkdir \
+  xmkdirp \
   xmmap \
   xmprotect \
   xmunmap \
@@ -139,6 +141,7 @@ libsupport-routines = \
   xsocket \
   xstrdup \
   xstrndup \
+  xsymlink \
   xsysconf \
   xunlink \
   xwaitpid \
@@ -151,6 +154,42 @@ ifeq ($(build-shared),yes)
 libsupport-inhibit-o += .o
 endif
 
+CFLAGS-support_paths.c = \
+		-DSRCDIR_PATH=\"`cd .. ; pwd`\" \
+		-DOBJDIR_PATH=\"`cd $(objpfx)/..; pwd`\" \
+		-DINSTDIR_PATH=\"$(prefix)\" \
+		-DLIBDIR_PATH=\"$(libdir)\"
+
+others: \
+	$(objpfx)test-container \
+	$(objpfx)links-dso-program \
+	$(objpfx)shell-container \
+	$(objpfx)echo-container \
+	$(objpfx)true-container
+
+LDLIBS-test-container = $(libsupport)
+
+others += test-container
+others-noinstall += test-container
+
+others += shell-container echo-container true-container
+others-noinstall += shell-container echo-container true-container
+
+$(objpfx)test-container : $(libsupport)
+$(objpfx)shell-container : $(libsupport)
+$(objpfx)echo-container : $(libsupport)
+$(objpfx)true-container : $(libsupport)
+
+# This exists only so we can guess which OS DSOs we need to copy into
+# the testing container.
+ifeq (,$(CXX))
+$(objpfx)links-dso-program : $(objpfx)links-dso-program-c.o
+	$(LINK.o) -o $@ $^
+else
+$(objpfx)links-dso-program : $(objpfx)links-dso-program.o
+	$(LINK.o) -o $@ $^ -lstdc++
+endif
+
 tests = \
   README-testing \
   tst-support-namespace \
diff --git a/support/echo-container.c b/support/echo-container.c
new file mode 100644
index 0000000000..e4d48df957
--- /dev/null
+++ b/support/echo-container.c
@@ -0,0 +1,34 @@
+/* Minimal /bin/echo for in-container use.
+   Copyright (C) 2018 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <stdio.h>
+
+int
+main (int argc, const char **argv)
+{
+  int i;
+
+  for (i = 1; i < argc; i++)
+    {
+      if (i > 1)
+	putchar (' ');
+      fputs (argv[i], stdout);
+    }
+  putchar ('\n');
+  return 0;
+}
diff --git a/support/links-dso-program-c.c b/support/links-dso-program-c.c
new file mode 100644
index 0000000000..d28a28a0d0
--- /dev/null
+++ b/support/links-dso-program-c.c
@@ -0,0 +1,9 @@
+#include <stdio.h>
+
+int
+main (int argc, char **argv)
+{
+  /* Complexity to keep gcc from optimizing this away.  */
+  printf ("This is a test %s.\n", argc > 1 ? argv[1] : "null");
+  return 0;
+}
diff --git a/support/links-dso-program.cc b/support/links-dso-program.cc
new file mode 100644
index 0000000000..dba6976c06
--- /dev/null
+++ b/support/links-dso-program.cc
@@ -0,0 +1,11 @@
+#include <iostream>
+
+using namespace std;
+
+int
+main (int argc, char **argv)
+{
+  /* Complexity to keep gcc from optimizing this away.  */
+  cout << (argc > 1 ? argv[1] : "null");
+  return 0;
+}
diff --git a/support/shell-container.c b/support/shell-container.c
new file mode 100644
index 0000000000..483c9b9aca
--- /dev/null
+++ b/support/shell-container.c
@@ -0,0 +1,396 @@
+/* Minimal /bin/sh for in-container use.
+   Copyright (C) 2018 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#define _FILE_OFFSET_BITS 64
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sched.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <sys/file.h>
+#include <sys/wait.h>
+#include <stdarg.h>
+#include <sys/sysmacros.h>
+#include <ctype.h>
+#include <utime.h>
+#include <errno.h>
+#include <error.h>
+
+#include <support/support.h>
+
+/* Design considerations
+
+ General rule: optimize for developer time, not run time.
+
+ Specifically:
+
+ * Don't worry about slow algorithms
+ * Don't worry about free'ing memory
+ * Don't implement anything the testsuite doesn't need.
+ * Line and argument counts are limited, see below.
+
+*/
+
+#define MAX_ARG_COUNT 100
+#define MAX_LINE_LENGTH 1000
+
+/* Debugging is enabled via --debug, which must be the first argument.  */
+static int debug_mode = 0;
+#define dprintf if (debug_mode) fprintf
+
+/* Emulate the "/bin/true" command.  Arguments are ignored.  */
+static int
+true_func (char **argv)
+{
+  return 0;
+}
+
+/* Emulate the "/bin/echo" command.  Options are ignored, arguments
+   are printed to stdout.  */
+static int
+echo_func (char **argv)
+{
+  int i;
+
+  for (i = 0; argv[i]; i++)
+    {
+      if (i > 0)
+	putchar (' ');
+      fputs (argv[i], stdout);
+    }
+  putchar ('\n');
+
+  return 0;
+}
+
+/* Emulate the "/bin/cp" command.  Options are ignored.  Only copies
+   one source file to one destination file.  Directory destinations
+   are not supported.  */
+static int
+copy_func (char **argv)
+{
+  char *sname = argv[0];
+  char *dname = argv[1];
+  int sfd, dfd;
+  struct stat st;
+
+  sfd = open (sname, O_RDONLY);
+  if (sfd < 0)
+    {
+      fprintf (stderr, "cp: unable to open %s for reading: %s\n",
+	       sname, strerror (errno));
+      return 1;
+    }
+
+  if (fstat (sfd, &st) < 0)
+    {
+      fprintf (stderr, "cp: unable to fstat %s: %s\n",
+	       sname, strerror (errno));
+      return 1;
+    }
+
+  dfd = open (dname, O_WRONLY | O_TRUNC | O_CREAT, 0600);
+  if (dfd < 0)
+    {
+      fprintf (stderr, "cp: unable to open %s for writing: %s\n",
+	       dname, strerror (errno));
+      return 1;
+    }
+
+  if (copy_file_range (sfd, 0, dfd, 0, st.st_size, 0) != st.st_size)
+    {
+      fprintf (stderr, "cp: cannot copy file %s to %s: %s\n",
+	       sname, dname, strerror (errno));
+      return 1;
+    }
+
+  close (sfd);
+  close (dfd);
+
+  chmod (dname, st.st_mode & 0777);
+
+  return 0;
+
+}
+
+/* This is a list of all the built-in commands we understand.  */
+static struct {
+  const char *name;
+  int (*func) (char **argv);
+} builtin_funcs[] = {
+  { "true", true_func },
+  { "echo", echo_func },
+  { "cp", copy_func },
+  { NULL, NULL }
+};
+
+/* Run one tokenized command.  argv[0] is the command.  argv is
+   NULL-terminated.  */
+static void
+run_command_array (char **argv)
+{
+  int i, j;
+  pid_t pid;
+  int status;
+  int (*builtin_func) (char **args);
+
+  if (argv[0] == NULL)
+    return;
+
+  builtin_func = NULL;
+
+  int new_stdin = 0;
+  int new_stdout = 1;
+  int new_stderr = 2;
+
+  dprintf (stderr, "run_command_array starting\n");
+  for (i = 0; argv[i]; i++)
+    dprintf (stderr, "   argv [%d] `%s'\n", i, argv[i]);
+
+  for (j = i = 0; argv[i]; i++)
+    {
+      if (strcmp (argv[i], "<") == 0 && argv[i + 1])
+	{
+	  new_stdin = open (argv[i + 1], O_WRONLY|O_CREAT|O_TRUNC, 0777);
+	  ++i;
+	  continue;
+	}
+      if (strcmp (argv[i], ">") == 0 && argv[i + 1])
+	{
+	  new_stdout = open (argv[i + 1], O_WRONLY|O_CREAT|O_TRUNC, 0777);
+	  ++i;
+	  continue;
+	}
+      if (strcmp (argv[i], ">>") == 0 && argv[i + 1])
+	{
+	  new_stdout = open (argv[i + 1], O_WRONLY|O_CREAT|O_APPEND, 0777);
+	  ++i;
+	  continue;
+	}
+      if (strcmp (argv[i], "2>") == 0 && argv[i + 1])
+	{
+	  new_stderr = open (argv[i + 1], O_WRONLY|O_CREAT|O_TRUNC, 0777);
+	  ++i;
+	  continue;
+	}
+      argv[j++] = argv[i];
+    }
+  argv[j] = NULL;
+
+
+  for (i = 0; builtin_funcs[i].name != NULL; i++)
+    if (strcmp (argv[0], builtin_funcs[i].name) == 0)
+       builtin_func = builtin_funcs[i].func;
+
+  dprintf (stderr, "builtin %p argv0 `%s'\n", builtin_func, argv[0]);
+
+  pid = fork ();
+  if (pid < 0)
+    {
+      fprintf (stderr, "sh; fork failed\n");
+      exit (1);
+    }
+
+  if (pid == 0)
+    {
+      if (new_stdin != 0)
+	{
+	  dup2 (new_stdin, 0);
+	  close (new_stdin);
+	}
+      if (new_stdout != 1)
+	{
+	  dup2 (new_stdout, 1);
+	  close (new_stdout);
+	}
+      if (new_stderr != 2)
+	{
+	  dup2 (new_stderr, 2);
+	  close (new_stdout);
+	}
+
+      if (builtin_func != NULL)
+	exit (builtin_func (argv + 1));
+
+      execvp (argv[0], argv);
+
+      fprintf (stderr, "sh: execing %s failed: %s",
+	       argv[0], strerror (errno));
+      exit (1);
+    }
+
+  waitpid (pid, &status, 0);
+
+  dprintf (stderr, "exiting run_command_array\n");
+
+  if (WIFEXITED (status))
+    {
+      int rv = WEXITSTATUS (status);
+      if (rv)
+	exit (rv);
+    }
+  else
+    exit (1);
+}
+
+/* Run one command-as-a-string, by tokenizing it.  Limited to
+   MAX_ARG_COUNT arguments.  Simple substitution is done of $1 to $9
+   (as whole separate tokens) from iargs[].  Quoted strings work if
+   the quotes wrap whole tokens; i.e. "foo bar" but not foo" bar".  */
+static void
+run_command_string (const char *cmdline, const char **iargs)
+{
+  char *args[MAX_ARG_COUNT+1];
+  int ap = 0;
+  const char *start, *end;
+  int nargs;
+
+  for (nargs = 0; iargs[nargs] != NULL; ++nargs)
+    ;
+
+  dprintf (stderr, "run_command_string starting: '%s'\n", cmdline);
+
+  while (ap < MAX_ARG_COUNT)
+    {
+      /* If the argument is quoted, this is the quote character, else NUL.  */
+      int in_quote = 0;
+
+      /* Skip whitespace up to the next token.  */
+      while (*cmdline && isspace (*cmdline))
+	cmdline ++;
+      if (*cmdline == 0)
+	break;
+
+      start = cmdline;
+      /* Check for quoted argument.  */
+      in_quote = (*cmdline == '\'' || *cmdline == '"') ? *cmdline : 0;
+
+      /* Skip to end of token; either by whitespace or matching quote.  */
+      dprintf (stderr, "in_quote %d\n", in_quote);
+      while (*cmdline
+	     && (!isspace (*cmdline) || in_quote))
+	{
+	  if (*cmdline == in_quote
+	      && cmdline != start)
+	    in_quote = 0;
+	  dprintf (stderr, "[%c]%d ", *cmdline, in_quote);
+	  cmdline ++;
+	}
+      dprintf (stderr, "\n");
+
+      /* Allocate space for this token and store it in args[].  */
+      end = cmdline;
+      dprintf (stderr, "start<%s> end<%s>\n", start, end);
+      args[ap] = (char *) xmalloc (end - start + 1);
+      memcpy (args[ap], start, end - start);
+      args[ap][end - start] = 0;
+
+      /* Strip off quotes, if found.  */
+      dprintf (stderr, "args[%d] = <%s>\n", ap, args[ap]);
+      if (args[ap][0] == '\''
+	  && args[ap][strlen (args[ap])-1] == '\'')
+	{
+	  args[ap][strlen (args[ap])-1] = 0;
+	  args[ap] ++;
+	}
+
+      else if (args[ap][0] == '"'
+	  && args[ap][strlen (args[ap])-1] == '"')
+	{
+	  args[ap][strlen (args[ap])-1] = 0;
+	  args[ap] ++;
+	}
+
+      /* Replace positional parameters like $4.  */
+      else if (args[ap][0] == '$'
+	       && isdigit (args[ap][1])
+	       && args[ap][2] == 0)
+	{
+	  int a = args[ap][1] - '1';
+	  if (0 <= a && a < nargs)
+	    args[ap] = strdup (iargs[a]);
+	}
+
+      ap ++;
+
+      if (*cmdline == 0)
+	break;
+    }
+
+  /* Lastly, NULL terminate the array and run it.  */
+  args[ap] = NULL;
+  run_command_array (args);
+}
+
+/* Run a script by reading lines and passing them to the above
+   function.  */
+static void
+run_script (const char *filename, const char **args)
+{
+  char line[MAX_LINE_LENGTH + 1];
+  dprintf (stderr, "run_script starting: '%s'\n", filename);
+  FILE *f = fopen (filename, "r");
+  if (f == NULL)
+    {
+      fprintf (stderr, "sh: %s: %s\n", filename, strerror (errno));
+      exit (1);
+    }
+  while (fgets (line, sizeof (line), f) != NULL)
+    {
+      if (line[0] == '#')
+	{
+	  dprintf (stderr, "comment: %s\n", line);
+	  continue;
+	}
+      run_command_string (line, args);
+    }
+  fclose (f);
+}
+
+int
+main (int argc, const char **argv)
+{
+  int i;
+
+  if (strcmp (argv[1], "--debug") == 0)
+    {
+      debug_mode = 1;
+      --argc;
+      ++argv;
+    }
+
+  dprintf (stderr, "container-sh starting:\n");
+  for (i = 0; i < argc; i++)
+    dprintf (stderr, "  argv[%d] is `%s'\n", i, argv[i]);
+
+  if (strcmp (argv[1], "-c") == 0)
+    run_command_string (argv[2], argv+3);
+  else
+    run_script (argv[1], argv+2);
+
+  dprintf (stderr, "normal exit 0\n");
+  return 0;
+}
diff --git a/support/support.h b/support/support.h
index b61fe0735c..1403510f11 100644
--- a/support/support.h
+++ b/support/support.h
@@ -25,6 +25,8 @@
 
 #include <stddef.h>
 #include <sys/cdefs.h>
+/* For mode_t.  */
+#include <sys/stat.h>
 
 __BEGIN_DECLS
 
@@ -76,6 +78,16 @@ char *xasprintf (const char *format, ...)
 char *xstrdup (const char *);
 char *xstrndup (const char *, size_t);
 
+/* These point to the TOP of the source/build tree, not your (or
+   support's) subdirectory.  */
+extern const char support_srcdir_root[];
+extern const char support_objdir_root[];
+
+/* Corresponds to the --prefix= passed to configure.  */
+extern const char support_install_prefix[];
+/* Corresponds to the install's lib/ or lib64/ directory.  */
+extern const char support_libdir_prefix[];
+
 __END_DECLS
 
 #endif /* SUPPORT_H */
diff --git a/support/support_paths.c b/support/support_paths.c
new file mode 100644
index 0000000000..a1c22315bd
--- /dev/null
+++ b/support/support_paths.c
@@ -0,0 +1,51 @@
+/* Various paths that might be needed.
+   Copyright (C) 2018 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <support/support.h>
+#include <support/check.h>
+
+/* The idea here is to make various makefile-level paths available to
+   support programs, as canonicalized absolute paths.  */
+
+/* These point to the TOP of the source/build tree, not your (or
+   support's) subdirectory.  */
+#ifdef SRCDIR_PATH
+const char support_srcdir_root[] = SRCDIR_PATH;
+#else
+# error please -DSRCDIR_PATH=something in the Makefile
+#endif
+
+#ifdef OBJDIR_PATH
+const char support_objdir_root[] = OBJDIR_PATH;
+#else
+# error please -DOBJDIR_PATH=something in the Makefile
+#endif
+
+#ifdef INSTDIR_PATH
+/* Corresponds to the --prefix= passed to configure.  */
+const char support_install_prefix[] = INSTDIR_PATH;
+#else
+# error please -DINSTDIR_PATH=something in the Makefile
+#endif
+
+#ifdef LIBDIR_PATH
+/* Corresponds to the install's lib/ or lib64/ directory.  */
+const char support_libdir_prefix[] = LIBDIR_PATH;
+#else
+# error please -DLIBDIR_PATH=something in the Makefile
+#endif
diff --git a/support/test-container.c b/support/test-container.c
new file mode 100644
index 0000000000..237f50c5b7
--- /dev/null
+++ b/support/test-container.c
@@ -0,0 +1,982 @@
+/* Run a test case in an isolated namespace.
+   Copyright (C) 2018 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#define _FILE_OFFSET_BITS 64
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sched.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <sys/file.h>
+#include <sys/wait.h>
+#include <stdarg.h>
+#include <sys/sysmacros.h>
+#include <ctype.h>
+#include <utime.h>
+#include <errno.h>
+#include <error.h>
+
+#include <support/support.h>
+#include <support/xunistd.h>
+#include "check.h"
+#include "test-driver.h"
+
+int verbose = 0;
+
+/* Running a test in a container is tricky.  There are two main
+   categories of things to do:
+
+   1. "Once" actions, like setting up the container and doing an
+      install into it.
+
+   2. "Per-test" actions, like copying in support files and
+      configuring the container.
+
+
+   "Once" actions:
+
+   * mkdir $buildroot/testroot.pristine/
+   * install into it
+   * rsync to $buildroot/testroot.root/
+
+   "Per-test" actions:
+   * maybe rsync to $buildroot/testroot.root/
+   * copy support files and test binary
+   * chroot/unshare
+   * set up any mounts (like /proc)
+
+   Magic files:
+
+   For test $srcdir/foo/mytest.c we look for $srcdir/foo/mytest.root
+   and, if found...
+
+   * mytest.root/ is rsync'd into container
+   * mytest.root/preclean.req causes fresh rsync (with delete) before
+     test if present
+   * mytest.root/mytset.script has a list of "commands" to run:
+       syntax:
+         # comment
+         mv FILE FILE
+	 cp FILE FILE
+	 rm FILE
+	 FILE must start with $B/, $S/, $I/, $L/, or /
+	  (expands to build dir, source dir, install dir, library dir
+	   (in container), or container's root)
+   * mytest.root/postclean.req causes fresh rsync (with delete) after
+     test if present
+
+   Note that $srcdir/foo/mytest.script may be used instead of a
+   $srcdir/foo/mytest.root/mytest.script in the sysroot template, if
+   there is no other reason for a sysroot.
+
+   Design goals:
+
+   * independent of other packages which may not be installed (like
+     rsync or Docker, or even "cp")
+
+   * Simple, easy to review code (i.e. prefer simple naive code over
+     complex efficient code)
+
+   * The current implementation ist parallel-make-safe, but only in
+     that it uses a lock to prevent parallel access to the testroot.  */
+
+\f
+/* Utility Functions */
+
+/* Like xunlink, but it's OK if the file already doesn't exist.  */
+void
+maybe_xunlink (const char *path)
+{
+  int rv = unlink (path);
+  if (rv < 0 && errno != ENOENT)
+    FAIL_EXIT1 ("unlink (\"%s\"): %m", path);
+}
+
+/* Like xmkdir, but it's OK if the directory already exists.  */
+void
+maybe_xmkdir (const char *path, mode_t mode)
+{
+  struct stat st;
+
+  if (stat (path, &st) == 0
+      && S_ISDIR (st.st_mode))
+    return;
+  xmkdir (path, mode);
+}
+
+/* Temporarily concatenate multiple strings into one.  Allows up to 10
+   temporary results; use strdup () if you need them to be
+   permanent.  */
+static char *
+concat (const char *str, ...)
+{
+  /* Assume initialized to NULL/zero.  */
+  static char *bufs[10];
+  static size_t buflens[10];
+  static int bufn = 0;
+  int n;
+  size_t len;
+  va_list ap, ap2;
+  char *cp;
+  char *next;
+
+  va_start (ap, str);
+  va_copy (ap2, ap);
+
+  n = bufn;
+  bufn = (bufn + 1) % 10;
+  len = strlen (str);
+
+  while ((next = va_arg (ap, char *)) != NULL)
+    len = len + strlen (next);
+
+  va_end (ap);
+
+  if (bufs[n] == NULL)
+    {
+      bufs[n] = xmalloc (len + 1); /* NUL */
+      buflens[n] = len + 1;
+    }
+  else if (buflens[n] < len + 1)
+    {
+      bufs[n] = xrealloc (bufs[n], len + 1); /* NUL */
+      buflens[n] = len + 1;
+    }
+
+  strcpy (bufs[n], str);
+  cp = strchr (bufs[n], '\0');
+  while ((next = va_arg (ap2, char *)) != NULL)
+    {
+      strcpy (cp, next);
+      cp = strchr (cp, '\0');
+    }
+  *cp = 0;
+  va_end (ap2);
+
+  return bufs[n];
+}
+
+/* Try to mount SRC onto DEST.  */
+static void
+trymount (const char *src, const char *dest)
+{
+  if (mount (src, dest, "", MS_BIND, NULL) < 0)
+    FAIL_EXIT1 ("can't mount %s onto %s\n", src, dest);
+}
+
+/* Special case of above for devices like /dev/zero where we have to
+   mount a device over a device, not a directory over a directory.  */
+static void
+devmount (const char *new_root_path, const char *which)
+{
+  int fd;
+  fd = open (concat (new_root_path, "/dev/", which, NULL),
+	     O_CREAT | O_TRUNC | O_RDWR, 0777);
+  xclose (fd);
+
+  trymount (concat ("/dev/", which, NULL),
+	    concat (new_root_path, "/dev/", which, NULL));
+}
+
+/* Returns true if the string "looks like" an environement variable
+   being set.  */
+static int
+is_env_setting (const char *a)
+{
+  int count_name = 0;
+
+  while (*a)
+    {
+      if (isalnum (*a) || *a == '_')
+	++count_name;
+      else if (*a == '=' && count_name > 0)
+	return 1;
+      else
+	return 0;
+      ++a;
+    }
+  return 0;
+}
+
+/* Break the_line into words and store in the_words.  Max nwords,
+   returns actual count.  */
+static int
+tokenize (char *the_line, char **the_words, int nwords)
+{
+  int rv = 0;
+
+  while (nwords > 0)
+    {
+      /* Skip leading whitespace, if any.  */
+      while (*the_line && isspace (*the_line))
+	++the_line;
+
+      /* End of line?  */
+      if (*the_line == 0)
+	return rv;
+
+      /* THE_LINE points to a non-whitespace character, so we have a
+	 word.  */
+      *the_words = the_line;
+      ++the_words;
+      nwords--;
+      ++rv;
+
+      /* Skip leading whitespace, if any.  */
+      while (*the_line && ! isspace (*the_line))
+	++the_line;
+
+      /* We now point at the trailing NUL *or* some whitespace.  */
+      if (*the_line == 0)
+	return rv;
+
+      /* It was whitespace, skip and keep tokenizing.  */
+      *the_line++ = 0;
+    }
+
+  /* We get here if we filled the words buffer.  */
+  return rv;
+}
+
+\f
+/* Mini-RSYNC implementation.  Optimize later.      */
+
+/* A few routines for an "rsync buffer" which stores the paths we're
+   working on.  We continuously grow and shrink the paths in each
+   buffer so there's lot of re-use.  */
+
+/* We rely on "initialized to zero" to set these up.  */
+typedef struct
+{
+  char *buf;
+  size_t len;
+  size_t size;
+} path_buf;
+
+static path_buf spath, dpath;
+
+static void
+r_setup (char *path, path_buf * pb)
+{
+  size_t len = strlen (path);
+  if (pb->buf == NULL || pb->size < len + 1)
+    {
+      /* Round up.  This is an arbitrary number, just to keep from
+	 reallocing too often.  */
+      size_t sz = ALIGN_UP (len + 1, 512);
+      if (pb->buf == NULL)
+	pb->buf = (char *) xmalloc (sz);
+      else
+	pb->buf = (char *) xrealloc (pb->buf, sz);
+      if (pb->buf == NULL)
+	FAIL_EXIT1 ("Out of memory while rsyncing\n");
+
+      pb->size = sz;
+    }
+  strcpy (pb->buf, path);
+  pb->len = len;
+}
+
+static void
+r_append (const char *path, path_buf * pb)
+{
+  size_t len = strlen (path) + pb->len;
+  if (pb->size < len + 1)
+    {
+      /* Round up */
+      size_t sz = ALIGN_UP (len + 1, 512);
+      pb->buf = (char *) xrealloc (pb->buf, sz);
+      if (pb->buf == NULL)
+	FAIL_EXIT1 ("Out of memory while rsyncing\n");
+
+      pb->size = sz;
+    }
+  strcpy (pb->buf + pb->len, path);
+  pb->len = len;
+}
+
+static int
+file_exists (char *path)
+{
+  struct stat st;
+  if (lstat (path, &st) == 0)
+    return 1;
+  return 0;
+}
+
+static void
+recursive_remove (char *path)
+{
+  pid_t child;
+  int status;
+
+  child = fork ();
+
+  switch (child) {
+  case -1:
+    FAIL_EXIT1 ("Unable to fork");
+  case 0:
+    /* Child.  */
+    execlp ("rm", "rm", "-rf", path, NULL);
+  default:
+    /* Parent.  */
+    waitpid (child, &status, 0);
+    /* "rm" would have already printed a suitable error message.  */
+    if (! WIFEXITED (status)
+	|| WEXITSTATUS (status) != 0)
+      exit (1);
+
+    break;
+  }
+}
+
+/* Used for both rsync and the mytest.script "cp" command.  */
+static void
+copy_one_file (const char *sname, const char *dname)
+{
+  int sfd, dfd;
+  struct stat st;
+  struct utimbuf times;
+
+  sfd = open (sname, O_RDONLY);
+  if (sfd < 0)
+    FAIL_EXIT1 ("unable to open %s for reading\n", sname);
+
+  if (fstat (sfd, &st) < 0)
+    FAIL_EXIT1 ("unable to fstat %s\n", sname);
+
+  dfd = open (dname, O_WRONLY | O_TRUNC | O_CREAT, 0600);
+  if (dfd < 0)
+    FAIL_EXIT1 ("unable to open %s for writing\n", dname);
+
+  if (copy_file_range (sfd, 0, dfd, 0, st.st_size, 0) != st.st_size)
+    FAIL_EXIT1 ("cannot copy file %s to %s\n", sname, dname);
+
+  xclose (sfd);
+  xclose (dfd);
+
+  if (chmod (dname, st.st_mode & 0777) < 0)
+    FAIL_EXIT1 ("chmod %s: %s\n", dname, strerror (errno));
+
+  times.actime = st.st_atime;
+  times.modtime = st.st_mtime;
+  if (utime (dname, &times) < 0)
+    FAIL_EXIT1 ("utime %s: %s\n", dname, strerror (errno));
+}
+
+/* We don't check *everything* about the two files to see if a copy is
+   needed, just the minimum to make sure we get the latest copy.  */
+static int
+need_sync (char *ap, char *bp, struct stat *a, struct stat *b)
+{
+  if ((a->st_mode & S_IFMT) != (b->st_mode & S_IFMT))
+    return 1;
+
+  if (S_ISLNK (a->st_mode))
+    {
+      int rv;
+      char *al, *bl;
+
+      if (a->st_size != b->st_size)
+	return 1;
+
+      al = xreadlink (ap);
+      bl = xreadlink (bp);
+      rv = strcmp (al, bl);
+      free (al);
+      free (bl);
+      if (rv == 0)
+	return 0; /* links are same */
+      return 1; /* links differ */
+    }
+
+  if (verbose)
+    {
+      if (a->st_size != b->st_size)
+	printf ("SIZE\n");
+      if ((a->st_mode & 0777) != (b->st_mode & 0777))
+	printf ("MODE\n");
+      if (a->st_mtime != b->st_mtime)
+	printf ("TIME\n");
+    }
+
+  if (a->st_size == b->st_size
+      && ((a->st_mode & 0777) == (b->st_mode & 0777))
+      && a->st_mtime == b->st_mtime)
+    return 0;
+
+  return 1;
+}
+
+static void
+rsync_1 (path_buf * src, path_buf * dest, int and_delete)
+{
+  DIR *dir;
+  struct dirent *de;
+  struct stat s, d;
+
+  r_append ("/", src);
+  r_append ("/", dest);
+
+  if (verbose)
+    printf ("sync %s to %s %s\n", src->buf, dest->buf,
+	    and_delete ? "and delete" : "");
+
+  size_t staillen = src->len;
+
+  size_t dtaillen = dest->len;
+
+  dir = opendir (src->buf);
+
+  while ((de = readdir (dir)) != NULL)
+    {
+      if (strcmp (de->d_name, ".") == 0
+	  || strcmp (de->d_name, "..") == 0)
+	continue;
+
+      src->len = staillen;
+      r_append (de->d_name, src);
+      dest->len = dtaillen;
+      r_append (de->d_name, dest);
+
+      s.st_mode = ~0;
+      d.st_mode = ~0;
+
+      if (lstat (src->buf, &s) != 0)
+	FAIL_EXIT1 ("%s obtained by readdir, but stat failed.\n", src->buf);
+
+      /* It's OK if this one fails, since we know the file might be
+	 missing.  */
+      lstat (dest->buf, &d);
+
+      if (! need_sync (src->buf, dest->buf, &s, &d))
+	{
+	  if (S_ISDIR (s.st_mode))
+	    rsync_1 (src, dest, and_delete);
+	  continue;
+	}
+
+      if (d.st_mode != ~0)
+	switch (d.st_mode & S_IFMT)
+	  {
+	  case S_IFDIR:
+	    if (!S_ISDIR (s.st_mode))
+	      {
+		if (verbose)
+		  printf ("-D %s\n", dest->buf);
+		recursive_remove (dest->buf);
+	      }
+	    break;
+
+	  default:
+	    if (verbose)
+	      printf ("-F %s\n", dest->buf);
+	    maybe_xunlink (dest->buf);
+	    break;
+	  }
+
+      switch (s.st_mode & S_IFMT)
+	{
+	case S_IFREG:
+	  if (verbose)
+	    printf ("+F %s\n", dest->buf);
+	  copy_one_file (src->buf, dest->buf);
+	  break;
+
+	case S_IFDIR:
+	  if (verbose)
+	    printf ("+D %s\n", dest->buf);
+	  maybe_xmkdir (dest->buf, (s.st_mode & 0777) | 0700);
+	  rsync_1 (src, dest, and_delete);
+	  break;
+
+	case S_IFLNK:
+	  {
+	    char *lp;
+	    if (verbose)
+	      printf ("+L %s\n", dest->buf);
+	    lp = xreadlink (src->buf);
+	    xsymlink (lp, dest->buf);
+	    free (lp);
+	    break;
+	  }
+
+	default:
+	  break;
+	}
+    }
+
+  closedir (dir);
+  src->len = staillen;
+  src->buf[staillen] = 0;
+  dest->len = dtaillen;
+  dest->buf[dtaillen] = 0;
+
+  if (!and_delete)
+    return;
+
+  /* The rest of this function removes any files/directories in DEST
+     that do not exist in SRC.  This is triggered as part of a
+     preclean or postsclean step.  */
+
+  dir = opendir (dest->buf);
+
+  while ((de = readdir (dir)) != NULL)
+    {
+      if (strcmp (de->d_name, ".") == 0
+	  || strcmp (de->d_name, "..") == 0)
+	continue;
+
+      src->len = staillen;
+      r_append (de->d_name, src);
+      dest->len = dtaillen;
+      r_append (de->d_name, dest);
+
+      s.st_mode = ~0;
+      d.st_mode = ~0;
+
+      lstat (src->buf, &s);
+      
+      if (lstat (dest->buf, &d) != 0)
+	FAIL_EXIT1 ("%s obtained by readdir, but stat failed.\n", dest->buf);
+
+      if (s.st_mode == ~0)
+	{
+	  /* dest exists and src doesn't, clean it.  */
+	  switch (d.st_mode & S_IFMT)
+	    {
+	    case S_IFDIR:
+	      if (!S_ISDIR (s.st_mode))
+		{
+		  if (verbose)
+		    printf ("-D %s\n", dest->buf);
+		  recursive_remove (dest->buf);
+		}
+	      break;
+
+	    default:
+	      if (verbose)
+		printf ("-F %s\n", dest->buf);
+	      maybe_xunlink (dest->buf);
+	      break;
+	    }
+	}
+    }
+
+  closedir (dir);
+}
+
+static void
+rsync (char *src, char *dest, int and_delete)
+{
+  r_setup (src, &spath);
+  r_setup (dest, &dpath);
+
+  rsync_1 (&spath, &dpath, and_delete);
+}
+
+\f
+int
+main (int argc, char **argv)
+{
+  pid_t child;
+  char *pristine_root_path;
+  char *new_root_path;
+  char *new_cwd_path;
+  char *new_objdir_path;
+  char *new_srcdir_path;
+  char **new_child_proc;
+  char *command_root;
+  char *command_base;
+  char *command_basename;
+  char *so_base;
+  int do_postclean = 0;
+
+  uid_t original_uid;
+  gid_t original_gid;
+  int UMAP;
+  int GMAP;
+  /* Used for "%lld %lld 1" so need not be large.  */
+  char tmp[100];
+  struct stat st;
+  int lock_fd;
+
+  setbuf (stdout, NULL);
+
+  /* The command line we're expecting looks like this:
+     env <set some vars> ld.so <library path> test-binary
+
+     We need to peel off any "env" or "ld.so" portion of the command
+     line, and keep track of which env vars we should preserve and
+     which we drop.  */
+
+  if (argc < 2)
+    {
+      fprintf (stderr, "Usage: containerize <program to run> <args...>\n");
+      exit (1);
+    }
+
+  if (strcmp (argv[1], "-v") == 0)
+    {
+      verbose = 1;
+      ++argv;
+      --argc;
+    }
+
+  if (strcmp (argv[1], "env") == 0)
+    {
+      ++argv;
+      --argc;
+      while (is_env_setting (argv[1]))
+	{
+	  /* List variables we do NOT want to propogate.  */
+#if 0
+	  /* until we discover why locale/iconv tests don't
+	     work against an installed tree...  */
+	  if (memcmp (argv[1], "GCONV_PATH=", 11)
+	      && memcmp (argv[1], "LOCPATH=", 8))
+#endif
+	    {
+	      /* Need to keep these.  Note that putenv stores a
+	         pointer to our argv.  */
+	      putenv (argv[1]);
+	    }
+	  ++argv;
+	  --argc;
+	}
+    }
+
+  if (strncmp (argv[1], concat (support_objdir_root, "/elf/ld-linux-", NULL),
+	       strlen (support_objdir_root) + 14) == 0)
+    {
+      ++argv;
+      --argc;
+      while (argv[1][0] == '-')
+	{
+	  if (strcmp (argv[1], "--library-path") == 0)
+	    {
+	      ++argv;
+	      --argc;
+	    }
+	  ++argv;
+	  --argc;
+	}
+    }
+
+  pristine_root_path = strdup (concat (support_objdir_root,
+				       "/testroot.pristine", NULL));
+  new_root_path = strdup (concat (support_objdir_root,
+				  "/testroot.root", NULL));
+  new_cwd_path = get_current_dir_name ();
+  new_child_proc = argv + 1;
+
+  lock_fd = open (concat (pristine_root_path, "/lock.fd", NULL),
+		 O_CREAT | O_TRUNC | O_RDWR, 0666);
+  if (lock_fd < 0)
+    FAIL_EXIT1 ("Cannot create testroot lock.\n");
+
+  while (flock (lock_fd, LOCK_EX) != 0)
+    {
+      if (errno != EINTR)
+	FAIL_EXIT1 ("Cannot lock testroot.\n");
+    }
+
+  xmkdirp (new_root_path, 0755);
+
+  /* We look for extra setup info in a subdir in the same spot as the
+     test, with the same name but a ".root" extension.  This is that
+     directory.  We try to look in the source tree if the path we're
+     given refers to the build tree, but we rely on the path to be
+     absolute.  This is what the glibc makefiles do.  */
+  command_root = concat (argv[1], ".root", NULL);
+  if (strncmp (command_root, support_objdir_root,
+	       strlen (support_objdir_root)) == 0
+      && command_root[strlen (support_objdir_root)] == '/')
+    command_root = concat (support_srcdir_root,
+			   argv[1] + strlen (support_objdir_root),
+			   ".root", NULL);
+  command_root = strdup (command_root);
+
+  /* This cuts off the ".root" we appended above.  */
+  command_base = strdup (command_root);
+  command_base[strlen (command_base) - 5] = 0;
+
+  /* This is the basename of the test we're running.  */
+  command_basename = strrchr (command_base, '/');
+  if (command_basename == NULL)
+    command_basename = command_base;
+  else
+    ++command_basename;
+
+  /* Shared object base directory.  */
+  so_base = strdup (argv[1]);
+  if (strrchr (so_base, '/') != NULL)
+    strrchr (so_base, '/')[1] = 0;
+
+  if (file_exists (concat (command_root, "/postclean.req", NULL)))
+    do_postclean = 1;
+
+  rsync (pristine_root_path, new_root_path,
+	 file_exists (concat (command_root, "/preclean.req", NULL)));
+
+  if (stat (command_root, &st) >= 0
+      && S_ISDIR (st.st_mode))
+    rsync (command_root, new_root_path, 0);
+
+  new_objdir_path = strdup (concat (new_root_path,
+				    support_objdir_root, NULL));
+  new_srcdir_path = strdup (concat (new_root_path,
+				    support_srcdir_root, NULL));
+
+  /* new_cwd_path starts with '/' so no "/" needed between the two.  */
+  xmkdirp (concat (new_root_path, new_cwd_path, NULL), 0755);
+  xmkdirp (new_srcdir_path, 0755);
+  xmkdirp (new_objdir_path, 0755);
+
+  original_uid = getuid ();
+  original_gid = getgid ();
+
+  /* Handle the cp/mv/rm "script" here.  */
+  {
+    char *the_line = NULL;
+    size_t line_len = 0;
+    char *fname = concat (command_root, "/",
+			  command_basename, ".script", NULL);
+    char *the_words[3];
+    FILE *f = fopen (fname, "r");
+
+    if (verbose && f)
+      fprintf (stderr, "running %s\n", fname);
+
+    if (f == NULL)
+      {
+	/* Try foo.script instead of foo.root/foo.script, as a shortcut.  */
+	fname = concat (command_base, ".script", NULL);
+	f = fopen (fname, "r");
+	if (verbose && f)
+	  fprintf (stderr, "running %s\n", fname);
+      }
+
+    /* Note that we do NOT look for a Makefile-generated foo.script in
+       the build directory.  If that is ever needed, this is the place
+       to add it.  */
+
+    /* This is where we "interpret" the mini-script which is <test>.script.  */
+    if (f != NULL)
+      {
+	while (getline (&the_line, &line_len, f) > 0)
+	  {
+	    int nt = tokenize (the_line, the_words, 3);
+	    int i;
+
+	    for (i = 1; i < nt; ++i)
+	      {
+		if (memcmp (the_words[i], "$B/", 3) == 0)
+		  the_words[i] = concat (support_objdir_root,
+					 the_words[i] + 2, NULL);
+		else if (memcmp (the_words[i], "$S/", 3) == 0)
+		  the_words[i] = concat (support_srcdir_root,
+					 the_words[i] + 2, NULL);
+		else if (memcmp (the_words[i], "$I/", 3) == 0)
+		  the_words[i] = concat (new_root_path,
+					 support_install_prefix,
+					 the_words[i] + 2, NULL);
+		else if (memcmp (the_words[i], "$L/", 3) == 0)
+		  the_words[i] = concat (new_root_path,
+					 support_libdir_prefix,
+					 the_words[i] + 2, NULL);
+		else if (the_words[i][0] == '/')
+		  the_words[i] = concat (new_root_path,
+					 the_words[i], NULL);
+	      }
+
+	    if (nt == 3 && the_words[2][strlen (the_words[2]) - 1] == '/')
+	      {
+		char *r = strrchr (the_words[1], '/');
+		if (r)
+		  the_words[2] = concat (the_words[2], r + 1, NULL);
+		else
+		  the_words[2] = concat (the_words[2], the_words[1], NULL);
+	      }
+
+	    if (nt == 2 && strcmp (the_words[0], "so") == 0)
+	      {
+		the_words[2] = concat (new_root_path, support_libdir_prefix,
+				       "/", the_words[1], NULL);
+		the_words[1] = concat (so_base, the_words[1], NULL);
+		copy_one_file (the_words[1], the_words[2]);
+	      }
+	    else if (nt == 3 && strcmp (the_words[0], "cp") == 0)
+	      {
+		copy_one_file (the_words[1], the_words[2]);
+	      }
+	    else if (nt == 3 && strcmp (the_words[0], "mv") == 0)
+	      {
+		if (rename (the_words[1], the_words[2]) < 0)
+		  FAIL_EXIT1 ("rename %s -> %s: %s", the_words[1],
+			      the_words[2], strerror (errno));
+	      }
+	    else if (nt == 3 && strcmp (the_words[0], "chmod") == 0)
+	      {
+		long int m;
+		m = strtol (the_words[1], NULL, 0);
+		if (chmod (the_words[2], m) < 0)
+		    FAIL_EXIT1 ("chmod %s: %s\n",
+				the_words[2], strerror (errno));
+
+	      }
+	    else if (nt == 2 && strcmp (the_words[0], "rm") == 0)
+	      {
+		maybe_xunlink (the_words[1]);
+	      }
+	    else if (nt > 0 && the_words[0][0] != '#')
+	      {
+		printf ("\033[31minvalid [%s]\033[0m\n", the_words[0]);
+	      }
+	  }
+	fclose (f);
+      }
+  }
+
+#ifdef CLONE_NEWNS
+  /* The unshare here gives us our own spaces and capabilities.  */
+  if (unshare (CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNS) < 0)
+    {
+      /* Older kernels may not support all the options.  */
+      if (errno == EINVAL)
+	FAIL_UNSUPPORTED ("unable to unshare user/fs: %s", strerror (errno));
+      else
+	FAIL_EXIT1 ("unable to unshare user/fs: %s", strerror (errno));
+    }
+#else
+  /* Some targets may not support unshare at all.  */
+  FAIL_UNSUPPORTED ("unshare support missing");
+#endif
+
+  /* Some systems, by default, all mounts leak out of the namespace.  */
+  if (mount ("none", "/", NULL, MS_REC | MS_PRIVATE, NULL) != 0)
+    FAIL_EXIT1 ("could not create a private mount namespace\n");
+
+  trymount (support_srcdir_root, new_srcdir_path);
+  trymount (support_objdir_root, new_objdir_path);
+
+  xmkdirp (concat (new_root_path, "/dev", NULL), 0755);
+  devmount (new_root_path, "null");
+  devmount (new_root_path, "zero");
+  devmount (new_root_path, "urandom");
+
+  /* We're done with the "old" root, switch to the new one.  */
+  if (chroot (new_root_path) < 0)
+    FAIL_EXIT1 ("Can't chroot to %s - ", new_root_path);
+
+  if (chdir (new_cwd_path) < 0)
+    FAIL_EXIT1 ("Can't cd to new %s - ", new_cwd_path);
+
+  /* To complete the containerization, we need to fork () at least
+     once.  We can't exec, nor can we somehow link the new child to
+     our parent.  So we run the child and propogate it's exit status
+     up.  */
+  child = fork ();
+  if (child < 0)
+    FAIL_EXIT1 ("Unable to fork");
+  else if (child > 0)
+    {
+      /* Parent.  */
+      int status;
+      waitpid (child, &status, 0);
+
+      /* There's a bit of magic here, since the buildroot is mounted
+	 in our space, the paths are still valid, and since the mounts
+	 aren't recursive, it sees *only* the built root, not anything
+	 we would normally se if we rsync'd to "/" like mounted /dev
+	 files.  */
+      if (do_postclean)
+	  rsync (pristine_root_path, new_root_path, 1);
+
+      if (WIFEXITED (status))
+	exit (WEXITSTATUS (status));
+
+      if (WIFSIGNALED (status))
+	{
+	  printf ("%%SIGNALLED%%\n");
+	  exit (77);
+	}
+
+      printf ("%%EXITERROR%%\n");
+      exit (78);
+    }
+
+  /* The rest is the child process, which is now PID 1 and "in" the
+     new root.  */
+
+  maybe_xmkdir ("/tmp", 0755);
+
+  /* Now that we're pid 1 (effectively "root") we can mount /proc  */
+  maybe_xmkdir ("/proc", 0777);
+  if (mount ("proc", "/proc", "proc", 0, NULL) < 0)
+    FAIL_EXIT1 ("Unable to mount /proc: ");
+
+  /* We map our original UID to the same UID in the container so we
+     can own our own files normally.  */
+  UMAP = open ("/proc/self/uid_map", O_WRONLY);
+  if (UMAP < 0)
+    FAIL_EXIT1 ("can't write to /proc/self/uid_map\n");
+
+  sprintf (tmp, "%lld %lld 1\n",
+	   (long long) original_uid, (long long) original_uid);
+  write (UMAP, tmp, strlen (tmp));
+  xclose (UMAP);
+
+  /* We must disable setgroups () before we can map our groups, else we
+     get EPERM.  */
+  GMAP = open ("/proc/self/setgroups", O_WRONLY);
+  if (GMAP >= 0)
+    {
+      /* We support kernels old enough to not have this.  */
+      write (GMAP, "deny\n", 5);
+      xclose (GMAP);
+    }
+
+  /* We map our original GID to the same GID in the container so we
+     can own our own files normally.  */
+  GMAP = open ("/proc/self/gid_map", O_WRONLY);
+  if (GMAP < 0)
+    FAIL_EXIT1 ("can't write to /proc/self/gid_map\n");
+
+  sprintf (tmp, "%lld %lld 1\n",
+	   (long long) original_gid, (long long) original_gid);
+  write (GMAP, tmp, strlen (tmp));
+  xclose (GMAP);
+
+  /* Now run the child.  */
+  execvp (new_child_proc[0], new_child_proc);
+
+  /* Or don't run the child?  */
+  FAIL_EXIT1 ("Unable to exec %s\n", new_child_proc[0]);
+
+  /* Because gcc won't know error () never returns...  */
+  exit (EXIT_UNSUPPORTED);
+}
diff --git a/support/true-container.c b/support/true-container.c
new file mode 100644
index 0000000000..57dc57e252
--- /dev/null
+++ b/support/true-container.c
@@ -0,0 +1,26 @@
+/* Minimal /bin/true for in-container use.
+   Copyright (C) 2018 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* Implements the in-container /bin/true, which always returns true
+   (0).  */
+
+int
+main (void)
+{
+  return 0;
+}
diff --git a/support/xmkdirp.c b/support/xmkdirp.c
new file mode 100644
index 0000000000..fada0452ea
--- /dev/null
+++ b/support/xmkdirp.c
@@ -0,0 +1,66 @@
+/* Error-checking replacement for "mkdir -p".
+   Copyright (C) 2018 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <support/support.h>
+#include <support/check.h>
+#include <support/xunistd.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+/* Equivalent of "mkdir -p".  Any failures cause FAIL_EXIT1 so no
+   return code is needed.  */
+
+void
+xmkdirp (const char *path, mode_t mode)
+{
+  struct stat s;
+  const char *slash_p;
+  int rv;
+
+  if (path[0] == 0)
+    return;
+
+  if (stat (path, &s) == 0)
+    {
+      if (S_ISDIR (s.st_mode))
+	return;
+      errno = EEXIST;
+      FAIL_EXIT1 ("mkdir_p (\"%s\", 0%o): %m", path, mode);
+    }
+
+  slash_p = strrchr (path, '/');
+  if (slash_p != NULL)
+    {
+      while (slash_p > path && slash_p[-1] == '/')
+	--slash_p;
+      if (slash_p > path)
+	{
+	  char *parent = xstrndup (path, slash_p - path);
+	  xmkdirp (parent, mode);
+	  free (parent);
+	}
+    }
+
+  rv = mkdir (path, mode);
+  if (rv != 0)
+    FAIL_EXIT1 ("mkdir_p (\"%s\", 0%o): %m", path, mode);
+
+  return;
+}
diff --git a/support/xsymlink.c b/support/xsymlink.c
new file mode 100644
index 0000000000..0f3edf640a
--- /dev/null
+++ b/support/xsymlink.c
@@ -0,0 +1,29 @@
+/* Error-checking replacement for "symlink".
+   Copyright (C) 2018 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <support/support.h>
+#include <support/check.h>
+
+#include <unistd.h>
+
+void
+xsymlink (const char *target, const char *linkpath)
+{
+  if (symlink (target, linkpath) < 0)
+    FAIL_EXIT1 ("symlink (\"%s\", \"%s\")", target, linkpath);
+}
diff --git a/support/xunistd.h b/support/xunistd.h
index 5fe5dae818..cdd4e8d92d 100644
--- a/support/xunistd.h
+++ b/support/xunistd.h
@@ -43,6 +43,10 @@ void xunlink (const char *path);
 long xsysconf (int name);
 long long xlseek (int fd, long long offset, int whence);
 void xftruncate (int fd, long long length);
+void xsymlink (const char *target, const char *linkpath);
+
+/* Equivalent of "mkdir -p".  */
+void xmkdirp (const char *, mode_t);
 
 /* Read the link at PATH.  The caller should free the returned string
    with free.  */

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: V8 test-in-container patch
  2018-08-16 17:59 V8 test-in-container patch DJ Delorie
@ 2018-08-16 18:54 ` Florian Weimer
  2018-08-17  2:14   ` DJ Delorie
  2018-08-17  2:53 ` Carlos O'Donell
  2018-10-09  9:55 ` Szabolcs Nagy
  2 siblings, 1 reply; 25+ messages in thread
From: Florian Weimer @ 2018-08-16 18:54 UTC (permalink / raw)
  To: DJ Delorie, libc-alpha; +Cc: Joseph Myers, carlos

On 08/16/2018 07:59 PM, DJ Delorie wrote:
> This still uses CLONE_NEWPID.  I recall spending a lot of time trying to
> get the "su-less sudo" working correctly, and it's fragile but works
> as-is.

Please have a look at misc/tst-ttyname.  It uses a bind mount to put 
/proc into the chroot, and it does not have this problem.

Thanks,
Florian

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: V8 test-in-container patch
  2018-08-16 18:54 ` Florian Weimer
@ 2018-08-17  2:14   ` DJ Delorie
  0 siblings, 0 replies; 25+ messages in thread
From: DJ Delorie @ 2018-08-17  2:14 UTC (permalink / raw)
  To: Florian Weimer; +Cc: libc-alpha, joseph, carlos


Florian Weimer <fweimer@redhat.com> writes:
> Please have a look at misc/tst-ttyname.  It uses a bind mount to put 
> /proc into the chroot, and it does not have this problem.

So that gets past the /proc mount, but if you bind mount /proc, it's in
the wrong namespace and you can't fix the UID map:

error: test-container.c:948: can't write to /proc/self/uid_map

Without the uid map, you don't own your own files in the new namespace.

Like I said, fragile :-P

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: V8 test-in-container patch
  2018-08-16 17:59 V8 test-in-container patch DJ Delorie
  2018-08-16 18:54 ` Florian Weimer
@ 2018-08-17  2:53 ` Carlos O'Donell
  2018-08-23  1:21   ` DJ Delorie
  2018-10-09  9:55 ` Szabolcs Nagy
  2 siblings, 1 reply; 25+ messages in thread
From: Carlos O'Donell @ 2018-08-17  2:53 UTC (permalink / raw)
  To: DJ Delorie, libc-alpha; +Cc: fweimer, Joseph Myers

On 08/16/2018 01:59 PM, DJ Delorie wrote:
> 
> This still uses CLONE_NEWPID.  I recall spending a lot of time trying to
> get the "su-less sudo" working correctly, and it's fragile but works
> as-is.
> 
> * Makefile (testroot.pristine): New rules to initialize the
> test-in-container "testroot".
> * Makerules (all-testsuite): Add tests-container.
> * Rules (tests): Add tests-container.
> (binaries-all-tests): Likewise.
> (tests-container): New, run these tests in the testroot container.
> * support/Makefile (others): Add *-container, support_paths.c,
> xmkdirp, and links-dso-program.
> * support/links-dso-program-c.c: New.
> * support/links-dso-program.cc: New.
> * support/test-container.c: New.
> * support/shell-container.c: New.
> * support/echo-container.c: New.
> * support/true-container.c: New.
> * support/xmkdirp.c: New.
> * support/xsymlink.c: New.
> * support/support_paths.c: New.
> * support/support.h: Add support paths prototypes.
> * support/xunistd.h: Add xmkdirp () and xsymlink ().
> 
> * nss/tst-nss-test3.c: Convert to test-in-container.
> * nss/tst-nss-test3.root/: New.
> 

OK for master with:

- Review suggested text and accept or reject with rationale.
- Fix error string typo in run_command_array.
- Delete #if 0/#endif iconv/gconv code.
- Successful build-many-glibcs run.

This version looks as ready modulo the above.

I'm dying to start using this to add more complex tests upstream.

Also anyone who wants to remove CLONE_NEWPID requirement has a
baseline to start testing with and making changes.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>

> diff --git a/Makefile b/Makefile
> index d3f25a525a..5290434f26 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -340,6 +340,52 @@ define summarize-tests
>  @! egrep -q -v '^(X?PASS|XFAIL|UNSUPPORTED):' $(objpfx)$1
>  endef
>  
> +# The intention here is to do ONE install of our build into the
> +# testroot.pristine/ directory, then rsync (internal to
> +# support/test-container) that to testroot.root/ at the start of each
> +# test.  That way we can promise each test a "clean" install, without
> +# having to do the install for each test.
> +#
> +# In addition, we have to copy some files (which we build) into this
> +# root in addition to what glibc installs.  For example, many tests
> +# require /bin/sh be present, and any shared objects that /bin/sh
> +# depends on.  We also build a "test" program in either C or (if
> +# available) C++ just so we can copy in any shared objects (which we
> +# do not build) that GCC-compiled programs depend on.

Suggest:

# In addition, we have to copy some files (which we build) into this
# root in addition to what glibc installs.  For example, many tests
# require additional programs including /bin/sh, /bin/true, and
# /bin/echo, all of which we build below to limit library dependencies
# to just those things in glibc and language support libraries which
# we also copy into the into the rootfs.  To determine what language
# support libraries we need we build a "test" program in either C or
# (if available) C++ just so we can copy in any shared objects
# (which we do not build) that GCC-compiled programs depend on.

> +
> +$(tests-container) $(addsuffix /tests,$(subdirs)) : \
> +		$(objpfx)testroot.pristine/install.stamp
> +$(objpfx)testroot.pristine/install.stamp :
> +	test -d $(objpfx)testroot.pristine || \
> +	  mkdir $(objpfx)testroot.pristine
> +	# We need a working /bin/sh for some of the tests.
> +	test -d $(objpfx)testroot.pristine/bin || \
> +	  mkdir $(objpfx)testroot.pristine/bin
> +	cp $(objpfx)support/shell-container $(objpfx)testroot.pristine/bin/sh
> +	cp $(objpfx)support/echo-container $(objpfx)testroot.pristine/bin/echo
> +	cp $(objpfx)support/true-container $(objpfx)testroot.pristine/bin/true
> +	# Copy these DSOs first so we can overwrite them with our own.
> +	for dso in `$(test-wrapper-env) LD_TRACE_LOADED_OBJECTS=1  \
> +		$(objpfx)elf/$(rtld-installed-name) \
> +		$(objpfx)testroot.pristine/bin/sh \
> +	        | grep / | sed 's/^[^/]*//' | sed 's/ .*//'` ;\
> +	  do \
> +	    test -d `dirname $(objpfx)testroot.pristine$$dso` || \
> +	      mkdir -p `dirname $(objpfx)testroot.pristine$$dso` ;\
> +	    $(test-wrapper) cp $$dso $(objpfx)testroot.pristine$$dso ;\
> +	  done
> +	for dso in `$(test-wrapper-env) LD_TRACE_LOADED_OBJECTS=1  \
> +		$(objpfx)elf/$(rtld-installed-name) \
> +		$(objpfx)support/links-dso-program \
> +	        | grep / | sed 's/^[^/]*//' | sed 's/ .*//'` ;\
> +	  do \
> +	    test -d `dirname $(objpfx)testroot.pristine$$dso` || \
> +	      mkdir -p `dirname $(objpfx)testroot.pristine$$dso` ;\
> +	    $(test-wrapper) cp $$dso $(objpfx)testroot.pristine$$dso ;\
> +	  done
> +	$(MAKE) install DESTDIR=$(objpfx)testroot.pristine
> +	touch $(objpfx)testroot.pristine/install.stamp

OK.

> +
>  tests-special-notdir = $(patsubst $(objpfx)%, %, $(tests-special))
>  tests: $(tests-special)
>  	$(..)scripts/merge-test-results.sh -s $(objpfx) "" \
> diff --git a/Makerules b/Makerules
> index a10a0b4d70..5d6434c74b 100644
> --- a/Makerules
> +++ b/Makerules
> @@ -1369,7 +1369,8 @@ xcheck: xtests
>  # The only difference between MODULE_NAME=testsuite and MODULE_NAME=nonlib is
>  # that almost all internal declarations from config.h, libc-symbols.h, and
>  # include/*.h are not available to 'testsuite' code, but are to 'nonlib' code.
> -all-testsuite := $(strip $(tests) $(xtests) $(test-srcs) $(test-extras))
> +all-testsuite := $(strip $(tests) $(xtests) $(test-srcs) $(test-extras) \
> +		 $(tests-container))

OK.

>  ifneq (,$(all-testsuite))
>  cpp-srcs-left = $(all-testsuite)
>  lib := testsuite
> diff --git a/Rules b/Rules
> index 706c8a749d..d4dc2b6f45 100644
> --- a/Rules
> +++ b/Rules
> @@ -130,12 +130,14 @@ others: $(py-const)
>  
>  ifeq ($(run-built-tests),no)
>  tests: $(addprefix $(objpfx),$(filter-out $(tests-unsupported), \
> -                                          $(tests) $(tests-internal)) \
> +                                          $(tests) $(tests-internal) \
> +					  $(tests-container)) \

OK.

>  			     $(test-srcs)) $(tests-special) \
>  			     $(tests-printers-programs)
>  xtests: tests $(xtests-special)
>  else
>  tests: $(tests:%=$(objpfx)%.out) $(tests-internal:%=$(objpfx)%.out) \
> +       $(tests-container:%=$(objpfx)%.out) \

OK.

>         $(tests-special) $(tests-printers-out)
>  xtests: tests $(xtests:%=$(objpfx)%.out) $(xtests-special)
>  endif
> @@ -149,7 +151,8 @@ tests-expected = $(tests) $(tests-internal) $(tests-printers)
>  endif
>  tests:
>  	$(..)scripts/merge-test-results.sh -s $(objpfx) $(subdir) \
> -	  $(sort $(tests-expected) $(tests-special-notdir:.out=)) \
> +	  $(sort $(tests-expected) $(tests-special-notdir:.out=) \
> +		 $(tests-container)) \

OK.

>  	  > $(objpfx)subdir-tests.sum
>  xtests:
>  	$(..)scripts/merge-test-results.sh -s $(objpfx) $(subdir) \
> @@ -158,7 +161,8 @@ xtests:
>  
>  ifeq ($(build-programs),yes)
>  binaries-all-notests = $(others) $(sysdep-others)
> -binaries-all-tests = $(tests) $(tests-internal) $(xtests) $(test-srcs)
> +binaries-all-tests = $(tests) $(tests-internal) $(xtests) $(test-srcs) \
> +		     $(tests-container)

OK.

>  binaries-all = $(binaries-all-notests) $(binaries-all-tests)
>  binaries-static-notests = $(others-static)
>  binaries-static-tests = $(tests-static) $(xtests-static)
> @@ -248,6 +252,16 @@ $(objpfx)%.out: /dev/null $(objpfx)%	# Make it 2nd arg for canned sequence.
>  	$(make-test-out) > $@; \
>  	$(evaluate-test)
>  
> +
> +# Any tests that require an isolated container (chroot) in which to
> +# run, should be added to tests-container.

Suggest:

# Any tests that require an isolated container (filesystem, network
# and pid namespaces) in which to run, should be added to
# tests-container.

> +$(tests-container:%=$(objpfx)%.out): $(objpfx)%.out : $(if $(wildcard $(objpfx)%.files),$(objpfx)%.files,/dev/null) $(objpfx)%
> +	$(test-wrapper-env) $(run-program-env) $(run-via-rtld-prefix) \
> +	  $(common-objpfx)support/test-container env $(run-program-env) $($*-ENV) \
> +	  $(host-test-program-cmd) $($*-ARGS) > $@; \
> +	$(evaluate-test)

OK.

> +
> +
>  # tests-unsupported lists tests that we will not try to build at all in
>  # this configuration.  Note this runs every time because it does not
>  # actually create its target.  The dependency on Makefile is meant to
> diff --git a/nss/Makefile b/nss/Makefile
> index 5209fc0456..e00a4f768f 100644
> --- a/nss/Makefile
> +++ b/nss/Makefile
> @@ -55,11 +55,13 @@ tests-internal		= tst-field
>  tests			= test-netdb test-digits-dots tst-nss-getpwent bug17079 \
>  			  tst-nss-test1 \
>  			  tst-nss-test2 \
> -			  tst-nss-test3 \
>  			  tst-nss-test4 \
>  			  tst-nss-test5
>  xtests			= bug-erange
>  
> +tests-container = \
> +			  tst-nss-test3

OK.

> +
>  # Tests which need libdl
>  ifeq (yes,$(build-shared))
>  tests += tst-nss-files-hosts-erange
> diff --git a/nss/tst-nss-test3.c b/nss/tst-nss-test3.c
> index d9d708ae7b..4112231778 100644
> --- a/nss/tst-nss-test3.c
> +++ b/nss/tst-nss-test3.c
> @@ -107,7 +107,11 @@ do_test (void)
>    int i;
>    struct group *g = NULL;
>  
> -  __nss_configure_lookup ("group", "test1");
> +/* Previously we used __nss_configure_lookup to isolate the test
> +   from the host environment and to get it to lookup from our new
> +   test1 NSS service module, but now this test is run in a different
> +   root filesystem via the test-container support and we directly
> +   configure the use of the test1 NSS service.  */

OK.

>  
>    setgrent ();
>  
> diff --git a/nss/tst-nss-test3.root/etc/nsswitch.conf b/nss/tst-nss-test3.root/etc/nsswitch.conf
> new file mode 100644
> index 0000000000..5e08fe5eea
> --- /dev/null
> +++ b/nss/tst-nss-test3.root/etc/nsswitch.conf
> @@ -0,0 +1 @@
> +group	test1

OK.

> diff --git a/nss/tst-nss-test3.root/tst-nss-test3.script b/nss/tst-nss-test3.root/tst-nss-test3.script
> new file mode 100644
> index 0000000000..a10beb1e6c
> --- /dev/null
> +++ b/nss/tst-nss-test3.root/tst-nss-test3.script
> @@ -0,0 +1,2 @@
> +cp $B/nss/libnss_test1.so $L/libnss_test1.so.2
> +cp $B/nss/libnss_test2.so $L/libnss_test2.so.2
> diff --git a/support/Makefile b/support/Makefile
> index 652d2cdf69..2c937761ab 100644
> --- a/support/Makefile
> +++ b/support/Makefile
> @@ -53,6 +53,7 @@ libsupport-routines = \
>    support_format_netent \
>    support_isolate_in_subprocess \
>    support_openpty \
> +  support_paths \
>    support_quote_blob \
>    support_record_failure \
>    support_run_diff \
> @@ -84,6 +85,7 @@ libsupport-routines = \
>    xmalloc \
>    xmemstream \
>    xmkdir \
> +  xmkdirp \
>    xmmap \
>    xmprotect \
>    xmunmap \
> @@ -139,6 +141,7 @@ libsupport-routines = \
>    xsocket \
>    xstrdup \
>    xstrndup \
> +  xsymlink \

OK.

>    xsysconf \
>    xunlink \
>    xwaitpid \
> @@ -151,6 +154,42 @@ ifeq ($(build-shared),yes)
>  libsupport-inhibit-o += .o
>  endif
>  
> +CFLAGS-support_paths.c = \
> +		-DSRCDIR_PATH=\"`cd .. ; pwd`\" \
> +		-DOBJDIR_PATH=\"`cd $(objpfx)/..; pwd`\" \
> +		-DINSTDIR_PATH=\"$(prefix)\" \
> +		-DLIBDIR_PATH=\"$(libdir)\"
> +
> +others: \
> +	$(objpfx)test-container \
> +	$(objpfx)links-dso-program \
> +	$(objpfx)shell-container \
> +	$(objpfx)echo-container \
> +	$(objpfx)true-container

OK.

> +
> +LDLIBS-test-container = $(libsupport)
> +
> +others += test-container
> +others-noinstall += test-container
> +
> +others += shell-container echo-container true-container
> +others-noinstall += shell-container echo-container true-container
> +
> +$(objpfx)test-container : $(libsupport)
> +$(objpfx)shell-container : $(libsupport)
> +$(objpfx)echo-container : $(libsupport)
> +$(objpfx)true-container : $(libsupport)
> +
> +# This exists only so we can guess which OS DSOs we need to copy into
> +# the testing container.
> +ifeq (,$(CXX))
> +$(objpfx)links-dso-program : $(objpfx)links-dso-program-c.o
> +	$(LINK.o) -o $@ $^
> +else
> +$(objpfx)links-dso-program : $(objpfx)links-dso-program.o
> +	$(LINK.o) -o $@ $^ -lstdc++
> +endif
> +

OK.

>  tests = \
>    README-testing \
>    tst-support-namespace \
> diff --git a/support/echo-container.c b/support/echo-container.c
> new file mode 100644
> index 0000000000..e4d48df957
> --- /dev/null
> +++ b/support/echo-container.c
> @@ -0,0 +1,34 @@
> +/* Minimal /bin/echo for in-container use.

OK.

> +   Copyright (C) 2018 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <http://www.gnu.org/licenses/>.  */
> +
> +#include <stdio.h>
> +
> +int
> +main (int argc, const char **argv)
> +{
> +  int i;
> +
> +  for (i = 1; i < argc; i++)
> +    {
> +      if (i > 1)
> +	putchar (' ');
> +      fputs (argv[i], stdout);
> +    }
> +  putchar ('\n');
> +  return 0;
> +}

OK.

> diff --git a/support/links-dso-program-c.c b/support/links-dso-program-c.c
> new file mode 100644
> index 0000000000..d28a28a0d0
> --- /dev/null
> +++ b/support/links-dso-program-c.c
> @@ -0,0 +1,9 @@
> +#include <stdio.h>
> +
> +int
> +main (int argc, char **argv)
> +{
> +  /* Complexity to keep gcc from optimizing this away.  */
> +  printf ("This is a test %s.\n", argc > 1 ? argv[1] : "null");
> +  return 0;
> +}

OK.

> diff --git a/support/links-dso-program.cc b/support/links-dso-program.cc
> new file mode 100644
> index 0000000000..dba6976c06
> --- /dev/null
> +++ b/support/links-dso-program.cc
> @@ -0,0 +1,11 @@
> +#include <iostream>
> +
> +using namespace std;
> +
> +int
> +main (int argc, char **argv)
> +{
> +  /* Complexity to keep gcc from optimizing this away.  */
> +  cout << (argc > 1 ? argv[1] : "null");
> +  return 0;
> +}

OK.

> diff --git a/support/shell-container.c b/support/shell-container.c
> new file mode 100644
> index 0000000000..483c9b9aca
> --- /dev/null
> +++ b/support/shell-container.c
> @@ -0,0 +1,396 @@
> +/* Minimal /bin/sh for in-container use.

OK.

> +   Copyright (C) 2018 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <http://www.gnu.org/licenses/>.  */
> +
> +#define _FILE_OFFSET_BITS 64
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sched.h>
> +#include <sys/syscall.h>
> +#include <unistd.h>
> +#include <sys/types.h>
> +#include <dirent.h>
> +#include <string.h>
> +#include <sys/mount.h>
> +#include <sys/stat.h>
> +#include <sys/fcntl.h>
> +#include <sys/file.h>
> +#include <sys/wait.h>
> +#include <stdarg.h>
> +#include <sys/sysmacros.h>
> +#include <ctype.h>
> +#include <utime.h>
> +#include <errno.h>
> +#include <error.h>
> +
> +#include <support/support.h>
> +
> +/* Design considerations
> +
> + General rule: optimize for developer time, not run time.
> +
> + Specifically:
> +
> + * Don't worry about slow algorithms
> + * Don't worry about free'ing memory
> + * Don't implement anything the testsuite doesn't need.
> + * Line and argument counts are limited, see below.
> +
> +*/

OK.

> +
> +#define MAX_ARG_COUNT 100
> +#define MAX_LINE_LENGTH 1000
> +
> +/* Debugging is enabled via --debug, which must be the first argument.  */
> +static int debug_mode = 0;
> +#define dprintf if (debug_mode) fprintf
> +
> +/* Emulate the "/bin/true" command.  Arguments are ignored.  */
> +static int
> +true_func (char **argv)
> +{
> +  return 0;
> +}
> +
> +/* Emulate the "/bin/echo" command.  Options are ignored, arguments
> +   are printed to stdout.  */
> +static int
> +echo_func (char **argv)
> +{
> +  int i;
> +
> +  for (i = 0; argv[i]; i++)
> +    {
> +      if (i > 0)
> +	putchar (' ');
> +      fputs (argv[i], stdout);
> +    }
> +  putchar ('\n');
> +
> +  return 0;
> +}
> +
> +/* Emulate the "/bin/cp" command.  Options are ignored.  Only copies
> +   one source file to one destination file.  Directory destinations
> +   are not supported.  */
> +static int
> +copy_func (char **argv)
> +{
> +  char *sname = argv[0];
> +  char *dname = argv[1];
> +  int sfd, dfd;
> +  struct stat st;
> +
> +  sfd = open (sname, O_RDONLY);
> +  if (sfd < 0)
> +    {
> +      fprintf (stderr, "cp: unable to open %s for reading: %s\n",
> +	       sname, strerror (errno));
> +      return 1;
> +    }
> +
> +  if (fstat (sfd, &st) < 0)
> +    {
> +      fprintf (stderr, "cp: unable to fstat %s: %s\n",
> +	       sname, strerror (errno));
> +      return 1;
> +    }
> +
> +  dfd = open (dname, O_WRONLY | O_TRUNC | O_CREAT, 0600);
> +  if (dfd < 0)
> +    {
> +      fprintf (stderr, "cp: unable to open %s for writing: %s\n",
> +	       dname, strerror (errno));
> +      return 1;
> +    }
> +
> +  if (copy_file_range (sfd, 0, dfd, 0, st.st_size, 0) != st.st_size)
> +    {
> +      fprintf (stderr, "cp: cannot copy file %s to %s: %s\n",
> +	       sname, dname, strerror (errno));
> +      return 1;
> +    }
> +
> +  close (sfd);
> +  close (dfd);
> +
> +  chmod (dname, st.st_mode & 0777);
> +
> +  return 0;
> +
> +}
> +
> +/* This is a list of all the built-in commands we understand.  */
> +static struct {
> +  const char *name;
> +  int (*func) (char **argv);
> +} builtin_funcs[] = {
> +  { "true", true_func },
> +  { "echo", echo_func },
> +  { "cp", copy_func },
> +  { NULL, NULL }
> +};
> +
> +/* Run one tokenized command.  argv[0] is the command.  argv is
> +   NULL-terminated.  */
> +static void
> +run_command_array (char **argv)
> +{
> +  int i, j;
> +  pid_t pid;
> +  int status;
> +  int (*builtin_func) (char **args);
> +
> +  if (argv[0] == NULL)
> +    return;
> +
> +  builtin_func = NULL;
> +
> +  int new_stdin = 0;
> +  int new_stdout = 1;
> +  int new_stderr = 2;
> +
> +  dprintf (stderr, "run_command_array starting\n");
> +  for (i = 0; argv[i]; i++)
> +    dprintf (stderr, "   argv [%d] `%s'\n", i, argv[i]);
> +
> +  for (j = i = 0; argv[i]; i++)
> +    {
> +      if (strcmp (argv[i], "<") == 0 && argv[i + 1])
> +	{
> +	  new_stdin = open (argv[i + 1], O_WRONLY|O_CREAT|O_TRUNC, 0777);
> +	  ++i;
> +	  continue;
> +	}
> +      if (strcmp (argv[i], ">") == 0 && argv[i + 1])
> +	{
> +	  new_stdout = open (argv[i + 1], O_WRONLY|O_CREAT|O_TRUNC, 0777);
> +	  ++i;
> +	  continue;
> +	}
> +      if (strcmp (argv[i], ">>") == 0 && argv[i + 1])
> +	{
> +	  new_stdout = open (argv[i + 1], O_WRONLY|O_CREAT|O_APPEND, 0777);
> +	  ++i;
> +	  continue;
> +	}
> +      if (strcmp (argv[i], "2>") == 0 && argv[i + 1])
> +	{
> +	  new_stderr = open (argv[i + 1], O_WRONLY|O_CREAT|O_TRUNC, 0777);
> +	  ++i;
> +	  continue;
> +	}
> +      argv[j++] = argv[i];
> +    }
> +  argv[j] = NULL;
> +
> +
> +  for (i = 0; builtin_funcs[i].name != NULL; i++)
> +    if (strcmp (argv[0], builtin_funcs[i].name) == 0)
> +       builtin_func = builtin_funcs[i].func;
> +
> +  dprintf (stderr, "builtin %p argv0 `%s'\n", builtin_func, argv[0]);
> +
> +  pid = fork ();
> +  if (pid < 0)
> +    {
> +      fprintf (stderr, "sh; fork failed\n");

Typo.

s/;/:/g

> +      exit (1);
> +    }
> +
> +  if (pid == 0)
> +    {
> +      if (new_stdin != 0)
> +	{
> +	  dup2 (new_stdin, 0);
> +	  close (new_stdin);
> +	}
> +      if (new_stdout != 1)
> +	{
> +	  dup2 (new_stdout, 1);
> +	  close (new_stdout);
> +	}
> +      if (new_stderr != 2)
> +	{
> +	  dup2 (new_stderr, 2);
> +	  close (new_stdout);
> +	}
> +
> +      if (builtin_func != NULL)
> +	exit (builtin_func (argv + 1));
> +
> +      execvp (argv[0], argv);
> +
> +      fprintf (stderr, "sh: execing %s failed: %s",
> +	       argv[0], strerror (errno));
> +      exit (1);
> +    }
> +
> +  waitpid (pid, &status, 0);
> +
> +  dprintf (stderr, "exiting run_command_array\n");
> +
> +  if (WIFEXITED (status))
> +    {
> +      int rv = WEXITSTATUS (status);
> +      if (rv)
> +	exit (rv);
> +    }
> +  else
> +    exit (1);
> +}
> +
> +/* Run one command-as-a-string, by tokenizing it.  Limited to
> +   MAX_ARG_COUNT arguments.  Simple substitution is done of $1 to $9
> +   (as whole separate tokens) from iargs[].  Quoted strings work if
> +   the quotes wrap whole tokens; i.e. "foo bar" but not foo" bar".  */
> +static void
> +run_command_string (const char *cmdline, const char **iargs)
> +{
> +  char *args[MAX_ARG_COUNT+1];
> +  int ap = 0;
> +  const char *start, *end;
> +  int nargs;
> +
> +  for (nargs = 0; iargs[nargs] != NULL; ++nargs)
> +    ;
> +
> +  dprintf (stderr, "run_command_string starting: '%s'\n", cmdline);
> +
> +  while (ap < MAX_ARG_COUNT)
> +    {
> +      /* If the argument is quoted, this is the quote character, else NUL.  */
> +      int in_quote = 0;
> +
> +      /* Skip whitespace up to the next token.  */
> +      while (*cmdline && isspace (*cmdline))
> +	cmdline ++;
> +      if (*cmdline == 0)
> +	break;
> +
> +      start = cmdline;
> +      /* Check for quoted argument.  */
> +      in_quote = (*cmdline == '\'' || *cmdline == '"') ? *cmdline : 0;
> +
> +      /* Skip to end of token; either by whitespace or matching quote.  */
> +      dprintf (stderr, "in_quote %d\n", in_quote);
> +      while (*cmdline
> +	     && (!isspace (*cmdline) || in_quote))
> +	{
> +	  if (*cmdline == in_quote
> +	      && cmdline != start)
> +	    in_quote = 0;
> +	  dprintf (stderr, "[%c]%d ", *cmdline, in_quote);
> +	  cmdline ++;
> +	}
> +      dprintf (stderr, "\n");
> +
> +      /* Allocate space for this token and store it in args[].  */
> +      end = cmdline;
> +      dprintf (stderr, "start<%s> end<%s>\n", start, end);
> +      args[ap] = (char *) xmalloc (end - start + 1);
> +      memcpy (args[ap], start, end - start);
> +      args[ap][end - start] = 0;
> +
> +      /* Strip off quotes, if found.  */
> +      dprintf (stderr, "args[%d] = <%s>\n", ap, args[ap]);
> +      if (args[ap][0] == '\''
> +	  && args[ap][strlen (args[ap])-1] == '\'')
> +	{
> +	  args[ap][strlen (args[ap])-1] = 0;
> +	  args[ap] ++;
> +	}
> +
> +      else if (args[ap][0] == '"'
> +	  && args[ap][strlen (args[ap])-1] == '"')
> +	{
> +	  args[ap][strlen (args[ap])-1] = 0;
> +	  args[ap] ++;
> +	}
> +
> +      /* Replace positional parameters like $4.  */
> +      else if (args[ap][0] == '$'
> +	       && isdigit (args[ap][1])
> +	       && args[ap][2] == 0)
> +	{
> +	  int a = args[ap][1] - '1';
> +	  if (0 <= a && a < nargs)
> +	    args[ap] = strdup (iargs[a]);
> +	}
> +
> +      ap ++;
> +
> +      if (*cmdline == 0)
> +	break;
> +    }
> +
> +  /* Lastly, NULL terminate the array and run it.  */
> +  args[ap] = NULL;
> +  run_command_array (args);
> +}
> +
> +/* Run a script by reading lines and passing them to the above
> +   function.  */
> +static void
> +run_script (const char *filename, const char **args)
> +{
> +  char line[MAX_LINE_LENGTH + 1];
> +  dprintf (stderr, "run_script starting: '%s'\n", filename);
> +  FILE *f = fopen (filename, "r");
> +  if (f == NULL)
> +    {
> +      fprintf (stderr, "sh: %s: %s\n", filename, strerror (errno));
> +      exit (1);
> +    }
> +  while (fgets (line, sizeof (line), f) != NULL)
> +    {
> +      if (line[0] == '#')
> +	{
> +	  dprintf (stderr, "comment: %s\n", line);
> +	  continue;
> +	}
> +      run_command_string (line, args);
> +    }
> +  fclose (f);
> +}
> +
> +int
> +main (int argc, const char **argv)
> +{
> +  int i;
> +
> +  if (strcmp (argv[1], "--debug") == 0)
> +    {
> +      debug_mode = 1;
> +      --argc;
> +      ++argv;
> +    }
> +
> +  dprintf (stderr, "container-sh starting:\n");
> +  for (i = 0; i < argc; i++)
> +    dprintf (stderr, "  argv[%d] is `%s'\n", i, argv[i]);
> +
> +  if (strcmp (argv[1], "-c") == 0)
> +    run_command_string (argv[2], argv+3);
> +  else
> +    run_script (argv[1], argv+2);
> +
> +  dprintf (stderr, "normal exit 0\n");
> +  return 0;
> +}

OK.

> diff --git a/support/support.h b/support/support.h
> index b61fe0735c..1403510f11 100644
> --- a/support/support.h
> +++ b/support/support.h
> @@ -25,6 +25,8 @@
>  
>  #include <stddef.h>
>  #include <sys/cdefs.h>
> +/* For mode_t.  */
> +#include <sys/stat.h>
>  
>  __BEGIN_DECLS
>  
> @@ -76,6 +78,16 @@ char *xasprintf (const char *format, ...)
>  char *xstrdup (const char *);
>  char *xstrndup (const char *, size_t);
>  
> +/* These point to the TOP of the source/build tree, not your (or
> +   support's) subdirectory.  */
> +extern const char support_srcdir_root[];
> +extern const char support_objdir_root[];
> +
> +/* Corresponds to the --prefix= passed to configure.  */
> +extern const char support_install_prefix[];
> +/* Corresponds to the install's lib/ or lib64/ directory.  */
> +extern const char support_libdir_prefix[];
> +

OK.

>  __END_DECLS
>  
>  #endif /* SUPPORT_H */
> diff --git a/support/support_paths.c b/support/support_paths.c
> new file mode 100644
> index 0000000000..a1c22315bd
> --- /dev/null
> +++ b/support/support_paths.c
> @@ -0,0 +1,51 @@
> +/* Various paths that might be needed.

OK.

> +   Copyright (C) 2018 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <http://www.gnu.org/licenses/>.  */
> +
> +#include <support/support.h>
> +#include <support/check.h>
> +
> +/* The idea here is to make various makefile-level paths available to
> +   support programs, as canonicalized absolute paths.  */
> +
> +/* These point to the TOP of the source/build tree, not your (or
> +   support's) subdirectory.  */
> +#ifdef SRCDIR_PATH
> +const char support_srcdir_root[] = SRCDIR_PATH;
> +#else
> +# error please -DSRCDIR_PATH=something in the Makefile
> +#endif
> +
> +#ifdef OBJDIR_PATH
> +const char support_objdir_root[] = OBJDIR_PATH;
> +#else
> +# error please -DOBJDIR_PATH=something in the Makefile
> +#endif
> +
> +#ifdef INSTDIR_PATH
> +/* Corresponds to the --prefix= passed to configure.  */
> +const char support_install_prefix[] = INSTDIR_PATH;
> +#else
> +# error please -DINSTDIR_PATH=something in the Makefile
> +#endif
> +
> +#ifdef LIBDIR_PATH
> +/* Corresponds to the install's lib/ or lib64/ directory.  */
> +const char support_libdir_prefix[] = LIBDIR_PATH;
> +#else
> +# error please -DLIBDIR_PATH=something in the Makefile
> +#endif

OK.

> diff --git a/support/test-container.c b/support/test-container.c
> new file mode 100644
> index 0000000000..237f50c5b7
> --- /dev/null
> +++ b/support/test-container.c
> @@ -0,0 +1,982 @@
> +/* Run a test case in an isolated namespace.

OK.

> +   Copyright (C) 2018 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <http://www.gnu.org/licenses/>.  */
> +
> +#define _FILE_OFFSET_BITS 64
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sched.h>
> +#include <sys/syscall.h>
> +#include <unistd.h>
> +#include <sys/types.h>
> +#include <dirent.h>
> +#include <string.h>
> +#include <sys/mount.h>
> +#include <sys/stat.h>
> +#include <sys/fcntl.h>
> +#include <sys/file.h>
> +#include <sys/wait.h>
> +#include <stdarg.h>
> +#include <sys/sysmacros.h>
> +#include <ctype.h>
> +#include <utime.h>
> +#include <errno.h>
> +#include <error.h>
> +
> +#include <support/support.h>
> +#include <support/xunistd.h>
> +#include "check.h"
> +#include "test-driver.h"
> +
> +int verbose = 0;
> +
> +/* Running a test in a container is tricky.  There are two main
> +   categories of things to do:
> +
> +   1. "Once" actions, like setting up the container and doing an
> +      install into it.
> +
> +   2. "Per-test" actions, like copying in support files and
> +      configuring the container.
> +
> +
> +   "Once" actions:
> +
> +   * mkdir $buildroot/testroot.pristine/
> +   * install into it
> +   * rsync to $buildroot/testroot.root/
> +
> +   "Per-test" actions:
> +   * maybe rsync to $buildroot/testroot.root/
> +   * copy support files and test binary
> +   * chroot/unshare
> +   * set up any mounts (like /proc)
> +
> +   Magic files:
> +
> +   For test $srcdir/foo/mytest.c we look for $srcdir/foo/mytest.root
> +   and, if found...
> +
> +   * mytest.root/ is rsync'd into container
> +   * mytest.root/preclean.req causes fresh rsync (with delete) before
> +     test if present
> +   * mytest.root/mytset.script has a list of "commands" to run:
> +       syntax:
> +         # comment
> +         mv FILE FILE
> +	 cp FILE FILE
> +	 rm FILE
> +	 FILE must start with $B/, $S/, $I/, $L/, or /
> +	  (expands to build dir, source dir, install dir, library dir
> +	   (in container), or container's root)
> +   * mytest.root/postclean.req causes fresh rsync (with delete) after
> +     test if present
> +
> +   Note that $srcdir/foo/mytest.script may be used instead of a
> +   $srcdir/foo/mytest.root/mytest.script in the sysroot template, if
> +   there is no other reason for a sysroot.
> +
> +   Design goals:
> +
> +   * independent of other packages which may not be installed (like
> +     rsync or Docker, or even "cp")
> +
> +   * Simple, easy to review code (i.e. prefer simple naive code over
> +     complex efficient code)
> +
> +   * The current implementation ist parallel-make-safe, but only in
> +     that it uses a lock to prevent parallel access to the testroot.  */

OK. Good, others can extend this if it becomes a performance bottleneck.

> +
> +\f
> +/* Utility Functions */
> +
> +/* Like xunlink, but it's OK if the file already doesn't exist.  */
> +void
> +maybe_xunlink (const char *path)
> +{
> +  int rv = unlink (path);
> +  if (rv < 0 && errno != ENOENT)
> +    FAIL_EXIT1 ("unlink (\"%s\"): %m", path);
> +}

OK.

> +
> +/* Like xmkdir, but it's OK if the directory already exists.  */
> +void
> +maybe_xmkdir (const char *path, mode_t mode)
> +{
> +  struct stat st;
> +
> +  if (stat (path, &st) == 0
> +      && S_ISDIR (st.st_mode))
> +    return;
> +  xmkdir (path, mode);
> +}

OK.

> +
> +/* Temporarily concatenate multiple strings into one.  Allows up to 10
> +   temporary results; use strdup () if you need them to be
> +   permanent.  */
> +static char *
> +concat (const char *str, ...)
> +{
> +  /* Assume initialized to NULL/zero.  */
> +  static char *bufs[10];
> +  static size_t buflens[10];
> +  static int bufn = 0;
> +  int n;
> +  size_t len;
> +  va_list ap, ap2;
> +  char *cp;
> +  char *next;
> +
> +  va_start (ap, str);
> +  va_copy (ap2, ap);
> +
> +  n = bufn;
> +  bufn = (bufn + 1) % 10;
> +  len = strlen (str);
> +
> +  while ((next = va_arg (ap, char *)) != NULL)
> +    len = len + strlen (next);
> +
> +  va_end (ap);
> +
> +  if (bufs[n] == NULL)
> +    {
> +      bufs[n] = xmalloc (len + 1); /* NUL */
> +      buflens[n] = len + 1;
> +    }
> +  else if (buflens[n] < len + 1)
> +    {
> +      bufs[n] = xrealloc (bufs[n], len + 1); /* NUL */
> +      buflens[n] = len + 1;
> +    }
> +
> +  strcpy (bufs[n], str);
> +  cp = strchr (bufs[n], '\0');
> +  while ((next = va_arg (ap2, char *)) != NULL)
> +    {
> +      strcpy (cp, next);
> +      cp = strchr (cp, '\0');
> +    }
> +  *cp = 0;
> +  va_end (ap2);
> +
> +  return bufs[n];
> +}

OK.

> +
> +/* Try to mount SRC onto DEST.  */
> +static void
> +trymount (const char *src, const char *dest)
> +{
> +  if (mount (src, dest, "", MS_BIND, NULL) < 0)
> +    FAIL_EXIT1 ("can't mount %s onto %s\n", src, dest);
> +}
> +
> +/* Special case of above for devices like /dev/zero where we have to
> +   mount a device over a device, not a directory over a directory.  */
> +static void
> +devmount (const char *new_root_path, const char *which)
> +{
> +  int fd;
> +  fd = open (concat (new_root_path, "/dev/", which, NULL),
> +	     O_CREAT | O_TRUNC | O_RDWR, 0777);
> +  xclose (fd);
> +
> +  trymount (concat ("/dev/", which, NULL),
> +	    concat (new_root_path, "/dev/", which, NULL));
> +}
> +
> +/* Returns true if the string "looks like" an environement variable
> +   being set.  */
> +static int
> +is_env_setting (const char *a)
> +{
> +  int count_name = 0;
> +
> +  while (*a)
> +    {
> +      if (isalnum (*a) || *a == '_')
> +	++count_name;
> +      else if (*a == '=' && count_name > 0)
> +	return 1;
> +      else
> +	return 0;
> +      ++a;
> +    }
> +  return 0;
> +}

OK.

> +
> +/* Break the_line into words and store in the_words.  Max nwords,
> +   returns actual count.  */
> +static int
> +tokenize (char *the_line, char **the_words, int nwords)
> +{
> +  int rv = 0;
> +
> +  while (nwords > 0)
> +    {
> +      /* Skip leading whitespace, if any.  */
> +      while (*the_line && isspace (*the_line))
> +	++the_line;
> +
> +      /* End of line?  */
> +      if (*the_line == 0)
> +	return rv;
> +
> +      /* THE_LINE points to a non-whitespace character, so we have a
> +	 word.  */
> +      *the_words = the_line;
> +      ++the_words;
> +      nwords--;
> +      ++rv;
> +
> +      /* Skip leading whitespace, if any.  */
> +      while (*the_line && ! isspace (*the_line))
> +	++the_line;
> +
> +      /* We now point at the trailing NUL *or* some whitespace.  */
> +      if (*the_line == 0)
> +	return rv;
> +
> +      /* It was whitespace, skip and keep tokenizing.  */
> +      *the_line++ = 0;
> +    }
> +
> +  /* We get here if we filled the words buffer.  */
> +  return rv;
> +}

OK.

> +
> +\f
> +/* Mini-RSYNC implementation.  Optimize later.      */
> +
> +/* A few routines for an "rsync buffer" which stores the paths we're
> +   working on.  We continuously grow and shrink the paths in each
> +   buffer so there's lot of re-use.  */
> +
> +/* We rely on "initialized to zero" to set these up.  */
> +typedef struct
> +{
> +  char *buf;
> +  size_t len;
> +  size_t size;
> +} path_buf;
> +
> +static path_buf spath, dpath;
> +
> +static void
> +r_setup (char *path, path_buf * pb)
> +{
> +  size_t len = strlen (path);
> +  if (pb->buf == NULL || pb->size < len + 1)
> +    {
> +      /* Round up.  This is an arbitrary number, just to keep from
> +	 reallocing too often.  */

OK.

> +      size_t sz = ALIGN_UP (len + 1, 512);
> +      if (pb->buf == NULL)
> +	pb->buf = (char *) xmalloc (sz);
> +      else
> +	pb->buf = (char *) xrealloc (pb->buf, sz);
> +      if (pb->buf == NULL)
> +	FAIL_EXIT1 ("Out of memory while rsyncing\n");
> +
> +      pb->size = sz;
> +    }
> +  strcpy (pb->buf, path);
> +  pb->len = len;
> +}
> +
> +static void
> +r_append (const char *path, path_buf * pb)
> +{
> +  size_t len = strlen (path) + pb->len;
> +  if (pb->size < len + 1)
> +    {
> +      /* Round up */
> +      size_t sz = ALIGN_UP (len + 1, 512);
> +      pb->buf = (char *) xrealloc (pb->buf, sz);
> +      if (pb->buf == NULL)
> +	FAIL_EXIT1 ("Out of memory while rsyncing\n");
> +
> +      pb->size = sz;
> +    }
> +  strcpy (pb->buf + pb->len, path);
> +  pb->len = len;
> +}
> +
> +static int
> +file_exists (char *path)
> +{
> +  struct stat st;
> +  if (lstat (path, &st) == 0)
> +    return 1;
> +  return 0;
> +}
> +
> +static void
> +recursive_remove (char *path)
> +{
> +  pid_t child;
> +  int status;
> +
> +  child = fork ();
> +
> +  switch (child) {
> +  case -1:
> +    FAIL_EXIT1 ("Unable to fork");
> +  case 0:
> +    /* Child.  */
> +    execlp ("rm", "rm", "-rf", path, NULL);
> +  default:
> +    /* Parent.  */
> +    waitpid (child, &status, 0);
> +    /* "rm" would have already printed a suitable error message.  */
> +    if (! WIFEXITED (status)
> +	|| WEXITSTATUS (status) != 0)
> +      exit (1);
> +
> +    break;
> +  }
> +}

OK.

> +
> +/* Used for both rsync and the mytest.script "cp" command.  */
> +static void
> +copy_one_file (const char *sname, const char *dname)
> +{
> +  int sfd, dfd;
> +  struct stat st;
> +  struct utimbuf times;
> +
> +  sfd = open (sname, O_RDONLY);
> +  if (sfd < 0)
> +    FAIL_EXIT1 ("unable to open %s for reading\n", sname);
> +
> +  if (fstat (sfd, &st) < 0)
> +    FAIL_EXIT1 ("unable to fstat %s\n", sname);
> +
> +  dfd = open (dname, O_WRONLY | O_TRUNC | O_CREAT, 0600);
> +  if (dfd < 0)
> +    FAIL_EXIT1 ("unable to open %s for writing\n", dname);
> +
> +  if (copy_file_range (sfd, 0, dfd, 0, st.st_size, 0) != st.st_size)
> +    FAIL_EXIT1 ("cannot copy file %s to %s\n", sname, dname);
> +
> +  xclose (sfd);
> +  xclose (dfd);
> +
> +  if (chmod (dname, st.st_mode & 0777) < 0)
> +    FAIL_EXIT1 ("chmod %s: %s\n", dname, strerror (errno));
> +
> +  times.actime = st.st_atime;
> +  times.modtime = st.st_mtime;
> +  if (utime (dname, &times) < 0)
> +    FAIL_EXIT1 ("utime %s: %s\n", dname, strerror (errno));
> +}

OK.

> +
> +/* We don't check *everything* about the two files to see if a copy is
> +   needed, just the minimum to make sure we get the latest copy.  */
> +static int
> +need_sync (char *ap, char *bp, struct stat *a, struct stat *b)
> +{
> +  if ((a->st_mode & S_IFMT) != (b->st_mode & S_IFMT))
> +    return 1;
> +
> +  if (S_ISLNK (a->st_mode))
> +    {
> +      int rv;
> +      char *al, *bl;
> +
> +      if (a->st_size != b->st_size)
> +	return 1;
> +
> +      al = xreadlink (ap);
> +      bl = xreadlink (bp);
> +      rv = strcmp (al, bl);
> +      free (al);
> +      free (bl);
> +      if (rv == 0)
> +	return 0; /* links are same */
> +      return 1; /* links differ */
> +    }
> +
> +  if (verbose)
> +    {
> +      if (a->st_size != b->st_size)
> +	printf ("SIZE\n");
> +      if ((a->st_mode & 0777) != (b->st_mode & 0777))
> +	printf ("MODE\n");
> +      if (a->st_mtime != b->st_mtime)
> +	printf ("TIME\n");
> +    }
> +
> +  if (a->st_size == b->st_size
> +      && ((a->st_mode & 0777) == (b->st_mode & 0777))
> +      && a->st_mtime == b->st_mtime)
> +    return 0;
> +
> +  return 1;
> +}

OK.

> +
> +static void
> +rsync_1 (path_buf * src, path_buf * dest, int and_delete)
> +{
> +  DIR *dir;
> +  struct dirent *de;
> +  struct stat s, d;
> +
> +  r_append ("/", src);
> +  r_append ("/", dest);
> +
> +  if (verbose)
> +    printf ("sync %s to %s %s\n", src->buf, dest->buf,
> +	    and_delete ? "and delete" : "");
> +
> +  size_t staillen = src->len;
> +
> +  size_t dtaillen = dest->len;
> +
> +  dir = opendir (src->buf);
> +
> +  while ((de = readdir (dir)) != NULL)
> +    {
> +      if (strcmp (de->d_name, ".") == 0
> +	  || strcmp (de->d_name, "..") == 0)
> +	continue;
> +
> +      src->len = staillen;
> +      r_append (de->d_name, src);
> +      dest->len = dtaillen;
> +      r_append (de->d_name, dest);
> +
> +      s.st_mode = ~0;
> +      d.st_mode = ~0;
> +
> +      if (lstat (src->buf, &s) != 0)
> +	FAIL_EXIT1 ("%s obtained by readdir, but stat failed.\n", src->buf);
> +
> +      /* It's OK if this one fails, since we know the file might be
> +	 missing.  */
> +      lstat (dest->buf, &d);
> +
> +      if (! need_sync (src->buf, dest->buf, &s, &d))
> +	{
> +	  if (S_ISDIR (s.st_mode))
> +	    rsync_1 (src, dest, and_delete);
> +	  continue;
> +	}
> +
> +      if (d.st_mode != ~0)
> +	switch (d.st_mode & S_IFMT)
> +	  {
> +	  case S_IFDIR:
> +	    if (!S_ISDIR (s.st_mode))
> +	      {
> +		if (verbose)
> +		  printf ("-D %s\n", dest->buf);
> +		recursive_remove (dest->buf);
> +	      }
> +	    break;
> +
> +	  default:
> +	    if (verbose)
> +	      printf ("-F %s\n", dest->buf);
> +	    maybe_xunlink (dest->buf);
> +	    break;
> +	  }
> +
> +      switch (s.st_mode & S_IFMT)
> +	{
> +	case S_IFREG:
> +	  if (verbose)
> +	    printf ("+F %s\n", dest->buf);
> +	  copy_one_file (src->buf, dest->buf);
> +	  break;
> +
> +	case S_IFDIR:
> +	  if (verbose)
> +	    printf ("+D %s\n", dest->buf);
> +	  maybe_xmkdir (dest->buf, (s.st_mode & 0777) | 0700);
> +	  rsync_1 (src, dest, and_delete);
> +	  break;
> +
> +	case S_IFLNK:
> +	  {
> +	    char *lp;
> +	    if (verbose)
> +	      printf ("+L %s\n", dest->buf);
> +	    lp = xreadlink (src->buf);
> +	    xsymlink (lp, dest->buf);
> +	    free (lp);
> +	    break;
> +	  }
> +
> +	default:
> +	  break;
> +	}
> +    }
> +
> +  closedir (dir);
> +  src->len = staillen;
> +  src->buf[staillen] = 0;
> +  dest->len = dtaillen;
> +  dest->buf[dtaillen] = 0;
> +
> +  if (!and_delete)
> +    return;
> +
> +  /* The rest of this function removes any files/directories in DEST
> +     that do not exist in SRC.  This is triggered as part of a
> +     preclean or postsclean step.  */
> +
> +  dir = opendir (dest->buf);
> +
> +  while ((de = readdir (dir)) != NULL)
> +    {
> +      if (strcmp (de->d_name, ".") == 0
> +	  || strcmp (de->d_name, "..") == 0)
> +	continue;
> +
> +      src->len = staillen;
> +      r_append (de->d_name, src);
> +      dest->len = dtaillen;
> +      r_append (de->d_name, dest);
> +
> +      s.st_mode = ~0;
> +      d.st_mode = ~0;
> +
> +      lstat (src->buf, &s);
> +      
> +      if (lstat (dest->buf, &d) != 0)
> +	FAIL_EXIT1 ("%s obtained by readdir, but stat failed.\n", dest->buf);
> +
> +      if (s.st_mode == ~0)
> +	{
> +	  /* dest exists and src doesn't, clean it.  */
> +	  switch (d.st_mode & S_IFMT)
> +	    {
> +	    case S_IFDIR:
> +	      if (!S_ISDIR (s.st_mode))
> +		{
> +		  if (verbose)
> +		    printf ("-D %s\n", dest->buf);
> +		  recursive_remove (dest->buf);
> +		}
> +	      break;
> +
> +	    default:
> +	      if (verbose)
> +		printf ("-F %s\n", dest->buf);
> +	      maybe_xunlink (dest->buf);
> +	      break;
> +	    }
> +	}
> +    }
> +
> +  closedir (dir);
> +}

OK.

> +
> +static void
> +rsync (char *src, char *dest, int and_delete)
> +{
> +  r_setup (src, &spath);
> +  r_setup (dest, &dpath);
> +
> +  rsync_1 (&spath, &dpath, and_delete);
> +}
> +
> +\f
> +int
> +main (int argc, char **argv)
> +{
> +  pid_t child;
> +  char *pristine_root_path;
> +  char *new_root_path;
> +  char *new_cwd_path;
> +  char *new_objdir_path;
> +  char *new_srcdir_path;
> +  char **new_child_proc;
> +  char *command_root;
> +  char *command_base;
> +  char *command_basename;
> +  char *so_base;
> +  int do_postclean = 0;
> +
> +  uid_t original_uid;
> +  gid_t original_gid;
> +  int UMAP;
> +  int GMAP;
> +  /* Used for "%lld %lld 1" so need not be large.  */
> +  char tmp[100];
> +  struct stat st;
> +  int lock_fd;
> +
> +  setbuf (stdout, NULL);
> +
> +  /* The command line we're expecting looks like this:
> +     env <set some vars> ld.so <library path> test-binary
> +
> +     We need to peel off any "env" or "ld.so" portion of the command
> +     line, and keep track of which env vars we should preserve and
> +     which we drop.  */

OK.

> +
> +  if (argc < 2)
> +    {
> +      fprintf (stderr, "Usage: containerize <program to run> <args...>\n");
> +      exit (1);
> +    }
> +
> +  if (strcmp (argv[1], "-v") == 0)
> +    {
> +      verbose = 1;
> +      ++argv;
> +      --argc;
> +    }
> +
> +  if (strcmp (argv[1], "env") == 0)
> +    {
> +      ++argv;
> +      --argc;
> +      while (is_env_setting (argv[1]))
> +	{
> +	  /* List variables we do NOT want to propogate.  */

> +#if 0
> +	  /* until we discover why locale/iconv tests don't
> +	     work against an installed tree...  */
> +	  if (memcmp (argv[1], "GCONV_PATH=", 11)
> +	      && memcmp (argv[1], "LOCPATH=", 8))
> +#endif

Delete this. When someone tries to convert the first locale/iconv
test we'll deal with it then. I expect that to get this to work you'll
have to create *specific* tests that know to run in a test-container
because otherwise they will specify some odd paths here and it might
work.

> +	    {
> +	      /* Need to keep these.  Note that putenv stores a
> +	         pointer to our argv.  */
> +	      putenv (argv[1]);
> +	    }
> +	  ++argv;
> +	  --argc;
> +	}
> +    }
> +
> +  if (strncmp (argv[1], concat (support_objdir_root, "/elf/ld-linux-", NULL),
> +	       strlen (support_objdir_root) + 14) == 0)
> +    {
> +      ++argv;
> +      --argc;
> +      while (argv[1][0] == '-')
> +	{
> +	  if (strcmp (argv[1], "--library-path") == 0)
> +	    {
> +	      ++argv;
> +	      --argc;
> +	    }
> +	  ++argv;
> +	  --argc;
> +	}
> +    }

OK.

> +
> +  pristine_root_path = strdup (concat (support_objdir_root,
> +				       "/testroot.pristine", NULL));
> +  new_root_path = strdup (concat (support_objdir_root,
> +				  "/testroot.root", NULL));
> +  new_cwd_path = get_current_dir_name ();
> +  new_child_proc = argv + 1;
> +
> +  lock_fd = open (concat (pristine_root_path, "/lock.fd", NULL),
> +		 O_CREAT | O_TRUNC | O_RDWR, 0666);
> +  if (lock_fd < 0)
> +    FAIL_EXIT1 ("Cannot create testroot lock.\n");
> +
> +  while (flock (lock_fd, LOCK_EX) != 0)
> +    {
> +      if (errno != EINTR)
> +	FAIL_EXIT1 ("Cannot lock testroot.\n");
> +    }

OK.

> +
> +  xmkdirp (new_root_path, 0755);
> +
> +  /* We look for extra setup info in a subdir in the same spot as the
> +     test, with the same name but a ".root" extension.  This is that
> +     directory.  We try to look in the source tree if the path we're
> +     given refers to the build tree, but we rely on the path to be
> +     absolute.  This is what the glibc makefiles do.  */
> +  command_root = concat (argv[1], ".root", NULL);
> +  if (strncmp (command_root, support_objdir_root,
> +	       strlen (support_objdir_root)) == 0
> +      && command_root[strlen (support_objdir_root)] == '/')
> +    command_root = concat (support_srcdir_root,
> +			   argv[1] + strlen (support_objdir_root),
> +			   ".root", NULL);
> +  command_root = strdup (command_root);
> +
> +  /* This cuts off the ".root" we appended above.  */
> +  command_base = strdup (command_root);
> +  command_base[strlen (command_base) - 5] = 0;
> +
> +  /* This is the basename of the test we're running.  */
> +  command_basename = strrchr (command_base, '/');
> +  if (command_basename == NULL)
> +    command_basename = command_base;
> +  else
> +    ++command_basename;

OK.

> +
> +  /* Shared object base directory.  */
> +  so_base = strdup (argv[1]);
> +  if (strrchr (so_base, '/') != NULL)
> +    strrchr (so_base, '/')[1] = 0;

OK.

> +
> +  if (file_exists (concat (command_root, "/postclean.req", NULL)))
> +    do_postclean = 1;
> +
> +  rsync (pristine_root_path, new_root_path,
> +	 file_exists (concat (command_root, "/preclean.req", NULL)));
> +
> +  if (stat (command_root, &st) >= 0
> +      && S_ISDIR (st.st_mode))
> +    rsync (command_root, new_root_path, 0);
> +
> +  new_objdir_path = strdup (concat (new_root_path,
> +				    support_objdir_root, NULL));
> +  new_srcdir_path = strdup (concat (new_root_path,
> +				    support_srcdir_root, NULL));
> +
> +  /* new_cwd_path starts with '/' so no "/" needed between the two.  */
> +  xmkdirp (concat (new_root_path, new_cwd_path, NULL), 0755);
> +  xmkdirp (new_srcdir_path, 0755);
> +  xmkdirp (new_objdir_path, 0755);

OK.

> +
> +  original_uid = getuid ();
> +  original_gid = getgid ();
> +
> +  /* Handle the cp/mv/rm "script" here.  */
> +  {
> +    char *the_line = NULL;
> +    size_t line_len = 0;
> +    char *fname = concat (command_root, "/",
> +			  command_basename, ".script", NULL);
> +    char *the_words[3];
> +    FILE *f = fopen (fname, "r");
> +
> +    if (verbose && f)
> +      fprintf (stderr, "running %s\n", fname);

OK.

> +
> +    if (f == NULL)
> +      {
> +	/* Try foo.script instead of foo.root/foo.script, as a shortcut.  */
> +	fname = concat (command_base, ".script", NULL);
> +	f = fopen (fname, "r");
> +	if (verbose && f)
> +	  fprintf (stderr, "running %s\n", fname);
> +      }

OK.

> +
> +    /* Note that we do NOT look for a Makefile-generated foo.script in
> +       the build directory.  If that is ever needed, this is the place
> +       to add it.  */

OK. Right we expect a static set of actions.

> +
> +    /* This is where we "interpret" the mini-script which is <test>.script.  */
> +    if (f != NULL)
> +      {
> +	while (getline (&the_line, &line_len, f) > 0)
> +	  {
> +	    int nt = tokenize (the_line, the_words, 3);
> +	    int i;
> +
> +	    for (i = 1; i < nt; ++i)
> +	      {
> +		if (memcmp (the_words[i], "$B/", 3) == 0)
> +		  the_words[i] = concat (support_objdir_root,
> +					 the_words[i] + 2, NULL);
> +		else if (memcmp (the_words[i], "$S/", 3) == 0)
> +		  the_words[i] = concat (support_srcdir_root,
> +					 the_words[i] + 2, NULL);
> +		else if (memcmp (the_words[i], "$I/", 3) == 0)
> +		  the_words[i] = concat (new_root_path,
> +					 support_install_prefix,
> +					 the_words[i] + 2, NULL);
> +		else if (memcmp (the_words[i], "$L/", 3) == 0)
> +		  the_words[i] = concat (new_root_path,
> +					 support_libdir_prefix,
> +					 the_words[i] + 2, NULL);
> +		else if (the_words[i][0] == '/')
> +		  the_words[i] = concat (new_root_path,
> +					 the_words[i], NULL);

OK.

> +	      }
> +
> +	    if (nt == 3 && the_words[2][strlen (the_words[2]) - 1] == '/')
> +	      {
> +		char *r = strrchr (the_words[1], '/');
> +		if (r)
> +		  the_words[2] = concat (the_words[2], r + 1, NULL);
> +		else
> +		  the_words[2] = concat (the_words[2], the_words[1], NULL);
> +	      }
> +
> +	    if (nt == 2 && strcmp (the_words[0], "so") == 0)
> +	      {
> +		the_words[2] = concat (new_root_path, support_libdir_prefix,
> +				       "/", the_words[1], NULL);
> +		the_words[1] = concat (so_base, the_words[1], NULL);
> +		copy_one_file (the_words[1], the_words[2]);
> +	      }
> +	    else if (nt == 3 && strcmp (the_words[0], "cp") == 0)
> +	      {
> +		copy_one_file (the_words[1], the_words[2]);
> +	      }
> +	    else if (nt == 3 && strcmp (the_words[0], "mv") == 0)
> +	      {
> +		if (rename (the_words[1], the_words[2]) < 0)
> +		  FAIL_EXIT1 ("rename %s -> %s: %s", the_words[1],
> +			      the_words[2], strerror (errno));
> +	      }
> +	    else if (nt == 3 && strcmp (the_words[0], "chmod") == 0)
> +	      {
> +		long int m;
> +		m = strtol (the_words[1], NULL, 0);
> +		if (chmod (the_words[2], m) < 0)
> +		    FAIL_EXIT1 ("chmod %s: %s\n",
> +				the_words[2], strerror (errno));
> +
> +	      }
> +	    else if (nt == 2 && strcmp (the_words[0], "rm") == 0)
> +	      {
> +		maybe_xunlink (the_words[1]);
> +	      }
> +	    else if (nt > 0 && the_words[0][0] != '#')
> +	      {
> +		printf ("\033[31minvalid [%s]\033[0m\n", the_words[0]);
> +	      }
> +	  }
> +	fclose (f);
> +      }
> +  }

OK.

> +
> +#ifdef CLONE_NEWNS
> +  /* The unshare here gives us our own spaces and capabilities.  */
> +  if (unshare (CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNS) < 0)
> +    {
> +      /* Older kernels may not support all the options.  */
> +      if (errno == EINVAL)
> +	FAIL_UNSUPPORTED ("unable to unshare user/fs: %s", strerror (errno));
> +      else
> +	FAIL_EXIT1 ("unable to unshare user/fs: %s", strerror (errno));
> +    }
> +#else
> +  /* Some targets may not support unshare at all.  */
> +  FAIL_UNSUPPORTED ("unshare support missing");

OK. Good, this should solve the Hurd check.

> +#endif
> +
> +  /* Some systems, by default, all mounts leak out of the namespace.  */
> +  if (mount ("none", "/", NULL, MS_REC | MS_PRIVATE, NULL) != 0)
> +    FAIL_EXIT1 ("could not create a private mount namespace\n");
> +
> +  trymount (support_srcdir_root, new_srcdir_path);
> +  trymount (support_objdir_root, new_objdir_path);
> +
> +  xmkdirp (concat (new_root_path, "/dev", NULL), 0755);
> +  devmount (new_root_path, "null");
> +  devmount (new_root_path, "zero");
> +  devmount (new_root_path, "urandom");

OK.

> +
> +  /* We're done with the "old" root, switch to the new one.  */
> +  if (chroot (new_root_path) < 0)
> +    FAIL_EXIT1 ("Can't chroot to %s - ", new_root_path);
> +
> +  if (chdir (new_cwd_path) < 0)
> +    FAIL_EXIT1 ("Can't cd to new %s - ", new_cwd_path);
> +
> +  /* To complete the containerization, we need to fork () at least
> +     once.  We can't exec, nor can we somehow link the new child to
> +     our parent.  So we run the child and propogate it's exit status
> +     up.  */
> +  child = fork ();
> +  if (child < 0)
> +    FAIL_EXIT1 ("Unable to fork");
> +  else if (child > 0)
> +    {
> +      /* Parent.  */
> +      int status;
> +      waitpid (child, &status, 0);
> +
> +      /* There's a bit of magic here, since the buildroot is mounted
> +	 in our space, the paths are still valid, and since the mounts
> +	 aren't recursive, it sees *only* the built root, not anything
> +	 we would normally se if we rsync'd to "/" like mounted /dev
> +	 files.  */
> +      if (do_postclean)
> +	  rsync (pristine_root_path, new_root_path, 1);
> +
> +      if (WIFEXITED (status))
> +	exit (WEXITSTATUS (status));
> +
> +      if (WIFSIGNALED (status))
> +	{
> +	  printf ("%%SIGNALLED%%\n");
> +	  exit (77);
> +	}
> +
> +      printf ("%%EXITERROR%%\n");
> +      exit (78);
> +    }
> +
> +  /* The rest is the child process, which is now PID 1 and "in" the
> +     new root.  */
> +
> +  maybe_xmkdir ("/tmp", 0755);

OK.

> +
> +  /* Now that we're pid 1 (effectively "root") we can mount /proc  */
> +  maybe_xmkdir ("/proc", 0777);
> +  if (mount ("proc", "/proc", "proc", 0, NULL) < 0)
> +    FAIL_EXIT1 ("Unable to mount /proc: ");
> +
> +  /* We map our original UID to the same UID in the container so we
> +     can own our own files normally.  */
> +  UMAP = open ("/proc/self/uid_map", O_WRONLY);
> +  if (UMAP < 0)
> +    FAIL_EXIT1 ("can't write to /proc/self/uid_map\n");
> +
> +  sprintf (tmp, "%lld %lld 1\n",
> +	   (long long) original_uid, (long long) original_uid);
> +  write (UMAP, tmp, strlen (tmp));
> +  xclose (UMAP);
> +
> +  /* We must disable setgroups () before we can map our groups, else we
> +     get EPERM.  */
> +  GMAP = open ("/proc/self/setgroups", O_WRONLY);
> +  if (GMAP >= 0)
> +    {
> +      /* We support kernels old enough to not have this.  */
> +      write (GMAP, "deny\n", 5);
> +      xclose (GMAP);
> +    }
> +
> +  /* We map our original GID to the same GID in the container so we
> +     can own our own files normally.  */
> +  GMAP = open ("/proc/self/gid_map", O_WRONLY);
> +  if (GMAP < 0)
> +    FAIL_EXIT1 ("can't write to /proc/self/gid_map\n");
> +
> +  sprintf (tmp, "%lld %lld 1\n",
> +	   (long long) original_gid, (long long) original_gid);
> +  write (GMAP, tmp, strlen (tmp));
> +  xclose (GMAP);
> +
> +  /* Now run the child.  */
> +  execvp (new_child_proc[0], new_child_proc);
> +
> +  /* Or don't run the child?  */
> +  FAIL_EXIT1 ("Unable to exec %s\n", new_child_proc[0]);
> +
> +  /* Because gcc won't know error () never returns...  */
> +  exit (EXIT_UNSUPPORTED);
> +}

OK.

> diff --git a/support/true-container.c b/support/true-container.c
> new file mode 100644
> index 0000000000..57dc57e252
> --- /dev/null
> +++ b/support/true-container.c
> @@ -0,0 +1,26 @@
> +/* Minimal /bin/true for in-container use.
> +   Copyright (C) 2018 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <http://www.gnu.org/licenses/>.  */
> +
> +/* Implements the in-container /bin/true, which always returns true
> +   (0).  */
> +
> +int
> +main (void)
> +{
> +  return 0;
> +}

OK.

> diff --git a/support/xmkdirp.c b/support/xmkdirp.c
> new file mode 100644
> index 0000000000..fada0452ea
> --- /dev/null
> +++ b/support/xmkdirp.c
> @@ -0,0 +1,66 @@
> +/* Error-checking replacement for "mkdir -p".

OK.

> +   Copyright (C) 2018 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <http://www.gnu.org/licenses/>.  */
> +
> +#include <support/support.h>
> +#include <support/check.h>
> +#include <support/xunistd.h>
> +
> +#include <stdlib.h>
> +#include <string.h>
> +#include <errno.h>
> +
> +/* Equivalent of "mkdir -p".  Any failures cause FAIL_EXIT1 so no
> +   return code is needed.  */
> +
> +void
> +xmkdirp (const char *path, mode_t mode)
> +{
> +  struct stat s;
> +  const char *slash_p;
> +  int rv;
> +
> +  if (path[0] == 0)
> +    return;
> +
> +  if (stat (path, &s) == 0)
> +    {
> +      if (S_ISDIR (s.st_mode))
> +	return;
> +      errno = EEXIST;
> +      FAIL_EXIT1 ("mkdir_p (\"%s\", 0%o): %m", path, mode);
> +    }
> +
> +  slash_p = strrchr (path, '/');
> +  if (slash_p != NULL)
> +    {
> +      while (slash_p > path && slash_p[-1] == '/')
> +	--slash_p;
> +      if (slash_p > path)
> +	{
> +	  char *parent = xstrndup (path, slash_p - path);
> +	  xmkdirp (parent, mode);
> +	  free (parent);
> +	}
> +    }
> +
> +  rv = mkdir (path, mode);
> +  if (rv != 0)
> +    FAIL_EXIT1 ("mkdir_p (\"%s\", 0%o): %m", path, mode);
> +
> +  return;
> +}

OK.

> diff --git a/support/xsymlink.c b/support/xsymlink.c
> new file mode 100644
> index 0000000000..0f3edf640a
> --- /dev/null
> +++ b/support/xsymlink.c
> @@ -0,0 +1,29 @@
> +/* Error-checking replacement for "symlink".
> +   Copyright (C) 2018 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <http://www.gnu.org/licenses/>.  */
> +
> +#include <support/support.h>
> +#include <support/check.h>
> +
> +#include <unistd.h>
> +
> +void
> +xsymlink (const char *target, const char *linkpath)
> +{
> +  if (symlink (target, linkpath) < 0)
> +    FAIL_EXIT1 ("symlink (\"%s\", \"%s\")", target, linkpath);
> +}

OK.

> diff --git a/support/xunistd.h b/support/xunistd.h
> index 5fe5dae818..cdd4e8d92d 100644
> --- a/support/xunistd.h
> +++ b/support/xunistd.h
> @@ -43,6 +43,10 @@ void xunlink (const char *path);
>  long xsysconf (int name);
>  long long xlseek (int fd, long long offset, int whence);
>  void xftruncate (int fd, long long length);
> +void xsymlink (const char *target, const char *linkpath);
> +
> +/* Equivalent of "mkdir -p".  */
> +void xmkdirp (const char *, mode_t);

OK.

>  
>  /* Read the link at PATH.  The caller should free the returned string
>     with free.  */
> 


-- 
Cheers,
Carlos.

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: V8 test-in-container patch
  2018-08-17  2:53 ` Carlos O'Donell
@ 2018-08-23  1:21   ` DJ Delorie
  2018-08-23 19:58     ` Joseph Myers
  0 siblings, 1 reply; 25+ messages in thread
From: DJ Delorie @ 2018-08-23  1:21 UTC (permalink / raw)
  To: Carlos O'Donell; +Cc: libc-alpha, fweimer, joseph


"Carlos O'Donell" <carlos@redhat.com> writes:
> OK for master with:

And having waited a few days for other comments, too...

Committed!  Thanks to all for reviews!

> - Review suggested text and accept or reject with rationale.

Accepted.  Thanks!

> - Fix error string typo in run_command_array.

Fixed.

> - Delete #if 0/#endif iconv/gconv code.

Deleted.

> - Successful build-many-glibcs run.

That took a while (because b-m-g takes a long time), and needed some
minor tweaks to handle the bootstrap step in build-many-glibcs.
Successful run with x86-linux and i686-hurd.  The Hurd maintainer will
need to provide suitable magic to containerize a test, later :-)

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: V8 test-in-container patch
  2018-08-23  1:21   ` DJ Delorie
@ 2018-08-23 19:58     ` Joseph Myers
  2018-08-23 20:34       ` DJ Delorie
  0 siblings, 1 reply; 25+ messages in thread
From: Joseph Myers @ 2018-08-23 19:58 UTC (permalink / raw)
  To: DJ Delorie; +Cc: Carlos O'Donell, libc-alpha, fweimer

This appears to have broken the build for ia64.

https://sourceware.org/ml/libc-testresults/2018-q3/msg00273.html

/scratch/jmyers/glibc-bot/install/compilers/ia64-linux-gnu/lib/gcc/ia64-glibc-linux-gnu/8.2.1/../../../../ia64-glibc-linux-gnu/bin/ld: warning: libunwind.so.7, needed by /scratch/jmyers/glibc-bot/install/compilers/ia64-linux-gnu/lib/gcc/ia64-glibc-linux-gnu/8.2.1/../../../../ia64-glibc-linux-gnu/lib/libstdc++.so, not found (try using -rpath or -rpath-link)

That's linking links-dso-program (why is that being built at glibc build 
time at all anyway, rather than just when testing)?  If using -lgcc_s you 
also need to use $(libunwind).

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: V8 test-in-container patch
  2018-08-23 19:58     ` Joseph Myers
@ 2018-08-23 20:34       ` DJ Delorie
  2018-08-23 20:43         ` Joseph Myers
  0 siblings, 1 reply; 25+ messages in thread
From: DJ Delorie @ 2018-08-23 20:34 UTC (permalink / raw)
  To: Joseph Myers; +Cc: carlos, libc-alpha, fweimer

Joseph Myers <joseph@codesourcery.com> writes:
> That's linking links-dso-program (why is that being built at glibc build 
> time at all anyway, rather than just when testing)?

Because our Makefiles are confusing, and that's the only target I could
find that was built early enough and had the magic to find crtn.o during
build-many-glibcs.

> If using -lgcc_s you also need to use $(libunwind).

Ah, thanks.  As I mentioned, Carlos and I are working on this.  If we
can find a way to defer building that, it should solve these types of
problems.

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: V8 test-in-container patch
  2018-08-23 20:34       ` DJ Delorie
@ 2018-08-23 20:43         ` Joseph Myers
  2018-08-23 22:57           ` DJ Delorie
  0 siblings, 1 reply; 25+ messages in thread
From: Joseph Myers @ 2018-08-23 20:43 UTC (permalink / raw)
  To: DJ Delorie; +Cc: carlos, libc-alpha, fweimer

On Thu, 23 Aug 2018, DJ Delorie wrote:

> > If using -lgcc_s you also need to use $(libunwind).
> 
> Ah, thanks.  As I mentioned, Carlos and I are working on this.  If we
> can find a way to defer building that, it should solve these types of
> problems.

I expect that even if this build gets deferred to when the testsuite is 
run, you'll still need to use $(libunwind) (given that all the links done 
in glibc build and testing are explicitly using -nostdlib then listing the 
required libraries, to avoid any pre-existing system libc getting 
involved, so you're losing GCC's built-in knowledge of when to use 
-lunwind).

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: V8 test-in-container patch
  2018-08-23 20:43         ` Joseph Myers
@ 2018-08-23 22:57           ` DJ Delorie
  2018-08-23 23:31             ` Joseph Myers
  0 siblings, 1 reply; 25+ messages in thread
From: DJ Delorie @ 2018-08-23 22:57 UTC (permalink / raw)
  To: Joseph Myers; +Cc: carlos, libc-alpha, fweimer

Joseph Myers <joseph@codesourcery.com> writes:
> I expect that even if this build gets deferred to when the testsuite is 
> run, you'll still need to use $(libunwind)

Sure, but I'm thinking it also might be a piece of the bootstrap puzzle
that we don't (or shouldn't) need to worry about.

How's this patch look?  The various macros seem undocumented, but this
looks like what the various test linkers are using.

diff --git a/support/Makefile b/support/Makefile
index b5fcb6ed86..166420de70 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -165,18 +165,14 @@ ifeq (,$(CXX))
 LINKS_DSO_PROGRAM = links-dso-program-c
 else
 LINKS_DSO_PROGRAM = links-dso-program
-LDLIBS-links-dso-program = -lstdc++ -lgcc_s
+LDLIBS-links-dso-program = -lstdc++ $(gnulib-tests)
 endif
 

(yes, I know it won't apply cleanly, it's part of a bigger mess I'm
working with ;)

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: V8 test-in-container patch
  2018-08-23 22:57           ` DJ Delorie
@ 2018-08-23 23:31             ` Joseph Myers
  2018-08-23 23:57               ` DJ Delorie
  0 siblings, 1 reply; 25+ messages in thread
From: Joseph Myers @ 2018-08-23 23:31 UTC (permalink / raw)
  To: DJ Delorie; +Cc: carlos, libc-alpha, fweimer

On Thu, 23 Aug 2018, DJ Delorie wrote:

> Joseph Myers <joseph@codesourcery.com> writes:
> > I expect that even if this build gets deferred to when the testsuite is 
> > run, you'll still need to use $(libunwind)
> 
> Sure, but I'm thinking it also might be a piece of the bootstrap puzzle
> that we don't (or shouldn't) need to worry about.
> 
> How's this patch look?  The various macros seem undocumented, but this
> looks like what the various test linkers are using.
> 
> diff --git a/support/Makefile b/support/Makefile
> index b5fcb6ed86..166420de70 100644
> --- a/support/Makefile
> +++ b/support/Makefile
> @@ -165,18 +165,14 @@ ifeq (,$(CXX))
>  LINKS_DSO_PROGRAM = links-dso-program-c
>  else
>  LINKS_DSO_PROGRAM = links-dso-program
> -LDLIBS-links-dso-program = -lstdc++ -lgcc_s
> +LDLIBS-links-dso-program = -lstdc++ $(gnulib-tests)

I think the question there is whether the --as-needed in $(gnulib-tests) 
is an issue for this purpose (whether you want the libraries linked in 
even if not used for anything).  Maybe in this case it doesn't matter 
because the libraries in question will in fact always be used.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: V8 test-in-container patch
  2018-08-23 23:31             ` Joseph Myers
@ 2018-08-23 23:57               ` DJ Delorie
  2018-08-24 14:41                 ` Joseph Myers
  0 siblings, 1 reply; 25+ messages in thread
From: DJ Delorie @ 2018-08-23 23:57 UTC (permalink / raw)
  To: Joseph Myers; +Cc: carlos, libc-alpha, fweimer

Joseph Myers <joseph@codesourcery.com> writes:
> I think the question there is whether the --as-needed in $(gnulib-tests) 
> is an issue for this purpose (whether you want the libraries linked in 
> even if not used for anything).  Maybe in this case it doesn't matter 
> because the libraries in question will in fact always be used.

Maybe, but good point... the purpose of that test binary is to include
anything that a test case might need, so we want to be paranoid.  How
about this then?  I hate to manually list so many things, but it looks
inevitable.

LDLIBS-links-dso-program = -lstdc++ -lgcc -lgcc_s $(libunwind)

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: V8 test-in-container patch
  2018-08-23 23:57               ` DJ Delorie
@ 2018-08-24 14:41                 ` Joseph Myers
  2018-08-25  2:12                   ` DJ Delorie
  0 siblings, 1 reply; 25+ messages in thread
From: Joseph Myers @ 2018-08-24 14:41 UTC (permalink / raw)
  To: DJ Delorie; +Cc: carlos, libc-alpha, fweimer

On Thu, 23 Aug 2018, DJ Delorie wrote:

> Joseph Myers <joseph@codesourcery.com> writes:
> > I think the question there is whether the --as-needed in $(gnulib-tests) 
> > is an issue for this purpose (whether you want the libraries linked in 
> > even if not used for anything).  Maybe in this case it doesn't matter 
> > because the libraries in question will in fact always be used.
> 
> Maybe, but good point... the purpose of that test binary is to include
> anything that a test case might need, so we want to be paranoid.  How
> about this then?  I hate to manually list so many things, but it looks
> inevitable.
> 
> LDLIBS-links-dso-program = -lstdc++ -lgcc -lgcc_s $(libunwind)

I think that's the correct list.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: V8 test-in-container patch
  2018-08-24 14:41                 ` Joseph Myers
@ 2018-08-25  2:12                   ` DJ Delorie
  2018-08-25  2:22                     ` Carlos O'Donell
  0 siblings, 1 reply; 25+ messages in thread
From: DJ Delorie @ 2018-08-25  2:12 UTC (permalink / raw)
  To: Joseph Myers; +Cc: carlos, libc-alpha, fweimer

Joseph Myers <joseph@codesourcery.com> writes:
>> LDLIBS-links-dso-program = -lstdc++ -lgcc -lgcc_s $(libunwind)
>
> I think that's the correct list.

Confirmed via testing and committed.

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: V8 test-in-container patch
  2018-08-25  2:12                   ` DJ Delorie
@ 2018-08-25  2:22                     ` Carlos O'Donell
  2018-08-25  2:29                       ` DJ Delorie
  0 siblings, 1 reply; 25+ messages in thread
From: Carlos O'Donell @ 2018-08-25  2:22 UTC (permalink / raw)
  To: DJ Delorie, Joseph Myers; +Cc: libc-alpha, fweimer

On 08/24/2018 10:12 PM, DJ Delorie wrote:
> Joseph Myers <joseph@codesourcery.com> writes:
>>> LDLIBS-links-dso-program = -lstdc++ -lgcc -lgcc_s $(libunwind)
>>
>> I think that's the correct list.
> 
> Confirmed via testing and committed.
> 

Does this mean build-many-glibcs test run for ia64 passes now?

-- 
Cheers,
Carlos.

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: V8 test-in-container patch
  2018-08-25  2:22                     ` Carlos O'Donell
@ 2018-08-25  2:29                       ` DJ Delorie
  2018-08-25  2:40                         ` Carlos O'Donell
       [not found]                         ` <CAP5F8c+0EjtjaOSf5itJxx385r70XrjGEDWMvsdf8QDmOVVDRA@mail.gmail.com>
  0 siblings, 2 replies; 25+ messages in thread
From: DJ Delorie @ 2018-08-25  2:29 UTC (permalink / raw)
  To: Carlos O'Donell; +Cc: joseph, libc-alpha, fweimer


"Carlos O'Donell" <carlos@redhat.com> writes:
> Does this mean build-many-glibcs test run for ia64 passes now?

Insofaras the IA64 compiler doesn't complain about missing symbols or
libraries, yes.  The bootstrap problem is still sortof there, wrt the
building of C++ libraries vs the presence of a C++ compiler.  IMHO we
still need to figure out a way to defer building links-dso-program until
we know we're going to run the testsuite.  The problem with that, is
that it must be run before anything uses the test container, and we
don't seem to have an easy way to trigger that in our Makefile rules.

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: V8 test-in-container patch
  2018-08-25  2:29                       ` DJ Delorie
@ 2018-08-25  2:40                         ` Carlos O'Donell
       [not found]                         ` <CAP5F8c+0EjtjaOSf5itJxx385r70XrjGEDWMvsdf8QDmOVVDRA@mail.gmail.com>
  1 sibling, 0 replies; 25+ messages in thread
From: Carlos O'Donell @ 2018-08-25  2:40 UTC (permalink / raw)
  To: DJ Delorie; +Cc: joseph, libc-alpha, fweimer

On 08/24/2018 10:28 PM, DJ Delorie wrote:
> 
> "Carlos O'Donell" <carlos@redhat.com> writes:
>> Does this mean build-many-glibcs test run for ia64 passes now?
> 
> Insofaras the IA64 compiler doesn't complain about missing symbols or
> libraries, yes.  The bootstrap problem is still sortof there, wrt the
> building of C++ libraries vs the presence of a C++ compiler.  IMHO we
> still need to figure out a way to defer building links-dso-program until
> we know we're going to run the testsuite.  The problem with that, is
> that it must be run before anything uses the test container, and we
> don't seem to have an easy way to trigger that in our Makefile rules.

Agreed, we'll keep working on that to solve Steve Ellcey's problem.

-- 
Cheers,
Carlos.

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: V8 test-in-container patch
       [not found]                         ` <CAP5F8c+0EjtjaOSf5itJxx385r70XrjGEDWMvsdf8QDmOVVDRA@mail.gmail.com>
@ 2018-08-25  2:56                           ` Carlos O'Donell
  2018-08-25  2:58                             ` Jason Duerstock
  2018-08-27 15:07                             ` Joseph Myers
  0 siblings, 2 replies; 25+ messages in thread
From: Carlos O'Donell @ 2018-08-25  2:56 UTC (permalink / raw)
  To: Jason Duerstock, dj; +Cc: joseph, GNU C Library, Florian Weimer

On 08/24/2018 10:52 PM, Jason Duerstock wrote:
> Apologies if I am asking in the wrong place, but what is involved in
> removing ia64's dependency on libunwind?

This needs to be discussed with the ia64 gcc maintainer (Jim Wilson?)
on the gcc mailing list.

Most architectures provide an unwinder in libgcc without any external
dependencies.

Likely what's involved is adding an unwinder to libgcc which works for
ia64 and doesn't depend on any external library.

I hope that answers your question.

-- 
Cheers,
Carlos.

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: V8 test-in-container patch
  2018-08-25  2:56                           ` Carlos O'Donell
@ 2018-08-25  2:58                             ` Jason Duerstock
  2018-08-27 15:07                             ` Joseph Myers
  1 sibling, 0 replies; 25+ messages in thread
From: Jason Duerstock @ 2018-08-25  2:58 UTC (permalink / raw)
  To: carlos; +Cc: dj, joseph, GNU C Library, Florian Weimer

It does, thank you.

On Fri, Aug 24, 2018 at 10:56 PM Carlos O'Donell <carlos@redhat.com> wrote:
>
> On 08/24/2018 10:52 PM, Jason Duerstock wrote:
> > Apologies if I am asking in the wrong place, but what is involved in
> > removing ia64's dependency on libunwind?
>
> This needs to be discussed with the ia64 gcc maintainer (Jim Wilson?)
> on the gcc mailing list.
>
> Most architectures provide an unwinder in libgcc without any external
> dependencies.
>
> Likely what's involved is adding an unwinder to libgcc which works for
> ia64 and doesn't depend on any external library.
>
> I hope that answers your question.
>
> --
> Cheers,
> Carlos.

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: V8 test-in-container patch
  2018-08-25  2:56                           ` Carlos O'Donell
  2018-08-25  2:58                             ` Jason Duerstock
@ 2018-08-27 15:07                             ` Joseph Myers
  1 sibling, 0 replies; 25+ messages in thread
From: Joseph Myers @ 2018-08-27 15:07 UTC (permalink / raw)
  To: Carlos O'Donell; +Cc: Jason Duerstock, dj, GNU C Library, Florian Weimer

On Fri, 24 Aug 2018, Carlos O'Donell wrote:

> On 08/24/2018 10:52 PM, Jason Duerstock wrote:
> > Apologies if I am asking in the wrong place, but what is involved in
> > removing ia64's dependency on libunwind?
> 
> This needs to be discussed with the ia64 gcc maintainer (Jim Wilson?)
> on the gcc mailing list.
> 
> Most architectures provide an unwinder in libgcc without any external
> dependencies.
> 
> Likely what's involved is adding an unwinder to libgcc which works for
> ia64 and doesn't depend on any external library.

That's the configuration we use for building GCC for ia64 in 
build-many-glibcs.py: the first (bootstrap) GCC is configured with 
--with-system-libunwind (meaning GCC doesn't try to build libunwind 
because that doesn't work before libc is built), but the second GCC is 
configured without that option (meaning GCC builds and installs its own 
libunwind).

Maybe the conventional approach used by distributions for ia64 is/was to 
use --with-system-libunwind always, with a separately built libunwind, but 
you *can* build libunwind from libgcc simply by not passing 
--with-system-libunwind.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: V8 test-in-container patch
  2018-08-16 17:59 V8 test-in-container patch DJ Delorie
  2018-08-16 18:54 ` Florian Weimer
  2018-08-17  2:53 ` Carlos O'Donell
@ 2018-10-09  9:55 ` Szabolcs Nagy
  2018-10-09 15:26   ` Carlos O'Donell
  2 siblings, 1 reply; 25+ messages in thread
From: Szabolcs Nagy @ 2018-10-09  9:55 UTC (permalink / raw)
  To: DJ Delorie, libc-alpha; +Cc: nd, fweimer, Joseph Myers, carlos

On 16/08/18 18:59, DJ Delorie wrote:
> +# The intention here is to do ONE install of our build into the
> +# testroot.pristine/ directory, then rsync (internal to
> +# support/test-container) that to testroot.root/ at the start of each
> +# test.  That way we can promise each test a "clean" install, without
> +# having to do the install for each test.
> +#
> +# In addition, we have to copy some files (which we build) into this
> +# root in addition to what glibc installs.  For example, many tests
> +# require /bin/sh be present, and any shared objects that /bin/sh
> +# depends on.  We also build a "test" program in either C or (if
> +# available) C++ just so we can copy in any shared objects (which we
> +# do not build) that GCC-compiled programs depend on.
> +
> +$(tests-container) $(addsuffix /tests,$(subdirs)) : \
> +		$(objpfx)testroot.pristine/install.stamp
> +$(objpfx)testroot.pristine/install.stamp :
> +	test -d $(objpfx)testroot.pristine || \
> +	  mkdir $(objpfx)testroot.pristine
> +	# We need a working /bin/sh for some of the tests.
> +	test -d $(objpfx)testroot.pristine/bin || \
> +	  mkdir $(objpfx)testroot.pristine/bin
> +	cp $(objpfx)support/shell-container $(objpfx)testroot.pristine/bin/sh
> +	cp $(objpfx)support/echo-container $(objpfx)testroot.pristine/bin/echo
> +	cp $(objpfx)support/true-container $(objpfx)testroot.pristine/bin/true
> +	# Copy these DSOs first so we can overwrite them with our own.
> +	for dso in `$(test-wrapper-env) LD_TRACE_LOADED_OBJECTS=1  \
> +		$(objpfx)elf/$(rtld-installed-name) \
> +		$(objpfx)testroot.pristine/bin/sh \
> +	        | grep / | sed 's/^[^/]*//' | sed 's/ .*//'` ;\
> +	  do \
> +	    test -d `dirname $(objpfx)testroot.pristine$$dso` || \
> +	      mkdir -p `dirname $(objpfx)testroot.pristine$$dso` ;\
> +	    $(test-wrapper) cp $$dso $(objpfx)testroot.pristine$$dso ;\
> +	  done

in the aarch64 buildbot log i see

for dso in ` env LD_TRACE_LOADED_OBJECTS=1  \
	/home/szabolcs/tx1-ubuntu-aarch64/glibc-aarch64-linux/build/build/elf/ld-linux-aarch64.so.1 \
	/home/szabolcs/tx1-ubuntu-aarch64/glibc-aarch64-linux/build/build/testroot.pristine/bin/sh \
        | grep / | sed 's/^[^/]*//' | sed 's/ .*//'` ;\
  do \
...
/home/szabolcs/tx1-ubuntu-aarch64/glibc-aarch64-linux/build/build/testroot.pristine/bin/sh: /lib/aarch64-linux-gnu/libc.so.6: version
`GLIBC_2.27' not found (required by /home/szabolcs/tx1-ubuntu-aarch64/glibc-aarch64-linux/build/build/testroot.pristine/bin/sh)

i think some --library-path should be passed here.

> +	for dso in `$(test-wrapper-env) LD_TRACE_LOADED_OBJECTS=1  \
> +		$(objpfx)elf/$(rtld-installed-name) \
> +		$(objpfx)support/links-dso-program \
> +	        | grep / | sed 's/^[^/]*//' | sed 's/ .*//'` ;\
> +	  do \
> +	    test -d `dirname $(objpfx)testroot.pristine$$dso` || \
> +	      mkdir -p `dirname $(objpfx)testroot.pristine$$dso` ;\
> +	    $(test-wrapper) cp $$dso $(objpfx)testroot.pristine$$dso ;\
> +	  done
> +	$(MAKE) install DESTDIR=$(objpfx)testroot.pristine
> +	touch $(objpfx)testroot.pristine/install.stamp
> +

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: V8 test-in-container patch
  2018-10-09  9:55 ` Szabolcs Nagy
@ 2018-10-09 15:26   ` Carlos O'Donell
  2018-10-10 10:35     ` Szabolcs Nagy
  0 siblings, 1 reply; 25+ messages in thread
From: Carlos O'Donell @ 2018-10-09 15:26 UTC (permalink / raw)
  To: Szabolcs Nagy, DJ Delorie, libc-alpha; +Cc: nd, fweimer, Joseph Myers

On 10/9/18 5:27 AM, Szabolcs Nagy wrote:
> On 16/08/18 18:59, DJ Delorie wrote:
>> +# The intention here is to do ONE install of our build into the
>> +# testroot.pristine/ directory, then rsync (internal to
>> +# support/test-container) that to testroot.root/ at the start of each
>> +# test.  That way we can promise each test a "clean" install, without
>> +# having to do the install for each test.
>> +#
>> +# In addition, we have to copy some files (which we build) into this
>> +# root in addition to what glibc installs.  For example, many tests
>> +# require /bin/sh be present, and any shared objects that /bin/sh
>> +# depends on.  We also build a "test" program in either C or (if
>> +# available) C++ just so we can copy in any shared objects (which we
>> +# do not build) that GCC-compiled programs depend on.
>> +
>> +$(tests-container) $(addsuffix /tests,$(subdirs)) : \
>> +		$(objpfx)testroot.pristine/install.stamp
>> +$(objpfx)testroot.pristine/install.stamp :
>> +	test -d $(objpfx)testroot.pristine || \
>> +	  mkdir $(objpfx)testroot.pristine
>> +	# We need a working /bin/sh for some of the tests.
>> +	test -d $(objpfx)testroot.pristine/bin || \
>> +	  mkdir $(objpfx)testroot.pristine/bin
>> +	cp $(objpfx)support/shell-container $(objpfx)testroot.pristine/bin/sh
>> +	cp $(objpfx)support/echo-container $(objpfx)testroot.pristine/bin/echo
>> +	cp $(objpfx)support/true-container $(objpfx)testroot.pristine/bin/true
>> +	# Copy these DSOs first so we can overwrite them with our own.
>> +	for dso in `$(test-wrapper-env) LD_TRACE_LOADED_OBJECTS=1  \
>> +		$(objpfx)elf/$(rtld-installed-name) \
>> +		$(objpfx)testroot.pristine/bin/sh \
>> +	        | grep / | sed 's/^[^/]*//' | sed 's/ .*//'` ;\
>> +	  do \
>> +	    test -d `dirname $(objpfx)testroot.pristine$$dso` || \
>> +	      mkdir -p `dirname $(objpfx)testroot.pristine$$dso` ;\
>> +	    $(test-wrapper) cp $$dso $(objpfx)testroot.pristine$$dso ;\
>> +	  done
> 
> in the aarch64 buildbot log i see
> 
> for dso in ` env LD_TRACE_LOADED_OBJECTS=1  \
> 	/home/szabolcs/tx1-ubuntu-aarch64/glibc-aarch64-linux/build/build/elf/ld-linux-aarch64.so.1 \
> 	/home/szabolcs/tx1-ubuntu-aarch64/glibc-aarch64-linux/build/build/testroot.pristine/bin/sh \
>         | grep / | sed 's/^[^/]*//' | sed 's/ .*//'` ;\
>   do \
> ...
> /home/szabolcs/tx1-ubuntu-aarch64/glibc-aarch64-linux/build/build/testroot.pristine/bin/sh: /lib/aarch64-linux-gnu/libc.so.6: version
> `GLIBC_2.27' not found (required by /home/szabolcs/tx1-ubuntu-aarch64/glibc-aarch64-linux/build/build/testroot.pristine/bin/sh)
> 
> i think some --library-path should be passed here.

Agreed. There should be a --library-path to the main build directory
where libc.so.6 is present, otherwise it won't match the loader.

...
	for dso in `$(test-wrapper-env) LD_TRACE_LOADED_OBJECTS=1  \
		$(objpfx)elf/$(rtld-installed-name) \
		--library-path $(objpfx):$(objpfx)/math:... \
                ^^^^^^^^^^^^^^^^^^^^^^^^^^
                Adds lookup for libc.so.6 and all other libraries.


		$(objpfx)testroot.pristine/bin/sh \
	        | grep / | sed 's/^[^/]*//' | sed 's/ .*//'` ;\
	  do \
...

The list of lookup paths needs to be dynamic and you can probably
crib the way testrun.sh knows, which I think is $(test-program-prefix)?

>> +	for dso in `$(test-wrapper-env) LD_TRACE_LOADED_OBJECTS=1  \
>> +		$(objpfx)elf/$(rtld-installed-name) \
>> +		$(objpfx)support/links-dso-program \
>> +	        | grep / | sed 's/^[^/]*//' | sed 's/ .*//'` ;\
>> +	  do \
>> +	    test -d `dirname $(objpfx)testroot.pristine$$dso` || \
>> +	      mkdir -p `dirname $(objpfx)testroot.pristine$$dso` ;\
>> +	    $(test-wrapper) cp $$dso $(objpfx)testroot.pristine$$dso ;\
>> +	  done
>> +	$(MAKE) install DESTDIR=$(objpfx)testroot.pristine
>> +	touch $(objpfx)testroot.pristine/install.stamp
>> +


-- 
Cheers,
Carlos.

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: V8 test-in-container patch
  2018-10-09 15:26   ` Carlos O'Donell
@ 2018-10-10 10:35     ` Szabolcs Nagy
  2018-10-10 20:16       ` DJ Delorie
  0 siblings, 1 reply; 25+ messages in thread
From: Szabolcs Nagy @ 2018-10-10 10:35 UTC (permalink / raw)
  To: Carlos O'Donell, DJ Delorie, libc-alpha; +Cc: nd, fweimer, Joseph Myers

On 09/10/18 16:21, Carlos O'Donell wrote:
> On 10/9/18 5:27 AM, Szabolcs Nagy wrote:
>> On 16/08/18 18:59, DJ Delorie wrote:
>>> +# The intention here is to do ONE install of our build into the
>>> +# testroot.pristine/ directory, then rsync (internal to
>>> +# support/test-container) that to testroot.root/ at the start of each
>>> +# test.  That way we can promise each test a "clean" install, without
>>> +# having to do the install for each test.
>>> +#
>>> +# In addition, we have to copy some files (which we build) into this
>>> +# root in addition to what glibc installs.  For example, many tests
>>> +# require /bin/sh be present, and any shared objects that /bin/sh
>>> +# depends on.  We also build a "test" program in either C or (if
>>> +# available) C++ just so we can copy in any shared objects (which we
>>> +# do not build) that GCC-compiled programs depend on.
>>> +
>>> +$(tests-container) $(addsuffix /tests,$(subdirs)) : \
>>> +		$(objpfx)testroot.pristine/install.stamp
>>> +$(objpfx)testroot.pristine/install.stamp :
>>> +	test -d $(objpfx)testroot.pristine || \
>>> +	  mkdir $(objpfx)testroot.pristine
>>> +	# We need a working /bin/sh for some of the tests.
>>> +	test -d $(objpfx)testroot.pristine/bin || \
>>> +	  mkdir $(objpfx)testroot.pristine/bin
>>> +	cp $(objpfx)support/shell-container $(objpfx)testroot.pristine/bin/sh
>>> +	cp $(objpfx)support/echo-container $(objpfx)testroot.pristine/bin/echo
>>> +	cp $(objpfx)support/true-container $(objpfx)testroot.pristine/bin/true
>>> +	# Copy these DSOs first so we can overwrite them with our own.
>>> +	for dso in `$(test-wrapper-env) LD_TRACE_LOADED_OBJECTS=1  \
>>> +		$(objpfx)elf/$(rtld-installed-name) \
>>> +		$(objpfx)testroot.pristine/bin/sh \
>>> +	        | grep / | sed 's/^[^/]*//' | sed 's/ .*//'` ;\
>>> +	  do \
>>> +	    test -d `dirname $(objpfx)testroot.pristine$$dso` || \
>>> +	      mkdir -p `dirname $(objpfx)testroot.pristine$$dso` ;\
>>> +	    $(test-wrapper) cp $$dso $(objpfx)testroot.pristine$$dso ;\
>>> +	  done
>>
>> in the aarch64 buildbot log i see
>>
>> for dso in ` env LD_TRACE_LOADED_OBJECTS=1  \
>> 	/home/szabolcs/tx1-ubuntu-aarch64/glibc-aarch64-linux/build/build/elf/ld-linux-aarch64.so.1 \
>> 	/home/szabolcs/tx1-ubuntu-aarch64/glibc-aarch64-linux/build/build/testroot.pristine/bin/sh \
>>         | grep / | sed 's/^[^/]*//' | sed 's/ .*//'` ;\
>>   do \
>> ...
>> /home/szabolcs/tx1-ubuntu-aarch64/glibc-aarch64-linux/build/build/testroot.pristine/bin/sh: /lib/aarch64-linux-gnu/libc.so.6: version
>> `GLIBC_2.27' not found (required by /home/szabolcs/tx1-ubuntu-aarch64/glibc-aarch64-linux/build/build/testroot.pristine/bin/sh)
>>
>> i think some --library-path should be passed here.
> 
> Agreed. There should be a --library-path to the main build directory
> where libc.so.6 is present, otherwise it won't match the loader.
> 
> ...
> 	for dso in `$(test-wrapper-env) LD_TRACE_LOADED_OBJECTS=1  \
> 		$(objpfx)elf/$(rtld-installed-name) \
> 		--library-path $(objpfx):$(objpfx)/math:... \
>                 ^^^^^^^^^^^^^^^^^^^^^^^^^^
>                 Adds lookup for libc.so.6 and all other libraries.
> 
> 
> 		$(objpfx)testroot.pristine/bin/sh \
> 	        | grep / | sed 's/^[^/]*//' | sed 's/ .*//'` ;\
> 	  do \
> ...
> 
> The list of lookup paths needs to be dynamic and you can probably
> crib the way testrun.sh knows, which I think is $(test-program-prefix)?
> 

if i replace $(objpfx)elf/$(rtld-installed-name) with
$(rtld-prefix) then i no longer get host libs from the
ubuntu multiarch directory into testroot.pristine,
instead i get stuff from my build directory, including
libgcc_s.so.1 and libstdc++.so.6 (which i copied there
before running the tests).

copying libs according to host lib paths only makes
sense if the container has the same lib paths set up.

is this step really needed? i thought all binaries in
the testroot are newly built so we should know exactly
the dependencies (including dlopen ones which are not
handled by this logic).

>>> +	for dso in `$(test-wrapper-env) LD_TRACE_LOADED_OBJECTS=1  \
>>> +		$(objpfx)elf/$(rtld-installed-name) \
>>> +		$(objpfx)support/links-dso-program \
>>> +	        | grep / | sed 's/^[^/]*//' | sed 's/ .*//'` ;\
>>> +	  do \
>>> +	    test -d `dirname $(objpfx)testroot.pristine$$dso` || \
>>> +	      mkdir -p `dirname $(objpfx)testroot.pristine$$dso` ;\
>>> +	    $(test-wrapper) cp $$dso $(objpfx)testroot.pristine$$dso ;\
>>> +	  done
>>> +	$(MAKE) install DESTDIR=$(objpfx)testroot.pristine
>>> +	touch $(objpfx)testroot.pristine/install.stamp
>>> +
> 
> 

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: V8 test-in-container patch
  2018-10-10 10:35     ` Szabolcs Nagy
@ 2018-10-10 20:16       ` DJ Delorie
  2018-10-11 10:00         ` Szabolcs Nagy
  0 siblings, 1 reply; 25+ messages in thread
From: DJ Delorie @ 2018-10-10 20:16 UTC (permalink / raw)
  To: Szabolcs Nagy; +Cc: carlos, libc-alpha, nd, fweimer, joseph


Szabolcs Nagy <szabolcs.nagy@arm.com> writes:
> is this step really needed? i thought all binaries in
> the testroot are newly built so we should know exactly
> the dependencies (including dlopen ones which are not
> handled by this logic).

The step is needed to handle DSOs that gcc adds dependencies for (like
libgcc_s.so).  We install them in the testroot in the same location as
outside the testroot.  This may be in /lib, or may be in
/home/me/cross/glibc/arm/lib, etc.

We do this part before the "make install" to ensure if any glibc DSOs
are pulled in from outside the build, they get overwritten by built
DSOs.

Adding flags to find our own DSOs at this step isn't important because
we need to copy them, it's important because we don't want to copy the
wrong ones into a location that might override the newly built ones
(i.e. earlier in ld.so's search path).

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: V8 test-in-container patch
  2018-10-10 20:16       ` DJ Delorie
@ 2018-10-11 10:00         ` Szabolcs Nagy
  2018-10-11 12:55           ` Joseph Myers
  0 siblings, 1 reply; 25+ messages in thread
From: Szabolcs Nagy @ 2018-10-11 10:00 UTC (permalink / raw)
  To: DJ Delorie; +Cc: nd, carlos, libc-alpha, fweimer, joseph

On 10/10/18 20:23, DJ Delorie wrote:
> 
> Szabolcs Nagy <szabolcs.nagy@arm.com> writes:
>> is this step really needed? i thought all binaries in
>> the testroot are newly built so we should know exactly
>> the dependencies (including dlopen ones which are not
>> handled by this logic).
> 
> The step is needed to handle DSOs that gcc adds dependencies for (like
> libgcc_s.so).  We install them in the testroot in the same location as
> outside the testroot.  This may be in /lib, or may be in
> /home/me/cross/glibc/arm/lib, etc.
> 

i still don't see what guarantees that copies will
be in the library search path inside the container.

if it's only done for libgcc_s.so.1 and libstdc++.so.6
then i'd just explicitly copy those two from the build
dir to the testroot lib prefix.

> We do this part before the "make install" to ensure if any glibc DSOs
> are pulled in from outside the build, they get overwritten by built
> DSOs.
> 
> Adding flags to find our own DSOs at this step isn't important because
> we need to copy them, it's important because we don't want to copy the
> wrong ones into a location that might override the newly built ones
> (i.e. earlier in ld.so's search path).
> 


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: V8 test-in-container patch
  2018-10-11 10:00         ` Szabolcs Nagy
@ 2018-10-11 12:55           ` Joseph Myers
  0 siblings, 0 replies; 25+ messages in thread
From: Joseph Myers @ 2018-10-11 12:55 UTC (permalink / raw)
  To: Szabolcs Nagy; +Cc: DJ Delorie, nd, carlos, libc-alpha, fweimer

On Thu, 11 Oct 2018, Szabolcs Nagy wrote:

> if it's only done for libgcc_s.so.1 and libstdc++.so.6
> then i'd just explicitly copy those two from the build
> dir to the testroot lib prefix.

libgcc has a different SONAME on some architectures.  On ia64 there's 
libunwind as well.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 25+ messages in thread

end of thread, other threads:[~2018-10-11 12:13 UTC | newest]

Thread overview: 25+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-08-16 17:59 V8 test-in-container patch DJ Delorie
2018-08-16 18:54 ` Florian Weimer
2018-08-17  2:14   ` DJ Delorie
2018-08-17  2:53 ` Carlos O'Donell
2018-08-23  1:21   ` DJ Delorie
2018-08-23 19:58     ` Joseph Myers
2018-08-23 20:34       ` DJ Delorie
2018-08-23 20:43         ` Joseph Myers
2018-08-23 22:57           ` DJ Delorie
2018-08-23 23:31             ` Joseph Myers
2018-08-23 23:57               ` DJ Delorie
2018-08-24 14:41                 ` Joseph Myers
2018-08-25  2:12                   ` DJ Delorie
2018-08-25  2:22                     ` Carlos O'Donell
2018-08-25  2:29                       ` DJ Delorie
2018-08-25  2:40                         ` Carlos O'Donell
     [not found]                         ` <CAP5F8c+0EjtjaOSf5itJxx385r70XrjGEDWMvsdf8QDmOVVDRA@mail.gmail.com>
2018-08-25  2:56                           ` Carlos O'Donell
2018-08-25  2:58                             ` Jason Duerstock
2018-08-27 15:07                             ` Joseph Myers
2018-10-09  9:55 ` Szabolcs Nagy
2018-10-09 15:26   ` Carlos O'Donell
2018-10-10 10:35     ` Szabolcs Nagy
2018-10-10 20:16       ` DJ Delorie
2018-10-11 10:00         ` Szabolcs Nagy
2018-10-11 12:55           ` Joseph Myers

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).