a2cf7d
commit eb447b7b4bd6177f876ba9420ad9e048c27bae91
a2cf7d
Author: David Kilroy <David.Kilroy@arm.com>
a2cf7d
Date:   Wed Feb 12 14:28:15 2020 -0300
a2cf7d
a2cf7d
    elf: Allow dlopen of filter object to work [BZ #16272]
a2cf7d
    
a2cf7d
    There are two fixes that are needed to be able to dlopen filter
a2cf7d
    objects. First _dl_map_object_deps cannot assume that map will be at
a2cf7d
    the beginning of l_searchlist.r_list[], as filtees are inserted before
a2cf7d
    map. Secondly dl_open_worker needs to ensure that filtees get
a2cf7d
    relocated.
a2cf7d
    
a2cf7d
    In _dl_map_object_deps:
a2cf7d
    
a2cf7d
    * avoiding removing relocation dependencies of map by setting
a2cf7d
      l_reserved to 0 and otherwise processing the rest of the search
a2cf7d
      list.
a2cf7d
    
a2cf7d
    * ensure that map remains at the beginning of l_initfini - the list
a2cf7d
      of things that need initialisation (and destruction). Do this by
a2cf7d
      splitting the copy up. This may not be required, but matches the
a2cf7d
      initialization order without dlopen.
a2cf7d
    
a2cf7d
    Modify dl_open_worker to relocate the objects in new->l_inifini.
a2cf7d
    new->l_initfini is constructed in _dl_map_object_deps, and lists the
a2cf7d
    objects that need initialization and destruction. Originally the list
a2cf7d
    of objects in new->l_next are relocated. All of these objects should
a2cf7d
    also be included in new->l_initfini (both lists are populated with
a2cf7d
    dependencies in _dl_map_object_deps). We can't use new->l_prev to pick
a2cf7d
    up filtees, as during a recursive dlopen from an interposed malloc
a2cf7d
    call, l->prev can contain objects that are not ready for relocation.
a2cf7d
    
a2cf7d
    Add tests to verify that symbols resolve to the filtee implementation
a2cf7d
    when auxiliary and filter objects are used, both as a normal link and
a2cf7d
    when dlopen'd.
a2cf7d
    
a2cf7d
    Tested by running the testsuite on x86_64.
a2cf7d
a2cf7d
# Conflicts:
a2cf7d
#	elf/Makefile
a2cf7d
a2cf7d
diff --git a/elf/Makefile b/elf/Makefile
a2cf7d
index a52d9b1f6a4364a7..b4b618ce62a9e6df 100644
a2cf7d
--- a/elf/Makefile
a2cf7d
+++ b/elf/Makefile
a2cf7d
@@ -192,7 +192,8 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
a2cf7d
 	 tst-latepthread tst-tls-manydynamic tst-nodelete-dlclose \
a2cf7d
 	 tst-debug1 tst-main1 tst-absolute-sym tst-absolute-zero tst-big-note \
a2cf7d
 	 tst-sonamemove-link tst-sonamemove-dlopen tst-initfinilazyfail \
a2cf7d
-	 tst-dlopenfail tst-dlopenfail-2
a2cf7d
+	 tst-dlopenfail tst-dlopenfail-2 \
a2cf7d
+	 tst-filterobj tst-filterobj-dlopen tst-auxobj tst-auxobj-dlopen
a2cf7d
 #	 reldep9
a2cf7d
 tests-internal += loadtest unload unload2 circleload1 \
a2cf7d
 	 neededtest neededtest2 neededtest3 neededtest4 \
a2cf7d
@@ -302,7 +303,8 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
a2cf7d
 		tst-sonamemove-runmod1 tst-sonamemove-runmod2 \
a2cf7d
 		tst-initlazyfailmod tst-finilazyfailmod \
a2cf7d
 		tst-dlopenfailmod1 tst-dlopenfaillinkmod tst-dlopenfailmod2 \
a2cf7d
-		tst-dlopenfailmod3
a2cf7d
+		tst-dlopenfailmod3 \
a2cf7d
+		tst-filterobj-flt tst-filterobj-aux tst-filterobj-filtee
a2cf7d
 
a2cf7d
 ifeq (yes,$(have-mtls-dialect-gnu2))
a2cf7d
 tests += tst-gnu2-tls1
a2cf7d
@@ -1626,3 +1628,15 @@ $(objpfx)tst-dlopen-nodelete-reloc-mod17.so: \
a2cf7d
   $(objpfx)tst-dlopen-nodelete-reloc-mod15.so \
a2cf7d
   $(objpfx)tst-dlopen-nodelete-reloc-mod16.so
a2cf7d
 LDFLAGS-tst-dlopen-nodelete-reloc-mod17.so = -Wl,--no-as-needed
a2cf7d
+
a2cf7d
+LDFLAGS-tst-filterobj-flt.so = -Wl,--filter=$(objpfx)tst-filterobj-filtee.so
a2cf7d
+$(objpfx)tst-filterobj: $(objpfx)tst-filterobj-flt.so
a2cf7d
+$(objpfx)tst-filterobj-dlopen: $(libdl)
a2cf7d
+$(objpfx)tst-filterobj.out: $(objpfx)tst-filterobj-filtee.so
a2cf7d
+$(objpfx)tst-filterobj-dlopen.out: $(objpfx)tst-filterobj-filtee.so
a2cf7d
+
a2cf7d
+LDFLAGS-tst-filterobj-aux.so = -Wl,--auxiliary=$(objpfx)tst-filterobj-filtee.so
a2cf7d
+$(objpfx)tst-auxobj: $(objpfx)tst-filterobj-aux.so
a2cf7d
+$(objpfx)tst-auxobj-dlopen: $(libdl)
a2cf7d
+$(objpfx)tst-auxobj.out: $(objpfx)tst-filterobj-filtee.so
a2cf7d
+$(objpfx)tst-auxobj-dlopen.out: $(objpfx)tst-filterobj-filtee.so
a2cf7d
diff --git a/elf/dl-deps.c b/elf/dl-deps.c
a2cf7d
index 9d9b1ba7f244348a..50f053a1586efdc3 100644
a2cf7d
--- a/elf/dl-deps.c
a2cf7d
+++ b/elf/dl-deps.c
a2cf7d
@@ -485,14 +485,18 @@ _dl_map_object_deps (struct link_map *map,
a2cf7d
 
a2cf7d
   map->l_searchlist.r_list = &l_initfini[nlist + 1];
a2cf7d
   map->l_searchlist.r_nlist = nlist;
a2cf7d
+  unsigned int map_index = UINT_MAX;
a2cf7d
 
a2cf7d
   for (nlist = 0, runp = known; runp; runp = runp->next)
a2cf7d
     {
a2cf7d
       if (__builtin_expect (trace_mode, 0) && runp->map->l_faked)
a2cf7d
 	/* This can happen when we trace the loading.  */
a2cf7d
 	--map->l_searchlist.r_nlist;
a2cf7d
-      else
a2cf7d
+      else {
a2cf7d
+	if (runp->map == map)
a2cf7d
+	  map_index = nlist;
a2cf7d
 	map->l_searchlist.r_list[nlist++] = runp->map;
a2cf7d
+      }
a2cf7d
 
a2cf7d
       /* Now clear all the mark bits we set in the objects on the search list
a2cf7d
 	 to avoid duplicates, so the next call starts fresh.  */
a2cf7d
@@ -550,13 +554,14 @@ Filters not supported with LD_TRACE_PRELINKING"));
a2cf7d
     }
a2cf7d
 
a2cf7d
   /* Maybe we can remove some relocation dependencies now.  */
a2cf7d
-  assert (map->l_searchlist.r_list[0] == map);
a2cf7d
   struct link_map_reldeps *l_reldeps = NULL;
a2cf7d
   if (map->l_reldeps != NULL)
a2cf7d
     {
a2cf7d
-      for (i = 1; i < nlist; ++i)
a2cf7d
+      for (i = 0; i < nlist; ++i)
a2cf7d
 	map->l_searchlist.r_list[i]->l_reserved = 1;
a2cf7d
 
a2cf7d
+      /* Avoid removing relocation dependencies of the main binary.  */
a2cf7d
+      map->l_reserved = 0;
a2cf7d
       struct link_map **list = &map->l_reldeps->list[0];
a2cf7d
       for (i = 0; i < map->l_reldeps->act; ++i)
a2cf7d
 	if (list[i]->l_reserved)
a2cf7d
@@ -581,16 +586,30 @@ Filters not supported with LD_TRACE_PRELINKING"));
a2cf7d
 	      }
a2cf7d
 	  }
a2cf7d
 
a2cf7d
-      for (i = 1; i < nlist; ++i)
a2cf7d
+      for (i = 0; i < nlist; ++i)
a2cf7d
 	map->l_searchlist.r_list[i]->l_reserved = 0;
a2cf7d
     }
