about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAdhemerval Zanella <adhemerval.zanella@linaro.org>2023-10-06 11:25:54 -0300
committerAdhemerval Zanella <adhemerval.zanella@linaro.org>2023-10-06 11:25:54 -0300
commit1a73ac45a777578853b219fc5055fde4b9c7ab0a (patch)
tree7e95ad64cafe164b9b081d5f0cee3e407cdfc5f1
parenta8c44318e595e2f220baf9c0f43020cd09e852a9 (diff)
downloadglibc-azanella/tunables.tar.gz
glibc-azanella/tunables.tar.xz
glibc-azanella/tunables.zip
elf: Do not duplicate GLIBC_TUNABLES string azanella/tunables
TODO: maybe adds extra tests
TODO: add comment why add spans to GLIBC_TUNABLES is safe
-rw-r--r--elf/dl-tunables.c58
-rw-r--r--elf/dl-tunables.h6
-rw-r--r--sysdeps/generic/dl-tunables-parse.h98
-rw-r--r--sysdeps/s390/cpu-features.c47
-rw-r--r--sysdeps/unix/sysv/linux/aarch64/cpu-features.c38
-rw-r--r--sysdeps/unix/sysv/linux/powerpc/cpu-features.c49
-rw-r--r--sysdeps/x86/cpu-tunables.c55
7 files changed, 207 insertions, 144 deletions
diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
index 6b5661dce4..ab76d4364a 100644
--- a/elf/dl-tunables.c
+++ b/elf/dl-tunables.c
@@ -36,28 +36,6 @@
 #define TUNABLES_INTERNAL 1
 #include "dl-tunables.h"
 
-#include <not-errno.h>
-
-static char *
-tunables_strdup (const char *in)
-{
-  size_t i = 0;
-
-  while (in[i++] != '\0');
-  char *out = __minimal_malloc (i + 1);
-
-  /* For most of the tunables code, we ignore user errors.  However,
-     this is a system error - and running out of memory at program
-     startup should be reported, so we do.  */
-  if (out == NULL)
-    _dl_fatal_printf ("failed to allocate memory to process tunables\n");
-
-  while (i-- > 0)
-    out[i] = in[i];
-
-  return out;
-}
-
 static char **
 get_next_env (char **envp, char **name, size_t *namelen, char **val,
 	      char ***prev_envp)
