ld.so: Implement the --list-diagnostics option

This commit is contained in:
Florian Weimer 2021-03-02 14:38:42 +01:00
parent 40d055a2dd
commit 851f32cf7b
10 changed files with 461 additions and 8 deletions

4
NEWS
View File

@ -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]

View File

@ -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

24
elf/dl-diagnostics-cpu.c Normal file
View File

@ -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)
{
}

View File

@ -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)
{
}

265
elf/dl-diagnostics.c Normal file
View File

@ -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);
}

46
elf/dl-diagnostics.h Normal file
View File

@ -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 */

View File

@ -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 */

View File

@ -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\

View File

@ -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)
{

View File

@ -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 ();
}