a2cf7d
 
a2cf7d
-  /* Sort the initializer list to take dependencies into account.  The binary
a2cf7d
-     itself will always be initialize last.  */
a2cf7d
-  memcpy (l_initfini, map->l_searchlist.r_list,
a2cf7d
-	  nlist * sizeof (struct link_map *));
a2cf7d
-  /* We can skip looking for the binary itself which is at the front of
a2cf7d
-     the search list.  */
a2cf7d
+  /* Sort the initializer list to take dependencies into account.  Always
a2cf7d
+     initialize the binary itself last.  */
a2cf7d
+  assert (map_index < nlist);
a2cf7d
+  if (map_index > 0)
a2cf7d
+    {
a2cf7d
+      /* Copy the binary into position 0.  */
a2cf7d
+      l_initfini[0] = map->l_searchlist.r_list[map_index];
a2cf7d
+
a2cf7d
+      /* Copy the filtees.  */
a2cf7d
+      for (i = 0; i < map_index; ++i)
a2cf7d
+	l_initfini[i+1] = map->l_searchlist.r_list[i];
a2cf7d
+
a2cf7d
+      /* Copy the remainder.  */
a2cf7d
+      for (i = map_index + 1; i < nlist; ++i)
a2cf7d
+	l_initfini[i] = map->l_searchlist.r_list[i];
a2cf7d
+    }
a2cf7d
+  else
a2cf7d
+    memcpy (l_initfini, map->l_searchlist.r_list,
a2cf7d
+	    nlist * sizeof (struct link_map *));
a2cf7d
+
a2cf7d
   _dl_sort_maps (&l_initfini[1], nlist - 1, NULL, false);