@@ -134,14 +112,14 @@ do_tunable_update_val (tunable_t *cur, const tunable_val_t *valp,
 /* Validate range of the input value and initialize the tunable CUR if it looks
    good.  */
 static void
-tunable_initialize (tunable_t *cur, const char *strval)
+tunable_initialize (tunable_t *cur, const char *strval, size_t len)
 {
-  tunable_val_t val;
+  tunable_val_t val = { 0 };
 
   if (cur->type.type_code != TUNABLE_TYPE_STRING)
     val.numval = (tunable_num_t) _dl_strtoul (strval, NULL);
   else
-    val.strval = strval;
+    val.strval = (struct tunable_str_t) { strval, len };
   do_tunable_update_val (cur, &val, NULL, NULL);
 }
 
@@ -158,6 +136,7 @@ struct tunable_toset_t
 {
   tunable_t *t;
   const char *value;
+  size_t len;
 };
 
 enum { tunables_list_size = array_length (tunable_list) };
@@ -166,18 +145,18 @@ enum { tunables_list_size = array_length (tunable_list) };
    where delimiters ':' are replaced with '\0', so string tunables are null
    terminated.  */
 static int
-parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
+parse_tunables_string (const char *valstring, struct tunable_toset_t *tunables)
 {
   if (valstring == NULL || *valstring == '\0')
     return 0;
 
-  char *p = valstring;
+  const char *p = valstring;
   bool done = false;
   int ntunables = 0;
 
   while (!done)
     {
-      char *name = p;
+      const char *name = p;
 
       /* First, find where the name ends.  */
       while (*p != '=' && *p != ':' && *p != '\0')
@@ -199,7 +178,7 @@ parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
       /* Skip the ':' or '='.  */
       p++;
 
-      char *value = p;
+      const char *value = p;
 
       while (*p != '=' && *p != ':' && *p != '\0')
 	p++;
@@ -208,8 +187,6 @@ parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
 	return 0;
       else if (*p == '\0')
 	done = true;
-      else
-	*p++ = '\0';
 
       /* Add the tunable if it exists.  */
       for (size_t i = 0; i < tunables_list_size; i++)
@@ -218,7 +195,8 @@ parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
 
 	  if (tunable_is_name (cur->name, name))
 	    {
-	      tunables[ntunables++] = (struct tunable_toset_t) { cur, value };
+	      tunables[ntunables++] =
+		(struct tunable_toset_t) { cur, value, p - value };
 	      break;
 	    }
 	}
@@ -228,12 +206,12 @@ parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
 }
 
 static void
-parse_tunables (char *valstring)
+parse_tunables (const char *valstring)
 {
   struct tunable_toset_t tunables[tunables_list_size];
   int ntunables = parse_tunables_string (valstring, tunables);
   for (int i = 0; i < ntunables; i++)
-    tunable_initialize (tunables[i].t, tunables[i].value);
+    tunable_initialize (tunables[i].t, tunables[i].value, tunables[i].len);
 }
 
 /* Initialize the tunables list from the environment.  For now we only use the
@@ -256,7 +234,7 @@ __tunables_init (char **envp)
     {
       if (tunable_is_name ("GLIBC_TUNABLES", envname))
 	{
-	  parse_tunables (tunables_strdup (envval));
+	  parse_tunables (envval);
 	  continue;
 	}
 
@@ -274,7 +252,7 @@ __tunables_init (char **envp)
 	  /* We have a match.  Initialize and move on to the next line.  */
 	  if (tunable_is_name (name, envname))
 	    {
-	      tunable_initialize (cur, envval);
+	      tunable_initialize (cur, envval, 0);
 	      break;
 	    }
 	}
@@ -288,7 +266,7 @@ __tunables_print (void)
     {
       const tunable_t *cur = &tunable_list[i];
       if (cur->type.type_code == TUNABLE_TYPE_STRING
-	  && cur->val.strval == NULL)
+	  && cur->val.strval.str == NULL)
 	_dl_printf ("%s:\n", cur->name);
       else
 	{
@@ -314,7 +292,9 @@ __tunables_print (void)
 			  (size_t) cur->type.max);
 	      break;
 	    case TUNABLE_TYPE_STRING:
-	      _dl_printf ("%s\n", cur->val.strval);
+	      _dl_printf ("%.*s\n",
+			  (int) cur->val.strval.len,
+			  cur->val.strval.str);
 	      break;
 	    default:
 	      __builtin_unreachable ();
@@ -349,7 +329,7 @@ __tunable_get_val (tunable_id_t id, void *valp, tunable_callback_t callback)
 	}
     case TUNABLE_TYPE_STRING:
 	{
-	  *((const char **)valp) = cur->val.strval;
+	  *((struct tunable_str_t **) valp) = &cur->val.strval;
 	  break;
 	}
     default:
diff --git a/elf/dl-tunables.h b/elf/dl-tunables.h
index 45c191e021..0e777d7d37 100644
--- a/elf/dl-tunables.h
+++ b/elf/dl-tunables.h
@@ -30,7 +30,11 @@ typedef intmax_t tunable_num_t;
 typedef union
 {
   tunable_num_t numval;
-  const char *strval;
+  struct tunable_str_t
+  {
+    const char *str;
+    size_t len;
+  } strval;
 } tunable_val_t;
 
 typedef void (*tunable_callback_t) (tunable_val_t *);
