diff --git a/elf/rtld.c b/elf/rtld.c
index 3b2e05bf4c..d959dab0d0 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -397,7 +397,8 @@ extern struct rtld_global_ro _rtld_local_ro
 
 
 static void dl_main (const ElfW(Phdr) *phdr, ElfW(Word) phnum,
-		     ElfW(Addr) *user_entry, ElfW(auxv_t) *auxv);
+		     ElfW(Addr) *user_entry, ElfW(auxv_t) *auxv,
+		     void **start_argptr);
 
 /* These two variables cannot be moved into .data.rel.ro.  */
 static struct libname_list _dl_rtld_libname;
@@ -1306,11 +1307,60 @@ rtld_setup_main_map (struct link_map *main_map)
   return has_interp;
 }
 
+static void
+_dl_start_args_adjust (void **start_argptr, int skip_args)
+{
+  void **sp = start_argptr;
+  void **p;
+  long argc;
+  char **argv;
+
+  if (skip_args == 0)
+    return;
+
+  /* Adjust argc on stack.  */
+  argc = (long) sp[0] - skip_args;
+  sp[0] = (void *) argc;
+
+  argv = (char **) (sp + 1); /* Necessary aliasing violation.  */
+  p = sp + skip_args;
+  /* Shuffle argv down.  */
+  do
+    *++sp = *++p;
+  while (*p != NULL);
+
+  /* Shuffle envp down.  */
+  do
+    *++sp = *++p;
+  while (*p != NULL);
+
+  /* Update globals in rtld.  */
+  _dl_argv = argv;
+  _environ = argv + argc + 1;
+#ifdef HAVE_AUX_VECTOR
+  GLRO(dl_auxv) = (ElfW(auxv_t) *) (sp + 1); /* Aliasing violation.  */
+
+  /* Shuffle auxv down. */
+  void *a, *b; /* Use a pair of pointers for an auxv entry.  */
+  unsigned long a_type;
+  do
+    {
+      a_type = ((ElfW(auxv_t) *) (p + 1))->a_type;
+      a = *++p;
+      b = *++p;
+      *++sp = a;
+      *++sp = b;
+    }
+  while (a_type != AT_NULL);
+#endif
+}
+
 static void
 dl_main (const ElfW(Phdr) *phdr,
 	 ElfW(Word) phnum,
 	 ElfW(Addr) *user_entry,
-	 ElfW(auxv_t) *auxv)
+	 ElfW(auxv_t) *auxv,
+	 void **start_argptr)
 {
   struct link_map *main_map;
   size_t file_size;
@@ -1359,6 +1409,7 @@ dl_main (const ElfW(Phdr) *phdr,
       rtld_is_main = true;
 
       char *argv0 = NULL;
+      int skip_args = 0;
 
       /* Note the place where the dynamic linker actually came from.  */
       GL(dl_rtld_map).l_name = rtld_progname;
@@ -1373,7 +1424,7 @@ dl_main (const ElfW(Phdr) *phdr,
 		GLRO(dl_lazy) = -1;
 	      }
 
-	    ++_dl_skip_args;
+	    ++skip_args;
 	    --_dl_argc;
 	    ++_dl_argv;
 	  }
@@ -1382,14 +1433,14 @@ dl_main (const ElfW(Phdr) *phdr,
 	    if (state.mode != rtld_mode_help)
 	      state.mode = rtld_mode_verify;
 
-	    ++_dl_skip_args;
+	    ++skip_args;
 	    --_dl_argc;
 	    ++_dl_argv;
 	  }
 	else if (! strcmp (_dl_argv[1], "--inhibit-cache"))
 	  {
 	    GLRO(dl_inhibit_cache) = 1;
-	    ++_dl_skip_args;
+	    ++skip_args;
 	    --_dl_argc;
 	    ++_dl_argv;
 	  }
@@ -1399,7 +1450,7 @@ dl_main (const ElfW(Phdr) *phdr,
 	    state.library_path = _dl_argv[2];
 	    state.library_path_source = "--library-path";
 
-	    _dl_skip_args += 2;
+	    skip_args += 2;
 	    _dl_argc -= 2;
 	    _dl_argv += 2;
 	  }
@@ -1408,7 +1459,7 @@ dl_main (const ElfW(Phdr) *phdr,
 	  {
 	    GLRO(dl_inhibit_rpath) = _dl_argv[2];
 
-	    _dl_skip_args += 2;
+	    skip_args += 2;
 	    _dl_argc -= 2;
 	    _dl_argv += 2;
 	  }
@@ -1416,14 +1467,14 @@ dl_main (const ElfW(Phdr) *phdr,
 	  {
 	    audit_list_add_string (&state.audit_list, _dl_argv[2]);
 
-	    _dl_skip_args += 2;
+	    skip_args += 2;
 	    _dl_argc -= 2;
 	    _dl_argv += 2;
 	  }
 	else if (! strcmp (_dl_argv[1], "--preload") && _dl_argc > 2)
 	  {
 	    state.preloadarg = _dl_argv[2];
-	    _dl_skip_args += 2;
+	    skip_args += 2;
 	    _dl_argc -= 2;
 	    _dl_argv += 2;
 	  }
@@ -1431,7 +1482,7 @@ dl_main (const ElfW(Phdr) *phdr,
 	  {
 	    argv0 = _dl_argv[2];
 
-	    _dl_skip_args += 2;
+	    skip_args += 2;
 	    _dl_argc -= 2;
 	    _dl_argv += 2;
 	  }
@@ -1439,7 +1490,7 @@ dl_main (const ElfW(Phdr) *phdr,
 		 && _dl_argc > 2)
 	  {
 	    state.glibc_hwcaps_prepend = _dl_argv[2];
-	    _dl_skip_args += 2;
+	    skip_args += 2;
 	    _dl_argc -= 2;
 	    _dl_argv += 2;
 	  }
@@ -1447,7 +1498,7 @@ dl_main (const ElfW(Phdr) *phdr,
 		 && _dl_argc > 2)
 	  {
 	    state.glibc_hwcaps_mask = _dl_argv[2];
-	    _dl_skip_args += 2;
+	    skip_args += 2;
 	    _dl_argc -= 2;
 	    _dl_argv += 2;
 	  }
@@ -1456,7 +1507,7 @@ dl_main (const ElfW(Phdr) *phdr,
 	  {
 	    state.mode = rtld_mode_list_tunables;
 
-	    ++_dl_skip_args;
+	    ++skip_args;
 	    --_dl_argc;
 	    ++_dl_argv;
 	  }
@@ -1465,7 +1516,7 @@ dl_main (const ElfW(Phdr) *phdr,
 	  {
 	    state.mode = rtld_mode_list_diagnostics;
 
-	    ++_dl_skip_args;
+	    ++skip_args;
 	    --_dl_argc;
 	    ++_dl_argv;
 	  }
@@ -1511,7 +1562,7 @@ dl_main (const ElfW(Phdr) *phdr,
 	    _dl_usage (ld_so_name, NULL);
 	}
 
-      ++_dl_skip_args;
+      ++skip_args;
       --_dl_argc;
       ++_dl_argv;
 
@@ -1610,6 +1661,8 @@ dl_main (const ElfW(Phdr) *phdr,
       /* Set the argv[0] string now that we've processed the executable.  */
       if (argv0 != NULL)
         _dl_argv[0] = argv0;
+      /* Adjust arguments for the application entry point.  */
+      _dl_start_args_adjust (start_argptr, skip_args);
     }
   else
     {
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 4a5e698db2..31de149f23 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -1165,7 +1165,8 @@ extern ElfW(Addr) _dl_sysdep_start (void **start_argptr,
 				    void (*dl_main) (const ElfW(Phdr) *phdr,
 						     ElfW(Word) phnum,
 						     ElfW(Addr) *user_entry,
-						     ElfW(auxv_t) *auxv))
+						     ElfW(auxv_t) *auxv,
+						     void **start_argptr))
      attribute_hidden;
 
 extern void _dl_sysdep_start_cleanup (void) attribute_hidden;
diff --git a/sysdeps/mach/hurd/dl-sysdep.c b/sysdeps/mach/hurd/dl-sysdep.c
index 3cbe075615..ff71aa6639 100644
--- a/sysdeps/mach/hurd/dl-sysdep.c
+++ b/sysdeps/mach/hurd/dl-sysdep.c
@@ -72,10 +72,11 @@ ElfW(Addr)
 _dl_sysdep_start (void **start_argptr,
 		  void (*dl_main) (const ElfW(Phdr) *phdr, ElfW(Word) phent,
 				   ElfW(Addr) *user_entry,
-				   ElfW(auxv_t) *auxv))
+				   ElfW(auxv_t) *auxv, void **start_argptr))
 {
   void go (intptr_t *argdata)
     {
+      char *orig_argv0;
       char **p;
 
       /* Cache the information in various global variables.  */
@@ -84,6 +85,8 @@ _dl_sysdep_start (void **start_argptr,
       _environ = &_dl_argv[_dl_argc + 1];
       for (p = _environ; *p++;); /* Skip environ pointers and terminator.  */
 
+      orig_argv0 = _dl_argv[0];
+
       if ((void *) p == _dl_argv[0])
 	{
 	  static struct hurd_startup_data nodata;
@@ -169,34 +172,29 @@ _dl_sysdep_start (void **start_argptr,
 	 up and leave us to transfer control to USER_ENTRY.  */
       (*dl_main) ((const ElfW(Phdr) *) _dl_hurd_data->phdr,
 		  _dl_hurd_data->phdrsz / sizeof (ElfW(Phdr)),
-		  (ElfW(Addr) *) &_dl_hurd_data->user_entry, NULL);
+		  (ElfW(Addr) *) &_dl_hurd_data->user_entry, NULL,
+		  (void **) argdata);
 
       /* The call above might screw a few things up.
 
-	 First of all, if _dl_skip_args is nonzero, we are ignoring
-	 the first few arguments.  However, if we have no Hurd startup
-	 data, it is the magical convention that ARGV[0] == P.  The
+	 P is the location after the terminating NULL of the list of
+	 environment variables.  It has to point to the Hurd startup
+	 data or if that's missing then P == ARGV[0] must hold. The
 	 startup code in init-first.c will get confused if this is not
 	 the case, so we must rearrange things to make it so.  We'll
-	 overwrite the origional ARGV[0] at P with ARGV[_dl_skip_args].
+	 recompute P and move the Hurd data or the new ARGV[0] there.
 
-	 Secondly, if we need to be secure, it removes some dangerous
-	 environment variables.  If we have no Hurd startup date this
-	 changes P (since that's the location after the terminating
-	 NULL in the list of environment variables).  We do the same
-	 thing as in the first case but make sure we recalculate P.
-	 If we do have Hurd startup data, we have to move the data
-	 such that it starts just after the terminating NULL in the
-	 environment list.
+	 Note: directly invoked ld.so can move arguments and env vars
+	 or in secure mode it can remove dangerous env vars.
 
 	 We use memmove, since the locations might overlap.  */
-      if (__libc_enable_secure || _dl_skip_args)
-	{
-	  char **newp;
 
-	  for (newp = _environ; *newp++;);
+      char **newp;
+      for (newp = _environ; *newp++;);
 
-	  if (_dl_argv[-_dl_skip_args] == (char *) p)
+      if (newp != p || _dl_argv[0] != orig_argv0)
+	{
+	  if (orig_argv0 == (char *) p)
 	    {
 	      if ((char *) newp != _dl_argv[0])
 		{
diff --git a/sysdeps/unix/sysv/linux/dl-sysdep.c b/sysdeps/unix/sysv/linux/dl-sysdep.c
index a67c454673..8c4a7e72b9 100644
--- a/sysdeps/unix/sysv/linux/dl-sysdep.c
+++ b/sysdeps/unix/sysv/linux/dl-sysdep.c
@@ -98,7 +98,8 @@ _dl_sysdep_parse_arguments (void **start_argptr,
 ElfW(Addr)
 _dl_sysdep_start (void **start_argptr,
 		  void (*dl_main) (const ElfW(Phdr) *phdr, ElfW(Word) phnum,
-				   ElfW(Addr) *user_entry, ElfW(auxv_t) *auxv))
+				   ElfW(Addr) *user_entry, ElfW(auxv_t) *auxv,
+				   void **start_argptr))
 {
   __libc_stack_end = DL_STACK_END (start_argptr);
 
@@ -138,7 +139,7 @@ _dl_sysdep_start (void **start_argptr,
     __libc_check_standard_fds ();
 
   (*dl_main) (dl_main_args.phdr, dl_main_args.phnum,
-              &dl_main_args.user_entry, GLRO(dl_auxv));
+	      &dl_main_args.user_entry, GLRO(dl_auxv), start_argptr);
   return dl_main_args.user_entry;
 }
 