a2cf7d
 
a2cf7d
   /* Terminate the list of dependencies.  */
a2cf7d
diff --git a/elf/dl-open.c b/elf/dl-open.c
a2cf7d
index d834b89754d2b073..d31356f7e17dfb14 100644
a2cf7d
--- a/elf/dl-open.c
a2cf7d
+++ b/elf/dl-open.c
a2cf7d
@@ -618,22 +618,25 @@ dl_open_worker (void *a)
a2cf7d
      allows IFUNC relocations to work and it also means copy
a2cf7d
      relocation of dependencies are if necessary overwritten.  */
a2cf7d
   unsigned int nmaps = 0;
a2cf7d
-  struct link_map *l = new;
a2cf7d
+  unsigned int j = 0;
a2cf7d
+  struct link_map *l = new->l_initfini[0];
a2cf7d
   do
a2cf7d
     {
a2cf7d
       if (! l->l_real->l_relocated)
a2cf7d
 	++nmaps;
a2cf7d
-      l = l->l_next;
a2cf7d
+      l = new->l_initfini[++j];
a2cf7d
     }
a2cf7d
   while (l != NULL);
a2cf7d
+  /* Stack allocation is limited by the number of loaded objects.  */
a2cf7d
   struct link_map *maps[nmaps];
a2cf7d
   nmaps = 0;
a2cf7d
-  l = new;
a2cf7d
+  j = 0;
a2cf7d
+  l = new->l_initfini[0];
a2cf7d
   do
a2cf7d
     {
a2cf7d
       if (! l->l_real->l_relocated)
a2cf7d
 	maps[nmaps++] = l;
a2cf7d
-      l = l->l_next;
a2cf7d
+      l = new->l_initfini[++j];
a2cf7d
     }
a2cf7d
   while (l != NULL);
a2cf7d
   _dl_sort_maps (maps, nmaps, NULL, false);