diff --git a/sysdeps/generic/dl-tunables-parse.h b/sysdeps/generic/dl-tunables-parse.h
new file mode 100644
index 0000000000..83cecaf416
--- /dev/null
+++ b/sysdeps/generic/dl-tunables-parse.h
@@ -0,0 +1,98 @@
+/* Helper functions to handle tunable strings.
+   Copyright (C) 2023 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_TUNABLES_PARSE_H
+#define _DL_TUNABLES_PARSE_H 1
+
+#ifndef DEFAULT_MEMCMP
+# define DEFAULT_MEMCMP memcmp
+#endif
+
+/* Compare the contents of STRVAL with STR of size LEN.  The STR might not
+   be null-terminated.   */
+static inline bool
+tunable_strcmp (const struct tunable_str_t *strval, const char *str,
+		size_t len)
+{
+  return strval->len == len && DEFAULT_MEMCMP (strval->str, str, len) == 0;
+}
+#define tunable_strcmp_cte(__tunable, __str)				\
+ ({									\
+    __builtin_constant_p (__str)					\
+    ? tunable_strcmp (&__tunable->strval, __str, sizeof (__str) - 1)	\
+    : tunable_strcmp (&__tunable->strval, __str, strlen (__str));	\
+ })
+
+/* Helper function to iterate over string tunable composed by multiple
+   suboptions separated by comma.  The tunable is represented as span (address
+   and size) from GLIBC_TUNABLES, so it might not be null terminated.
+
+   For instance, to print all the items:
+
+     struct tunable_str_comma_t st;
+     tunable_str_comma_init (&st, valp);
+
+     struct tunable_str_t tstr;
+     while (tunable_str_comma_next (&st, &tstr))
+      {
+        if (tstr.len == 0)
+	  continue;
+
+	_dl_printf ("[%s] %.*s (%d)\n", __func__,
+		    (int) tstr.len,
+		    tstr.str,
+		    (int) tstr.len);
+      }  */
+
+struct tunable_str_comma_t
+{
+  const char *p;
+  size_t plen;
+  size_t maxplen;
+};
+
+static inline void
+tunable_str_comma_init (struct tunable_str_comma_t *state, tunable_val_t *valp)
+{
+  state->p = valp->strval.str;
+  state->plen = 0;
+  state->maxplen = valp->strval.len;
+}
+
+static inline bool
+tunable_str_comma_next (struct tunable_str_comma_t *state,
+			struct tunable_str_t *str)
+{
+  if (*state->p == '\0' || state->plen >= state->maxplen)
+    return false;
+
+  const char *c;
+  for (c = state->p; *c != ','; c++, state->plen++)
+    if (*c == '\0' || state->plen == state->maxplen)
+      break;
+
+  str->str = state->p;
+  str->len = c - state->p;
+
+  state->p = c + 1;
+  state->plen++;
+
+  return true;
+}
+
+#endif
diff --git a/sysdeps/s390/cpu-features.c b/sysdeps/s390/cpu-features.c
index 39f8c23a60..c1898d8e1e 100644
--- a/sysdeps/s390/cpu-features.c
+++ b/sysdeps/s390/cpu-features.c
@@ -22,6 +22,7 @@
 #include <ifunc-memcmp.h>
 #include <string.h>
 extern __typeof (memcmp) MEMCMP_DEFAULT;
+#include <dl-tunables-parse.h>
 
 #define S390_COPY_CPU_FEATURES(SRC_PTR, DEST_PTR)	\
   (DEST_PTR)->hwcap = (SRC_PTR)->hwcap;			\
@@ -51,32 +52,23 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
   struct cpu_features cpu_features_curr;
   S390_COPY_CPU_FEATURES (cpu_features, &cpu_features_curr);
 
