This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: RFC: x86: Fall back to lazy binding for unrelocated IFUNC symbol [BZ #23240]


On Mon, May 28, 2018 at 6:45 AM, Carlos O'Donell <carlos@redhat.com> wrote:
> On 05/28/2018 05:27 AM, Florian Weimer wrote:
>> On 05/27/2018 04:36 PM, H.J. Lu wrote:
>>
>>> I'd like to avoid these messages.
>>
>> We could restrict the warning to LD_DEBUG=bindings.
>
> I agree that this would be a better solution.
>
> Disabling BIND_NOW is the wrong thing to do in this case.
>

Here is the updated patch.  It fails to back lazy binding only
if GOT is writable.


-- 
H.J.
From 8c7faa86f7c920c530a56100d7e2bbbe9c5b62e9 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Sun, 27 May 2018 07:15:35 -0700
Subject: [PATCH] x86: Fall back to lazy binding if possible

Since we can't call unrelocated IFUNC function to get the real function
addresss, this patch falls back to lazy binding for unrelocated IFUNC
symbol if lazy binding is possible.  Also in case of unresolved weak
reference in shared object which may be resolved later with lazy binding,
this patch sets up lazy binding if lazy binding is possible.

Tested on x86-64 and i686 with and without --enable-bind-now.

	[BZ #23176]
	[BZ #23240]
	* elf/Makefile (tests): Add reldep6a.
	(tests-internal): Add ifuncpreload1
	(modules-names): Add ifuncpreloadmod1a and ifuncpreloadmod1b.
	($(objpfx)reldep6a): New.
	($(objpfx)reldep6a.out): Likewise.
	(reldep6a-ENV): Likewise.
	($(objpfx)ifuncpreload1): Likewise.
	($(objpfx)ifuncpreload1.out): Likewise.
	(ifuncpreload1-ENV): Likewise.
	* elf/ifuncpreload1.c: New file.
	* elf/ifuncpreloadmod1a.c: Likewise.
	* elf/ifuncpreloadmod1b.c: Likewise.
	* elf/reldep6a.c: Likewise.
	* sysdeps/i386/dl-machine.h (elf_machine_runtime_setup): Always
	set up GOT.
	(elf_machine_rel): Fall back to lazy binding for unrelocated
	IFUNC symbol and unresolved weak reference if possible.   Issue
	IFUNC warning only for LD_DEBUG=bindings.
	* sysdeps/x86_64/dl-machine.h (elf_machine_runtime_setup): Always
	set up GOT.
	(elf_machine_rela): Fall back to lazy binding for unrelocated
	IFUNC symbol and unresolved weak reference in shared object if
	possible.  Issue IFUNC warning only for LD_DEBUG=bindings.
---
 elf/Makefile                | 18 ++++++++++---
 elf/ifuncpreload1.c         | 39 ++++++++++++++++++++++++++++
 elf/ifuncpreloadmod1a.c     | 23 ++++++++++++++++
 elf/ifuncpreloadmod1b.c     | 49 ++++++++++++++++++++++++++++++++++
 elf/reldep6a.c              |  1 +
 sysdeps/i386/dl-machine.h   | 52 +++++++++++++++++++++++++++++++------
 sysdeps/x86_64/dl-machine.h | 52 +++++++++++++++++++++++++++++++------
 7 files changed, 214 insertions(+), 20 deletions(-)
 create mode 100644 elf/ifuncpreload1.c
 create mode 100644 elf/ifuncpreloadmod1a.c
 create mode 100644 elf/ifuncpreloadmod1b.c
 create mode 100644 elf/reldep6a.c

diff --git a/elf/Makefile b/elf/Makefile
index 2dcd2b88e0..ee03d588eb 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -170,8 +170,8 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
 	 constload1 order noload filter \
 	 reldep reldep2 reldep3 reldep4 nodelete nodelete2 \
 	 nodlopen nodlopen2 lateglobal initfirst global \
-	 restest2 next dblload dblunload reldep5 reldep6 reldep7 reldep8 \
-	 tst-tls4 tst-tls5 \
+	 restest2 next dblload dblunload reldep5 reldep6 reldep6a \
+	 reldep7 reldep8 tst-tls4 tst-tls5 \
 	 tst-tls10 tst-tls11 tst-tls12 tst-tls13 tst-tls14 tst-tls15 \
 	 tst-tls16 tst-tls17 tst-tls18 tst-tls19 tst-tls-dlinfo \
 	 tst-align tst-align2 \
@@ -328,7 +328,7 @@ tests-internal += \
 	 ifuncmain1staticpic \
 	 ifuncmain2 ifuncmain2pic ifuncmain3 ifuncmain4 \
 	 ifuncmain5 ifuncmain5pic ifuncmain5staticpic \
-	 ifuncmain7 ifuncmain7pic
+	 ifuncmain7 ifuncmain7pic ifuncpreload1
 ifunc-test-modules = ifuncdep1 ifuncdep1pic ifuncdep2 ifuncdep2pic \
 		     ifuncdep5 ifuncdep5pic
 extra-test-objs += $(ifunc-test-modules:=.o)
@@ -339,7 +339,8 @@ ifunc-pie-tests = ifuncmain1pie ifuncmain1vispie ifuncmain1staticpie \
 tests-internal += $(ifunc-pie-tests)
 tests-pie += $(ifunc-pie-tests)
 endif
-modules-names += ifuncmod1 ifuncmod3 ifuncmod5 ifuncmod6
+modules-names += ifuncmod1 ifuncmod3 ifuncmod5 ifuncmod6 \
+		 ifuncpreloadmod1a ifuncpreloadmod1b
 endif
 endif
 
@@ -885,6 +886,10 @@ $(objpfx)reldep5.out: $(objpfx)reldepmod5.so $(objpfx)reldepmod6.so
 $(objpfx)reldep6: $(libdl)
 $(objpfx)reldep6.out: $(objpfx)reldep6mod3.so $(objpfx)reldep6mod4.so
 
+$(objpfx)reldep6a: $(libdl)
+$(objpfx)reldep6a.out: $(objpfx)reldep6mod3.so $(objpfx)reldep6mod4.so
+reldep6a-ENV = LD_BIND_NOW=1
+
 $(objpfx)reldep7: $(libdl)
 $(objpfx)reldep7.out: $(objpfx)reldep7mod1.so $(objpfx)reldep7mod2.so
 
@@ -1454,3 +1459,8 @@ tst-libc_dlvsym-static-ENV = \
 $(objpfx)tst-libc_dlvsym-static.out: $(objpfx)tst-libc_dlvsym-dso.so
 
 $(objpfx)tst-big-note: $(objpfx)tst-big-note-lib.so
+
+$(objpfx)ifuncpreload1: $(objpfx)ifuncpreloadmod1a.so
+$(objpfx)ifuncpreload1.out: $(objpfx)ifuncpreloadmod1b.so
+ifuncpreload1-ENV = \
+  LD_PRELOAD=$(objpfx)ifuncpreloadmod1b.so LD_BIND_NOW=1
diff --git a/elf/ifuncpreload1.c b/elf/ifuncpreload1.c
new file mode 100644
index 0000000000..172df33ac3
--- /dev/null
+++ b/elf/ifuncpreload1.c
@@ -0,0 +1,39 @@
+/* Test for relocation over with IFUNC symbols.
+   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/check.h>
+#include <support/support.h>
+#include <support/test-driver.h>
+
+extern void bar (char *, const char *, unsigned int);
+
+static int
+do_test (void)
+{
+  char dst[50];
+  const char src[] =
+    {
+      "This is a test"
+    };
+  bar (dst, src, sizeof (src));
+  if (__builtin_memcmp (dst, src, sizeof (src)) != 0)
+    __builtin_abort ();
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/ifuncpreloadmod1a.c b/elf/ifuncpreloadmod1a.c
new file mode 100644
index 0000000000..be9f8832b8
--- /dev/null
+++ b/elf/ifuncpreloadmod1a.c
@@ -0,0 +1,23 @@
+/* Shared module to test for relocation over with IFUNC symbols.
+   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/>.  */
+
+void
+bar (char *dst, const char *src, unsigned int size)
+{
+  __builtin_memmove (dst, src, size);
+}
diff --git a/elf/ifuncpreloadmod1b.c b/elf/ifuncpreloadmod1b.c
new file mode 100644
index 0000000000..1194ae20e4
--- /dev/null
+++ b/elf/ifuncpreloadmod1b.c
@@ -0,0 +1,49 @@
+/* Shared module to test for relocation over with IFUNC symbols.
+   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 <stddef.h>
+
+void *
+my_memmove(void *dst_p, const void *src_p, size_t n)
+{
+  const char *src = src_p;
+  char *dst = dst_p;
+  char *ret = dst;
+  if (src < dst)
+    {
+      dst += n;
+      src += n;
+      while (n--)
+	*--dst = *--src;
+    }
+  else
+    while (n--)
+      *dst++ = *src++;
+  return ret;
+}
+
+void *memmove (void *, const void *, size_t)
+  __attribute__ ((ifunc ("resolve_memmove")));
+
+typedef void *(*memmove_t) (void *, const void *, size_t);
+
+static memmove_t
+resolve_memmove (void)
+{
+  return my_memmove;
+}
diff --git a/elf/reldep6a.c b/elf/reldep6a.c
new file mode 100644
index 0000000000..28ed7008fa
--- /dev/null
+++ b/elf/reldep6a.c
@@ -0,0 +1 @@
+#include "reldep6.c"
diff --git a/sysdeps/i386/dl-machine.h b/sysdeps/i386/dl-machine.h
index 1afdcbd9ea..d5abd6c7dc 100644
--- a/sysdeps/i386/dl-machine.h
+++ b/sysdeps/i386/dl-machine.h
@@ -68,7 +68,12 @@ elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
   extern void _dl_runtime_resolve (Elf32_Word) attribute_hidden;
   extern void _dl_runtime_profile (Elf32_Word) attribute_hidden;
 
-  if (l->l_info[DT_JMPREL] && lazy)
+  /* Set up GOT if lazy binding is allowed since we may have to fall
+     back to lazy binding even with non-lazy binding is requested.   */
+  if (l->l_info[DT_JMPREL]
+      && (lazy
+	  || l->l_relro_size == 0
+	  || l->l_info[DT_BIND_NOW] == NULL))
     {
       /* The GOT entries for functions in the PLT have not yet been filled
 	 in.  Their initial contents will arrange when called to push an
@@ -332,16 +337,31 @@ elf_machine_rel (struct link_map *map, const Elf32_Rel *reloc,
 	      && sym_map->l_type != lt_executable
 	      && !sym_map->l_relocated)
 	    {
-	      const char *strtab
-		= (const char *) D_PTR (map, l_info[DT_STRTAB]);
-	      _dl_error_printf ("\
+	      /* NB: The symbol reference is resolved to IFUNC symbol
+		 from an unrelocated shared object.  Relocate the GOT
+		 entry to enable lazy binding.  Issue a warning if lazy
+		 binding isn't possible or GOT will be made read-only
+		 after shared objects have been relocated.  */
+	      if (r_type == R_386_JMP_SLOT
+		  && ((Elf32_Addr *) D_PTR (map,
+					    l_info[DT_PLTGOT]))[2] != 0)
+		value = map->l_addr + *reloc_addr;
+	      else if (__glibc_unlikely (GLRO(dl_debug_mask)
+					 & (DL_DEBUG_BINDINGS|DL_DEBUG_PRELINK)))
+		{
+		  const char *strtab
+		    = (const char *) D_PTR (map, l_info[DT_STRTAB]);
+		  _dl_error_printf ("\
 %s: Relink `%s' with `%s' for IFUNC symbol `%s'\n",
-				RTLD_PROGNAME, map->l_name,
-				sym_map->l_name,
-				strtab + refsym->st_name);
+				    RTLD_PROGNAME, map->l_name,
+				    sym_map->l_name,
+				    strtab + refsym->st_name);
+		  value = ((Elf32_Addr (*) (void)) value) ();
+		}
 	    }
+	  else
 # endif
-	  value = ((Elf32_Addr (*) (void)) value) ();
+	    value = ((Elf32_Addr (*) (void)) value) ();
 	}
 
       switch (r_type)
@@ -353,7 +373,23 @@ elf_machine_rel (struct link_map *map, const Elf32_Rel *reloc,
 	  break;
 # endif
 	case R_386_GLOB_DAT:
+# ifdef RTLD_BOOTSTRAP
+	case R_386_JMP_SLOT:
+# endif
+	  *reloc_addr = value;
+	  break;
+# ifndef RTLD_BOOTSTRAP
 	case R_386_JMP_SLOT:
+	  /* NB: If a weak symbol reference in shared object isn't
+	     resolved, relocate the GOT entry to enable lazy binding
+	     if possible.  */
+	  if (sym_map == NULL
+	      && map->l_type != lt_executable
+	      && ELF32_ST_BIND (refsym->st_info) == STB_WEAK
+	      && ((Elf32_Addr *) D_PTR (map,
+					l_info[DT_PLTGOT]))[2] != 0)
+	    value = map->l_addr + *reloc_addr;
+# endif
 	  *reloc_addr = value;
 	  break;
 
diff --git a/sysdeps/x86_64/dl-machine.h b/sysdeps/x86_64/dl-machine.h
index 1942ed5061..cf094aa03a 100644
--- a/sysdeps/x86_64/dl-machine.h
+++ b/sysdeps/x86_64/dl-machine.h
@@ -73,7 +73,12 @@ elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
   extern void _dl_runtime_profile_avx (ElfW(Word)) attribute_hidden;
   extern void _dl_runtime_profile_avx512 (ElfW(Word)) attribute_hidden;
 
-  if (l->l_info[DT_JMPREL] && lazy)
+  /* Set up GOT if lazy binding is allowed since we may have to fall
+     back to lazy binding even with non-lazy binding is requested.   */
+  if (l->l_info[DT_JMPREL]
+      && (lazy
+	  || l->l_relro_size == 0
+	  || l->l_info[DT_BIND_NOW] == NULL))
     {
       /* The GOT entries for functions in the PLT have not yet been filled
 	 in.  Their initial contents will arrange when called to push an
@@ -318,16 +323,31 @@ elf_machine_rela (struct link_map *map, const ElfW(Rela) *reloc,
 	      && sym_map->l_type != lt_executable
 	      && !sym_map->l_relocated)
 	    {
-	      const char *strtab
-		= (const char *) D_PTR (map, l_info[DT_STRTAB]);
-	      _dl_error_printf ("\
+	      /* NB: The symbol reference is resolved to IFUNC symbol
+		 from an unrelocated shared object.  Relocate the GOT
+		 entry to enable lazy binding.  Issue a warning if lazy
+		 binding isn't possible or GOT will be made read-only
+		 after shared objects have been relocated.  */
+	      if (r_type == R_X86_64_JUMP_SLOT
+		  && ((ElfW(Addr) *) D_PTR (map,
+					    l_info[DT_PLTGOT]))[2] != 0)
+		value = map->l_addr + *reloc_addr;
+	      else if (__glibc_unlikely (GLRO(dl_debug_mask)
+					 & (DL_DEBUG_BINDINGS|DL_DEBUG_PRELINK)))
+		{
+		  const char *strtab
+		    = (const char *) D_PTR (map, l_info[DT_STRTAB]);
+		  _dl_error_printf ("\
 %s: Relink `%s' with `%s' for IFUNC symbol `%s'\n",
-				RTLD_PROGNAME, map->l_name,
-				sym_map->l_name,
-				strtab + refsym->st_name);
+				    RTLD_PROGNAME, map->l_name,
+				    sym_map->l_name,
+				    strtab + refsym->st_name);
+		  value = ((ElfW(Addr) (*) (void)) value) ();
+		}
 	    }
+	  else
 # endif
-	  value = ((ElfW(Addr) (*) (void)) value) ();
+	    value = ((ElfW(Addr) (*) (void)) value) ();
 	}
 
       switch (r_type)
@@ -348,7 +368,23 @@ elf_machine_rela (struct link_map *map, const ElfW(Rela) *reloc,
 	  value = sym->st_size;
 # endif
 	case R_X86_64_GLOB_DAT:
+# ifdef RTLD_BOOTSTRAP
+	case R_X86_64_JUMP_SLOT:
+# endif
+	  *reloc_addr = value + reloc->r_addend;
+	  break;
+# ifndef RTLD_BOOTSTRAP
 	case R_X86_64_JUMP_SLOT:
+	  /* NB: If a weak symbol reference in shared object isn't
+	     resolved, relocate the GOT entry to enable lazy binding
+	     if possible.  */
+	  if (sym_map == NULL
+	      && map->l_type != lt_executable
+	      && ELFW(ST_BIND) (refsym->st_info) == STB_WEAK
+	      && ((ElfW(Addr) *) D_PTR (map,
+					l_info[DT_PLTGOT]))[2] != 0)
+	    value = map->l_addr + *reloc_addr;
+# endif
 	  *reloc_addr = value + reloc->r_addend;
 	  break;
 
-- 
2.17.0


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]