diff --git a/elf/Makefile b/elf/Makefile
index 26986c0692..85cfb876b5 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -380,15 +380,59 @@ tests += tst-protected1a tst-protected1b
 $(objpfx)tst-protected1a: $(addprefix $(objpfx),tst-protected1moda.so tst-protected1modb.so)
 $(objpfx)tst-protected1b: $(addprefix $(objpfx),tst-protected1modb.so tst-protected1moda.so)
 tst-protected1modb.so-no-z-defs = yes
+ifeq (yes,$(have-fno-direct-extern-access))
+CFLAGS-tst-protected1a.c += -fno-direct-extern-access
+CFLAGS-tst-protected1b.c += -fno-direct-extern-access
+CFLAGS-tst-protected1moda.c += -fno-direct-extern-access
+CFLAGS-tst-protected1modb.c += -fno-direct-extern-access
+else
 # These tests fail with GCC versions prior to 5.1 and with some versions
 # of binutils.  See https://sourceware.org/bugzilla/show_bug.cgi?id=17709
 # and https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65248 for details.
 # Perhaps in future we can make these XFAILs conditional on some detection
 # of compiler/linker behavior/version.
+# NB: These tests pass with -fno-direct-extern-access when GOT entries
+# are used for undefined data accesses.
 test-xfail-tst-protected1a = yes
 test-xfail-tst-protected1b = yes
 endif
+ifeq (yes,$(have-z-indirect-extern-access))
+LDFLAGS-tst-protected1moda.so += -Wl,-z,indirect-extern-access
+LDFLAGS-tst-protected1modb.so += -Wl,-z,indirect-extern-access
+endif
+endif
 ifeq (yesyes,$(have-fpie)$(build-shared))
+ifeq (yes,$(have-z-indirect-extern-access))
+modules-names += tst-protected2moda tst-protected2modb
+tests += tst-protected2a tst-protected2b
+tests += tst-protected2apie tst-protected2bpie
+tests-pie += tst-protected2apie tst-protected2bpie
+test-extras += tst-protected2moda2 tst-protected2modb2
+extra-test-objs += tst-protected2moda2.os tst-protected2modb2.os
+LDFLAGS-tst-protected2moda.so += -Wl,-z,indirect-extern-access
+LDFLAGS-tst-protected2modb.so += -Wl,-z,indirect-extern-access
+CFLAGS-tst-protected2apie.c += $(PIE-ccflag)
+CFLAGS-tst-protected2bpie.c += $(PIE-ccflag)
+ifeq (yes,$(have-fno-direct-extern-access))
+CFLAGS-tst-protected2a.c += -fno-direct-extern-access
+CFLAGS-tst-protected2b.c += -fno-direct-extern-access
+CFLAGS-tst-protected2moda.c += -fno-direct-extern-access
+CFLAGS-tst-protected2moda2.c += -fno-direct-extern-access
+CFLAGS-tst-protected2modb.c += -fno-direct-extern-access
+CFLAGS-tst-protected2modb2.c += -fno-direct-extern-access
+else
+# These non-PIE tests fail when GOT entries are not used for undefined
+# function pointers.
+test-xfail-tst-protected2a = yes
+test-xfail-tst-protected2b = yes
+endif
+$(objpfx)tst-protected2moda.so: $(objpfx)tst-protected2moda2.os
+$(objpfx)tst-protected2modb.so: $(objpfx)tst-protected2modb2.os
+$(objpfx)tst-protected2a: $(addprefix $(objpfx),tst-protected2moda.so tst-protected2modb.so)
+$(objpfx)tst-protected2b: $(addprefix $(objpfx),tst-protected2modb.so tst-protected2moda.so)
+$(objpfx)tst-protected2apie: $(addprefix $(objpfx),tst-protected2moda.so tst-protected2modb.so)
+$(objpfx)tst-protected2bpie: $(addprefix $(objpfx),tst-protected2modb.so tst-protected2moda.so)
+endif
 modules-names += tst-piemod1
 tests += tst-pie1 tst-pie2 tst-dlopen-pie tst-dlopen-tlsmodid-pie \
   tst-dlopen-self-pie
@@ -483,6 +527,16 @@ tests += tst-prelink
 tests-internal += tst-prelink-cmp
 # Don't compile tst-prelink.c with PIE for GLOB_DAT relocation.
 CFLAGS-tst-prelink.c += -fno-pie
