@@ -13,6 +13,7 @@ AUTHORS
copyr-*
copying.*
glibc-*
+!glibc-var.h
configparms
@@ -35,6 +35,7 @@ dl-routines = $(addprefix dl-,load lookup object reloc deps hwcaps \
ifeq (yes,$(use-ldconfig))
dl-routines += dl-cache
endif
+dl-routines += dl-glibc-var
all-dl-routines = $(dl-routines) $(sysdep-dl-routines)
# But they are absent from the shared libc, because that code is in ld.so.
elide-routines.os = $(all-dl-routines) dl-support enbl-secure dl-origin \
@@ -52,9 +52,11 @@ ld {
}
GLIBC_PRIVATE {
# Those are in the dynamic linker, but used by libc.so.
+ __glibc_var_init;
__libc_enable_secure;
_dl_allocate_tls; _dl_allocate_tls_init;
_dl_argv; _dl_find_dso_for_object; _dl_get_tls_static_info;
+ _dl_glibc_var;
_dl_deallocate_tls; _dl_make_stack_executable; _dl_out_of_memory;
_dl_rtld_di_serinfo; _dl_starting_up; _dl_tls_setup;
_rtld_global; _rtld_global_ro;
@@ -22,10 +22,10 @@
#include <ldsodefs.h>
/* Walk through the environment of the process and return all entries
- starting with `LD_'. */
+ starting with `LD_' or 'GLIBC_'. */
char *
internal_function
-_dl_next_ld_env_entry (char ***position)
+_dl_next_ld_env_entry (char ***position, char *first)
{
char **current = *position;
char *result = NULL;
@@ -35,6 +35,7 @@ _dl_next_ld_env_entry (char ***position)
if (__builtin_expect ((*current)[0] == 'L', 0)
&& (*current)[1] == 'D' && (*current)[2] == '_')
{
+ *first = (*current)[0];
result = &(*current)[3];
/* Save current position for next visit. */
@@ -43,6 +44,20 @@ _dl_next_ld_env_entry (char ***position)
break;
}
+ if (__builtin_expect ((*current)[0] == 'G', 0)
+ && (*current)[1] == 'L' && (*current)[2] == 'I'
+ && (*current)[3] == 'B' && (*current)[4] == 'C'
+ && (*current)[5] == '_')
+ {
+ *first = (*current)[0];
+ result = &(*current)[6];
+
+ /* Save current position for next visit. */
+ *position = ++current;
+
+ break;
+ }
+
++current;
}
new file mode 100644
@@ -0,0 +1,110 @@
+/* Fast access to GLIBC_* environment variables, without having to walk
+ the environment multiple times.
+ Copyright (C) 2013 Free Software Foundation, Inc.
+
+ 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 <string.h>
+#include <glibc-var.h>
+
+#define _GLIBC_VAR_ENTRY(a) [GLIBC_VAR_##a] = { #a, sizeof(#a) - 1, NULL }
+
+struct glibc_var _dl_glibc_var[] =
+{
+ _GLIBC_VAR_ENTRY(PTHREAD_MUTEX),
+ _GLIBC_VAR_ENTRY(PTHREAD_RWLOCK),
+ /* Add more GLIBC_ variables here. */
+ /* Make the names as long as possible to pass code review. */
+ [GLIBC_VAR_MAX] = { NULL, 0, NULL }
+};
+
+internal_function void
+__record_glibc_var (char *name, int len, char *val)
+{
+ int i;
+
+ for (i = 0; i < GLIBC_VAR_MAX; i++)
+ {
+ struct glibc_var *v = &_dl_glibc_var[i];
+
+ if (len == v->len && memcmp (v->name, name, v->len) == 0)
+ {
+ v->val = val;
+ break;
+ }
+ }
+ /* Ignore unknown GLIBC_ variables. */
+}
+
+#ifndef SHARED
+
+/* If SHARED the env walk is shared with rtld.c. */
+
+static char *
+next_env_entry (char first, char ***position)
+{
+ char **current = *position;
+ char *result = NULL;
+
+ while (*current != NULL)
+ {
+ if ((*current)[0] == first)
+ {
+ result = *current;
+ *position = ++current;
+ break;
+ }
+
+ ++current;
+ }
+
+ return result;
+}
+
+/* May be called from libpthread. */
+
+void
+__glibc_var_init (int argc __attribute__ ((unused)),
+ char **argv __attribute__ ((unused)),
+ char **environ)
+{
+ char *envline;
+ static int initialized;
+
+ if (initialized != 0)
+ return;
+ initialized = 1;
+
+ while ((envline = next_env_entry ('G', &environ)) != NULL)
+ {
+ if (envline[1] == 'L' && envline[2] == 'I' && envline[3] == 'B'
+ && envline[4] == 'C' && envline[5] == '_')
+ {
+ char *e = envline + 6;
+ while (*e && *e != '=')
+ e++;
+ if (*e == 0)
+ continue;
+ __record_glibc_var (envline + 6, e - (envline + 6), e + 1);
+ }
+ }
+}
+
+void (*const __glibc_var_init_array []) (int, char **, char **)
+ __attribute__ ((section (".preinit_array"), aligned (sizeof (void *)))) =
+{
+ &__glibc_var_init
+};
+#endif
@@ -41,6 +41,7 @@
#include <tls.h>
#include <stap-probe.h>
#include <stackinfo.h>
+#include <glibc-var.h>
#include <assert.h>
@@ -2338,7 +2339,9 @@ process_dl_audit (char *str)
/* Process all environments variables the dynamic linker must recognize.
Since all of them start with `LD_' we are a bit smarter while finding
- all the entries. */
+ all the entries. In addition we also save a bunch of GLIBC_ variables
+ used by other parts of glibc, so that each startup only has to walk the
+ environment once. */
extern char **_environ attribute_hidden;
@@ -2349,12 +2352,13 @@ process_envvars (enum mode *modep)
char *envline;
enum mode mode = normal;
char *debug_output = NULL;
+ char first;
/* This is the default place for profiling data file. */
GLRO(dl_profile_output)
= &"/var/tmp\0/var/profile"[__libc_enable_secure ? 9 : 0];
- while ((envline = _dl_next_ld_env_entry (&runp)) != NULL)
+ while ((envline = _dl_next_ld_env_entry (&runp, &first)) != NULL)
{
size_t len = 0;
@@ -2367,6 +2371,15 @@ process_envvars (enum mode *modep)
invalid memory below. */
continue;
+ /* Must be for GLIBC_ */
+ if (first == 'G')
+ {
+ __record_glibc_var (envline, len, envline + len + 1);
+ continue;
+ }
+
+ /* Must be for LD_ */
+
switch (len)
{
case 4:
new file mode 100644
@@ -0,0 +1,49 @@
+/* Fast access to GLIBC_* environment variables, without having to walk
+ the environment. Register new ones in in elf/glibc-var.c
+ Copyright (C) 2013 Free Software Foundation, Inc.
+
+ 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/>. */
+
+#ifndef _GLIBC_VAR_H
+# define _GLIBC_VAR_H 1
+
+# include <libc-symbols.h>
+
+enum
+{
+ GLIBC_VAR_PTHREAD_MUTEX,
+ GLIBC_VAR_PTHREAD_RWLOCK,
+ GLIBC_VAR_MAX
+};
+
+struct glibc_var
+{
+ const char *name;
+ int len;
+ char *val;
+};
+
+extern struct glibc_var _dl_glibc_var[];
+extern void __record_glibc_var (char *name, int len, char *val) internal_function;
+
+/* Call this if you're in a constructor that may run before glibc-var's. */
+# ifndef SHARED
+extern void __glibc_var_init (int ac, char **av, char **env);
+# else
+/* For shared this is always done in the dynamic linker early enough. */
+# define __glibc_var_init(a,b,c) do {} while (0)
+# endif
+
+#endif
@@ -893,9 +893,9 @@ extern void _dl_mcount_wrapper (void *selfpc);
/* Show the members of the auxiliary array passed up from the kernel. */
extern void _dl_show_auxv (void) internal_function;
-/* Return all environment variables starting with `LD_', one after the
- other. */
-extern char *_dl_next_ld_env_entry (char ***position) internal_function;
+/* Return all environment variables starting with `LD_' or `GLIBC_', one
+ after the other. */
+extern char *_dl_next_ld_env_entry (char ***position, char *first) internal_function;
/* Return an array with the names of the important hardware capabilities. */
extern const struct r_strlenpair *_dl_important_hwcaps (const char *platform,
From: Andi Kleen <ak@linux.intel.com> This adds a general "vectorized getenv" for glibc internal use. The motivation is to allow subsystems to access environment variables cheaply without having to rescan the environment completely. The dynamic linker already walks the environment to look for its LD_* variables. Extend this code to look for a number of pre-registered GLIBC_* variables too. This can be done at basically no additional cost. The only two variables currently pre-registered are for the lock elision code. For static builds which do not use the dynamic linker a similar environment walking function is called at init time. The variable values are saved in a global array that can be directly accessed by libc and related libraries like libpthread. 2014-12-17 Andi Kleen <ak@linux.intel.com> * elf/Makefile (dl-glibc-var.o): Add new file. * elf/Versions (_dl_glibc_var, __glibc_var_init): Export new symbols as GLIBC_PRIVATE. * elf/dl-environ.c (_dl_next_ld_env_entry): Look for GLIBC_ prefixes too. Return first character in new argument. * elf/dl-glibc-var.c (struct glibc_var): Define global array. (__record_glibc_var): New function to save glibc variables. (next_env_entry): Function to walk environment. (__glibc_var_init): Fallback function for static builds. * elf/rtld.c: Update comment. (process_envvars): Handle GLIBC_ variables and call __record_glibc_var. * include/glibc-var.h: New file. * sysdeps/generic/ldsodefs.h (_dl_next_ld_env_entry): Update prototype. * .gitignore: Add exception for glibc-var.h. --- .gitignore | 1 + elf/Makefile | 1 + elf/Versions | 2 + elf/dl-environ.c | 19 +++++++- elf/dl-glibc-var.c | 110 +++++++++++++++++++++++++++++++++++++++++++++ elf/rtld.c | 17 ++++++- include/glibc-var.h | 49 ++++++++++++++++++++ sysdeps/generic/ldsodefs.h | 6 +-- 8 files changed, 198 insertions(+), 7 deletions(-) create mode 100644 elf/dl-glibc-var.c create mode 100644 include/glibc-var.h