e354a5
commit e0f1a58f3d1f4f55591b524e9dcff23cc98a509e
e354a5
Author: Florian Weimer <fweimer@redhat.com>
e354a5
Date:   Thu Oct 8 10:57:10 2020 +0200
e354a5
e354a5
    elf: Implement ld.so --help
e354a5
    
e354a5
    --help processing is deferred to the point where the executable has
e354a5
    been loaded, so that it is possible to eventually include information
e354a5
    from the main executable in the help output.
e354a5
    
e354a5
    As suggested in the GNU command-line interface guidelines, the help
e354a5
    message is printed to standard output, and the exit status is
e354a5
    successful.
e354a5
    
e354a5
    Handle usage errors closer to the GNU command-line interface
e354a5
    guidelines.
e354a5
    
e354a5
    Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
e354a5
e354a5
diff --git a/elf/dl-main.h b/elf/dl-main.h
e354a5
index 79c9c40056504f80..ac7249a580214860 100644
e354a5
--- a/elf/dl-main.h
e354a5
+++ b/elf/dl-main.h
e354a5
@@ -63,6 +63,7 @@ struct audit_list
e354a5
 enum rtld_mode
e354a5
   {
e354a5
     rtld_mode_normal, rtld_mode_list, rtld_mode_verify, rtld_mode_trace,
e354a5
+    rtld_mode_help,
e354a5
   };
e354a5
 
e354a5
 /* Aggregated state information extracted from environment variables
e354a5
@@ -101,6 +102,11 @@ call_init_paths (const struct dl_main_state *state)
e354a5
 }
e354a5
 
e354a5
 /* Print ld.so usage information and exit.  */
e354a5
-_Noreturn void _dl_usage (void) attribute_hidden;
e354a5
+_Noreturn void _dl_usage (const char *argv0, const char *wrong_option)
e354a5
+  attribute_hidden;
e354a5
+
e354a5
+/* Print ld.so --help output and exit.  */
e354a5
+_Noreturn void _dl_help (const char *argv0, struct dl_main_state *state)
e354a5
+  attribute_hidden;
e354a5
 
e354a5
 #endif /* _DL_MAIN */
e354a5
diff --git a/elf/dl-usage.c b/elf/dl-usage.c
e354a5
index f3d89d22b71d7d12..c1820dca2fa117ee 100644
e354a5
--- a/elf/dl-usage.c
e354a5
+++ b/elf/dl-usage.c
e354a5
@@ -19,12 +19,24 @@
e354a5
 #include <dl-cache.h>
e354a5
 #include <dl-main.h>
e354a5
 #include <ldsodefs.h>
e354a5
+#include <unistd.h>
e354a5
 
e354a5
 void