+ifeq ($(have-fno-direct-extern-access),yes)
+# Compile tst-prelink.c with -fdirect-extern-acces to keepp COPY
+# relocation.
+CFLAGS-tst-prelink.c += -fdirect-extern-access
+endif
+ifeq ($(have-z-indirect-extern-access),yes)
+# Link tst-prelink with -z noindirect-extern-access to keepp COPY
+# relocation.
+LDFLAGS-tst-prelink += -Wl,-z,noindirect-extern-access
+endif
 tst-prelink-no-pie = yes
 endif
 
diff --git a/elf/tst-protected1moda.c b/elf/tst-protected1moda.c
index eeb18306bb..3d0eb1e877 100644
--- a/elf/tst-protected1moda.c
+++ b/elf/tst-protected1moda.c
@@ -17,17 +17,13 @@
 
 #include "tst-protected1mod.h"
 
-int protected1 = 3;
+int protected1 __attribute__ ((visibility("protected"))) = 3;
 static int expected_protected1 = 3;
-int protected2 = 4;
+int protected2 __attribute__ ((visibility("protected"))) = 4;
 static int expected_protected2 = 4;
-int protected3 = 5;
+int protected3 __attribute__ ((visibility("protected"))) = 5;
 static int expected_protected3 = 5;
 
