From patchwork Wed Feb 24 13:42:28 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 42137 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 30912394EC33; Wed, 24 Feb 2021 14:38:38 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 30912394EC33 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1614177518; bh=/t7b28K4qmAQFwJRC6SEnu554f8Ld9sqiED6VTPc3T0=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=OzOv6I7FZ8ZZU1P6NNfXDrotBqIhewsWrWAukQQUAipOtEat/Y57qZv5xyoZL1oxp X0w4MIHdSwxLSnr/MmHVJ3vY7OdGM7wxKgxTvBkxysbtCAiOV3cizM1BymRd1KmeQg 0EwROmr8resF3SO4P2jTTmzyCynNwMYV9GW11Cv0= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [63.128.21.124]) by sourceware.org (Postfix) with ESMTP id 3742E394EC21 for ; Wed, 24 Feb 2021 14:38:35 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 3742E394EC21 Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-482-HnwTu9V4MciaMESxaCR_Qw-1; Wed, 24 Feb 2021 09:37:06 -0500 X-MC-Unique: HnwTu9V4MciaMESxaCR_Qw-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 740A21895840 for ; Wed, 24 Feb 2021 13:41:44 +0000 (UTC) Received: from oldenburg.str.redhat.com (ovpn-113-131.ams2.redhat.com [10.36.113.131]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 65E3519C46 for ; Wed, 24 Feb 2021 13:41:43 +0000 (UTC) To: libc-alpha@sourceware.org Subject: [PATCH 1/2] ld.so: Implement the --list-diagnostics option Date: Wed, 24 Feb 2021 14:42:28 +0100 Message-ID: <875z2hshtn.fsf@oldenburg.str.redhat.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (gnu/linux) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-12.2 required=5.0 tests=BAYES_00, DKIM_INVALID, DKIM_SIGNED, GIT_PATCH_0, KAM_DMARC_STATUS, KAM_SHORT, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Florian Weimer via Libc-alpha From: Florian Weimer Reply-To: Florian Weimer Errors-To: libc-alpha-bounces@sourceware.org Sender: "Libc-alpha" --- NEWS | 4 + elf/Makefile | 5 +- elf/dl-diagnostics.c | 327 +++++++++++++++++++++++++++++++++++++++ elf/dl-main.h | 5 +- elf/dl-usage.c | 1 + elf/rtld.c | 18 ++- sysdeps/generic/dl-diagnostics.h | 28 ++++ sysdeps/generic/ldsodefs.h | 14 ++ 8 files changed, 394 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..e272a72ccf 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 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.c b/elf/dl-diagnostics.c new file mode 100644 index 0000000000..778592b76b --- /dev/null +++ b/elf/dl-diagnostics.c @@ -0,0 +1,327 @@ +/* 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 + . */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "trusted-dirs.h" +#include "version.h" + +#include + +/* 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" + ; + 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; + } +} + +/* On Hurd, uname is not available on ld.so. This corresponds to a + missing domainname member. */ +#define PRINT_UNAME (_UTSNAME_DOMAIN_LENGTH > 0) + +#if PRINT_UNAME +/* 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); + } +} +#endif + +/* 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); +} + +#ifdef HAVE_AUX_VECTOR +/* 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_putc ('\n'); + ++index; + } +} +#endif /* HAVE_AUX_VECTOR */ + +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 (); +#if PRINT_UNAME + print_uname (); +#endif + print_version (); + +#ifdef HAVE_AUX_VECTOR + print_auxv (); +#endif + + _dl_diagnostics_sysdeps (); + + _exit (EXIT_SUCCESS); +} 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/generic/dl-diagnostics.h b/sysdeps/generic/dl-diagnostics.h new file mode 100644 index 0000000000..5db7416dfe --- /dev/null +++ b/sysdeps/generic/dl-diagnostics.h @@ -0,0 +1,28 @@ +/* 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 + . */ + +#ifndef _DL_DIAGNOSTICS_H +#define _DL_DIAGNOSTICS_H + +/* The generic version has no extra information to print. */ +static inline void +_dl_diagnostics_sysdeps (void) +{ +} + +#endif /* _DL_DIAGNOSTICS_H */ diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h index 9720a4e446..da0143cf44 100644 --- a/sysdeps/generic/ldsodefs.h +++ b/sysdeps/generic/ldsodefs.h @@ -790,6 +790,20 @@ void _dl_fatal_printf (const char *fmt, ...) __attribute__ ((__format__ (__printf__, 1, 2), __noreturn__)); rtld_hidden_proto (_dl_fatal_printf) +/* 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; + /* An exception raised by the _dl_signal_error function family and caught by _dl_catch_error function family. Exceptions themselves are copied as part of the raise operation, but the strings are