[v2] ld.so: Implement the --list-diagnostics option
Commit Message
---
v2: Add comment regarding terminator of the unfiltered list.
Rework sysdeps handling for auxv/uname/CPUs.
NEWS | 4 +
elf/Makefile | 5 +-
elf/dl-diagnostics-cpu.c | 24 +++
elf/dl-diagnostics-kernel.c | 24 +++
elf/dl-diagnostics.c | 265 ++++++++++++++++++++++++
elf/dl-diagnostics.h | 46 ++++
elf/dl-main.h | 5 +-
elf/dl-usage.c | 1 +
elf/rtld.c | 18 +-
sysdeps/unix/sysv/linux/dl-diagnostics-kernel.c | 77 +++++++
10 files changed, 461 insertions(+), 8 deletions(-)
Comments
On Tue, Mar 2, 2021 at 2:33 AM Florian Weimer via Libc-alpha
<libc-alpha@sourceware.org> wrote:
>
> ---
> v2: Add comment regarding terminator of the unfiltered list.
> Rework sysdeps handling for auxv/uname/CPUs.
>
> NEWS | 4 +
> elf/Makefile | 5 +-
> elf/dl-diagnostics-cpu.c | 24 +++
> elf/dl-diagnostics-kernel.c | 24 +++
> elf/dl-diagnostics.c | 265 ++++++++++++++++++++++++
> elf/dl-diagnostics.h | 46 ++++
> elf/dl-main.h | 5 +-
> elf/dl-usage.c | 1 +
> elf/rtld.c | 18 +-
> sysdeps/unix/sysv/linux/dl-diagnostics-kernel.c | 77 +++++++
> 10 files changed, 461 insertions(+), 8 deletions(-)
>
> diff --git a/NEWS b/NEWS
> index 37ba6ac0d0..73a1a0df97 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -14,6 +14,10 @@ Major new features:
> constant on Linux. MINSIGSTKSZ is redefined to sysconf(_SC_MINSIGSTKSZ)
> and SIGSTKSZ is redefined to sysconf (_SC_SIGSTKSZ).
>
> +* The dynamic linker implements the --list-diagnostics option, printing
> + a dump of information related to IFUNC resolver operation and
> + glibc-hwcaps subdirectory selection.
> +
> Deprecated and removed features, and other changes affecting compatibility:
>
> [Add deprecations, removals and changes affecting compatibility here]
> diff --git a/elf/Makefile b/elf/Makefile
> index 16c89b6d07..798f134f5e 100644
> --- a/elf/Makefile
> +++ b/elf/Makefile
> @@ -66,7 +66,7 @@ elide-routines.os = $(all-dl-routines) dl-support enbl-secure dl-origin \
> # interpreter and operating independent of libc.
> rtld-routines = rtld $(all-dl-routines) dl-sysdep dl-environ dl-minimal \
> dl-error-minimal dl-conflict dl-hwcaps dl-hwcaps_split dl-hwcaps-subdirs \
> - dl-usage
> + dl-usage dl-diagnostics dl-diagnostics-kernel dl-diagnostics-cpu
> all-rtld-routines = $(rtld-routines) $(sysdep-rtld-routines)
>
> CFLAGS-dl-runtime.c += -fexceptions -fasynchronous-unwind-tables
> @@ -677,6 +677,9 @@ CFLAGS-cache.c += $(SYSCONF-FLAGS)
> CFLAGS-rtld.c += $(SYSCONF-FLAGS)
> CFLAGS-dl-usage.c += $(SYSCONF-FLAGS) \
> -D'RTLD="$(rtlddir)/$(rtld-installed-name)"'
> +CFLAGS-dl-diagnostics.c += $(SYSCONF-FLAGS) \
> + -D'PREFIX="$(prefix)"' \
> + -D'RTLD="$(rtlddir)/$(rtld-installed-name)"'
>
> cpp-srcs-left := $(all-rtld-routines:=.os)
> lib := rtld
> diff --git a/elf/dl-diagnostics-cpu.c b/elf/dl-diagnostics-cpu.c
> new file mode 100644
> index 0000000000..f7d149764b
> --- /dev/null
> +++ b/elf/dl-diagnostics-cpu.c
> @@ -0,0 +1,24 @@
> +/* Print CPU diagnostics data in ld.so. Stub version.
> + Copyright (C) 2021 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 <dl-diagnostics.h>
> +
> +void
> +_dl_diagnostics_cpu (void)
> +{
> +}
> diff --git a/elf/dl-diagnostics-kernel.c b/elf/dl-diagnostics-kernel.c
> new file mode 100644
> index 0000000000..831c358f14
> --- /dev/null
> +++ b/elf/dl-diagnostics-kernel.c
> @@ -0,0 +1,24 @@
> +/* Print kernel diagnostics data in ld.so. Stub version.
> + Copyright (C) 2021 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 <dl-diagnostics.h>
> +
> +void
> +_dl_diagnostics_kernel (void)
> +{
> +}
> diff --git a/elf/dl-diagnostics.c b/elf/dl-diagnostics.c
> new file mode 100644
> index 0000000000..c131200937
> --- /dev/null
> +++ b/elf/dl-diagnostics.c
> @@ -0,0 +1,265 @@
> +/* Print diagnostics data in ld.so.
> + Copyright (C) 2021 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 <gnu/lib-names.h>
> +#include <stdbool.h>
> +#include <stddef.h>
> +#include <unistd.h>
> +
> +#include <dl-diagnostics.h>
> +#include <dl-hwcaps.h>
> +#include <dl-main.h>
> +#include <dl-procinfo.h>
> +#include <dl-sysdep.h>
> +#include <ldsodefs.h>
> +#include "trusted-dirs.h"
> +#include "version.h"
> +
> +/* Write CH to standard output. */
> +static void
> +_dl_putc (char ch)
> +{
> + _dl_write (STDOUT_FILENO, &ch, 1);
> +}
> +
> +/* Print CH to standard output, quoting it if necessary. */
> +static void
> +print_quoted_char (char ch)
> +{
> + if (ch < ' ' || ch > '~')
> + {
> + char buf[4];
> + buf[0] = '\\';
> + buf[1] = '0' + ((ch >> 6) & 7);
> + buf[2] = '0' + ((ch >> 6) & 7);
> + buf[3] = '0' + (ch & 7);
> + _dl_write (STDOUT_FILENO, buf, 4);
> + }
> + else
> + {
> + if (ch == '\\' || ch == '"')
> + _dl_putc ('\\');
> + _dl_putc (ch);
> + }
> +}
> +
> +/* Print S of LEN bytes to standard output, quoting characters as
> + needed. */
> +static void
> +print_string_length (const char *s, size_t len)
> +{
> + _dl_putc ('"');
> + for (size_t i = 0; i < len; ++i)
> + print_quoted_char (s[i]);
> + _dl_putc ('"');
> +}
> +
> +void
> +_dl_diagnostics_print_string (const char *s)
> +{
> + if (s == NULL)
> + {
> + _dl_printf ("0x0");
> + return;
> + }
> +
> + _dl_putc ('"');
> + while (*s != '\0')
> + {
> + print_quoted_char (*s);
> + ++s;
> + }
> + _dl_putc ('"');
> +}
> +
> +void
> +_dl_diagnostics_print_labeled_string (const char *label, const char *s)
> +{
> + _dl_printf ("%s=", label);
> + _dl_diagnostics_print_string (s);
> + _dl_putc ('\n');
> +}
> +
> +void
> +_dl_diagnostics_print_labeled_value (const char *label, uint64_t value)
> +{
> + if (sizeof (value) == sizeof (unsigned long int))
> + /* _dl_printf can print 64-bit values directly. */
> + _dl_printf ("%s=0x%lx\n", label, (unsigned long int) value);
> + else
> + {
> + uint32_t high = value >> 32;
> + uint32_t low = value;
> + if (high == 0)
> + _dl_printf ("%s=0x%x\n", label, low);
> + else
> + _dl_printf ("%s=0x%x%08x\n", label, high, low);
> + }
> +}
> +
> +/* Return true if ENV is an unfiltered environment variable. */
> +static bool
> +unfiltered_envvar (const char *env, size_t *name_length)
> +{
> + char *env_equal = strchr (env, '=');
> + if (env_equal == NULL)
> + {
> + /* Always dump malformed entries. */
> + *name_length = strlen (env);
> + return true;
> + }
> + size_t envname_length = env_equal - env;
> + *name_length = envname_length;
> +
> + /* LC_ and LD_ variables. */
> + if (env[0] == 'L' && (env[1] == 'C' || env[1] == 'D')
> + && env[2] == '_')
> + return true;
> +
> + /* MALLOC_ variables. */
> + if (strncmp (env, "MALLOC_", strlen ("MALLOC_")) == 0)
> + return true;
> +
> + static const char unfiltered[] =
> + "DATEMSK\0"
> + "GCONV_PATH\0"
> + "GETCONF_DIR\0"
> + "GETCONF_DIR\0"
> + "GLIBC_TUNABLES\0"
> + "GMON_OUTPUT_PREFIX\0"
> + "HESIOD_CONFIG\0"
> + "HES_DOMAIN\0"
> + "HOSTALIASES\0"
> + "I18NPATH\0"
> + "IFS\0"
> + "LANG\0"
> + "LOCALDOMAIN\0"
> + "LOCPATH\0"
> + "MSGVERB\0"
> + "NIS_DEFAULTS\0"
> + "NIS_GROUP\0"
> + "NIS_PATH\0"
> + "NLSPATH\0"
> + "PATH\0"
> + "POSIXLY_CORRECT\0"
> + "RESOLV_HOST_CONF\0"
> + "RES_OPTIONS\0"
> + "SEV_LEVEL\0"
> + "TMPDIR\0"
> + "TZ\0"
> + "TZDIR\0"
> + /* Two null bytes at the end to mark the end of the list via an
> + empty substring. */
> + ;
> + for (const char *candidate = unfiltered; *candidate != '\0'; )
> + {
> + size_t candidate_length = strlen (candidate);
> + if (candidate_length == envname_length
> + && memcmp (candidate, env, candidate_length) == 0)
> + return true;
> + candidate += candidate_length + 1;
> + }
> +
> + return false;
> +}
> +
> +/* Dump the process environment. */
> +static void
> +print_environ (char **environ)
> +{
> + unsigned int index = 0;
> + for (char **envp = environ; *envp != NULL; ++envp)
> + {
> + char *env = *envp;
> + size_t name_length;
> + bool unfiltered = unfiltered_envvar (env, &name_length);
> + _dl_printf ("env%s[0x%x]=",
> + unfiltered ? "" : "_filtered", index);
> + if (unfiltered)
> + _dl_diagnostics_print_string (env);
> + else
> + print_string_length (env, name_length);
> + _dl_putc ('\n');
> + ++index;
> + }
> +}
> +
> +/* Print configured paths and the built-in search path. */
> +static void
> +print_paths (void)
> +{
> + _dl_diagnostics_print_labeled_string ("path.prefix", PREFIX);
> + _dl_diagnostics_print_labeled_string ("path.rtld", RTLD);
> + _dl_diagnostics_print_labeled_string ("path.sysconfdir", SYSCONFDIR);
> +
> + unsigned int index = 0;
> + static const char *system_dirs = SYSTEM_DIRS "\0";
> + for (const char *e = system_dirs; *e != '\0'; )
> + {
> + size_t len = strlen (e);
> + _dl_printf ("path.system_dirs[0x%x]=", index);
> + print_string_length (e, len);
> + _dl_putc ('\n');
> + ++index;
> + e += len + 1;
> + }
> +}
> +
> +/* Print information about the glibc version. */
> +static void
> +print_version (void)
> +{
> + _dl_diagnostics_print_labeled_string ("version.release", RELEASE);
> + _dl_diagnostics_print_labeled_string ("version.version", VERSION);
> +}
> +
> +void
> +_dl_print_diagnostics (char **environ)
> +{
> +#ifdef HAVE_DL_DISCOVER_OSVERSION
> + _dl_diagnostics_print_labeled_value
> + ("dl_discover_osversion", _dl_discover_osversion ());
> +#endif
> + _dl_diagnostics_print_labeled_string ("dl_dst_lib", DL_DST_LIB);
> + _dl_diagnostics_print_labeled_value ("dl_hwcap", GLRO (dl_hwcap));
> + _dl_diagnostics_print_labeled_value ("dl_hwcap_important", HWCAP_IMPORTANT);
> + _dl_diagnostics_print_labeled_value ("dl_hwcap2", GLRO (dl_hwcap2));
> + _dl_diagnostics_print_labeled_string
> + ("dl_hwcaps_subdirs", _dl_hwcaps_subdirs);
> + _dl_diagnostics_print_labeled_value
> + ("dl_hwcaps_subdirs_active", _dl_hwcaps_subdirs_active ());
> + _dl_diagnostics_print_labeled_value ("dl_osversion", GLRO (dl_osversion));
> + _dl_diagnostics_print_labeled_value ("dl_pagesize", GLRO (dl_pagesize));
> + _dl_diagnostics_print_labeled_string ("dl_platform", GLRO (dl_platform));
> + _dl_diagnostics_print_labeled_string
> + ("dl_profile_output", GLRO (dl_profile_output));
> + _dl_diagnostics_print_labeled_value
> + ("dl_string_platform", _dl_string_platform ( GLRO (dl_platform)));
> +
> + _dl_diagnostics_print_labeled_string ("dso.ld", LD_SO);
> + _dl_diagnostics_print_labeled_string ("dso.libc", LIBC_SO);
> +
> + print_environ (environ);
> + print_paths ();
> + print_version ();
> +
> + _dl_diagnostics_kernel ();
> + _dl_diagnostics_cpu ();
> +
> + _exit (EXIT_SUCCESS);
> +}
> diff --git a/elf/dl-diagnostics.h b/elf/dl-diagnostics.h
> new file mode 100644
> index 0000000000..27dcb12bca
> --- /dev/null
> +++ b/elf/dl-diagnostics.h
> @@ -0,0 +1,46 @@
> +/* Interfaces for printing diagnostics in ld.so.
> + Copyright (C) 2021 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 _DL_DIAGNOSTICS_H
> +#define _DL_DIAGNOSTICS_H
> +
> +#include <stdint.h>
> +
> +/* Write the null-terminated string to standard output, surrounded in
> + quotation marks. */
> +void _dl_diagnostics_print_string (const char *s) attribute_hidden;
> +
> +/* Like _dl_diagnostics_print_string, but add a LABEL= prefix, and a
> + newline character as a suffix. */
> +void _dl_diagnostics_print_labeled_string (const char *label, const char *s)
> + attribute_hidden;
> +
> +/* Print LABEL=VALUE to standard output, followed by a newline
> + character. */
> +void _dl_diagnostics_print_labeled_value (const char *label, uint64_t value)
> + attribute_hidden;
> +
> +/* Print diagnostics data for the kernel. Called from
> + _dl_print_diagnostics. */
> +void _dl_diagnostics_kernel (void) attribute_hidden;
> +
> +/* Print diagnostics data for the CPU(s). Called from
> + _dl_print_diagnostics. */
> +void _dl_diagnostics_cpu (void) attribute_hidden;
> +
> +#endif /* _DL_DIAGNOSTICS_H */
> diff --git a/elf/dl-main.h b/elf/dl-main.h
> index 3a5e13c739..d3820e0063 100644
> --- a/elf/dl-main.h
> +++ b/elf/dl-main.h
> @@ -63,7 +63,7 @@ struct audit_list
> enum rtld_mode
> {
> rtld_mode_normal, rtld_mode_list, rtld_mode_verify, rtld_mode_trace,
> - rtld_mode_list_tunables, rtld_mode_help,
> + rtld_mode_list_tunables, rtld_mode_list_diagnostics, rtld_mode_help,
> };
>
> /* Aggregated state information extracted from environment variables
> @@ -121,4 +121,7 @@ _Noreturn void _dl_version (void) attribute_hidden;
> _Noreturn void _dl_help (const char *argv0, struct dl_main_state *state)
> attribute_hidden;
>
> +/* Print a diagnostics dump. */
> +_Noreturn void _dl_print_diagnostics (char **environ) attribute_hidden;
> +
> #endif /* _DL_MAIN */
> diff --git a/elf/dl-usage.c b/elf/dl-usage.c
> index 6e26818bd7..5ad3a72559 100644
> --- a/elf/dl-usage.c
> +++ b/elf/dl-usage.c
> @@ -261,6 +261,7 @@ setting environment variables (which would be inherited by subprocesses).\n\
> --list-tunables list all tunables with minimum and maximum values\n"
> #endif
> "\
> + --list-diagnostics list diagnostics information\n\
> --help display this help and exit\n\
> --version output version information and exit\n\
> \n\
> diff --git a/elf/rtld.c b/elf/rtld.c
> index 596b6ac3d9..94a00e2049 100644
> --- a/elf/rtld.c
> +++ b/elf/rtld.c
> @@ -141,6 +141,7 @@ static void dl_main_state_init (struct dl_main_state *state);
> /* 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. */
> +extern char **_environ attribute_hidden;
> static void process_envvars (struct dl_main_state *state);
>
> #ifdef DL_ARGV_NOT_RELRO
> @@ -1287,6 +1288,14 @@ dl_main (const ElfW(Phdr) *phdr,
> ++_dl_argv;
> }
> #endif
> + else if (! strcmp (_dl_argv[1], "--list-diagnostics"))
> + {
> + state.mode = rtld_mode_list_diagnostics;
> +
> + ++_dl_skip_args;
> + --_dl_argc;
> + ++_dl_argv;
> + }
> else if (strcmp (_dl_argv[1], "--help") == 0)
> {
> state.mode = rtld_mode_help;
> @@ -1315,6 +1324,9 @@ dl_main (const ElfW(Phdr) *phdr,
> }
> #endif
>
> + if (state.mode == rtld_mode_list_diagnostics)
> + _dl_print_diagnostics (_environ);
> +
> /* If we have no further argument the program was called incorrectly.
> Grant the user some education. */
> if (_dl_argc < 2)
> @@ -2649,12 +2661,6 @@ a filename can be specified using the LD_DEBUG_OUTPUT environment variable.\n");
> }
> }
>
> -/* 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. */
> -extern char **_environ attribute_hidden;
> -
> -
> static void
> process_envvars (struct dl_main_state *state)
> {
> diff --git a/sysdeps/unix/sysv/linux/dl-diagnostics-kernel.c b/sysdeps/unix/sysv/linux/dl-diagnostics-kernel.c
> new file mode 100644
> index 0000000000..59f6402c54
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/dl-diagnostics-kernel.c
> @@ -0,0 +1,77 @@
> +/* Print kernel diagnostics data in ld.so. Linux version.
> + Copyright (C) 2021 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 <dl-diagnostics.h>
> +#include <ldsodefs.h>
> +#include <sys/utsname.h>
> +
> +/* Dump the auxiliary vector to standard output. */
> +static void
> +print_auxv (void)
> +{
> + /* See _dl_show_auxv. The code below follows the general output
> + format for diagnostic dumps. */
> + unsigned int index = 0;
> + for (ElfW(auxv_t) *av = GLRO(dl_auxv); av->a_type != AT_NULL; ++av)
> + {
> + _dl_printf ("auxv[0x%x].a_type=0x%lx\n"
> + "auxv[0x%x].a_val=",
> + index, (unsigned long int) av->a_type, index);
> + if (av->a_type == AT_EXECFN
> + || av->a_type == AT_PLATFORM
> + || av->a_type == AT_BASE_PLATFORM)
> + /* The address of the strings is not useful at all, so print
> + the strings themselvs. */
> + _dl_diagnostics_print_string ((const char *) av->a_un.a_val);
> + else
> + _dl_printf ("0x%lx", (unsigned long int) av->a_un.a_val);
> + _dl_printf ("\n");
> + ++index;
> + }
> +}
> +
> +/* Print one uname entry. */
> +static void
> +print_utsname_entry (const char *field, const char *value)
> +{
> + _dl_printf ("uname.");
> + _dl_diagnostics_print_labeled_string (field, value);
> +}
> +
> +/* Print information from uname, including the kernel version. */
> +static void
> +print_uname (void)
> +{
> + struct utsname uts;
> + if (__uname (&uts) == 0)
> + {
> + print_utsname_entry ("sysname", uts.sysname);
> + print_utsname_entry ("nodename", uts.nodename);
> + print_utsname_entry ("release", uts.release);
> + print_utsname_entry ("version", uts.version);
> + print_utsname_entry ("machine", uts.machine);
> + print_utsname_entry ("domainname", uts.domainname);
> + }
> +}
> +
> +void
> +_dl_diagnostics_kernel (void)
> +{
> + print_auxv ();
> + print_uname ();
> +}
>
LGTM.
Thanks.
@@ -14,6 +14,10 @@ Major new features:
constant on Linux. MINSIGSTKSZ is redefined to sysconf(_SC_MINSIGSTKSZ)
and SIGSTKSZ is redefined to sysconf (_SC_SIGSTKSZ).
+* The dynamic linker implements the --list-diagnostics option, printing
+ a dump of information related to IFUNC resolver operation and
+ glibc-hwcaps subdirectory selection.
+
Deprecated and removed features, and other changes affecting compatibility:
[Add deprecations, removals and changes affecting compatibility here]
@@ -66,7 +66,7 @@ elide-routines.os = $(all-dl-routines) dl-support enbl-secure dl-origin \
# interpreter and operating independent of libc.
rtld-routines = rtld $(all-dl-routines) dl-sysdep dl-environ dl-minimal \
dl-error-minimal dl-conflict dl-hwcaps dl-hwcaps_split dl-hwcaps-subdirs \
- dl-usage
+ dl-usage dl-diagnostics dl-diagnostics-kernel dl-diagnostics-cpu
all-rtld-routines = $(rtld-routines) $(sysdep-rtld-routines)
CFLAGS-dl-runtime.c += -fexceptions -fasynchronous-unwind-tables
@@ -677,6 +677,9 @@ CFLAGS-cache.c += $(SYSCONF-FLAGS)
CFLAGS-rtld.c += $(SYSCONF-FLAGS)
CFLAGS-dl-usage.c += $(SYSCONF-FLAGS) \
-D'RTLD="$(rtlddir)/$(rtld-installed-name)"'
+CFLAGS-dl-diagnostics.c += $(SYSCONF-FLAGS) \
+ -D'PREFIX="$(prefix)"' \
+ -D'RTLD="$(rtlddir)/$(rtld-installed-name)"'
cpp-srcs-left := $(all-rtld-routines:=.os)
lib := rtld
new file mode 100644
@@ -0,0 +1,24 @@
+/* Print CPU diagnostics data in ld.so. Stub version.
+ Copyright (C) 2021 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 <dl-diagnostics.h>
+
+void
+_dl_diagnostics_cpu (void)
+{
+}
new file mode 100644
@@ -0,0 +1,24 @@
+/* Print kernel diagnostics data in ld.so. Stub version.
+ Copyright (C) 2021 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 <dl-diagnostics.h>
+
+void
+_dl_diagnostics_kernel (void)
+{
+}
new file mode 100644
@@ -0,0 +1,265 @@
+/* Print diagnostics data in ld.so.
+ Copyright (C) 2021 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 <gnu/lib-names.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <unistd.h>
+
+#include <dl-diagnostics.h>
+#include <dl-hwcaps.h>
+#include <dl-main.h>
+#include <dl-procinfo.h>
+#include <dl-sysdep.h>
+#include <ldsodefs.h>
+#include "trusted-dirs.h"
+#include "version.h"
+
+/* Write CH to standard output. */
+static void
+_dl_putc (char ch)
+{
+ _dl_write (STDOUT_FILENO, &ch, 1);
+}
+
+/* Print CH to standard output, quoting it if necessary. */
+static void
+print_quoted_char (char ch)
+{
+ if (ch < ' ' || ch > '~')
+ {
+ char buf[4];
+ buf[0] = '\\';
+ buf[1] = '0' + ((ch >> 6) & 7);
+ buf[2] = '0' + ((ch >> 6) & 7);
+ buf[3] = '0' + (ch & 7);
+ _dl_write (STDOUT_FILENO, buf, 4);
+ }
+ else
+ {
+ if (ch == '\\' || ch == '"')
+ _dl_putc ('\\');
+ _dl_putc (ch);
+ }
+}
+
+/* Print S of LEN bytes to standard output, quoting characters as
+ needed. */
+static void
+print_string_length (const char *s, size_t len)
+{
+ _dl_putc ('"');
+ for (size_t i = 0; i < len; ++i)
+ print_quoted_char (s[i]);
+ _dl_putc ('"');
+}
+
+void
+_dl_diagnostics_print_string (const char *s)
+{
+ if (s == NULL)
+ {
+ _dl_printf ("0x0");
+ return;
+ }
+
+ _dl_putc ('"');
+ while (*s != '\0')
+ {
+ print_quoted_char (*s);
+ ++s;
+ }
+ _dl_putc ('"');
+}
+
+void
+_dl_diagnostics_print_labeled_string (const char *label, const char *s)
+{
+ _dl_printf ("%s=", label);
+ _dl_diagnostics_print_string (s);
+ _dl_putc ('\n');
+}
+
+void
+_dl_diagnostics_print_labeled_value (const char *label, uint64_t value)
+{
+ if (sizeof (value) == sizeof (unsigned long int))
+ /* _dl_printf can print 64-bit values directly. */
+ _dl_printf ("%s=0x%lx\n", label, (unsigned long int) value);
+ else
+ {
+ uint32_t high = value >> 32;
+ uint32_t low = value;
+ if (high == 0)
+ _dl_printf ("%s=0x%x\n", label, low);
+ else
+ _dl_printf ("%s=0x%x%08x\n", label, high, low);
+ }
+}
+
+/* Return true if ENV is an unfiltered environment variable. */
+static bool
+unfiltered_envvar (const char *env, size_t *name_length)
+{
+ char *env_equal = strchr (env, '=');
+ if (env_equal == NULL)
+ {
+ /* Always dump malformed entries. */
+ *name_length = strlen (env);
+ return true;
+ }
+ size_t envname_length = env_equal - env;
+ *name_length = envname_length;
+
+ /* LC_ and LD_ variables. */
+ if (env[0] == 'L' && (env[1] == 'C' || env[1] == 'D')
+ && env[2] == '_')
+ return true;
+
+ /* MALLOC_ variables. */
+ if (strncmp (env, "MALLOC_", strlen ("MALLOC_")) == 0)
+ return true;
+
+ static const char unfiltered[] =
+ "DATEMSK\0"
+ "GCONV_PATH\0"
+ "GETCONF_DIR\0"
+ "GETCONF_DIR\0"
+ "GLIBC_TUNABLES\0"
+ "GMON_OUTPUT_PREFIX\0"
+ "HESIOD_CONFIG\0"
+ "HES_DOMAIN\0"
+ "HOSTALIASES\0"
+ "I18NPATH\0"
+ "IFS\0"
+ "LANG\0"
+ "LOCALDOMAIN\0"
+ "LOCPATH\0"
+ "MSGVERB\0"
+ "NIS_DEFAULTS\0"
+ "NIS_GROUP\0"
+ "NIS_PATH\0"
+ "NLSPATH\0"
+ "PATH\0"
+ "POSIXLY_CORRECT\0"
+ "RESOLV_HOST_CONF\0"
+ "RES_OPTIONS\0"
+ "SEV_LEVEL\0"
+ "TMPDIR\0"
+ "TZ\0"
+ "TZDIR\0"
+ /* Two null bytes at the end to mark the end of the list via an
+ empty substring. */
+ ;
+ for (const char *candidate = unfiltered; *candidate != '\0'; )
+ {
+ size_t candidate_length = strlen (candidate);
+ if (candidate_length == envname_length
+ && memcmp (candidate, env, candidate_length) == 0)
+ return true;
+ candidate += candidate_length + 1;
+ }
+
+ return false;
+}
+
+/* Dump the process environment. */
+static void
+print_environ (char **environ)
+{
+ unsigned int index = 0;
+ for (char **envp = environ; *envp != NULL; ++envp)
+ {
+ char *env = *envp;
+ size_t name_length;
+ bool unfiltered = unfiltered_envvar (env, &name_length);
+ _dl_printf ("env%s[0x%x]=",
+ unfiltered ? "" : "_filtered", index);
+ if (unfiltered)
+ _dl_diagnostics_print_string (env);
+ else
+ print_string_length (env, name_length);
+ _dl_putc ('\n');
+ ++index;
+ }
+}
+
+/* Print configured paths and the built-in search path. */
+static void
+print_paths (void)
+{
+ _dl_diagnostics_print_labeled_string ("path.prefix", PREFIX);
+ _dl_diagnostics_print_labeled_string ("path.rtld", RTLD);
+ _dl_diagnostics_print_labeled_string ("path.sysconfdir", SYSCONFDIR);
+
+ unsigned int index = 0;
+ static const char *system_dirs = SYSTEM_DIRS "\0";
+ for (const char *e = system_dirs; *e != '\0'; )
+ {
+ size_t len = strlen (e);
+ _dl_printf ("path.system_dirs[0x%x]=", index);
+ print_string_length (e, len);
+ _dl_putc ('\n');
+ ++index;
+ e += len + 1;
+ }
+}
+
+/* Print information about the glibc version. */
+static void
+print_version (void)
+{
+ _dl_diagnostics_print_labeled_string ("version.release", RELEASE);
+ _dl_diagnostics_print_labeled_string ("version.version", VERSION);
+}
+
+void
+_dl_print_diagnostics (char **environ)
+{
+#ifdef HAVE_DL_DISCOVER_OSVERSION
+ _dl_diagnostics_print_labeled_value
+ ("dl_discover_osversion", _dl_discover_osversion ());
+#endif
+ _dl_diagnostics_print_labeled_string ("dl_dst_lib", DL_DST_LIB);
+ _dl_diagnostics_print_labeled_value ("dl_hwcap", GLRO (dl_hwcap));
+ _dl_diagnostics_print_labeled_value ("dl_hwcap_important", HWCAP_IMPORTANT);
+ _dl_diagnostics_print_labeled_value ("dl_hwcap2", GLRO (dl_hwcap2));
+ _dl_diagnostics_print_labeled_string
+ ("dl_hwcaps_subdirs", _dl_hwcaps_subdirs);
+ _dl_diagnostics_print_labeled_value
+ ("dl_hwcaps_subdirs_active", _dl_hwcaps_subdirs_active ());
+ _dl_diagnostics_print_labeled_value ("dl_osversion", GLRO (dl_osversion));
+ _dl_diagnostics_print_labeled_value ("dl_pagesize", GLRO (dl_pagesize));
+ _dl_diagnostics_print_labeled_string ("dl_platform", GLRO (dl_platform));
+ _dl_diagnostics_print_labeled_string
+ ("dl_profile_output", GLRO (dl_profile_output));
+ _dl_diagnostics_print_labeled_value
+ ("dl_string_platform", _dl_string_platform ( GLRO (dl_platform)));
+
+ _dl_diagnostics_print_labeled_string ("dso.ld", LD_SO);
+ _dl_diagnostics_print_labeled_string ("dso.libc", LIBC_SO);
+
+ print_environ (environ);
+ print_paths ();
+ print_version ();
+
+ _dl_diagnostics_kernel ();
+ _dl_diagnostics_cpu ();
+
+ _exit (EXIT_SUCCESS);
+}
new file mode 100644
@@ -0,0 +1,46 @@
+/* Interfaces for printing diagnostics in ld.so.
+ Copyright (C) 2021 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 _DL_DIAGNOSTICS_H
+#define _DL_DIAGNOSTICS_H
+
+#include <stdint.h>
+
+/* Write the null-terminated string to standard output, surrounded in
+ quotation marks. */
+void _dl_diagnostics_print_string (const char *s) attribute_hidden;
+
+/* Like _dl_diagnostics_print_string, but add a LABEL= prefix, and a
+ newline character as a suffix. */
+void _dl_diagnostics_print_labeled_string (const char *label, const char *s)
+ attribute_hidden;
+
+/* Print LABEL=VALUE to standard output, followed by a newline
+ character. */
+void _dl_diagnostics_print_labeled_value (const char *label, uint64_t value)
+ attribute_hidden;
+
+/* Print diagnostics data for the kernel. Called from
+ _dl_print_diagnostics. */
+void _dl_diagnostics_kernel (void) attribute_hidden;
+
+/* Print diagnostics data for the CPU(s). Called from
+ _dl_print_diagnostics. */
+void _dl_diagnostics_cpu (void) attribute_hidden;
+
+#endif /* _DL_DIAGNOSTICS_H */
@@ -63,7 +63,7 @@ struct audit_list
enum rtld_mode
{
rtld_mode_normal, rtld_mode_list, rtld_mode_verify, rtld_mode_trace,
- rtld_mode_list_tunables, rtld_mode_help,
+ rtld_mode_list_tunables, rtld_mode_list_diagnostics, rtld_mode_help,
};
/* Aggregated state information extracted from environment variables
@@ -121,4 +121,7 @@ _Noreturn void _dl_version (void) attribute_hidden;
_Noreturn void _dl_help (const char *argv0, struct dl_main_state *state)
attribute_hidden;
+/* Print a diagnostics dump. */
+_Noreturn void _dl_print_diagnostics (char **environ) attribute_hidden;
+
#endif /* _DL_MAIN */
@@ -261,6 +261,7 @@ setting environment variables (which would be inherited by subprocesses).\n\
--list-tunables list all tunables with minimum and maximum values\n"
#endif
"\
+ --list-diagnostics list diagnostics information\n\
--help display this help and exit\n\
--version output version information and exit\n\
\n\
@@ -141,6 +141,7 @@ static void dl_main_state_init (struct dl_main_state *state);
/* 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. */
+extern char **_environ attribute_hidden;
static void process_envvars (struct dl_main_state *state);
#ifdef DL_ARGV_NOT_RELRO
@@ -1287,6 +1288,14 @@ dl_main (const ElfW(Phdr) *phdr,
++_dl_argv;
}
#endif
+ else if (! strcmp (_dl_argv[1], "--list-diagnostics"))
+ {
+ state.mode = rtld_mode_list_diagnostics;
+
+ ++_dl_skip_args;
+ --_dl_argc;
+ ++_dl_argv;
+ }
else if (strcmp (_dl_argv[1], "--help") == 0)
{
state.mode = rtld_mode_help;
@@ -1315,6 +1324,9 @@ dl_main (const ElfW(Phdr) *phdr,
}
#endif
+ if (state.mode == rtld_mode_list_diagnostics)
+ _dl_print_diagnostics (_environ);
+
/* If we have no further argument the program was called incorrectly.
Grant the user some education. */
if (_dl_argc < 2)
@@ -2649,12 +2661,6 @@ a filename can be specified using the LD_DEBUG_OUTPUT environment variable.\n");
}
}
-/* 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. */
-extern char **_environ attribute_hidden;
-
-
static void
process_envvars (struct dl_main_state *state)
{
new file mode 100644
@@ -0,0 +1,77 @@
+/* Print kernel diagnostics data in ld.so. Linux version.
+ Copyright (C) 2021 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 <dl-diagnostics.h>
+#include <ldsodefs.h>
+#include <sys/utsname.h>
+
+/* Dump the auxiliary vector to standard output. */
+static void
+print_auxv (void)
+{
+ /* See _dl_show_auxv. The code below follows the general output
+ format for diagnostic dumps. */
+ unsigned int index = 0;
+ for (ElfW(auxv_t) *av = GLRO(dl_auxv); av->a_type != AT_NULL; ++av)
+ {
+ _dl_printf ("auxv[0x%x].a_type=0x%lx\n"
+ "auxv[0x%x].a_val=",
+ index, (unsigned long int) av->a_type, index);
+ if (av->a_type == AT_EXECFN
+ || av->a_type == AT_PLATFORM
+ || av->a_type == AT_BASE_PLATFORM)
+ /* The address of the strings is not useful at all, so print
+ the strings themselvs. */
+ _dl_diagnostics_print_string ((const char *) av->a_un.a_val);
+ else
+ _dl_printf ("0x%lx", (unsigned long int) av->a_un.a_val);
+ _dl_printf ("\n");
+ ++index;
+ }
+}
+
+/* Print one uname entry. */
+static void
+print_utsname_entry (const char *field, const char *value)
+{
+ _dl_printf ("uname.");
+ _dl_diagnostics_print_labeled_string (field, value);
+}
+
+/* Print information from uname, including the kernel version. */
+static void
+print_uname (void)
+{
+ struct utsname uts;
+ if (__uname (&uts) == 0)
+ {
+ print_utsname_entry ("sysname", uts.sysname);
+ print_utsname_entry ("nodename", uts.nodename);
+ print_utsname_entry ("release", uts.release);
+ print_utsname_entry ("version", uts.version);
+ print_utsname_entry ("machine", uts.machine);
+ print_utsname_entry ("domainname", uts.domainname);
+ }
+}
+
+void
+_dl_diagnostics_kernel (void)
+{
+ print_auxv ();
+ print_uname ();
+}