From ea373fd5401ba94f51c0dbb24046053a5aa2c5ee Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Mon, 1 Jun 2020 14:11:32 -0700
Subject: [PATCH] libc.so: Add --list-tunables

Add --list-tunables to __libc_main to print tunables with min and max
values.  In order to pass argc and argv to __libc_main, each target
must provide a suitable __libc_main.  Since tunable_list may be updated
after relocated, attribute_relro is removed from tunable_list.

2 functions, __tunable_update_val and __tunables_print, are added to
update tunable values and print tunable values.
---
 csu/Makefile                      |  4 +-
 csu/{version.c => libc-version.c} |  8 ++-
 elf/Versions                      |  6 +++
 elf/dl-tunables.c                 | 89 +++++++++++++++++++++++++------
 elf/dl-tunables.h                 | 17 ++++++
 scripts/gen-tunables.awk          |  2 +-
 sysdeps/i386/start.S              | 28 +++++++---
 sysdeps/x86/Makefile              |  1 +
 sysdeps/x86/cacheinfo.c           | 24 ++++++++-
 sysdeps/x86/init-arch.h           |  2 +
 sysdeps/x86/libc-main.S           | 36 +++++++++++++
 sysdeps/x86/libc-version.c        | 55 +++++++++++++++++++
 sysdeps/x86_64/start.S            | 34 +++++++++---
 13 files changed, 269 insertions(+), 37 deletions(-)
 rename csu/{version.c => libc-version.c} (93%)
 create mode 100644 sysdeps/x86/libc-main.S
 create mode 100644 sysdeps/x86/libc-version.c

diff --git a/csu/Makefile b/csu/Makefile
index 555ae27dea..951a093f15 100644
--- a/csu/Makefile
+++ b/csu/Makefile
@@ -26,8 +26,8 @@ subdir := csu
 
 include ../Makeconfig
 
-routines = init-first libc-start $(libc-init) sysdep version check_fds \
-	   libc-tls elf-init dso_handle
+routines = init-first libc-start $(libc-init) sysdep libc-version \
+	   check_fds libc-tls elf-init dso_handle
 aux	 = errno
 elide-routines.os = libc-tls
 static-only-routines = elf-init
diff --git a/csu/version.c b/csu/libc-version.c
similarity index 93%
rename from csu/version.c
rename to csu/libc-version.c
index 57b49dfd8a..9b0f4cb94b 100644
--- a/csu/version.c
+++ b/csu/libc-version.c
@@ -61,12 +61,16 @@ __gnu_get_libc_version (void)
 }
 weak_alias (__gnu_get_libc_version, gnu_get_libc_version)
 
+#ifndef LIBC_MAIN
+# define LIBC_MAIN __libc_main
+#endif
+
 /* This function is the entry point for the shared object.
    Running the library as a program will get here.  */
 
-extern void __libc_main (void) __attribute__ ((noreturn));
+extern void LIBC_MAIN (void) __attribute__ ((noreturn, visibility ("hidden")));
 void
-__libc_main (void)
+LIBC_MAIN (void)
 {
   __libc_print_version ();
   _exit (0);
diff --git a/elf/Versions b/elf/Versions
index be88c48e6d..bf9d7dff9b 100644
--- a/elf/Versions
+++ b/elf/Versions
@@ -76,5 +76,11 @@ ld {
 
     # Set value of a tunable.
     __tunable_get_val;
+
+    # Update value of a tunable.
+    __tunable_update_val;
+
+    # Print all tunables.
+    __tunables_print;
   }
 }
diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
index a1001fe313..0e84b40cb2 100644
--- a/elf/dl-tunables.c
+++ b/elf/dl-tunables.c
@@ -100,31 +100,39 @@ get_next_env (char **envp, char **name, size_t *namelen, char **val,
     }									      \
 })
 