-  const char *token = valp->strval;
-  do
+  struct tunable_str_comma_t st;
+  tunable_str_comma_init (&st, valp);
+
+  struct tunable_str_t tstr;
+  while (tunable_str_comma_next (&st, &tstr))
     {
-      const char *token_end, *feature;
-      bool disable;
-      size_t token_len;
-      size_t feature_len;
-
-      /* Find token separator or end of string.  */
-      for (token_end = token; *token_end != ','; token_end++)
-	if (*token_end == '\0')
-	  break;
-
-      /* Determine feature.  */
-      token_len = token_end - token;
-      if (*token == '-')
-	{
-	  disable = true;
-	  feature = token + 1;
-	  feature_len = token_len - 1;
-	}
-      else
+      if (tstr.len == 0)
+	continue;
+
+      const char *feature = tstr.str;
+      size_t feature_len = tstr.len;
+
+      bool disable = *feature == '-';
+      if (disable)
 	{
-	  disable = false;
-	  feature = token;
-	  feature_len = token_len;
+	  feature = feature + 1;
+	  feature_len = feature_len - 1;
 	}
 
       /* Handle only the features here which are really used in the
@@ -187,14 +179,7 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
 	  else
 	    cpu_features_curr.stfle_bits[0] |= stfle_bits0_mask;
 	}
-
-      /* Jump over current token ... */
-      token += token_len;
-
-      /* ... and skip token separator for next round.  */
-      if (*token == ',') token++;
     }
-  while (*token != '\0');
 
   /* Copy back the features after checking that no unsupported features were
      enabled by user.  */
diff --git a/sysdeps/unix/sysv/linux/aarch64/cpu-features.c b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
index dc09c1c827..3f1a6bcd62 100644
--- a/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
+++ b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
@@ -16,10 +16,12 @@
    License along with the GNU C Library; if not, see
    <https://www.gnu.org/licenses/>.  */
 
+#include <array_length.h>
 #include <cpu-features.h>
 #include <sys/auxv.h>
 #include <elf/dl-hwcaps.h>
 #include <sys/prctl.h>
+#include <dl-tunables-parse.h>
 
 #define DCZID_DZP_MASK (1 << 4)
 #define DCZID_BS_MASK (0xf)
@@ -33,28 +35,32 @@
 struct cpu_list
 {
   const char *name;
+  size_t len;
   uint64_t midr;
 };
 
