@@ -25,7 +25,7 @@ headers = elf.h bits/elfclass.h link.h bits/link.h
routines = $(all-dl-routines) dl-support dl-iteratephdr \
dl-addr dl-addr-obj enbl-secure dl-profstub \
dl-origin dl-libc dl-sym dl-sysdep dl-error \
- dl-reloc-static-pie libc_early_init
+ dl-reloc-static-pie libc_early_init libc_vars
# The core dynamic linking functions are in libc for the static and
# profiled libraries.
@@ -35,7 +35,7 @@ dl-routines = $(addprefix dl-,load lookup object reloc deps \
execstack open close trampoline \
exception sort-maps lookup-direct \
call-libc-early-init write \
- thread_gscope_wait)
+ thread_gscope_wait libc_vars libc_vars_init)
ifeq (yes,$(use-ldconfig))
dl-routines += dl-cache
endif
@@ -186,9 +186,14 @@ tests-internal := tst-tls1 tst-tls2 $(tests-static-internal)
tests-static := $(tests-static-normal) $(tests-static-internal)
ifeq (yes,$(build-shared))
-tests-static += tst-tls9-static tst-single_threaded-static-dlopen
+tests-static += \
+ tst-getauxval-static \
+ tst-single_threaded-static-dlopen \
+ tst-tls9-static \
+
static-dlopen-environment = \
LD_LIBRARY_PATH=$(ld-library-path):$(common-objpfx)dlfcn
+tst-getauxval-static-ENV = $(static-dlopen-environment)
tst-tls9-static-ENV = $(static-dlopen-environment)
tst-single_threaded-static-dlopen-ENV = $(static-dlopen-environment)
@@ -343,6 +348,7 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
libmarkermod2-1 libmarkermod2-2 \
libmarkermod3-1 libmarkermod3-2 libmarkermod3-3 \
libmarkermod4-1 libmarkermod4-2 libmarkermod4-3 libmarkermod4-4 \
+ tst-auxvalmod \
# Most modules build with _ISOMAC defined, but those filtered out
# depend on internal headers.
@@ -1894,3 +1900,6 @@ $(objpfx)tst-glibc-hwcaps-mask.out: \
# Generic dependency for sysdeps implementation of
# tst-glibc-hwcaps-cache.
$(objpfx)tst-glibc-hwcaps-cache.out: $(objpfx)tst-glibc-hwcaps
+
+$(objpfx)tst-getauxval-static: $(common-objpfx)dlfcn/libdl.a
+$(objpfx)tst-getauxval-static.out: $(objpfx)tst-auxvalmod.so
@@ -31,6 +31,8 @@ libc {
# Internal error handling support. Interposes the functions in ld.so.
_dl_signal_exception; _dl_catch_exception;
_dl_signal_error; _dl_catch_error;
+
+ __libc_vars;
}
}
@@ -19,9 +19,12 @@
#include <assert.h>
#include <ldsodefs.h>
#include <libc-early-init.h>
+#include <libc-vars.h>
#include <link.h>
#include <stddef.h>
+#include <libc-vars-init.h>
+
void
_dl_call_libc_early_init (struct link_map *libc_map, _Bool initial)
{
@@ -39,3 +42,23 @@ _dl_call_libc_early_init (struct link_map *libc_map, _Bool initial)
= DL_SYMBOL_ADDRESS (libc_map, sym);
early_init (initial);
}
+
+void
+_dl_call_libc_vars_init (struct link_map *libc_map)
+{
+ /* There is nothing to do if we did not actually load libc.so. */
+ if (libc_map == NULL)
+ return;
+
+ const ElfW (Sym) *sym
+ = _dl_lookup_direct (libc_map, "__libc_vars",
+ 0x59f05378, /* dl_new_hash output. */
+ "GLIBC_PRIVATE",
+ 0x0963cf85); /* _dl_elf_hash output. */
+ assert (sym != NULL);
+ assert (sym->st_size == sizeof (struct libc_vars));
+ struct libc_vars *vars = DL_SYMBOL_ADDRESS (libc_map, sym);
+
+ _dl_libc_vars_init (vars);
+ _dl_call_libc_vars_init_cpu (libc_map);
+}
new file mode 100644
@@ -0,0 +1,19 @@
+/* Local copy of __libc_vars for ld.so.
+ Copyright (C) 2020 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
+ <https://www.gnu.org/licenses/>. */
+
+#include "libc_vars.c"
new file mode 100644
@@ -0,0 +1,31 @@
+/* Update __libc_vars in libc.so with the data from ld.so.
+ Copyright (C) 2020 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
+ <https://www.gnu.org/licenses/>. */
+
+#include <libc-vars.h>
+
+#include <libc-vars-init.h>
+
+void
+_dl_libc_vars_init (struct libc_vars *vars)
+{
+#ifdef HAVE_AUX_VECTOR
+ vars->auxv = __libc_vars.auxv;
+#endif
+
+ _dl_libc_vars_init_cpu (vars);
+}
@@ -35,6 +35,7 @@
#include <libc-internal.h>
#include <array_length.h>
#include <libc-early-init.h>
+#include <libc-vars.h>
#include <dl-dst.h>
#include <dl-prop.h>
@@ -590,6 +591,15 @@ dl_open_worker (void *a)
(void) _dl_check_map_versions (new->l_searchlist.r_list[i]->l_real,
0, 0);
+ /* Initialize __libc_vars before relocation. This has to come after
+ the call to _dl_check_map_versions, so that lookups of versioned
+ symbols work. */
+ if (!args->libc_already_loaded)
+ {
+ struct link_map *libc_map = GL (dl_ns)[args->nsid].libc_map;
+ _dl_call_libc_vars_init (libc_map);
+ }
+
#ifdef SHARED
/* Auditing checkpoint: we have added all objects. */
if (__glibc_unlikely (GLRO(dl_naudit) > 0))
@@ -37,6 +37,7 @@
#include <dl-vdso.h>
#include <dl-vdso-setup.h>
#include <dl-auxv.h>
+#include <libc-vars.h>
extern char *__progname;
char **_dl_argv = &__progname; /* This is checked for some error messages. */
@@ -154,7 +155,6 @@ int _dl_debug_fd = STDERR_FILENO;
int _dl_correct_cache_id = _DL_CACHE_DEFAULT_ID;
-ElfW(auxv_t) *_dl_auxv;
const ElfW(Phdr) *_dl_phdr;
size_t _dl_phnum;
uint64_t _dl_hwcap __attribute__ ((nocommon));
@@ -232,7 +232,7 @@ _dl_aux_init (ElfW(auxv_t) *av)
uid_t uid = 0;
gid_t gid = 0;
- _dl_auxv = av;
+ __libc_vars.auxv = av;
for (; av->a_type != AT_NULL; ++av)
switch (av->a_type)
{
@@ -42,6 +42,7 @@
#include <dl-procinfo.h>
#include <dl-osinfo.h>
#include <libc-internal.h>
+#include <libc-vars.h>
#include <tls.h>
#include <dl-tunables.h>
@@ -110,12 +111,12 @@ _dl_sysdep_start (void **start_argptr,
__libc_stack_end = DL_STACK_END (start_argptr);
DL_FIND_ARG_COMPONENTS (start_argptr, _dl_argc, _dl_argv, _environ,
- GLRO(dl_auxv));
+ __libc_vars.auxv);
user_entry = (ElfW(Addr)) ENTRY_POINT;
GLRO(dl_platform) = NULL; /* Default to nothing known about the platform. */
- for (av = GLRO(dl_auxv); av->a_type != AT_NULL; set_seen (av++))
+ for (av = __libc_vars.auxv; av->a_type != AT_NULL; set_seen (av++))
switch (av->a_type)
{
case AT_PHDR:
@@ -247,7 +248,7 @@ _dl_sysdep_start (void **start_argptr,
if (__builtin_expect (__libc_enable_secure, 0))
__libc_check_standard_fds ();
- (*dl_main) (phdr, phnum, &user_entry, GLRO(dl_auxv));
+ (*dl_main) (phdr, phnum, &user_entry, __libc_vars.auxv);
return user_entry;
}
@@ -270,7 +271,7 @@ _dl_show_auxv (void)
close by (otherwise the array will be too large). In case we have
to support a platform where these requirements are not fulfilled
some alternative implementation has to be used. */
- for (av = GLRO(dl_auxv); av->a_type != AT_NULL; ++av)
+ for (av = __libc_vars.auxv; av->a_type != AT_NULL; ++av)
{
static const struct
{
new file mode 100644
@@ -0,0 +1,22 @@
+/* Definition of __libc_vars for libc.
+ Copyright (C) 2020 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
+ <https://www.gnu.org/licenses/>. */
+
+#include <libc-vars.h>
+
+struct libc_vars __libc_vars attribute_relro;
+hidden_def (__libc_vars)
@@ -50,6 +50,7 @@
#include <dl-main.h>
#include <list.h>
#include <gnu/lib-names.h>
+#include <libc-vars.h>
#include <assert.h>
@@ -2311,6 +2312,10 @@ dl_main (const ElfW(Phdr) *phdr,
_rtld_main_check (main_map, _dl_argv[0]);
+ /* We are about to perform relocation. Make sure that __libc_vars
+ has been initialized. */
+ _dl_call_libc_vars_init (GL (dl_ns)[LM_ID_BASE].libc_map);
+
if (prelinked)
{
if (main_map->l_info [ADDRIDX (DT_GNU_CONFLICT)] != NULL)
new file mode 100644
@@ -0,0 +1,31 @@
+/* Wrapper for getauxval testing.
+ Copyright (C) 2020 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
+ <https://www.gnu.org/licenses/>. */
+
+#include <errno.h>
+#include <sys/auxv.h>
+
+#include "tst-auxvalmod.h"
+
+unsigned long int
+getauxval_wrapper (unsigned long int type, int *errnop)
+{
+ errno = *errnop;
+ unsigned long int result = getauxval (type);
+ *errnop = errno;
+ return result;
+}
new file mode 100644
@@ -0,0 +1,23 @@
+/* Wrapper for getauxval testing.
+ Copyright (C) 2020 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
+ <https://www.gnu.org/licenses/>. */
+
+/* Call getauxval, write errno value after the call to ERRNOP, and
+ return the getauxval result. ERRNOP is needed because across a
+ static dlopen boundary, errno might have a different address, and
+ the caller would never see the intended value. */
+unsigned long int getauxval_wrapper (unsigned long int type, int *errnop);
new file mode 100644
@@ -0,0 +1,54 @@
+/* Test getauxval from a dynamic library after static dlopen.
+ Copyright (C) 2020 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
+ <https://www.gnu.org/licenses/>. */
+
+#include <errno.h>
+#include <support/check.h>
+#include <support/test-driver.h>
+#include <support/xdlfcn.h>
+#include <sys/auxv.h>
+
+#include "tst-auxvalmod.h"
+
+static int
+do_test (void)
+{
+ unsigned long missing_auxv_type;
+ for (missing_auxv_type = 0; ; ++missing_auxv_type)
+ {
+ errno = 0;
+ if (getauxval (missing_auxv_type) == 0 && errno != 0)
+ break;
+ }
+ TEST_COMPARE (errno, ENOENT);
+
+ void *handle = xdlopen ("tst-auxvalmod.so", RTLD_LAZY);
+
+ __typeof__ (getauxval_wrapper) *wrapper
+ = xdlsym (handle, "getauxval_wrapper");
+ int inner_errno = 0;
+ TEST_COMPARE (getauxval (AT_RANDOM), wrapper (AT_RANDOM, &inner_errno));
+
+ inner_errno = 0;
+ TEST_COMPARE (wrapper (missing_auxv_type, &inner_errno), 0);
+ TEST_COMPARE (inner_errno, ENOENT);
+
+ xdlclose (handle);
+ return 0;
+}
+
+#include <support/test-driver.c>
@@ -19,6 +19,7 @@
#include <errno.h>
#include <ldsodefs.h>
#include <stdbool.h>
+#include <libc-vars.h>
bool
__getauxval2 (unsigned long int type, unsigned long int *result)
@@ -39,7 +40,7 @@ __getauxval2 (unsigned long int type, unsigned long int *result)
}
#ifdef HAVE_AUX_VECTOR
- for (p = GLRO(dl_auxv); p->a_type != AT_NULL; p++)
+ for (p = __libc_vars.auxv; p->a_type != AT_NULL; p++)
if (p->a_type == type)
{
*result = p->a_un.a_val;
@@ -575,11 +575,6 @@ struct rtld_global_ro
EXTERN uint64_t _dl_hwcap_mask;
#endif
-#ifdef HAVE_AUX_VECTOR
- /* Pointer to the auxv list supplied to the program at startup. */
- EXTERN ElfW(auxv_t) *_dl_auxv;
-#endif
-
/* Get architecture specific definitions. */
#include <dl-procinfo.c>
new file mode 100644
@@ -0,0 +1,28 @@
+/* CPU-specific extensions for __libc_vars. Generic version.
+ Copyright (C) 2020 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
+ <https://www.gnu.org/licenses/>. */
+
+#ifndef _LIBC_VARS_CPU_H
+#define _LIBC_VARS_CPU_H
+
+/* The generic version does not have CPU-specific extensions. */
+#define HAVE_LIBC_VARS_CPU 0
+
+struct libc_vars;
+struct link_map;
+
+#endif /* _LIBC_VARS_CPU_H */
new file mode 100644
@@ -0,0 +1,32 @@
+/* Initialization of __libc_vars. Generic version.
+ Copyright (C) 2020 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
+ <https://www.gnu.org/licenses/>. */
+
+/* This file is included from elf/dl-libc_vars_init.c
+ and elf/dl-call-libc-early-init.c.
+
+ The generic version does not perform any extra initialization. */
+
+static inline void
+_dl_libc_vars_init_cpu (struct libc_vars *vars)
+{
+}
+
+static inline void
+_dl_call_libc_vars_init_cpu (struct link_map *libc_map)
+{
+}
new file mode 100644
@@ -0,0 +1,59 @@
+/* Definition of __libc_vars and related update functionality.
+ Copyright (C) 2020 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
+ <https://www.gnu.org/licenses/>. */
+
+#ifndef _LIBC_VARS_H
+#define _LIBC_VARS_H
+
+#include <ldsodefs.h>
+#include <libc-vars-cpu.h>
+#include <link.h>
+#include <stdint.h>
+
+struct libc_vars
+{
+#ifdef HAVE_AUX_VECTOR
+ /* Pointer to the auxv list supplied to the program at startup. */
+ ElfW (auxv_t) *auxv;
+#endif
+#if HAVE_LIBC_VARS_CPU
+ struct libc_vars_cpu cpu;
+#endif
+};
+
+/* Initialized by ld.so immediate after loading libc.so.6, prior to
+ relocation. The variable is duplicated in ld.so, where it is set
+ up by the startup code. It is only exported (under GLIBC_PRIVATE)
+ in libc.so, and this copy is updated in _dl_libc_vars_init.
+
+ In the static case, the initialization has to be put into the
+ startup code, __libc_vars is part of libc.a. */
+extern struct libc_vars __libc_vars attribute_relro;
+#if IS_IN (libc) || IS_IN (rtld)
+hidden_proto (__libc_vars)
+#endif
+
+/* Called by the dynamic linker to initialize __libc_vars prior to
+ relocation. This function resides in ld.so, but updates the data
+ in libc.so. The similar function __libc_early_init resides in
+ libc.so and performs post-relocation initialization. */
+void _dl_libc_vars_init (struct libc_vars *vars) attribute_hidden;
+
+/* Invoke _dl_libc_vars_init for __libc_vars in LIBC_MAP. */
+void _dl_call_libc_vars_init (struct link_map *libc_map) attribute_hidden;
+
+#endif /* _LIBC_VARS_H */
@@ -23,21 +23,11 @@
void
_dl_var_init (void *array[])
{
- /* It has to match "variables" below. */
- enum
- {
- DL_PAGESIZE = 0,
- DL_AUXV = 1,
- DL_HWCAP = 2,
- DL_HWCAP2 = 3,
- DL_CACHE_LINE_SIZE = 4
- };
-
- GLRO(dl_pagesize) = *((size_t *) array[DL_PAGESIZE]);
- GLRO(dl_auxv) = (ElfW(auxv_t) *) *((size_t *) array[DL_AUXV]);
- GLRO(dl_hwcap) = *((unsigned long int *) array[DL_HWCAP]);
- GLRO(dl_hwcap2) = *((unsigned long int *) array[DL_HWCAP2]);
- GLRO(dl_cache_line_size) = (int) *((int *) array[DL_CACHE_LINE_SIZE]);
+ int index = 0;
+ GLRO(dl_pagesize) = *((size_t *) array[index++]);
+ GLRO(dl_hwcap) = *((unsigned long int *) array[index++]);
+ GLRO(dl_hwcap2) = *((unsigned long int *) array[index++]);
+ GLRO(dl_cache_line_size) = (int) *((int *) array[index++]);
}
#else
@@ -45,7 +35,6 @@ _dl_var_init (void *array[])
static void *variables[] =
{
&GLRO(dl_pagesize),
- &GLRO(dl_auxv),
&GLRO(dl_hwcap),
&GLRO(dl_hwcap2),
&GLRO(dl_cache_line_size)