abe59f
Added $(objpfx)tst-audit19a: $(libdl) to elf/Makefile since
abe59f
we still need $(libdl) in RHEL8.
abe59f
abe59f
commit 063f9ba220f434c7f30dd65c4cff17c0c458a7cf
abe59f
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
abe59f
Date:   Wed Jun 30 10:24:09 2021 -0300
abe59f
abe59f
    elf: Avoid unnecessary slowdown from profiling with audit (BZ#15533)
abe59f
    
abe59f
    The rtld-audit interfaces introduces a slowdown due to enabling
abe59f
    profiling instrumentation (as if LD_AUDIT implied LD_PROFILE).
abe59f
    However, instrumenting is only necessary if one of audit libraries
abe59f
    provides PLT callbacks (la_pltenter or la_pltexit symbols).  Otherwise,
abe59f
    the slowdown can be avoided.
abe59f
    
abe59f
    The following patch adjusts the logic that enables profiling to iterate
abe59f
    over all audit modules and check if any of those provides a PLT hook.
abe59f
    To keep la_symbind to work even without PLT callbacks, _dl_fixup now
abe59f
    calls the audit callback if the modules implements it.
abe59f
    
abe59f
    Co-authored-by: Alexander Monakov <amonakov@ispras.ru>
abe59f
    
abe59f
    Checked on x86_64-linux-gnu, i686-linux-gnu, and aarch64-linux-gnu.
abe59f
    
abe59f
    Reviewed-by: Florian Weimer <fweimer@redhat.com>
abe59f
abe59f
Conflicts:
abe59f
	elf/Makefile
abe59f
abe59f
diff --git a/elf/Makefile b/elf/Makefile
abe59f
index 08a32a712a34f2cc..0cc03ffe2984ee50 100644
abe59f
--- a/elf/Makefile
abe59f
+++ b/elf/Makefile
abe59f
@@ -221,12 +221,14 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
abe59f
 	 tst-dlmopen-gethostbyname \
abe59f
 	 tst-audit17 \
abe59f
 	 tst-audit18 \
abe59f
+	 tst-audit19b \
abe59f
 #	 reldep9
abe59f
 tests-internal += loadtest unload unload2 circleload1 \
abe59f
 	 neededtest neededtest2 neededtest3 neededtest4 \
abe59f
 	 tst-tls3 tst-tls6 tst-tls7 tst-tls8 tst-dlmopen2 \
abe59f
 	 tst-ptrguard1 tst-stackguard1 tst-libc_dlvsym \
abe59f
-	 tst-create_format1 tst-tls-surplus tst-dl-hwcaps_split
abe59f
+	 tst-create_format1 tst-tls-surplus tst-dl-hwcaps_split \
abe59f
+	 tst-audit19a
abe59f
 tests-container += tst-pldd tst-preload-pthread-libc
abe59f
 ifeq ($(build-hardcoded-path-in-tests),yes)
abe59f
 tests += tst-dlopen-aout
abe59f
@@ -358,6 +360,9 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
abe59f
 		tst-dlmopen-gethostbyname-mod \
abe59f
 		tst-auditmod18 \
abe59f
 		tst-audit18mod \
abe59f
+		tst-auditmod19a \
abe59f
+		tst-auditmod19b \
abe59f
+		tst-audit19bmod \
abe59f
 
abe59f
 # Most modules build with _ISOMAC defined, but those filtered out
abe59f
 # depend on internal headers.
abe59f
@@ -1548,6 +1553,14 @@ $(objpfx)tst-audit18.out: $(objpfx)tst-auditmod18.so \
abe59f
 			  $(objpfx)tst-audit18mod.so
abe59f
 tst-audit18-ARGS = -- $(host-test-program-cmd)
abe59f
 
abe59f
+$(objpfx)tst-audit19a: $(libdl)
abe59f
+$(objpfx)tst-audit19a.out: $(objpfx)tst-auditmod19a.so
abe59f
+tst-audit19a-ENV = LD_AUDIT=$(objpfx)tst-auditmod19a.so
abe59f
+
abe59f
+$(objpfx)tst-audit19b.out: $(objpfx)tst-auditmod19b.so
abe59f
+$(objpfx)tst-audit19b: $(objpfx)tst-audit19bmod.so
abe59f
+tst-audit19b-ARGS = -- $(host-test-program-cmd)
abe59f
+
abe59f
 # tst-sonamemove links against an older implementation of the library.
abe59f
 LDFLAGS-tst-sonamemove-linkmod1.so = \
abe59f
   -Wl,--version-script=tst-sonamemove-linkmod1.map \
abe59f
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
abe59f
index 19de5de067a5ef07..7a84b1fa8c3a7fdd 100644
abe59f
--- a/elf/dl-reloc.c
abe59f
+++ b/elf/dl-reloc.c
abe59f
@@ -178,12 +178,28 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
abe59f
   int skip_ifunc = reloc_mode & __RTLD_NOIFUNC;
abe59f
 
abe59f
 #ifdef SHARED
abe59f
+  bool consider_symbind = false;
abe59f
   /* If we are auditing, install the same handlers we need for profiling.  */
abe59f
   if ((reloc_mode & __RTLD_AUDIT) == 0)
abe59f
-    consider_profiling |= GLRO(dl_audit) != NULL;
abe59f
+    {
abe59f
+      struct audit_ifaces *afct = GLRO(dl_audit);
abe59f
+      for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
abe59f
+	{
abe59f
+	  /* Profiling is needed only if PLT hooks are provided.  */
abe59f
+	  if (afct->ARCH_LA_PLTENTER != NULL
abe59f
+	      || afct->ARCH_LA_PLTEXIT != NULL)
abe59f
+	    consider_profiling = 1;
abe59f
+	  if (afct->symbind != NULL)
abe59f
+	    consider_symbind = true;
abe59f
+
abe59f
+	  afct = afct->next;
abe59f
+	}
abe59f
+    }
abe59f
 #elif defined PROF
abe59f
   /* Never use dynamic linker profiling for gprof profiling code.  */
abe59f
 # define consider_profiling 0
abe59f
+#else
abe59f
+# define consider_symbind 0
abe59f
 #endif
abe59f
 
abe59f
   if (l->l_relocated)
abe59f
@@ -278,7 +294,7 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
abe59f
     ELF_DYNAMIC_RELOCATE (l, scope, lazy, consider_profiling, skip_ifunc);
abe59f
 
abe59f
 #ifndef PROF
abe59f
-    if (__glibc_unlikely (consider_profiling)
abe59f
+    if ((consider_profiling || consider_symbind)
abe59f
 	&& l->l_info[DT_PLTRELSZ] != NULL)
abe59f
       {
abe59f
 	/* Allocate the array which will contain the already found
abe59f
diff --git a/elf/dl-runtime.c b/elf/dl-runtime.c
abe59f
index ec0b2164825fa538..71ec65264ff780fb 100644
abe59f
--- a/elf/dl-runtime.c
abe59f
+++ b/elf/dl-runtime.c
abe59f
@@ -123,6 +123,37 @@ _dl_fixup (
abe59f
       && __builtin_expect (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC, 0))
abe59f
     value = elf_ifunc_invoke (DL_FIXUP_VALUE_ADDR (value));
abe59f
 
abe59f
+#ifdef SHARED
abe59f
+  /* Auditing checkpoint: we have a new binding.  Provide the auditing
abe59f
+     libraries the possibility to change the value and tell us whether further
abe59f
+     auditing is wanted.
abe59f
+     The l_reloc_result is only allocated if there is an audit module which
abe59f
+     provides a la_symbind.  */
abe59f
+  if (l->l_reloc_result != NULL)
abe59f
+    {
abe59f
+      /* This is the address in the array where we store the result of previous
abe59f
+	 relocations.  */
abe59f
+      struct reloc_result *reloc_result
abe59f
+	= &l->l_reloc_result[reloc_index (pltgot, reloc_arg, sizeof (PLTREL))];
abe59f
+      unsigned int init = atomic_load_acquire (&reloc_result->init);
abe59f
+      if (init == 0)
abe59f
+	{
abe59f
+	  _dl_audit_symbind (l, reloc_result, sym, &value, result);
abe59f
+
abe59f
+	  /* Store the result for later runs.  */
abe59f
+	  if (__glibc_likely (! GLRO(dl_bind_not)))
abe59f
+	    {
abe59f
+	      reloc_result->addr = value;
abe59f
+	      /* Guarantee all previous writes complete before init is
abe59f
+		 updated.  See CONCURRENCY NOTES below.  */
abe59f
+	      atomic_store_release (&reloc_result->init, 1);
abe59f
+	    }
abe59f
+	}
abe59f
+      else
abe59f
+	value = reloc_result->addr;
abe59f
+    }
abe59f
+#endif
abe59f
+
abe59f
   /* Finally, fix up the plt itself.  */
abe59f
   if (__glibc_unlikely (GLRO(dl_bind_not)))
abe59f
     return value;
abe59f
diff --git a/elf/rtld.c b/elf/rtld.c
abe59f
index 767acd122262b824..2994578ba3a5f911 100644
abe59f
--- a/elf/rtld.c
abe59f
+++ b/elf/rtld.c
abe59f
@@ -1027,13 +1027,7 @@ ERROR: audit interface '%s' requires version %d (maximum supported version %d);
abe59f
     "la_objsearch\0"
abe59f
     "la_objopen\0"
abe59f
     "la_preinit\0"
abe59f
-#if __ELF_NATIVE_CLASS == 32
abe59f
-    "la_symbind32\0"
abe59f
-#elif __ELF_NATIVE_CLASS == 64
abe59f
-    "la_symbind64\0"
abe59f
-#else
abe59f
-# error "__ELF_NATIVE_CLASS must be defined"
abe59f
-#endif
abe59f
+    LA_SYMBIND "\0"
abe59f
 #define STRING(s) __STRING (s)
abe59f
     "la_" STRING (ARCH_LA_PLTENTER) "\0"
abe59f
     "la_" STRING (ARCH_LA_PLTEXIT) "\0"
abe59f
diff --git a/elf/tst-audit19a.c b/elf/tst-audit19a.c
abe59f
new file mode 100644
abe59f
index 0000000000000000..035cde9351c2711b
abe59f
--- /dev/null
abe59f
+++ b/elf/tst-audit19a.c
abe59f
@@ -0,0 +1,38 @@
abe59f
+/* Check if DT_AUDIT a module without la_plt{enter,exit} symbols does not incur
abe59f
+   in profiling (BZ#15533).
abe59f
+   Copyright (C) 2021 Free Software Foundation, Inc.
abe59f
+   This file is part of the GNU C Library.
abe59f
+
abe59f
+   The GNU C Library is free software; you can redistribute it and/or
abe59f
+   modify it under the terms of the GNU Lesser General Public
abe59f
+   License as published by the Free Software Foundation; either
abe59f
+   version 2.1 of the License, or (at your option) any later version.
abe59f
+
abe59f
+   The GNU C Library is distributed in the hope that it will be useful,
abe59f
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
abe59f
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
abe59f
+   Lesser General Public License for more details.
abe59f
+
abe59f
+   You should have received a copy of the GNU Lesser General Public
abe59f
+   License along with the GNU C Library; if not, see
abe59f
+   <https://www.gnu.org/licenses/>.  */
abe59f
+
abe59f
+#include <link.h>
abe59f
+#include <support/xdlfcn.h>
abe59f
+#include <support/check.h>
abe59f
+
abe59f
+static int
abe59f
+do_test (void)
abe59f
+{
abe59f
+  void *h = xdlopen ("tst-auditmod19a.so", RTLD_NOW);
abe59f
+
abe59f
+  struct link_map *lmap;
abe59f
+  TEST_VERIFY_EXIT (dlinfo (h, RTLD_DI_LINKMAP, &lmap) == 0);
abe59f
+
abe59f
+  /* The internal array is only allocated if profiling is enabled.  */
abe59f
+  TEST_VERIFY (lmap->l_reloc_result == NULL);
abe59f
+
abe59f
+  return 0;
abe59f
+}
abe59f
+
abe59f
+#include <support/test-driver.c>
abe59f
diff --git a/elf/tst-audit19b.c b/elf/tst-audit19b.c
abe59f
new file mode 100644
abe59f
index 0000000000000000..da015734f24e0d79
abe59f
--- /dev/null
abe59f
+++ b/elf/tst-audit19b.c
abe59f
@@ -0,0 +1,94 @@
abe59f
+/* Check if DT_AUDIT a module with la_plt{enter,exit} call la_symbind
abe59f
+   for lazy resolution.
abe59f
+   Copyright (C) 2021 Free Software Foundation, Inc.
abe59f
+   This file is part of the GNU C Library.
abe59f
+
abe59f
+   The GNU C Library is free software; you can redistribute it and/or
abe59f
+   modify it under the terms of the GNU Lesser General Public
abe59f
+   License as published by the Free Software Foundation; either
abe59f
+   version 2.1 of the License, or (at your option) any later version.
abe59f
+
abe59f
+   The GNU C Library is distributed in the hope that it will be useful,
abe59f
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
abe59f
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
abe59f
+   Lesser General Public License for more details.
abe59f
+
abe59f
+   You should have received a copy of the GNU Lesser General Public
abe59f
+   License along with the GNU C Library; if not, see
abe59f
+   <https://www.gnu.org/licenses/>.  */
abe59f
+
abe59f
+#include <getopt.h>
abe59f
+#include <support/capture_subprocess.h>
abe59f
+#include <support/check.h>
abe59f
+#include <support/xstdio.h>
abe59f
+#include <stdlib.h>
abe59f
+#include <string.h>
abe59f
+#include <stdbool.h>
abe59f
+
abe59f
+static int restart;
abe59f
+#define CMDLINE_OPTIONS \
abe59f
+  { "restart", no_argument, &restart, 1 },
abe59f
+
abe59f
+int tst_audit18bmod1_func (void);
abe59f
+
abe59f
+static int
abe59f
+handle_restart (void)
abe59f
+{
abe59f
+  TEST_COMPARE (tst_audit18bmod1_func (), 10);
abe59f
+  return 0;
abe59f
+}
abe59f
+
abe59f
+static inline bool
abe59f
+startswith (const char *str, const char *pre)
abe59f
+{
abe59f
+  size_t lenpre = strlen (pre);
abe59f
+  size_t lenstr = strlen (str);
abe59f
+  return lenstr < lenpre ? false : memcmp (pre, str, lenpre) == 0;
abe59f
+}
abe59f
+
abe59f
+static int
abe59f
+do_test (int argc, char *argv[])
abe59f
+{
abe59f
+  /* We must have either:
abe59f
+     - One our fource parameters left if called initially:
abe59f
+       + path to ld.so         optional
abe59f
+       + "--library-path"      optional
abe59f
+       + the library path      optional
abe59f
+       + the application name  */
abe59f
+
abe59f
+  if (restart)
abe59f
+    return handle_restart ();
abe59f
+
abe59f
+  char *spargv[9];
abe59f
+  int i = 0;
abe59f
+  for (; i < argc - 1; i++)
abe59f
+    spargv[i] = argv[i + 1];
abe59f
+  spargv[i++] = (char *) "--direct";
abe59f
+  spargv[i++] = (char *) "--restart";
abe59f
+  spargv[i] = NULL;
abe59f
+
abe59f
+  setenv ("LD_AUDIT", "tst-auditmod18b.so", 0);
abe59f
+  struct support_capture_subprocess result
abe59f
+    = support_capture_subprogram (spargv[0], spargv);
abe59f
+  support_capture_subprocess_check (&result, "tst-audit18b", 0, sc_allow_stderr);
abe59f
+
abe59f
+  bool find_symbind = false;
abe59f
+
abe59f
+  FILE *out = fmemopen (result.err.buffer, result.err.length, "r");
abe59f
+  TEST_VERIFY (out != NULL);
abe59f
+  char *buffer = NULL;
abe59f
+  size_t buffer_length = 0;
abe59f
+  while (xgetline (&buffer, &buffer_length, out))
abe59f
+    if (startswith (buffer, "la_symbind: tst_audit18bmod1_func") == 0)
abe59f
+      find_symbind = true;
abe59f
+
abe59f
+  TEST_COMPARE (find_symbind, true);
abe59f
+
abe59f
+  free (buffer);
abe59f
+  xfclose (out);
abe59f
+
abe59f
+  return 0;
abe59f
+}
abe59f
+
abe59f
+#define TEST_FUNCTION_ARGV do_test
abe59f
+#include <support/test-driver.c>
abe59f
diff --git a/elf/tst-audit19bmod.c b/elf/tst-audit19bmod.c
abe59f
new file mode 100644
abe59f
index 0000000000000000..9ffdcd8f3ffbc38e
abe59f
--- /dev/null
abe59f
+++ b/elf/tst-audit19bmod.c
abe59f
@@ -0,0 +1,23 @@
abe59f
+/* Extra module for tst-audit18b.
abe59f
+   Copyright (C) 2021 Free Software Foundation, Inc.
abe59f
+   This file is part of the GNU C Library.
abe59f
+
abe59f
+   The GNU C Library is free software; you can redistribute it and/or
abe59f
+   modify it under the terms of the GNU Lesser General Public
abe59f
+   License as published by the Free Software Foundation; either
abe59f
+   version 2.1 of the License, or (at your option) any later version.
abe59f
+
abe59f
+   The GNU C Library is distributed in the hope that it will be useful,
abe59f
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
abe59f
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
abe59f
+   Lesser General Public License for more details.
abe59f
+
abe59f
+   You should have received a copy of the GNU Lesser General Public
abe59f
+   License along with the GNU C Library; if not, see
abe59f
+   <https://www.gnu.org/licenses/>.  */
abe59f
+
abe59f
+int
abe59f
+tst_audit18bmod1_func (void)
abe59f
+{
abe59f
+  return 10;
abe59f
+}
abe59f
diff --git a/elf/tst-auditmod19a.c b/elf/tst-auditmod19a.c
abe59f
new file mode 100644
abe59f
index 0000000000000000..f58204099457743d
abe59f
--- /dev/null
abe59f
+++ b/elf/tst-auditmod19a.c
abe59f
@@ -0,0 +1,25 @@
abe59f
+/* Audit module for tst-audit18a.
abe59f
+   Copyright (C) 2021 Free Software Foundation, Inc.
abe59f
+   This file is part of the GNU C Library.
abe59f
+
abe59f
+   The GNU C Library is free software; you can redistribute it and/or
abe59f
+   modify it under the terms of the GNU Lesser General Public
abe59f
+   License as published by the Free Software Foundation; either
abe59f
+   version 2.1 of the License, or (at your option) any later version.
abe59f
+
abe59f
+   The GNU C Library is distributed in the hope that it will be useful,
abe59f
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
abe59f
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
abe59f
+   Lesser General Public License for more details.
abe59f
+
abe59f
+   You should have received a copy of the GNU Lesser General Public
abe59f
+   License along with the GNU C Library; if not, see
abe59f
+   <https://www.gnu.org/licenses/>.  */
abe59f
+
abe59f
+#include <link.h>
abe59f
+
abe59f
+unsigned int
abe59f
+la_version (unsigned int version)
abe59f
+{
abe59f
+  return LAV_CURRENT;
abe59f
+}
abe59f
diff --git a/elf/tst-auditmod19b.c b/elf/tst-auditmod19b.c
abe59f
new file mode 100644
abe59f
index 0000000000000000..e2248b2a75946746
abe59f
--- /dev/null
abe59f
+++ b/elf/tst-auditmod19b.c
abe59f
@@ -0,0 +1,46 @@
abe59f
+/* Audit module for tst-audit18b.
abe59f
+   Copyright (C) 2021 Free Software Foundation, Inc.
abe59f
+   This file is part of the GNU C Library.
abe59f
+
abe59f
+   The GNU C Library is free software; you can redistribute it and/or
abe59f
+   modify it under the terms of the GNU Lesser General Public
abe59f
+   License as published by the Free Software Foundation; either
abe59f
+   version 2.1 of the License, or (at your option) any later version.
abe59f
+
abe59f
+   The GNU C Library is distributed in the hope that it will be useful,
abe59f
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
abe59f
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
abe59f
+   Lesser General Public License for more details.
abe59f
+
abe59f
+   You should have received a copy of the GNU Lesser General Public
abe59f
+   License along with the GNU C Library; if not, see
abe59f
+   <https://www.gnu.org/licenses/>.  */
abe59f
+
abe59f
+#include <link.h>
abe59f
+#include <string.h>
abe59f
+#include <stdio.h>
abe59f
+
abe59f
+unsigned int
abe59f
+la_version (unsigned int version)
abe59f
+{
abe59f
+  return LAV_CURRENT;
abe59f
+}
abe59f
+
abe59f
+unsigned int
abe59f
+la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie)
abe59f
+{
abe59f
+  return LA_FLG_BINDTO | LA_FLG_BINDFROM;
abe59f
+}
abe59f
+
abe59f
+uintptr_t
abe59f
+#if __ELF_NATIVE_CLASS == 32
abe59f
+la_symbind32 (Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook,
abe59f
+	      uintptr_t *defcook, unsigned int *flags, const char *symname)
abe59f
+#else
abe59f
+la_symbind64 (Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook,
abe59f
+	      uintptr_t *defcook, unsigned int *flags, const char *symname)
abe59f
+#endif
abe59f
+{
abe59f
+  fprintf (stderr, "la_symbind: %s\n", symname);
abe59f
+  return sym->st_value;
abe59f
+}
abe59f
diff --git a/include/link.h b/include/link.h
abe59f
index cdd011f59445e490..dd491989beb41353 100644
abe59f
--- a/include/link.h
abe59f
+++ b/include/link.h
abe59f
@@ -353,8 +353,10 @@ struct link_map
abe59f
 
abe59f
 #if __ELF_NATIVE_CLASS == 32
abe59f
 # define symbind symbind32
abe59f
+# define LA_SYMBIND "la_symbind32"
abe59f
 #elif __ELF_NATIVE_CLASS == 64
abe59f
 # define symbind symbind64
abe59f
+# define LA_SYMBIND "la_symbind64"
abe59f
 #else
abe59f
 # error "__ELF_NATIVE_CLASS must be defined"
abe59f
 #endif