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