-asm (".protected protected1");
-asm (".protected protected2");
-asm (".protected protected3");
-
 void
 set_protected1a (int i)
 {
diff --git a/elf/tst-protected1modb.c b/elf/tst-protected1modb.c
index 2cb1e61b17..ca82c64689 100644
--- a/elf/tst-protected1modb.c
+++ b/elf/tst-protected1modb.c
@@ -19,11 +19,9 @@
 #include "tst-protected1mod.h"
 
 int protected1 = -3;
-int protected3 = -5;
+int protected3 __attribute__ ((visibility("protected"))) = -5;
 static int expected_protected3 = -5;
 
-asm (".protected protected3");
-
 void
 set_protected1b (int i)
 {
diff --git a/elf/tst-protected2a.c b/elf/tst-protected2a.c
new file mode 100644
index 0000000000..21b666e12b
--- /dev/null
+++ b/elf/tst-protected2a.c
@@ -0,0 +1,130 @@
+/* Test the protected visibility when main is linked with moda and modb
+   in that order:
+   1. Protected function symbols, protected1, protected2 and protected3,
+      defined in moda, are used in moda.
+   2. Protected function symbol, protected3, defined in modb, are used
+      in modb.
+   3. Symbol, protected1, defined in moda, is also used in main and modb.
+   4. Symbol, protected2, defined in main, is used in main.
+   5. Symbol, protected3, defined in moda, is also used in main.
+
+   Copyright (C) 2021 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "tst-protected2mod.h"
+
+int
+protected2 (void)
+{
+  return -1;
+}
+
+int
+__attribute__ ((weak, noclone, noinline))
+call_ptr (protected_func_type ptr)
+{
+  return ptr ();
+}
+
+int
+do_test (void)
+{
+  int res = 0;
+
+  /* Check if we get the same address for the protected function symbol.  */
+  protected_func_type ptr = protected1a_p ();
+  if (&protected1 != ptr)
+    {
+      puts ("`protected1' in main and moda doesn't have the same address");
+      res = 1;
+    }
+  ptr = protected1b_p ();
+  if (&protected1 != ptr)
+    {
+      puts ("`protected1' in main and modb doesn't have the same address");
+      res = 1;
+    }
+
+  /* Check if we get the right protected function symbol.  */
+  if (call_ptr (ptr) != 3)
+    {
+      puts ("`protected1' in main and moda doesn't return the same value");
+      res = 1;
+    }
+
+  /* Check if we get the right function defined in executable.  */
+  if (protected2 () != -1)
+    {
+      puts ("`protected2' in main returns the wrong value");
+      res = 1;
+    }
+
+  /* Check `protected1' in moda.  */
+  if (!check_protected1 ())
+    {
+      puts ("`protected1' in moda returns the wrong value");
+      res = 1;
+    }
+
+  /* Check `protected2' in moda.  */
+  if (!check_protected2 ())
+    {
+      puts ("`protected2' in moda returns the wrong value");
+      res = 1;
+    }
+
+  /* Check if we get the same address for the protected function symbol.  */
+  if (&protected3 != protected3a_p ())
+    {
+      puts ("`protected3' in main and moda doesn't have the same address");
+      res = 1;
+    }
+  if (&protected3 == protected3b_p ())
+    {
+      puts ("`protected3' in main and modb has the same address");
+      res = 1;
+    }
+
+  /* Check if we get the right value for the protected data symbol.  */
+  if (protected3 () != 5)
+    {
+      puts ("`protected3' in main and moda doesn't return the same value");
+      res = 1;
+    }
+
+  /* Check `protected3' in moda.  */
+  if (!check_protected3a ())
+    {
+      puts ("`protected3' in moda has the wrong value");
+      res = 1;
+    }
+
+  /* Check `protected3' in modb.  */
+  if (!check_protected3b ())
+    {
+      puts ("`protected3' in modb has the wrong value");
+      res = 1;
+    }
+
+  return res;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-protected2apie.c b/elf/tst-protected2apie.c
new file mode 100644
index 0000000000..28a7aa3d1a
--- /dev/null
+++ b/elf/tst-protected2apie.c
@@ -0,0 +1 @@
+#include "tst-protected2a.c"
diff --git a/elf/tst-protected2b.c b/elf/tst-protected2b.c
new file mode 100644
index 0000000000..500323e33f
--- /dev/null
+++ b/elf/tst-protected2b.c
@@ -0,0 +1,121 @@
+/* Test the protected visibility when main is linked with modb and moda
+   in that order:
+   1. Protected function symbols, protected1, protected2 and protected3,
+      defined in moda, are used in moda.
+   2. Protected function symbol, protected3, defined in modb, are used
+      in modb.
+   3. Symbol, protected1, defined in modb, is used in main and modb.
+   4. Symbol, protected2, defined in main, is used in main.
+   5. Symbol, protected3, defined in modb, is also used in main.
+
+   Copyright (C) 2021 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "tst-protected2mod.h"
+
+int
+protected2 (void)
+{
+  return -1;
+}
+
+int
+do_test (void)
+{
+  int res = 0;
+
+  /* Check if we get the same address for the protected data symbol.  */
+  if (&protected1 == protected1a_p ())
+    {
+      puts ("`protected1' in main and moda has the same address");
+      res = 1;
+    }
+  if (&protected1 != protected1b_p ())
+    {
+      puts ("`protected1' in main and modb doesn't have the same address");
+      res = 1;
+    }
+
+  /* Check if we get the right protected function symbol.  */
+  if (protected1 () != -3)
+    {
+      puts ("`protected1' in main and modb doesn't return the same value");
+      res = 1;
+    }
+
+  /* Check if we get the right function defined in executable.  */
+  if (protected2 () != -1)
+    {
+      puts ("`protected2' in main returns the wrong value");
+      res = 1;
+    }
+
+  /* Check `protected1' in moda.  */
+  if (!check_protected1 ())
+    {
+      puts ("`protected1' in moda returns the wrong value");
+      res = 1;
+    }
+
+  /* Check `protected2' in moda.  */
+  if (!check_protected2 ())
+    {
+      puts ("`protected2' in moda returns the wrong value");
+      res = 1;
+    }
+
+  /* Check if we get the same address for the protected function symbol.  */
+  if (&protected3 == protected3a_p ())
+    {
+      puts ("`protected3' in main and moda has the same address");
+      res = 1;
+    }
+  if (&protected3 != protected3b_p ())
+    {
+      puts ("`protected3' in main and modb doesn't have the same address");
+      res = 1;
+    }
+
+  /* Check if we get the right protected function symbol.  */
+  if (protected3 () != -5)
+    {
+      puts ("`protected3' in main and modb doesn't return the same value");
+      res = 1;
+    }
+
+  /* Check `protected3' in moda.  */
+  if (!check_protected3a ())
+    {
+      puts ("`protected3' in moda returns the wrong value");
+      res = 1;
+    }
+
+  /* Check `protected3' in modb.  */
+  if (!check_protected3b ())
+    {
+      puts ("`protected3' in modb returns the wrong value");
+      res = 1;
+    }
+
+  return res;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-protected2bpie.c b/elf/tst-protected2bpie.c
new file mode 100644
index 0000000000..8dcfbd04cb
--- /dev/null
+++ b/elf/tst-protected2bpie.c
@@ -0,0 +1 @@
+#include "tst-protected2b.c"
diff --git a/elf/tst-protected2mod.h b/elf/tst-protected2mod.h
new file mode 100644
index 0000000000..feb28ab0d5
--- /dev/null
+++ b/elf/tst-protected2mod.h
@@ -0,0 +1,35 @@
+/* Test protected function symbols.
+   Copyright (C) 2021 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
+   <https://www.gnu.org/licenses/>.  */
+
+/* Prototypes for the functions in the DSOs.  */
+extern int protected1 (void);
+extern int protected2 (void);
+extern int protected3 (void);
+
+typedef int (*protected_func_type) (void);
+
+extern protected_func_type protected1a_p (void);
+extern protected_func_type protected1b_p (void);
+
+extern int check_protected1 (void);
+extern int check_protected2 (void);
+
+extern int check_protected3a (void);
+extern protected_func_type protected3a_p (void);
+extern int check_protected3b (void);
+extern protected_func_type protected3b_p (void);
diff --git a/elf/tst-protected2moda.c b/elf/tst-protected2moda.c
new file mode 100644
index 0000000000..db04e8dfb9
--- /dev/null
+++ b/elf/tst-protected2moda.c
@@ -0,0 +1,52 @@
+/* Test protected function symbols.
+   Copyright (C) 2021 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include "tst-protected2mod.h"
+
+__attribute__ ((visibility("protected")))
+int
+protected1 (void)
+{
+  return 3;
+}
+
+__attribute__ ((visibility("protected")))
+int
+protected2 (void)
+{
+  return 4;
+}
+
+__attribute__ ((visibility("protected")))
+int
+protected3 (void)
+{
+  return 5;
+}
+
+protected_func_type
+protected1a_p (void)
+{
+  return &protected1;
+}
+
+protected_func_type
+protected3a_p (void)
+{
+  return &protected3;
+}
diff --git a/elf/tst-protected2moda2.c b/elf/tst-protected2moda2.c
new file mode 100644
index 0000000000..fae72177f9
--- /dev/null
+++ b/elf/tst-protected2moda2.c
@@ -0,0 +1,41 @@
+/* Test protected function symbols.
+   Copyright (C) 2021 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include "tst-protected2mod.h"
+
+extern int protected1 (void) __attribute__ ((visibility("protected")));
+extern int protected2 (void) __attribute__ ((visibility("protected")));
+extern int protected3 (void) __attribute__ ((visibility("protected")));
+
+int
+check_protected1 (void)
+{
+  return protected1 () == 3;
+}
+
+int
+check_protected2 (void)
+{
+  return protected2 () == 4;
+}
+
+int
+check_protected3a (void)
+{
+  return protected3 () == 5;
+}
diff --git a/elf/tst-protected2modb.c b/elf/tst-protected2modb.c
new file mode 100644
index 0000000000..3c5063f0c3
--- /dev/null
+++ b/elf/tst-protected2modb.c
@@ -0,0 +1,45 @@
+/* Test protected function symbols.
+   Copyright (C) 2021 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <stdlib.h>
+#include "tst-protected2mod.h"
+
+int
+protected1 (void)
+{
+  return -3;
+}
+
+__attribute__ ((visibility("protected")))
+int
+protected3 (void)
+{
+  return -5;
+}
+
+protected_func_type
+protected1b_p (void)
+{
+  return &protected1;
+}
+
+protected_func_type
+protected3b_p (void)
+{
+  return &protected3;
+}
diff --git a/elf/tst-protected2modb2.c b/elf/tst-protected2modb2.c
new file mode 100644
index 0000000000..b21b827134
--- /dev/null
+++ b/elf/tst-protected2modb2.c
@@ -0,0 +1,28 @@
+/* Test protected function symbols.
+   Copyright (C) 2021 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <stdlib.h>
+#include "tst-protected2mod.h"
+
+extern int protected3 (void) __attribute__ ((visibility("protected")));
+
+int
+check_protected3b (void)
+{
+  return protected3 () == -5;
+}