-static struct cpu_list cpu_list[] = {
-      {"falkor",	 0x510FC000},
-      {"thunderxt88",	 0x430F0A10},
-      {"thunderx2t99",   0x431F0AF0},
-      {"thunderx2t99p1", 0x420F5160},
-      {"phecda",	 0x680F0000},
-      {"ares",		 0x411FD0C0},
-      {"emag",		 0x503F0001},
-      {"kunpeng920", 	 0x481FD010},
-      {"a64fx",		 0x460F0010},
-      {"generic", 	 0x0}
+static const struct cpu_list cpu_list[] =
+{
+#define CPU_LIST_ENTRY(__str, __num) { __str, sizeof (__str) - 1, __num }
+  CPU_LIST_ENTRY ("falkor",         0x510FC000),
+  CPU_LIST_ENTRY ("thunderxt88",    0x430F0A10),
+  CPU_LIST_ENTRY ("thunderx2t99",   0x431F0AF0),
+  CPU_LIST_ENTRY ("thunderx2t99p1", 0x420F5160),
+  CPU_LIST_ENTRY ("phecda",         0x680F0000),
+  CPU_LIST_ENTRY ("ares",           0x411FD0C0),
+  CPU_LIST_ENTRY ("emag",           0x503F0001),
+  CPU_LIST_ENTRY ("kunpeng920",     0x481FD010),
+  CPU_LIST_ENTRY ("a64fx",          0x460F0010),
+  CPU_LIST_ENTRY ("generic",        0x0),
 };
 
 static uint64_t
-get_midr_from_mcpu (const char *mcpu)
+get_midr_from_mcpu (const struct tunable_str_t *mcpu)
 {
-  for (int i = 0; i < sizeof (cpu_list) / sizeof (struct cpu_list); i++)
-    if (strcmp (mcpu, cpu_list[i].name) == 0)
+  for (int i = 0; i < array_length (cpu_list); i++) {
+    if (tunable_strcmp (mcpu, cpu_list[i].name, cpu_list[i].len))
       return cpu_list[i].midr;
+  }
 
   return UINT64_MAX;
 }
@@ -65,7 +71,9 @@ init_cpu_features (struct cpu_features *cpu_features)
   register uint64_t midr = UINT64_MAX;
 
   /* Get the tunable override.  */
-  const char *mcpu = TUNABLE_GET (glibc, cpu, name, const char *, NULL);
+  const struct tunable_str_t *mcpu = TUNABLE_GET (glibc, cpu, name,
+						  struct tunable_str_t *,
+						  NULL);
   if (mcpu != NULL)
     midr = get_midr_from_mcpu (mcpu);
 
diff --git a/sysdeps/unix/sysv/linux/powerpc/cpu-features.c b/sysdeps/unix/sysv/linux/powerpc/cpu-features.c
index 7c6e20e702..6709213576 100644
--- a/sysdeps/unix/sysv/linux/powerpc/cpu-features.c
+++ b/sysdeps/unix/sysv/linux/powerpc/cpu-features.c
@@ -20,6 +20,7 @@
 #include <stdint.h>
 #include <cpu-features.h>
 #include <elf/dl-tunables.h>
+#include <dl-tunables-parse.h>
 #include <unistd.h>
 #include <string.h>
 
@@ -43,38 +44,33 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
   struct cpu_features *cpu_features = &GLRO(dl_powerpc_cpu_features);
   unsigned long int tcbv_hwcap = cpu_features->hwcap;
   unsigned long int tcbv_hwcap2 = cpu_features->hwcap2;
-  const char *token = valp->strval;
-  do
+
+  struct tunable_str_comma_t st;
+  tunable_str_comma_init (&st, valp);
+
+  struct tunable_str_t tstr;
+  while (tunable_str_comma_next (&st, &tstr))
     {
-      const char *token_end, *feature;
-      bool disable;
-      size_t token_len, i, feature_len, offset = 0;
-      /* Find token separator or end of string.  */
-      for (token_end = token; *token_end != ','; token_end++)
-	if (*token_end == '\0')
-	  break;
+      if (tstr.len == 0)
+	continue;
 
-      /* Determine feature.  */
-      token_len = token_end - token;
-      if (*token == '-')
-	{
-	  disable = true;
-	  feature = token + 1;
-	  feature_len = token_len - 1;
-	}
-      else
+      const char *feature = tstr.str;
+      size_t feature_len = tstr.len;
+
+      bool disable = *feature == '-';
+      if (disable)
 	{
-	  disable = false;
-	  feature = token;
-	  feature_len = token_len;
+	  feature = feature + 1;
+	  feature_len = feature_len - 1;
 	}
-      for (i = 0; i < array_length (hwcap_tunables); ++i)
+
+      size_t offset = 0;
+      for (int i = 0; i < array_length (hwcap_tunables); ++i)
 	{
 	  const char *hwcap_name = hwcap_names + offset;
 	  size_t hwcap_name_len = strlen (hwcap_name);
 	  /* Check the tunable name on the supported list.  */
-	  if (hwcap_name_len == feature_len
-	      && memcmp (feature, hwcap_name, feature_len) == 0)
+	  if (tunable_strcmp (&tstr, hwcap_name, hwcap_name_len))
 	    {
 	      /* Update the hwcap and hwcap2 bits.  */
 	      if (disable)
@@ -98,12 +94,7 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
 	    }
 	  offset += hwcap_name_len + 1;
 	}
-	token += token_len;
-	/* ... and skip token separator for next round.  */
-	if (*token == ',')
-	  token++;
     }
-  while (*token != '\0');
 }
 
 static inline void