e354a5
-_dl_usage (void)
e354a5
+_dl_usage (const char *argv0, const char *wrong_option)
e354a5
 {
e354a5
-  _dl_fatal_printf ("\
e354a5
-Usage: ld.so [OPTION]... EXECUTABLE-FILE [ARGS-FOR-PROGRAM...]\n\
e354a5
+  if (wrong_option != NULL)
e354a5
+    _dl_error_printf ("%s: unrecognized option '%s'\n", argv0, wrong_option);
e354a5
+  else
e354a5
+    _dl_error_printf ("%s: missing program name\n", argv0);
e354a5
+  _dl_error_printf ("Try '%s --help' for more information.\n", argv0);
e354a5
+  _exit (EXIT_FAILURE);
e354a5
+}
e354a5
+
e354a5
+void
e354a5
+_dl_help (const char *argv0, struct dl_main_state *state)
e354a5
+{
e354a5
+  _dl_printf ("\
e354a5
+Usage: %s [OPTION]... EXECUTABLE-FILE [ARGS-FOR-PROGRAM...]\n\
e354a5
 You have invoked `ld.so', the helper program for shared library executables.\n\
e354a5
 This program usually lives in the file `/lib/ld.so', and special directives\n\
e354a5
 in executable files using ELF shared libraries tell the system's program\n\
e354a5
@@ -47,5 +59,9 @@ of this helper program; chances are you did not intend to run this program.\n\
e354a5
                         in LIST\n\
e354a5
   --audit LIST          use objects named in LIST as auditors\n\
e354a5
   --preload LIST        preload objects named in LIST\n\
e354a5
-  --argv0 STRING        set argv[0] to STRING before running\n");
e354a5
+  --argv0 STRING        set argv[0] to STRING before running\n\
e354a5
+  --help                display this help and exit\n\
e354a5
+",
e354a5
+              argv0);
e354a5
+  _exit (EXIT_SUCCESS);
e354a5
 }
e354a5
diff --git a/elf/rtld.c b/elf/rtld.c
e354a5
index 8e91cee41b62b894..b92641cb1c2d99a6 100644
e354a5
--- a/elf/rtld.c
e354a5
+++ b/elf/rtld.c
e354a5
@@ -1145,6 +1145,7 @@ dl_main (const ElfW(Phdr) *phdr,
e354a5
   /* Set up a flag which tells we are just starting.  */
e354a5
   _dl_starting_up = 1;
e354a5
 
e354a5
+  const char *ld_so_name = _dl_argv[0];
e354a5
   if (*user_entry == (ElfW(Addr)) ENTRY_POINT)
e354a5
     {
e354a5
       /* Ho ho.  We are not the program interpreter!  We are the program
e354a5
@@ -1172,8 +1173,12 @@ dl_main (const ElfW(Phdr) *phdr,
e354a5
       while (_dl_argc > 1)
e354a5
 	if (! strcmp (_dl_argv[1], "--list"))
e354a5
 	  {
e354a5
-	    state.mode = rtld_mode_list;
e354a5
-	    GLRO(dl_lazy) = -1;	/* This means do no dependency analysis.  */
e354a5
+	    if (state.mode != rtld_mode_help)
e354a5
+	      {
e354a5
+	       state.mode = rtld_mode_list;
e354a5
+		/* This means do no dependency analysis.  */
e354a5
+		GLRO(dl_lazy) = -1;
e354a5
+	      }
e354a5
 
e354a5
 	    ++_dl_skip_args;
e354a5
 	    --_dl_argc;
e354a5
@@ -1181,7 +1186,8 @@ dl_main (const ElfW(Phdr) *phdr,
e354a5
 	  }
e354a5
 	else if (! strcmp (_dl_argv[1], "--verify"))
e354a5
 	  {
e354a5
-	    state.mode = rtld_mode_verify;
e354a5
+	    if (state.mode != rtld_mode_help)
e354a5
+	      state.mode = rtld_mode_verify;
e354a5
 
e354a5
 	    ++_dl_skip_args;
e354a5
 	    --_dl_argc;
e354a5
@@ -1236,13 +1242,34 @@ dl_main (const ElfW(Phdr) *phdr,
e354a5
 	    _dl_argc -= 2;
e354a5
 	    _dl_argv += 2;
e354a5
 	  }
e354a5
+	else if (strcmp (_dl_argv[1], "--help") == 0)
e354a5
+	  {
e354a5
+	    state.mode = rtld_mode_help;
e354a5
+	    --_dl_argc;
e354a5
+	    ++_dl_argv;
e354a5
+	  }
e354a5
+	else if (_dl_argv[1][0] == '-' && _dl_argv[1][1] == '-')
e354a5
+	  {
e354a5
+	   if (_dl_argv[1][1] == '\0')
e354a5
+	     /* End of option list.  */
e354a5
+	     break;
e354a5
+	   else
e354a5
+	     /* Unrecognized option.  */
e354a5
+	     _dl_usage (ld_so_name, _dl_argv[1]);
e354a5
+	  }
e354a5
 	else
e354a5
 	  break;
e354a5
 
e354a5
       /* If we have no further argument the program was called incorrectly.
e354a5
 	 Grant the user some education.  */
e354a5
       if (_dl_argc < 2)
e354a5
-	_dl_usage ();
e354a5
+	{
e354a5
+	  if (state.mode == rtld_mode_help)
e354a5
+	    /* --help without an executable is not an error.  */
e354a5
+	    _dl_help (ld_so_name, &state);
e354a5
+	  else
e354a5
+	    _dl_usage (ld_so_name, NULL);
e354a5
+	}
e354a5
 
e354a5
       ++_dl_skip_args;
e354a5
       --_dl_argc;
e354a5
@@ -1267,7 +1294,8 @@ dl_main (const ElfW(Phdr) *phdr,
e354a5
 	    break;
e354a5
 	  }
e354a5
 
e354a5
-      if (__glibc_unlikely (state.mode == rtld_mode_verify))
e354a5
+      if (__glibc_unlikely (state.mode == rtld_mode_verify
e354a5
+			    || state.mode == rtld_mode_help))
e354a5
 	{
e354a5
 	  const char *objname;
e354a5
 	  const char *err_str = NULL;
e354a5
@@ -1280,9 +1308,16 @@ dl_main (const ElfW(Phdr) *phdr,
e354a5
 	  (void) _dl_catch_error (&objname, &err_str, &malloced, map_doit,
e354a5
 				  &args);
e354a5
 	  if (__glibc_unlikely (err_str != NULL))
e354a5
-	    /* We don't free the returned string, the programs stops
e354a5
-	       anyway.  */
e354a5
-	    _exit (EXIT_FAILURE);
e354a5
+	    {
e354a5
+	      /* We don't free the returned string, the programs stops
e354a5
+		 anyway.  */
e354a5
+	      if (state.mode == rtld_mode_help)
e354a5
+		/* Mask the failure to load the main object.  The help
e354a5
+		   message contains less information in this case.  */
e354a5
+		_dl_help (ld_so_name, &state);
e354a5
+	      else
e354a5
+		_exit (EXIT_FAILURE);
e354a5
+	    }
e354a5
 	}
e354a5
       else
e354a5
 	{
e354a5
@@ -1632,6 +1667,11 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
e354a5
   audit_list_add_dynamic_tag (&state.audit_list, main_map, DT_AUDIT);
e354a5
   audit_list_add_dynamic_tag (&state.audit_list, main_map, DT_DEPAUDIT);
e354a5
 
e354a5
+  /* At this point, all data has been obtained that is included in the
e354a5
+     --help output.  */
e354a5
+  if (__glibc_unlikely (state.mode == rtld_mode_help))
e354a5
+    _dl_help (ld_so_name, &state);
e354a5
+
e354a5
   /* If we have auditing DSOs to load, do it now.  */
e354a5
   bool need_security_init = true;
e354a5
   if (state.audit_list.length > 0)