a2cf7d
diff --git a/elf/tst-auxobj-dlopen.c b/elf/tst-auxobj-dlopen.c
a2cf7d
new file mode 100644
a2cf7d
index 0000000000000000..cb54aba19470a1fe
a2cf7d
--- /dev/null
a2cf7d
+++ b/elf/tst-auxobj-dlopen.c
a2cf7d
@@ -0,0 +1,47 @@
a2cf7d
+/* Test for BZ#16272, dlopen'ing an auxiliary filter object.
a2cf7d
+   Ensure that symbols from the resolve correctly.
a2cf7d
+
a2cf7d
+   Copyright (C) 2020 Free Software Foundation, Inc.
a2cf7d
+   This file is part of the GNU C Library.
a2cf7d
+
a2cf7d
+   The GNU C Library is free software; you can redistribute it and/or
a2cf7d
+   modify it under the terms of the GNU Lesser General Public
a2cf7d
+   License as published by the Free Software Foundation; either
a2cf7d
+   version 2.1 of the License, or (at your option) any later version.
a2cf7d
+
a2cf7d
+   The GNU C Library is distributed in the hope that it will be useful,
a2cf7d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
a2cf7d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
a2cf7d
+   Lesser General Public License for more details.
a2cf7d
+
a2cf7d
+   You should have received a copy of the GNU Lesser General Public
a2cf7d
+   License along with the GNU C Library; if not, see
a2cf7d
+   <https://www.gnu.org/licenses/>.  */
a2cf7d
+
a2cf7d
+#include <stdio.h>
a2cf7d
+#include <support/check.h>
a2cf7d
+#include <support/xdlfcn.h>
a2cf7d
+
a2cf7d
+static int do_test (void)
a2cf7d
+{
a2cf7d
+  void *lib = xdlopen ("tst-filterobj-aux.so", RTLD_LAZY);
a2cf7d
+  char *(*fn)(void) = xdlsym (lib, "get_text");
a2cf7d
+  const char* text = fn ();
a2cf7d
+
a2cf7d
+  printf ("%s\n", text);
a2cf7d
+
a2cf7d
+  /* Verify the text matches what we expect from the filtee */
a2cf7d
+  TEST_COMPARE_STRING (text, "Hello from filtee (PASS)");
a2cf7d
+
a2cf7d
+  fn = xdlsym (lib, "get_text2");
a2cf7d
+  text = fn ();
a2cf7d
+
a2cf7d
+  printf ("%s\n", text);
a2cf7d
+
a2cf7d
+  /* Verify the text matches what we expect from the auxiliary object */
a2cf7d
+  TEST_COMPARE_STRING (text, "Hello from auxiliary filter object (PASS)");
a2cf7d
+
a2cf7d
+  return 0;
a2cf7d
+}
a2cf7d
+
a2cf7d
+#include <support/test-driver.c>
a2cf7d
diff --git a/elf/tst-auxobj.c b/elf/tst-auxobj.c
a2cf7d
new file mode 100644
a2cf7d
index 0000000000000000..bdc7713b04b4a79b
a2cf7d
--- /dev/null
a2cf7d
+++ b/elf/tst-auxobj.c
a2cf7d
@@ -0,0 +1,42 @@
a2cf7d
+/* Test that symbols from auxiliary filter objects are resolved to the
a2cf7d
+   filtee.
a2cf7d
+
a2cf7d
+   Copyright (C) 2020 Free Software Foundation, Inc.
a2cf7d
+   This file is part of the GNU C Library.
a2cf7d
+
a2cf7d
+   The GNU C Library is free software; you can redistribute it and/or
a2cf7d
+   modify it under the terms of the GNU Lesser General Public
a2cf7d
+   License as published by the Free Software Foundation; either
a2cf7d
+   version 2.1 of the License, or (at your option) any later version.
a2cf7d
+
a2cf7d
+   The GNU C Library is distributed in the hope that it will be useful,
a2cf7d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
a2cf7d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
a2cf7d
+   Lesser General Public License for more details.
a2cf7d
+
a2cf7d
+   You should have received a copy of the GNU Lesser General Public
a2cf7d
+   License along with the GNU C Library; if not, see
a2cf7d
+   <https://www.gnu.org/licenses/>.  */
a2cf7d
+
a2cf7d
+#include <stdio.h>
a2cf7d
+#include <support/check.h>
a2cf7d
+#include "tst-filterobj-filtee.h"
a2cf7d
+
a2cf7d
+static int do_test (void)
a2cf7d
+{
a2cf7d
+  const char* text = get_text ();
a2cf7d
+  printf ("%s\n", text);
a2cf7d
+
a2cf7d
+  /* Verify the text matches what we expect from the filtee */
a2cf7d
+  TEST_COMPARE_STRING (text, "Hello from filtee (PASS)");
a2cf7d
+
a2cf7d
+  text = get_text2 ();
a2cf7d
+  printf ("%s\n", text);
a2cf7d
+
a2cf7d
+  /* Verify the text matches what we expect from the auxiliary object */
a2cf7d
+  TEST_COMPARE_STRING (text, "Hello from auxiliary filter object (PASS)");
a2cf7d
+
a2cf7d
+  return 0;
a2cf7d
+}
a2cf7d
+
a2cf7d
+#include <support/test-driver.c>
a2cf7d
diff --git a/elf/tst-filterobj-aux.c b/elf/tst-filterobj-aux.c
a2cf7d
new file mode 100644
a2cf7d
index 0000000000000000..0b732f2fb3a69a7f
a2cf7d
--- /dev/null
a2cf7d
+++ b/elf/tst-filterobj-aux.c
a2cf7d
@@ -0,0 +1,33 @@
a2cf7d
+/* Auxiliary filter object.
a2cf7d
+   Contains symbols to be resolved in filtee, and one which doesn't.
a2cf7d
+
a2cf7d
+   Copyright (C) 2020 Free Software Foundation, Inc.
a2cf7d
+   This file is part of the GNU C Library.
a2cf7d
+
a2cf7d
+   The GNU C Library is free software; you can redistribute it and/or
a2cf7d
+   modify it under the terms of the GNU Lesser General Public
a2cf7d
+   License as published by the Free Software Foundation; either
a2cf7d
+   version 2.1 of the License, or (at your option) any later version.
a2cf7d
+
a2cf7d
+   The GNU C Library is distributed in the hope that it will be useful,
a2cf7d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
a2cf7d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
a2cf7d
+   Lesser General Public License for more details.
a2cf7d
+
a2cf7d
+   You should have received a copy of the GNU Lesser General Public
a2cf7d
+   License along with the GNU C Library; if not, see
a2cf7d
+   <https://www.gnu.org/licenses/>.  */
a2cf7d
+
a2cf7d
+#include "tst-filterobj-filtee.h"
a2cf7d
+
a2cf7d
+/* We never want to see the output of the auxiliary object.  */
a2cf7d
+const char *get_text (void)
a2cf7d
+{
a2cf7d
+  return "Hello from auxiliary filter object (FAIL)";
a2cf7d
+}
a2cf7d
+
a2cf7d
+/* The filtee doesn't implement this symbol, so this should resolve.  */
a2cf7d
+const char *get_text2 (void)
a2cf7d
+{
a2cf7d
+  return "Hello from auxiliary filter object (PASS)";
a2cf7d
+}
a2cf7d
diff --git a/elf/tst-filterobj-dlopen.c b/elf/tst-filterobj-dlopen.c
a2cf7d
new file mode 100644
a2cf7d
index 0000000000000000..c5b5072979802b98
a2cf7d
--- /dev/null
a2cf7d
+++ b/elf/tst-filterobj-dlopen.c
a2cf7d
@@ -0,0 +1,39 @@
a2cf7d
+/* Test for BZ#16272, dlopen'ing a filter object.
a2cf7d
+   Ensure that symbols from the filter object resolve to the filtee.
a2cf7d
+
a2cf7d
+   Copyright (C) 2020 Free Software Foundation, Inc.
a2cf7d
+   This file is part of the GNU C Library.
a2cf7d
+
a2cf7d
+   The GNU C Library is free software; you can redistribute it and/or
a2cf7d
+   modify it under the terms of the GNU Lesser General Public
a2cf7d
+   License as published by the Free Software Foundation; either
a2cf7d
+   version 2.1 of the License, or (at your option) any later version.
a2cf7d
+
a2cf7d
+   The GNU C Library is distributed in the hope that it will be useful,
a2cf7d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
a2cf7d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
a2cf7d
+   Lesser General Public License for more details.
a2cf7d
+
a2cf7d
+   You should have received a copy of the GNU Lesser General Public
a2cf7d
+   License along with the GNU C Library; if not, see
a2cf7d
+   <https://www.gnu.org/licenses/>.  */
a2cf7d
+
a2cf7d
+#include <stdio.h>
a2cf7d
+#include <support/check.h>
a2cf7d
+#include <support/xdlfcn.h>
a2cf7d
+
a2cf7d
+static int do_test (void)
a2cf7d
+{
a2cf7d
+  void *lib = xdlopen ("tst-filterobj-flt.so", RTLD_LAZY);
a2cf7d
+  char *(*fn)(void) = xdlsym (lib, "get_text");
a2cf7d
+  const char* text = fn ();
a2cf7d
+
a2cf7d
+  printf ("%s\n", text);
a2cf7d
+
a2cf7d
+  /* Verify the text matches what we expect from the filtee */
a2cf7d
+  TEST_COMPARE_STRING (text, "Hello from filtee (PASS)");
a2cf7d
+
a2cf7d
+  return 0;
a2cf7d
+}
a2cf7d
+
a2cf7d
+#include <support/test-driver.c>
a2cf7d
diff --git a/elf/tst-filterobj-filtee.c b/elf/tst-filterobj-filtee.c
a2cf7d
new file mode 100644
a2cf7d
index 0000000000000000..8fa557cbd251f53c
a2cf7d
--- /dev/null
a2cf7d
+++ b/elf/tst-filterobj-filtee.c
a2cf7d
@@ -0,0 +1,27 @@
a2cf7d
+/* Filtee for BZ#16272 test.
a2cf7d
+   Contains desired symbol implementations.
a2cf7d
+
a2cf7d
+   Copyright (C) 2020 Free Software Foundation, Inc.
a2cf7d
+   This file is part of the GNU C Library.
a2cf7d
+
a2cf7d
+   The GNU C Library is free software; you can redistribute it and/or
a2cf7d
+   modify it under the terms of the GNU Lesser General Public
a2cf7d
+   License as published by the Free Software Foundation; either
a2cf7d
+   version 2.1 of the License, or (at your option) any later version.
a2cf7d
+
a2cf7d
+   The GNU C Library is distributed in the hope that it will be useful,
a2cf7d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
a2cf7d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
a2cf7d
+   Lesser General Public License for more details.
a2cf7d
+
a2cf7d
+   You should have received a copy of the GNU Lesser General Public
a2cf7d
+   License along with the GNU C Library; if not, see
a2cf7d
+   <https://www.gnu.org/licenses/>.  */
a2cf7d
+
a2cf7d
+#include "tst-filterobj-filtee.h"
a2cf7d
+
a2cf7d
+/* This is the real implementation that wants to be called */
a2cf7d
+const char *get_text (void)
a2cf7d
+{
a2cf7d
+  return "Hello from filtee (PASS)";
a2cf7d
+}
a2cf7d
diff --git a/elf/tst-filterobj-filtee.h b/elf/tst-filterobj-filtee.h
a2cf7d
new file mode 100644
a2cf7d
index 0000000000000000..46aee28178b88a77
a2cf7d
--- /dev/null
a2cf7d
+++ b/elf/tst-filterobj-filtee.h
a2cf7d
@@ -0,0 +1,24 @@
a2cf7d
+/* Filtee header for BZ#16272 test.
a2cf7d
+   Contains prototypes for symbols implemented in the filtee.
a2cf7d
+
a2cf7d
+   Copyright (C) 2020 Free Software Foundation, Inc.
a2cf7d
+   This file is part of the GNU C Library.
a2cf7d
+
a2cf7d
+   The GNU C Library is free software; you can redistribute it and/or
a2cf7d
+   modify it under the terms of the GNU Lesser General Public
a2cf7d
+   License as published by the Free Software Foundation; either
a2cf7d
+   version 2.1 of the License, or (at your option) any later version.
a2cf7d
+
a2cf7d
+   The GNU C Library is distributed in the hope that it will be useful,
a2cf7d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
a2cf7d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
a2cf7d
+   Lesser General Public License for more details.
a2cf7d
+
a2cf7d
+   You should have received a copy of the GNU Lesser General Public
a2cf7d
+   License along with the GNU C Library; if not, see
a2cf7d
+   <https://www.gnu.org/licenses/>.  */
a2cf7d
+
a2cf7d
+const char *get_text (void);
a2cf7d
+
a2cf7d
+/* For testing auxiliary filter object.  */
a2cf7d
+const char *get_text2 (void);
a2cf7d
diff --git a/elf/tst-filterobj-flt.c b/elf/tst-filterobj-flt.c
a2cf7d
new file mode 100644
a2cf7d
index 0000000000000000..5062654be6f14a80
a2cf7d
--- /dev/null
a2cf7d
+++ b/elf/tst-filterobj-flt.c
a2cf7d
@@ -0,0 +1,27 @@
a2cf7d
+/* Filter object for BZ#16272 test.
a2cf7d
+   Contains symbols to be resolved in filtee.
a2cf7d
+
a2cf7d
+   Copyright (C) 2020 Free Software Foundation, Inc.
a2cf7d
+   This file is part of the GNU C Library.
a2cf7d
+
a2cf7d
+   The GNU C Library is free software; you can redistribute it and/or
a2cf7d
+   modify it under the terms of the GNU Lesser General Public
a2cf7d
+   License as published by the Free Software Foundation; either
a2cf7d
+   version 2.1 of the License, or (at your option) any later version.
a2cf7d
+
a2cf7d
+   The GNU C Library is distributed in the hope that it will be useful,
a2cf7d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
a2cf7d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
a2cf7d
+   Lesser General Public License for more details.
a2cf7d
+
a2cf7d
+   You should have received a copy of the GNU Lesser General Public
a2cf7d
+   License along with the GNU C Library; if not, see
a2cf7d
+   <https://www.gnu.org/licenses/>.  */
a2cf7d
+
a2cf7d
+#include "tst-filterobj-filtee.h"
a2cf7d
+
a2cf7d
+/* We never want to see the output of the filter object */
a2cf7d
+const char *get_text (void)
a2cf7d
+{
a2cf7d
+  return "Hello from filter object (FAIL)";
a2cf7d
+}
a2cf7d
diff --git a/elf/tst-filterobj.c b/elf/tst-filterobj.c
a2cf7d
new file mode 100644
a2cf7d
index 0000000000000000..96bfae019ea670bc
a2cf7d
--- /dev/null
a2cf7d
+++ b/elf/tst-filterobj.c
a2cf7d
@@ -0,0 +1,36 @@
a2cf7d
+/* Test that symbols from filter objects are resolved to the filtee.
a2cf7d
+
a2cf7d
+   Copyright (C) 2020 Free Software Foundation, Inc.
a2cf7d
+   This file is part of the GNU C Library.
a2cf7d
+
a2cf7d
+   The GNU C Library is free software; you can redistribute it and/or
a2cf7d
+   modify it under the terms of the GNU Lesser General Public
a2cf7d
+   License as published by the Free Software Foundation; either
a2cf7d
+   version 2.1 of the License, or (at your option) any later version.
a2cf7d
+
a2cf7d
+   The GNU C Library is distributed in the hope that it will be useful,
a2cf7d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
a2cf7d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
a2cf7d
+   Lesser General Public License for more details.
a2cf7d
+
a2cf7d
+   You should have received a copy of the GNU Lesser General Public
a2cf7d
+   License along with the GNU C Library; if not, see
a2cf7d
+   <https://www.gnu.org/licenses/>.  */
a2cf7d
+
a2cf7d
+#include <stdio.h>
a2cf7d
+#include <support/check.h>
a2cf7d
+#include "tst-filterobj-filtee.h"
a2cf7d
+
a2cf7d
+static int do_test (void)
a2cf7d
+{
a2cf7d
+  const char* text = get_text ();
a2cf7d
+
a2cf7d
+  printf ("%s\n", text);
a2cf7d
+
a2cf7d
+  /* Verify the text matches what we expect from the filtee */
a2cf7d
+  TEST_COMPARE_STRING (text, "Hello from filtee (PASS)");
a2cf7d
+
a2cf7d
+  return 0;
a2cf7d
+}
a2cf7d
+
a2cf7d
+#include <support/test-driver.c>