diff --git a/sysdeps/x86/cpu-tunables.c b/sysdeps/x86/cpu-tunables.c
index 0d4f328585..48e6a865a1 100644
--- a/sysdeps/x86/cpu-tunables.c
+++ b/sysdeps/x86/cpu-tunables.c
@@ -38,6 +38,7 @@ extern __typeof (memcmp) DEFAULT_MEMCMP;
 #else
 # define DEFAULT_MEMCMP	memcmp
 #endif
+#include <dl-tunables-parse.h>
 
 #define CHECK_GLIBC_IFUNC_CPU_OFF(f, cpu_features, name, len)		\
   _Static_assert (sizeof (#name) - 1 == len, #name " != " #len);	\
@@ -106,33 +107,33 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
      NOTE: the IFUNC selection may change over time.  Please check all
      multiarch implementations when experimenting.  */
 
-  const char *p = valp->strval, *c;
   struct cpu_features *cpu_features = &GLRO(dl_x86_cpu_features);
-  size_t len;
 
-  do
+  struct tunable_str_comma_t st;
+  tunable_str_comma_init (&st, valp);
+
+  struct tunable_str_t tstr;
+  while (tunable_str_comma_next (&st, &tstr))
     {
-      const char *n;
-      bool disable;
-      size_t nl;
+      if (tstr.len == 0)
+	continue;
 
-      for (c = p; *c != ','; c++)
-	if (*c == '\0')
-	  break;
+      const char *n = tstr.str;
+      size_t len = tstr.len;
 
-      len = c - p;
-      disable = *p == '-';
+      bool disable = *n == '-';
       if (disable)
 	{
-	  n = p + 1;
-	  nl = len - 1;
-	}
-      else
-	{
-	  n = p;
-	  nl = len;
+	  n = n + 1;
+	  len = len - 1;
 	}
-      switch (nl)
+
+	_dl_printf ("[%s] %.*s (%d)\n", __func__,
+		    (int) tstr.len,
+		    tstr.str,
+		    (int) tstr.len);
+
+      switch (len)
 	{
 	default:
 	  break;
@@ -280,9 +281,7 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
 	    }
 	  break;
 	}
-      p += len + 1;
     }
-  while (*c != '\0');
 }
 
 #if CET_ENABLED
@@ -290,12 +289,11 @@ attribute_hidden
 void
 TUNABLE_CALLBACK (set_x86_ibt) (tunable_val_t *valp)
 {
-  if (DEFAULT_MEMCMP (valp->strval, "on", sizeof ("on")) == 0)
+  if (tunable_strcmp_cte (valp, "on"))
     GL(dl_x86_feature_control).ibt = cet_always_on;
-  else if (DEFAULT_MEMCMP (valp->strval, "off", sizeof ("off")) == 0)
+  else if (tunable_strcmp_cte (valp, "off"))
     GL(dl_x86_feature_control).ibt = cet_always_off;
-  else if (DEFAULT_MEMCMP (valp->strval, "permissive",
-			   sizeof ("permissive")) == 0)
+  else if (tunable_strcmp_cte (valp, "permissive"))
     GL(dl_x86_feature_control).ibt = cet_permissive;
 }
 
@@ -303,12 +301,11 @@ attribute_hidden
 void
 TUNABLE_CALLBACK (set_x86_shstk) (tunable_val_t *valp)
 {
-  if (DEFAULT_MEMCMP (valp->strval, "on", sizeof ("on")) == 0)
+  if (tunable_strcmp_cte (valp, "on"))
     GL(dl_x86_feature_control).shstk = cet_always_on;
-  else if (DEFAULT_MEMCMP (valp->strval, "off", sizeof ("off")) == 0)
+  else if (tunable_strcmp_cte (valp, "off"))
     GL(dl_x86_feature_control).shstk = cet_always_off;
-  else if (DEFAULT_MEMCMP (valp->strval, "permissive",
-			   sizeof ("permissive")) == 0)
+  else if (tunable_strcmp_cte (valp, "permissive"))
     GL(dl_x86_feature_control).shstk = cet_permissive;
 }
 #endif