| commit 851f32cf7bf7067f73b991610778915edd57d7b4 |
| Author: Florian Weimer <fweimer@redhat.com> |
| Date: Tue Mar 2 14:38:42 2021 +0100 |
| |
| ld.so: Implement the --list-diagnostics option |
| |
| diff --git a/elf/Makefile b/elf/Makefile |
| index aa65ec59f143bccf..d246f1c0d9e019fd 100644 |
| |
| |
| @@ -64,7 +64,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 |
| @@ -672,6 +672,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 0000000000000000..f7d149764bcb35a1 |
| |
| |
| @@ -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 0000000000000000..831c358f1463cbf4 |
| |
| |
| @@ -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 0000000000000000..bef224b36cbf5fc3 |
| |
| |
| @@ -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 0000000000000000..27dcb12bca12e5b6 |
| |
| |
| @@ -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 9e7b51d8f010e904..9fbbdb0fac09adf3 100644 |
| |
| |
| @@ -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 908b4894b3014b2d..e19e1791d9169da2 100644 |
| |
| |
| @@ -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 54b621ec5ca014fa..d14c388f548d6d51 100644 |
| |
| |
| @@ -138,6 +138,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 |
| @@ -1273,6 +1274,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; |
| @@ -1301,6 +1310,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) |
| @@ -2623,12 +2635,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 0000000000000000..59f6402c547ba590 |
| |
| |
| @@ -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 (); |
| +} |