]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
elf: Implement ld.so --help
authorFlorian Weimer <fweimer@redhat.com>
Thu, 8 Oct 2020 08:57:10 +0000 (10:57 +0200)
committerFlorian Weimer <fweimer@redhat.com>
Thu, 8 Oct 2020 13:00:39 +0000 (15:00 +0200)
--help processing is deferred to the point where the executable has
been loaded, so that it is possible to eventually include information
from the main executable in the help output.

As suggested in the GNU command-line interface guidelines, the help
message is printed to standard output, and the exit status is
successful.

Handle usage errors closer to the GNU command-line interface
guidelines.

Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
elf/dl-main.h
elf/dl-usage.c
elf/rtld.c

index 79c9c40056504f805269d5982ba07f8a094f3271..ac7249a580214860fcaea97cc3e5ba9d621dc244 100644 (file)
@@ -63,6 +63,7 @@ struct audit_list
 enum rtld_mode
   {
     rtld_mode_normal, rtld_mode_list, rtld_mode_verify, rtld_mode_trace,
+    rtld_mode_help,
   };
 
 /* Aggregated state information extracted from environment variables
@@ -101,6 +102,11 @@ call_init_paths (const struct dl_main_state *state)
 }
 
 /* Print ld.so usage information and exit.  */
-_Noreturn void _dl_usage (void) attribute_hidden;
+_Noreturn void _dl_usage (const char *argv0, const char *wrong_option)
+  attribute_hidden;
+
+/* Print ld.so --help output and exit.  */
+_Noreturn void _dl_help (const char *argv0, struct dl_main_state *state)
+  attribute_hidden;
 
 #endif /* _DL_MAIN */
index f3d89d22b71d7d128c1761f544233718394c46b3..c1820dca2fa117eea5f1270e91cb3043c1f6280f 100644 (file)
 #include <dl-cache.h>
 #include <dl-main.h>
 #include <ldsodefs.h>
+#include <unistd.h>
 
 void
-_dl_usage (void)
+_dl_usage (const char *argv0, const char *wrong_option)
 {
-  _dl_fatal_printf ("\
-Usage: ld.so [OPTION]... EXECUTABLE-FILE [ARGS-FOR-PROGRAM...]\n\
+  if (wrong_option != NULL)
+    _dl_error_printf ("%s: unrecognized option '%s'\n", argv0, wrong_option);
+  else
+    _dl_error_printf ("%s: missing program name\n", argv0);
+  _dl_error_printf ("Try '%s --help' for more information.\n", argv0);
+  _exit (EXIT_FAILURE);
+}
+
+void
+_dl_help (const char *argv0, struct dl_main_state *state)
+{
+  _dl_printf ("\
+Usage: %s [OPTION]... EXECUTABLE-FILE [ARGS-FOR-PROGRAM...]\n\
 You have invoked `ld.so', the helper program for shared library executables.\n\
 This program usually lives in the file `/lib/ld.so', and special directives\n\
 in executable files using ELF shared libraries tell the system's program\n\
@@ -47,5 +59,9 @@ of this helper program; chances are you did not intend to run this program.\n\
                         in LIST\n\
   --audit LIST          use objects named in LIST as auditors\n\
   --preload LIST        preload objects named in LIST\n\
-  --argv0 STRING        set argv[0] to STRING before running\n");
+  --argv0 STRING        set argv[0] to STRING before running\n\
+  --help                display this help and exit\n\
+",
+              argv0);
+  _exit (EXIT_SUCCESS);
 }
