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