+#define TUNABLE_UPDATE_VAL(__cur, __val, __min, __max, __type)		      \
+({									      \
+  (__cur)->type.min = (__min);						      \
+  (__cur)->type.max = (__max);						      \
+  (__cur)->val.numval = (__val);					      \
+  (__cur)->initialized = true;						      \
+})
+
 static void
-do_tunable_update_val (tunable_t *cur, const void *valp)
+do_tunable_update_val (tunable_t *cur, const void *valp,
+		       const void *minp, const void *maxp)
 {
-  uint64_t val;
+  uint64_t val, min, max;
 
   if (cur->type.type_code != TUNABLE_TYPE_STRING)
-    val = *((int64_t *) valp);
+    {
+      val = *((int64_t *) valp);
+      if (minp)
+	min = *((int64_t *) minp);
+      if (maxp)
+	max = *((int64_t *) maxp);
+    }
 
   switch (cur->type.type_code)
     {
     case TUNABLE_TYPE_INT_32:
-	{
-	  TUNABLE_SET_VAL_IF_VALID_RANGE (cur, val, int64_t);
-	  break;
-	}
     case TUNABLE_TYPE_UINT_64:
-	{
-	  TUNABLE_SET_VAL_IF_VALID_RANGE (cur, val, uint64_t);
-	  break;
-	}
     case TUNABLE_TYPE_SIZE_T:
-	{
-	  TUNABLE_SET_VAL_IF_VALID_RANGE (cur, val, uint64_t);
-	  break;
-	}
+      if (minp && maxp)
+	TUNABLE_UPDATE_VAL (cur, val, min, max, int64_t);
+      else
+	TUNABLE_SET_VAL_IF_VALID_RANGE (cur, val, int64_t);
+      break;
     case TUNABLE_TYPE_STRING:
 	{
 	  cur->val.strval = valp;
@@ -153,7 +161,7 @@ tunable_initialize (tunable_t *cur, const char *strval)
       cur->initialized = true;
       valp = strval;
     }
-  do_tunable_update_val (cur, valp);
+  do_tunable_update_val (cur, valp, NULL, NULL);
 }
 
 void
@@ -161,8 +169,17 @@ __tunable_set_val (tunable_id_t id, void *valp)
 {
   tunable_t *cur = &tunable_list[id];
 
-  do_tunable_update_val (cur, valp);
+  do_tunable_update_val (cur, valp, NULL, NULL);
+}
+
+void
+__tunable_update_val (tunable_id_t id, void *valp, void *minp, void *maxp)
+{
+  tunable_t *cur = &tunable_list[id];
+
+  do_tunable_update_val (cur, valp, minp, maxp);
 }
+rtld_hidden_def (__tunable_update_val)
 
 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
 /* Parse the tunable string TUNESTR and adjust it to drop any tunables that may
@@ -361,6 +378,44 @@ __tunables_init (char **envp)
     }
 }
 
+void
+__tunables_print (void)
+{
+  _dl_printf ("tunables:\n");
+  for (int i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
+    {
+      tunable_t *cur = &tunable_list[i];
+      _dl_printf ("  %s: ", cur->name);
+      switch (cur->type.type_code)
+	{
+	case TUNABLE_TYPE_INT_32:
+	  _dl_printf ("0x%x (min: 0x%x, max: 0x%x)\n",
+		      (int) cur->val.numval,
+		      (int) cur->type.min,
+		      (int) cur->type.max);
+	  break;
+	case TUNABLE_TYPE_UINT_64:
+	  _dl_printf ("0x%lx (min: 0x%lx, max: 0x%lx)\n",
+		      (long int) cur->val.numval,
+		      (long int) cur->type.min,
+		      (long int) cur->type.max);
+	  break;
+	case TUNABLE_TYPE_SIZE_T:
+	  _dl_printf ("0x%Zx (min: 0x%Zx, max: 0x%Zx)\n",
+		      (size_t) cur->val.numval,
+		      (size_t) cur->type.min,
+		      (size_t) cur->type.max);
+	  break;
+	case TUNABLE_TYPE_STRING:
+	  _dl_printf ("%s\n", cur->val.strval ? cur->val.strval : "");
+	  break;
+	default:
+	  __builtin_unreachable ();
+	}
+    }
+}
+rtld_hidden_def (__tunables_print)
+
 /* Set the tunable value.  This is called by the module that the tunable exists
    in. */
 void
diff --git a/elf/dl-tunables.h b/elf/dl-tunables.h
index 969e50327b..577c5d3369 100644
--- a/elf/dl-tunables.h
+++ b/elf/dl-tunables.h
@@ -67,10 +67,14 @@ typedef struct _tunable tunable_t;
 # include "dl-tunable-list.h"
 
 extern void __tunables_init (char **);
+extern void __tunables_print (void);
 extern void __tunable_get_val (tunable_id_t, void *, tunable_callback_t);
 extern void __tunable_set_val (tunable_id_t, void *);
+extern void __tunable_update_val (tunable_id_t, void *, void *, void *);
 rtld_hidden_proto (__tunables_init)
+rtld_hidden_proto (__tunables_print)
 rtld_hidden_proto (__tunable_get_val)
+rtld_hidden_proto (__tunable_update_val)
 
 /* Define TUNABLE_GET and TUNABLE_SET in short form if TOP_NAMESPACE and
    TUNABLE_NAMESPACE are defined.  This is useful shorthand to get and set
@@ -80,11 +84,16 @@ rtld_hidden_proto (__tunable_get_val)
   TUNABLE_GET_FULL (TOP_NAMESPACE, TUNABLE_NAMESPACE, __id, __type, __cb)
 # define TUNABLE_SET(__id, __type, __val) \
   TUNABLE_SET_FULL (TOP_NAMESPACE, TUNABLE_NAMESPACE, __id, __type, __val)
+# define TUNABLE_UPDATE(__id, __type, __val, __min, __max) \
+  TUNABLE_UPDATE_FULL (TOP_NAMESPACE, TUNABLE_NAMESPACE, __id, __type, \
+		       __val, __min, __max)
 #else
 # define TUNABLE_GET(__top, __ns, __id, __type, __cb) \
   TUNABLE_GET_FULL (__top, __ns, __id, __type, __cb)
 # define TUNABLE_SET(__top, __ns, __id, __type, __val) \
   TUNABLE_SET_FULL (__top, __ns, __id, __type, __val)
+# define TUNABLE_UPDATE(__top, __ns, __id, __type, __val, __min, __max) \
+  TUNABLE_UPDATE_FULL (__top, __ns, __id, __type, __val, __min, __max)
 #endif
 
 /* Get and return a tunable value.  If the tunable was set externally and __CB
@@ -104,6 +113,14 @@ rtld_hidden_proto (__tunable_get_val)
 			& (__type) {__val});				      \
 })
 
+/* Update a tunable value.  */
+# define TUNABLE_UPDATE_FULL(__top, __ns, __id, __type, __val, __min, __max) \
+({									      \
+  __tunable_update_val (TUNABLE_ENUM_NAME (__top, __ns, __id),		      \
+			& (__type) {__val},  & (__type) {__min},	      \
+			& (__type) {__max});				      \
+})
+
 /* Namespace sanity for callback functions.  Use this macro to keep the
    namespace of the modules clean.  */
 # define TUNABLE_CALLBACK(__name) _dl_tunable_ ## __name
diff --git a/scripts/gen-tunables.awk b/scripts/gen-tunables.awk
index 622199061a..7d699b3818 100644
--- a/scripts/gen-tunables.awk
+++ b/scripts/gen-tunables.awk
@@ -158,7 +158,7 @@ END {
 
   # Finally, the tunable list.
   print "\n#ifdef TUNABLES_INTERNAL"
-  print "static tunable_t tunable_list[] attribute_relro = {"
+  print "static tunable_t tunable_list[] = {"
   for (tnm in types) {
     split (tnm, indices, SUBSEP);
     t = indices[1];
diff --git a/sysdeps/i386/start.S b/sysdeps/i386/start.S
index c57b25f055..6d2e76e5cb 100644
--- a/sysdeps/i386/start.S
+++ b/sysdeps/i386/start.S
@@ -54,7 +54,13 @@
 
 #include <sysdep.h>
 
-ENTRY (_start)
+#ifdef LIBC_MAIN
+# define START __libc_main
+#else
+# define START _start
+#endif
+
+ENTRY (START)
 	/* Clearing frame pointer is insufficient, use CFI.  */
 	cfi_undefined (eip)
 	/* Clear the frame pointer.  The ABI suggests this be done, to mark
@@ -75,6 +81,11 @@ ENTRY (_start)
 	pushl %eax		/* Push garbage because we allocate
 				   28 more bytes.  */
 
+#ifdef LIBC_MAIN
+	pushl %ecx		/* Push second argument: argv.  */
+	pushl %esi		/* Push first argument: argc.  */
+	call LIBC_MAIN
+#else
 	/* Provide the highest stack address to the user code (for stacks
 	   which grow downwards).  */
 	pushl %esp
@@ -82,7 +93,7 @@ ENTRY (_start)
 	pushl %edx		/* Push address of the shared library
 				   termination function.  */
 
-#ifdef PIC
+# ifdef PIC
 	/* Load PIC register.  */
 	call 1f
 	addl $_GLOBAL_OFFSET_TABLE_, %ebx
@@ -96,9 +107,9 @@ ENTRY (_start)
 	pushl %ecx		/* Push second argument: argv.  */
 	pushl %esi		/* Push first argument: argc.  */
 
-# ifdef SHARED
+#  ifdef SHARED
 	pushl main@GOT(%ebx)
-# else
+#  else
 	/* Avoid relocation in static PIE since _start is called before
 	   it is relocated.  Don't use "leal main@GOTOFF(%ebx), %eax"
 	   since main may be in a shared object.  Linker will convert
@@ -106,12 +117,12 @@ ENTRY (_start)
 	   if main is defined locally.  */
 	movl main@GOT(%ebx), %eax
 	pushl %eax
-# endif
+#  endif
 
 	/* Call the user's main function, and exit with its value.
 	   But let the libc call main.    */
 	call __libc_start_main@PLT
-#else
+# else
 	/* Push address of our own entry points to .fini and .init.  */
 	pushl $__libc_csu_fini
 	pushl $__libc_csu_init
@@ -124,6 +135,7 @@ ENTRY (_start)
 	/* Call the user's main function, and exit with its value.
 	   But let the libc call main.    */
 	call __libc_start_main
+# endif
 #endif
 
 	hlt			/* Crash if somehow `exit' does return.  */
@@ -132,8 +144,9 @@ ENTRY (_start)
 1:	movl	(%esp), %ebx
 	ret
 #endif
-END (_start)
+END (START)
 
+#ifndef LIBC_MAIN
 /* To fulfill the System V/i386 ABI we need this symbol.  Yuck, it's so
    meaningless since we don't support machines < 80386.  */
 	.section .rodata
@@ -149,3 +162,4 @@ __data_start:
 	.long 0
 	.weak data_start
 	data_start = __data_start
+#endif
diff --git a/sysdeps/x86/Makefile b/sysdeps/x86/Makefile
index beab426f67..de6aed89ee 100644
--- a/sysdeps/x86/Makefile
+++ b/sysdeps/x86/Makefile
@@ -1,5 +1,6 @@
 ifeq ($(subdir),csu)
 gen-as-const-headers += cpu-features-offsets.sym
+routines += libc-main
 endif
 
 ifeq ($(subdir),elf)
diff --git a/sysdeps/x86/cacheinfo.c b/sysdeps/x86/cacheinfo.c
index 4322328a1b..73c88c0636 100644
--- a/sysdeps/x86/cacheinfo.c
+++ b/sysdeps/x86/cacheinfo.c
@@ -24,6 +24,10 @@
 #include <unistd.h>
 #include <cpuid.h>
 #include <init-arch.h>
+#if HAVE_TUNABLES
+# define TUNABLE_NAMESPACE cpu
+# include <elf/dl-tunables.h>
+#endif
 
 static const struct intel_02_cache_info
 {
@@ -771,7 +775,7 @@ intel_bug_no_cache_info:
 }
 
 
-static void
+void
 __attribute__((constructor))
 init_cacheinfo (void)
 {
@@ -785,6 +789,13 @@ init_cacheinfo (void)
   long int core;
   unsigned int threads = 0;
   const struct cpu_features *cpu_features = __get_cpu_features ();
+  static bool finished = false;
+
+  /* NB: This may be called from __x86_libc_main.  */
+  if (finished)
+    return;
+
+  finished = true;
 
   if (cpu_features->basic.kind == arch_kind_intel)
     {
@@ -918,6 +929,17 @@ init_cacheinfo (void)
 
   if (cpu_features->rep_stosb_threshold)
     __x86_rep_stosb_threshold = cpu_features->rep_stosb_threshold;
+
+#if HAVE_TUNABLES
+  TUNABLE_UPDATE (x86_non_temporal_threshold, long int,
+		  __x86_shared_non_temporal_threshold, 0,
+		  (long int) -1);
+  TUNABLE_UPDATE (x86_rep_movsb_threshold, long int,
+		  __x86_rep_movsb_threshold,
+		  minimum_rep_movsb_threshold, (long int) -1);
+  TUNABLE_UPDATE (x86_rep_stosb_threshold, long int,
+		  __x86_rep_stosb_threshold, 0, (long int) -1);
+#endif
 }
 
 #endif
diff --git a/sysdeps/x86/init-arch.h b/sysdeps/x86/init-arch.h
index d6f59cf962..619eb847f4 100644
--- a/sysdeps/x86/init-arch.h
+++ b/sysdeps/x86/init-arch.h
@@ -23,6 +23,8 @@
 #include <ifunc-init.h>
 #include <isa.h>
 
+extern void init_cacheinfo (void) __attribute__ ((visibility ("hidden")));
+
 #ifndef __x86_64__
 /* Due to the reordering and the other nifty extensions in i686, it is
    not really good to use heavily i586 optimized code on an i686.  It's
diff --git a/sysdeps/x86/libc-main.S b/sysdeps/x86/libc-main.S
new file mode 100644
index 0000000000..5ef80d5b63
--- /dev/null
+++ b/sysdeps/x86/libc-main.S
@@ -0,0 +1,36 @@
+/* Copyright (C) 2020 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.
+
+   In addition to the permissions in the GNU Lesser General Public
+   License, the Free Software Foundation gives you unlimited
+   permission to link the compiled version of this file with other
+   programs, and to distribute those programs without any restriction
+   coming from the use of this file. (The GNU Lesser General Public
+   License restrictions do apply in other respects; for example, they
+   cover modification of the file, and distribution when not linked
+   into another program.)
+
+   Note that people who make modified versions of this file are not
+   obligated to grant this special exception for their modified
+   versions; it is their choice whether to do so. The GNU Lesser
+   General Public License gives permission to release a modified
+   version without this exception; this exception also makes it
+   possible to release a modified version which carries forward this
+   exception.
+
+   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/>.  */
+
+#define LIBC_MAIN __x86_libc_main
+#include "start.S"
diff --git a/sysdeps/x86/libc-version.c b/sysdeps/x86/libc-version.c
new file mode 100644
index 0000000000..7a0a2cecef
--- /dev/null
+++ b/sysdeps/x86/libc-version.c
@@ -0,0 +1,55 @@
+/* Copyright (C) 2020 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/>.  */
+
+#if HAVE_TUNABLES
+# include <stdint.h>
+# include <stdbool.h>
+# include <string.h>
+# include <init-arch.h>
+# include <elf/dl-tunables.h>
+#endif
+
+#define LIBC_MAIN __generic_libc_main
+#include <csu/libc-version.c>
+
+/* This function is the entry point for the shared object.
+   Running the library as a program will get here.  */
+
+extern void __x86_libc_main (int, char **)
+  __attribute__ ((noreturn, visibility ("hidden")));
+
+void
+__x86_libc_main (int argc, char **argv)
+{
+#if HAVE_TUNABLES
+  bool stop = false;
+  while (argc > 1)
+    if (! strcmp (argv[1], "--list-tunables"))
+      {
+	init_cacheinfo ();
+	__tunables_print ();
+	stop = true;
+	argc--;
+      }
+    else
+      break;
+
+  if (stop)
+    _exit (0);
+#endif
+  __generic_libc_main ();
+}
diff --git a/sysdeps/x86_64/start.S b/sysdeps/x86_64/start.S
index 7477b632f7..18d910257b 100644
--- a/sysdeps/x86_64/start.S
+++ b/sysdeps/x86_64/start.S
@@ -55,7 +55,13 @@
 
 #include <sysdep.h>
 
-ENTRY (_start)
+#ifdef LIBC_MAIN
+# define START __libc_main
+#else
+# define START _start
+#endif
+
+ENTRY (START)
 	/* Clearing frame pointer is insufficient, use CFI.  */
 	cfi_undefined (rip)
 	/* Clear the frame pointer.  The ABI suggests this be done, to mark
@@ -76,16 +82,24 @@ ENTRY (_start)
 	rtld_fini:	%r9
 	stack_end:	stack.	*/
 
+#ifdef LIBC_MAIN
+# define ARGC_REG	rdi
+# define ARGV_REG	RSI_LP
+#else
+# define ARGC_REG	rsi
+# define ARGV_REG	RDX_LP
+#endif
+
 	mov %RDX_LP, %R9_LP	/* Address of the shared library termination
 				   function.  */
 #ifdef __ILP32__
 	mov (%rsp), %esi	/* Simulate popping 4-byte argument count.  */
 	add $4, %esp
 #else
-	popq %rsi		/* Pop the argument count.  */
+	popq %ARGC_REG		/* Pop the argument count.  */
 #endif
 	/* argv starts just at the current stack top.  */
-	mov %RSP_LP, %RDX_LP
+	mov %RSP_LP, %ARGV_REG
 	/* Align the stack to a 16 byte boundary to follow the ABI.  */
 	and  $~15, %RSP_LP
 
@@ -96,19 +110,22 @@ ENTRY (_start)
 	   which grow downwards).  */
 	pushq %rsp
 
-#ifdef PIC
+#ifdef LIBC_MAIN
+	call LIBC_MAIN
+#else
+# ifdef PIC
 	/* Pass address of our own entry points to .fini and .init.  */
 	mov __libc_csu_fini@GOTPCREL(%rip), %R8_LP
 	mov __libc_csu_init@GOTPCREL(%rip), %RCX_LP
 
 	mov main@GOTPCREL(%rip), %RDI_LP
-#else
+# else
 	/* Pass address of our own entry points to .fini and .init.  */
 	mov $__libc_csu_fini, %R8_LP
 	mov $__libc_csu_init, %RCX_LP
 
 	mov $main, %RDI_LP
-#endif
+# endif
 
 	/* Call the user's main function, and exit with its value.
 	   But let the libc call main.  Since __libc_start_main in
@@ -118,10 +135,12 @@ ENTRY (_start)
 	   2.26 or above can convert indirect branch into direct
 	   branch.  */
 	call *__libc_start_main@GOTPCREL(%rip)
+#endif
 
 	hlt			/* Crash if somehow `exit' does return.	 */
-END (_start)
+END (START)
 
+#ifndef LIBC_MAIN
 /* Define a symbol for the first piece of initialized data.  */
 	.data
 	.globl __data_start
@@ -129,3 +148,4 @@ __data_start:
 	.long 0
 	.weak data_start
 	data_start = __data_start
+#endif
-- 
2.26.2