index c0609e431061f5bebb6c28c68bff6d691162fdac..2eeec981a58ad22ee0e0f31771a8a879ccc32c69 100644 (file)
@@ -1151,6 +1151,7 @@ dl_main (const ElfW(Phdr) *phdr,
   _dl_starting_up = 1;
 #endif
 
+  const char *ld_so_name = _dl_argv[0];
   if (*user_entry == (ElfW(Addr)) ENTRY_POINT)
     {
       /* Ho ho.  We are not the program interpreter!  We are the program
@@ -1178,8 +1179,12 @@ dl_main (const ElfW(Phdr) *phdr,
       while (_dl_argc > 1)
        if (! strcmp (_dl_argv[1], "--list"))
          {
-           state.mode = rtld_mode_list;
-           GLRO(dl_lazy) = -1; /* This means do no dependency analysis.  */
+           if (state.mode != rtld_mode_help)
+             {
+              state.mode = rtld_mode_list;
+               /* This means do no dependency analysis.  */
+               GLRO(dl_lazy) = -1;
+             }
 
            ++_dl_skip_args;
            --_dl_argc;
@@ -1187,7 +1192,8 @@ dl_main (const ElfW(Phdr) *phdr,
          }
        else if (! strcmp (_dl_argv[1], "--verify"))
          {
-           state.mode = rtld_mode_verify;
+           if (state.mode != rtld_mode_help)
+             state.mode = rtld_mode_verify;
 
            ++_dl_skip_args;
            --_dl_argc;
@@ -1242,13 +1248,34 @@ dl_main (const ElfW(Phdr) *phdr,
            _dl_argc -= 2;
            _dl_argv += 2;
          }
+       else if (strcmp (_dl_argv[1], "--help") == 0)
+         {
+           state.mode = rtld_mode_help;
+           --_dl_argc;
+           ++_dl_argv;
+         }
+       else if (_dl_argv[1][0] == '-' && _dl_argv[1][1] == '-')
+         {
+          if (_dl_argv[1][1] == '\0')
+            /* End of option list.  */
+            break;
+          else
+            /* Unrecognized option.  */
+            _dl_usage (ld_so_name, _dl_argv[1]);
+         }
        else
          break;
 
       /* If we have no further argument the program was called incorrectly.
         Grant the user some education.  */
       if (_dl_argc < 2)
-       _dl_usage ();
+       {
+         if (state.mode == rtld_mode_help)
+           /* --help without an executable is not an error.  */
+           _dl_help (ld_so_name, &state);
+         else
+           _dl_usage (ld_so_name, NULL);
+       }
 
       ++_dl_skip_args;
       --_dl_argc;
@@ -1273,7 +1300,8 @@ dl_main (const ElfW(Phdr) *phdr,
            break;
          }
 
-      if (__glibc_unlikely (state.mode == rtld_mode_verify))
+      if (__glibc_unlikely (state.mode == rtld_mode_verify
+                           || state.mode == rtld_mode_help))
        {
          const char *objname;
          const char *err_str = NULL;
@@ -1286,9 +1314,16 @@ dl_main (const ElfW(Phdr) *phdr,
          (void) _dl_catch_error (&objname, &err_str, &malloced, map_doit,
                                  &args);
          if (__glibc_unlikely (err_str != NULL))
-           /* We don't free the returned string, the programs stops
-              anyway.  */
-           _exit (EXIT_FAILURE);
+           {
+             /* We don't free the returned string, the programs stops
+                anyway.  */
+             if (state.mode == rtld_mode_help)
+               /* Mask the failure to load the main object.  The help
+                  message contains less information in this case.  */
+               _dl_help (ld_so_name, &state);
+             else
+               _exit (EXIT_FAILURE);
+           }
        }
       else
        {
@@ -1647,6 +1682,11 @@ dl_main (const ElfW(Phdr) *phdr,
   audit_list_add_dynamic_tag (&state.audit_list, main_map, DT_AUDIT);
   audit_list_add_dynamic_tag (&state.audit_list, main_map, DT_DEPAUDIT);
 
+  /* At this point, all data has been obtained that is included in the
+     --help output.  */
+  if (__glibc_unlikely (state.mode == rtld_mode_help))
+    _dl_help (ld_so_name, &state);
+
   /* If we have auditing DSOs to load, do it now.  */
   bool need_security_init = true;
   if (state.audit_list.length > 0)