This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
[PATCH] Save reference to kernel provided envp in the linker
- From: Siddhesh Poyarekar <siddhesh at redhat dot com>
- To: libc-alpha at sourceware dot org
- Date: Fri, 21 Dec 2012 18:48:56 +0530
- Subject: [PATCH] Save reference to kernel provided envp in the linker
Hi,
A circular dependency between libpthread.so and libc.so ensures that
libpthread.so constructor is called first. Due to this, libpthread.so
constructor does not have access to the environment variables, since a
relocation wipes out the __environ that the dynamic linker had, which
gets updated when the libc.so constructor is called.
A new reference within the dynamic linker saves the kernel-provided
envp, which getenv falls back to if the libc.so constructor has not
been called yet.
I have verified that this change does not cause any regressions. I
have also verified using some dummy code within nptl that it
recognizes environment variables with this patch and not without it.
OK for 2.18?
Siddhesh
ChangeLog:
* csu/init-first.c (__libc_environ_initialized): New flag.
(_init): Set __LIBC_ENVIRON_INITIALIZED once __ENVIRON is
initialized.
* elf/dl-support.c (_dl_environ): New variable.
* elf/dl-sysdep.c (_dl_sysdep_start): Set _DL_ENVIRON with the
kernel provided ENVP.
* elf/rtld.c (_rtld_global_ro): Initialize member _DL_ENVIRON.
* stdlib/getenv.c (getenv): Fall back to dl_environ if
__LIBC_ENVIRON_INITIALIZED is not set.
* sysdeps/generic/ldsodefs.h (struct rtld_global_ro): New member
_DL_ENVIRON.
diff --git a/csu/init-first.c b/csu/init-first.c
index 0cfabbc..f40c58c 100644
--- a/csu/init-first.c
+++ b/csu/init-first.c
@@ -33,6 +33,8 @@
used in the process. Safe assumption if initializer never runs. */
int __libc_multiple_libcs attribute_hidden = 1;
+int __libc_environ_initialized attribute_hidden = 0;
+
/* Remember the command line argument and enviroment contents for
later calls of initializers for dynamic libraries. */
int __libc_argc attribute_hidden;
@@ -74,6 +76,8 @@ _init (int argc, char **argv, char **envp)
__libc_argv = argv;
__environ = envp;
+ __libc_environ_initialized = 1;
+
#ifndef SHARED
__libc_init_secure ();
diff --git a/elf/dl-support.c b/elf/dl-support.c
index 81e7172..62b6c8c 100644
--- a/elf/dl-support.c
+++ b/elf/dl-support.c
@@ -127,6 +127,10 @@ int _dl_correct_cache_id = _DL_CACHE_DEFAULT_ID;
ElfW(auxv_t) *_dl_auxv;
ElfW(Phdr) *_dl_phdr;
+
+/* This is the environ that the kernel gave us. */
+char **_dl_environ;
+
size_t _dl_phnum;
uint64_t _dl_hwcap __attribute__ ((nocommon));
diff --git a/elf/dl-sysdep.c b/elf/dl-sysdep.c
index 65a9046..bfe6b42 100644
--- a/elf/dl-sysdep.c
+++ b/elf/dl-sysdep.c
@@ -108,8 +108,10 @@ _dl_sysdep_start (void **start_argptr,
#endif
__libc_stack_end = DL_STACK_END (start_argptr);
- DL_FIND_ARG_COMPONENTS (start_argptr, _dl_argc, INTUSE(_dl_argv), _environ,
- GLRO(dl_auxv));
+ DL_FIND_ARG_COMPONENTS (start_argptr, _dl_argc, INTUSE(_dl_argv),
+ GLRO(dl_environ), GLRO(dl_auxv));
+
+ _environ = GLRO(dl_environ);
user_entry = (ElfW(Addr)) ENTRY_POINT;
GLRO(dl_platform) = NULL; /* Default to nothing known about the platform. */
diff --git a/elf/rtld.c b/elf/rtld.c
index b0126e5..72ddf5c 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -165,6 +165,7 @@ struct rtld_global_ro _rtld_global_ro attribute_relro =
._dl_pointer_guard = 1,
._dl_pagesize = EXEC_PAGESIZE,
._dl_inhibit_cache = 0,
+ ._dl_environ = NULL,
/* Function pointers. */
._dl_debug_printf = _dl_debug_printf,
diff --git a/stdlib/getenv.c b/stdlib/getenv.c
index 8398dab..08cb507 100644
--- a/stdlib/getenv.c
+++ b/stdlib/getenv.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991,92,94,96,98,99,2002,2005 Free Software Foundation, Inc.
+/* Copyright (C) 1991-2012 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
@@ -21,7 +21,9 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <ldsodefs.h>
+extern int __libc_environ_initialized;
/* Return the value of the environment variable NAME. This implementation
is tuned a bit in that it assumes no environment variable has an empty
@@ -36,8 +38,12 @@ getenv (name)
size_t len = strlen (name);
char **ep;
uint16_t name_start;
+ char **cur_env = __environ;
- if (__environ == NULL || name[0] == '\0')
+ if (__builtin_expect (__libc_environ_initialized == 0, 0))
+ cur_env = GLRO(dl_environ);
+
+ if (cur_env == NULL || name[0] == '\0')
return NULL;
if (name[1] == '\0')
@@ -54,7 +60,7 @@ getenv (name)
#error "Funny byte order."
# endif
#endif
- for (ep = __environ; *ep != NULL; ++ep)
+ for (ep = cur_env; *ep != NULL; ++ep)
{
#if _STRING_ARCH_unaligned
uint16_t ep_start = *(uint16_t *) *ep;
@@ -77,7 +83,7 @@ getenv (name)
len -= 2;
name += 2;
- for (ep = __environ; *ep != NULL; ++ep)
+ for (ep = cur_env; *ep != NULL; ++ep)
{
#if _STRING_ARCH_unaligned
uint16_t ep_start = *(uint16_t *) *ep;
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index c667e34..45bdd74 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -498,6 +498,8 @@ struct rtld_global_ro
/* Pointer to the auxv list supplied to the program at startup. */
EXTERN ElfW(auxv_t) *_dl_auxv;
+ EXTERN char **_dl_environ;
+
/* Get architecture specific definitions. */
#define PROCINFO_DECL
#ifndef PROCINFO_CLASS