readlib \
static-stubs \
stringtable \
+ tunconf \
xmalloc \
xstrdup \
# ldconfig-modules
#include <dl-cache.h>
#include <version.h>
#include <stringtable.h>
+#include <tunconf.h>
/* Used to store library names, paths, and other strings. */
static struct stringtable strings;
/* Print the extension information in *EXT. */
static void
-print_extensions (struct cache_extension_all_loaded *ext)
+print_extensions (struct cache_extension_all_loaded *ext,
+ const char *cache_data)
{
if (ext->sections[cache_extension_tag_generator].base != NULL)
{
ext->sections[cache_extension_tag_generator].size, stdout);
putchar ('\n');
}
+ if (ext->sections[cache_extension_tag_tunables].base != NULL)
+ {
+ struct tunable_header_cached *thc;
+ struct tunable_entry_cached *tec;
+ int i, count;
+
+ thc = (struct tunable_header_cached *)
+ ext->sections[cache_extension_tag_tunables].base;
+ tec = thc->tunables;
+ count = thc->num_tunables;
+ printf("tunables sig 0x%08x ver 0x%08x count %u\n",
+ thc->signature, thc->version, thc->num_tunables);
+ /* Check that COUNT won't overflow our data block. */
+ assert (ext->sections[cache_extension_tag_tunables].base
+ + ext->sections[cache_extension_tag_tunables].size
+ == (void *) & tec[count]);
+ for (i = 0; i < count; ++ i)
+ {
+ printf (" [%d] %s = %s [flags 0x%08x (",
+ i,
+ cache_data + tec[i].name_offset,
+ cache_data + tec[i].value_offset,
+ tec[i].flags);
+ if (tec[i].flags & TUNCONF_FLAG_PARSED)
+ printf ("parsed,");
+ if (tec[i].flags & TUNCONF_FLAG_NEGATIVE)
+ printf ("negative,");
+ if ((tec[i].flags & TUNCONF_FLAG_OVERRIDABLE)
+ == TUNCONF_OVERRIDE_ALLOW)
+ printf ("overridable");
+ else
+ printf ("nonoverridable");
+ if (tec[i].flag_offset != 0)
+ printf (",'%s'", cache_data + tec[i].flag_offset);
+ printf (")]\n");
+ }
+ }
}
/* Print the whole cache file, if a file contains the new cache format
cache_new->libs[i].hwcap, hwcaps_string,
cache_data + cache_new->libs[i].value);
}
- print_extensions (&ext);
+ print_extensions (&ext, cache_data);
}
/* Cleanup. */
munmap (cache, cache_size);
if (p->used)
hwcaps_array[p->section_index] = str_offset + p->name->offset;
+ struct tunable_header_cached *tunable_data;
+ size_t tunable_size;
+ size_t tunable_aligner = 0;
+
+ tunable_data = get_tunconf_ext (str_offset);
+
+ if (tunable_data == NULL)
+ {
+ /* There is no section for tunables data. */
+ hwcaps_offset -= sizeof (struct cache_extension_section);
+ }
+
/* This is the offset of the generator string. */
uint32_t generator_offset = hwcaps_offset;
if (hwcaps_count == 0)
ext->sections[xid].size = hwcaps_size;
}
+ if (tunable_data != NULL)
+ {
+ uint32_t tunable_offset_ua;
+ uint32_t tunable_offset;
+
+ tunable_size = TUNCONF_SIZE (tunable_data);
+ tunable_offset_ua = generator_offset + strlen (generator);
+ tunable_offset = ALIGN_UP (tunable_offset_ua, 8);
+ tunable_aligner = tunable_offset - tunable_offset_ua;
+
+ ++xid;
+ ext->sections[xid].tag = cache_extension_tag_tunables;
+ ext->sections[xid].flags = 0;
+ ext->sections[xid].offset = tunable_offset;
+ ext->sections[xid].size = tunable_size;
+ }
+
++xid;
ext->count = xid;
assert (xid <= cache_extension_count);
|| write (fd, generator, strlen (generator)) != strlen (generator))
error (EXIT_FAILURE, errno, _("Writing of cache extension data failed"));
+ if (tunable_data)
+ {
+ if (write (fd, " ", tunable_aligner) != tunable_aligner
+ || write (fd, tunable_data, tunable_size) != tunable_size)
+ error (EXIT_FAILURE, errno, _("Writing of cache tunable data failed"));
+ free (tunable_data);
+ }
+
free (hwcaps_array);
free (ext);
}
free (temp_name);
free (file_entries);
}
+
+struct stringtable_entry *
+cache_store_string (const char *string)
+{
+ return stringtable_add (&strings, string);
+}
#include <dl-cache.h>
#include <dl-hwcaps.h>
#include <dl-is_dso.h>
+#include "tunconf.h"
#ifndef LD_SO_CONF
# define LD_SO_CONF SYSCONFDIR "/ld.so.conf"
#endif
+#ifndef TUNABLES_CONF
+# define TUNABLES_CONF SYSCONFDIR "/tunables.conf"
+#endif
+
/* Get libc version number. */
#include <version.h>
/* Cache file to use. */
static char *cache_file;
-/* Configuration file. */
+/* Configuration file for libraries. */
static const char *config_file;
+/* Configuration file for tunables. */
+static const char *tunconfig_file;
+
/* Name and version of program. */
static void print_version (FILE *stream, struct argp_state *state);
void (*argp_program_version_hook) (FILE *, struct argp_state *)
{ NULL, 'X', NULL, 0, N_("Don't update symbolic links"), 0},
{ NULL, 'r', N_("ROOT"), 0, N_("Change to and use ROOT as root directory"), 0},
{ NULL, 'C', N_("CACHE"), 0, N_("Use CACHE as cache file"), 0},
- { NULL, 'f', N_("CONF"), 0, N_("Use CONF as configuration file"), 0},
+ { NULL, 'f', N_("CONF"), 0, N_("Use CONF as configuration file for libraries"), 0},
+ { NULL, 't', N_("TUNCONF"), 0, N_("Use TUNCONF as configuration file for tunables"), 0},
{ NULL, 'n', NULL, 0, N_("Only process directories specified on the command line. Don't build cache."), 0},
{ NULL, 'l', NULL, 0, N_("Manually link individual libraries."), 0},
{ "format", 'c', N_("FORMAT"), 0, N_("Format to use: new (default), old, or compat"), 0},
case 'f':
config_file = arg;
break;
+ case 't':
+ tunconfig_file = arg;
+ break;
case 'i':
opt_ignore_aux_cache = 1;
break;
}
static void
-add_dir_callback (const char *line, const char *from_file, int from_line)
+add_dir_callback (char *line, const char *from_file, int from_line)
{
if (!strncasecmp (line, "hwcap", 5) && isblank (line[5]))
error (0, 0, _("%s:%u: hwcap directive ignored"), from_file, from_line);
if (config_file == NULL)
config_file = LD_SO_CONF;
+ if (tunconfig_file == NULL)
+ tunconfig_file = TUNABLES_CONF;
+
if (opt_print_cache)
{
if (opt_chroot != NULL)
search_dirs ();
+ parse_tunconf (tunconfig_file, opt_chroot);
+
if (opt_build_cache)
{
save_cache (cache_file);
--- /dev/null
+/* Manage /etc/tunables.*
+ Copyright (C) 1999-2023 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <https://www.gnu.org/licenses/>. */
+
+#include <alloca.h>
+#include <argp.h>
+#include <assert.h>
+#include <error.h>
+#include <inttypes.h>
+#include <glob.h>
+#include <libgen.h>
+#include <libintl.h>
+#include <locale.h>
+#include <programs/xmalloc.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <stdlib.h>
+#include <string.h>
+#define TUNABLES_INTERNAL
+#include <elf/dl-tunables.h>
+#include <unistd.h>
+
+#include <ldconfig.h>
+#include <dl-cache.h>
+#include <version.h>
+#include <stringtable.h>
+#include <array_length.h>
+
+#include "tunconf.h"
+
+/*----------------------------------------------------------------------*/
+
+#ifndef TUNABLES_CONF
+# define TUNABLES_CONF SYSCONFDIR "/tunables.conf"
+#endif
+
+#ifndef TUNABLES_CACHE
+# define TUNABLES_CACHE SYSCONFDIR "/tunables.cache"
+#endif
+
+/* Tunable Override Policies. */
+typedef enum {
+ TOP_ALLOW = 0, /* let the environment variable override */
+ TOP_DENY /* no override allowed */
+} TOP;
+
+struct tunable_entry_int {
+ struct stringtable_entry *name;
+ struct stringtable_entry *value;
+ TOP top;
+ int tunable_id;
+ int value_is_negative:1;
+ int value_was_parsed:1;
+ unsigned long long value_ull;
+ signed long long value_sll;
+
+ struct tunable_entry_int *next;
+};
+
+struct tunable_entry_int *entry_list;
+
+/*----------------------------------------------------------------------*/
+
+static void
+add_tunable (char *line, const char *filename, int lineno)
+{
+ TOP top = TOP_ALLOW;
+ char *name;
+ char *value;
+ char *eq;
+ char *orig_line;
+ struct tunable_entry_int *entry;
+ int i, id;
+ static struct tunable_entry_int **entry_list_next = &entry_list;
+
+ orig_line = line;
+
+ /* Leading whitespace has already been stripped. */
+
+ /* Canonicalize the line. */
+ for (i=0; line[i]; i++)
+ {
+ if (line[i] == '\t')
+ line[i] = ' ';
+ if (line[i] == '\n' || line[i] == '\r')
+ {
+ line[i] = '\0';
+ break;
+ }
+ }
+
+ /* Parse modifiers. */
+ while (*line)
+ {
+ if (strncmp (line, "overridable ", 13) == 0)
+ {
+ top = TOP_ALLOW;
+ /* The line++ below skips the space. */
+ line += 12;
+ }
+ else if (strncmp (line, "nonoverridable ", 16) == 0)
+ {
+ top = TOP_DENY;
+ line += 15;
+ }
+ else switch (*line)
+ {
+ case '+':
+ top = TOP_ALLOW;
+ break;
+ case '-':
+ top = TOP_DENY;
+ break;
+ case ' ':
+ break;
+
+ default:
+ goto done;
+ }
+ line ++;
+ }
+ done:
+
+ /* NAME now points to the start of the tunable name. */
+ name = line;
+
+ /* Look for the '=' separator. */
+ eq = strchr (line, '=');
+ if (eq == NULL)
+ {
+ error_at_line (0, 0, filename, lineno,
+ "syntax error, line ignored: `%s' (missing '=')",
+ orig_line);
+ return;
+ }
+
+ if (eq == name)
+ {
+ error_at_line (0, 0, filename, lineno,
+ "syntax error, line ignored: `%s' (missing tunable name)",
+ orig_line);
+ return;
+ }
+
+ /* At this point, EQ actually points to '='. */
+ value = eq + 1;
+
+ while (*value && isspace(*value))
+ value ++;
+
+ if (*value == 0)
+ {
+ error_at_line (0, 0, filename, lineno,
+ "syntax error, line ignored: `%s' (missing value)",
+ orig_line);
+ return;
+ }
+
+ /* VALUE now points to the start of the value. */
+
+ /* Split the string into name and value c-strings. */
+ *eq = 0;
+ /* Trim trailing whitespace off NAME. */
+ while (*name && isspace (name[strlen(name)-1]))
+ name[strlen(name)-1] = 0;
+ /* Trim trailing whitespace off VALUE. */
+ while (*value && isspace (value[strlen(value)-1]))
+ value[strlen(value)-1] = 0;
+
+ id = -1;
+ for (i = 0; i < array_length (tunable_list); i ++)
+ if (strcmp (tunable_list[i].name, name) == 0)
+ {
+ id = i;
+ break;
+ }
+ if (id == -1)
+ printf ("%s:%d: Warning: tunable %s not recognized.\n",
+ filename, lineno, name);
+
+ entry = (struct tunable_entry_int *) xcalloc (sizeof (struct tunable_entry_int), 1);
+ entry->name = cache_store_string (name);
+ entry->value = cache_store_string (value);
+ entry->tunable_id = id;
+ entry->top = top;
+
+ if (value[0] == '-')
+ {
+ entry->value_is_negative = 1;
+ if (sscanf (value, "%lld", &entry->value_sll) == 1)
+ entry->value_was_parsed = 1;
+ }
+ else
+ {
+ entry->value_is_negative = 0;
+ if (sscanf (value, "%llu", &entry->value_ull) == 1)
+ entry->value_was_parsed = 1;
+ }
+
+ *entry_list_next = entry;
+ entry_list_next = & (entry->next);
+}
+
+void
+parse_tunconf (const char *filename, char *opt_chroot)
+{
+ ldconfig_parse_config (filename, opt_chroot, add_tunable);
+}
+
+struct tunable_header_cached *
+get_tunconf_ext (uint32_t string_table_offset)
+{
+ struct tunable_entry_int *tei;
+ struct tunable_header_cached *thc;
+ size_t count;
+ size_t size;
+
+ /* First, count the number of entries we have. */
+ tei = entry_list;
+ count = 0;
+ while (tei != NULL)
+ {
+ ++ count;
+ tei = tei->next;
+ }
+ if (count == 0)
+ return NULL;
+
+ /* Allocate enough space for the whole cached block. */
+ size = sizeof (struct tunable_header_cached)
+ + sizeof (struct tunable_entry_cached) * count;
+ thc = (struct tunable_header_cached *) xmalloc (size);
+
+ /* Now, fill in the structures. */
+
+ thc->signature = TUNCONF_SIGNATURE;
+ thc->version = TUNCONF_VERSION;
+ thc->num_tunables = count;
+ thc->unused_1 = 0;
+
+ tei = entry_list;
+ count = 0;
+ while (tei != NULL)
+ {
+ struct tunable_entry_cached *tec;
+
+ tec = & ( thc->tunables[count] );
+
+ tec->flags = 0;
+ if (tei->value_was_parsed)
+ tec->flags |= TUNCONF_FLAG_PARSED;
+ if (tei->value_is_negative)
+ tec->flags |= TUNCONF_FLAG_NEGATIVE;
+ switch (tei->top)
+ {
+ case TOP_ALLOW:
+ tec->flags |= TUNCONF_OVERRIDE_ALLOW;
+ break;
+ case TOP_DENY:
+ tec->flags |= TUNCONF_OVERRIDE_DENY;
+ break;
+ }
+
+ tec->tunable_id = tei->tunable_id;
+ tec->name_offset = tei->name->offset + string_table_offset;
+ tec->value_offset = tei->value->offset + string_table_offset;
+ tec->flag_offset = 0;
+ tec->unused_1 = 0;
+ if (tei->value_is_negative)
+ tec->parsed_value = (uint64_t) tei->value_sll;
+ else
+ tec->parsed_value = (uint64_t) tei->value_ull;
+
+ ++ count;
+ tei = tei->next;
+ }
+
+ return thc;
+}
--- /dev/null
+#define TUNCONF_SIGNATURE 0x7c3ba94f
+#define TUNCONF_VERSION 0x01000000
+
+#define TUNCONF_FLAG_PARSED 0x00000001
+#define TUNCONF_FLAG_NEGATIVE 0x00000002
+
+#define TUNCONF_FLAG_OVERRIDABLE 0x0000000C
+#define TUNCONF_OVERRIDE_DENY 0x00000004
+#define TUNCONF_OVERRIDE_ALLOW 0x00000000
+
+#define TUNCONF_FLAG_FILTER 0x0000ff00
+#define TUNCONF_FILTER_NONE 0x00000000
+#define TUNCONF_FILTER_PERPROC 0x00000100
+
+/* An array of [num_tunables] of these follows the below. */
+struct tunable_entry_cached {
+ uint32_t flags;
+ uint32_t tunable_id;
+ uint32_t name_offset;
+ uint32_t value_offset;
+ uint32_t flag_offset;
+ uint32_t unused_1; /* for alignment */
+ uint64_t parsed_value;
+};
+
+/* One of these is at the beginning of the tunable data block. */
+struct tunable_header_cached {
+ uint32_t signature;
+ uint32_t version;
+ uint32_t num_tunables;
+ uint32_t unused_1; /* for alignment */
+ struct tunable_entry_cached tunables[0 /* num_tunables */];
+};
+
+void parse_tunconf (const char *filename, char *opt_chroot);
+
+struct tunable_header_cached * get_tunconf_ext (uint32_t str_offset);
+#define TUNCONF_SIZE(thc_p) (sizeof(struct tunable_header_cached) \
+ + thc_p->num_tunables * sizeof (struct tunable_entry_cached))
size must be a multiple of 4. */
cache_extension_tag_glibc_hwcaps,
+ /* Array of system-wide tunable information.
+
+ For this section, 8-byte alignment is required, and the section
+ size must be a multiple of 8. */
+ cache_extension_tag_tunables,
+
/* Total number of known cache extension tags. */
cache_extension_count
};
hwcaps->flags = 0;
}
}
+ {
+ /* Section must not be empty, it must be aligned at 8 bytes, and
+ the size must be a multiple of 8. */
+ struct cache_extension_loaded *tun
+ = &loaded->sections[cache_extension_tag_tunables];
+ if (tun->size == 0
+ || ((uintptr_t) tun->base % 8) != 0
+ || (tun->size % 8) != 0)
+ {
+ tun->base = NULL;
+ tun->size = 0;
+ tun->flags = 0;
+ }
+ }
}
static bool __attribute__ ((unused))
unsigned int isa_level,
struct glibc_hwcaps_subdirectory *);
+extern struct stringtable_entry *cache_store_string (const char *string);
+
extern void init_aux_cache (void);
extern void load_aux_cache (const char *aux_cache_name);
extern enum opt_format opt_format;
/* Declared in ldconfig-parse.c */
-typedef void (*ldconfig_parse_config_cb) (const char *line,
- const char *from_file, int from_line);
+typedef void (*ldconfig_parse_config_cb) (char *line,
+ const char *from_file, int from_line);
void ldconfig_parse_config (const char *filename, char *opt_chroot,
ldconfig_parse_config_cb cb);