diff mbox series

[v2,1/3] elf: Do not rely on relocation dependencies for destructor sorting

Message ID 10bf09764f3a6adfd12db33c5e71fb65452ac338.1643901334.git.fweimer@redhat.com
State New
Headers show
Series Predictable ELF destructor ordering | expand

Checks

Context Check Description
dj/TryBot-apply_patch success Patch applied to master at the time it was sent

Commit Message

Florian Weimer Feb. 3, 2022, 3:17 p.m. UTC
The new solution to tst-bz15311 preserves the <a<b<c<d<e order
in both cases, but f and g are not ordered in reverse construction
order yet.  This is fine given that the test case is technically
undefined.
---
v2: Rebase.

 elf/dl-close.c             |   2 +-
 elf/dl-deps.c              |   3 +-
 elf/dl-fini.c              |   2 +-
 elf/dl-sort-maps.c         | 105 ++++---------------------------------
 elf/dso-sort-tests-1.def   |   6 +--
 sysdeps/generic/ldsodefs.h |   2 +-
 6 files changed, 15 insertions(+), 105 deletions(-)

Comments

Adhemerval Zanella Feb. 14, 2022, 8:11 p.m. UTC | #1
On 03/02/2022 12:17, Florian Weimer via Libc-alpha wrote:
> The new solution to tst-bz15311 preserves the <a<b<c<d<e order
> in both cases, but f and g are not ordered in reverse construction
> order yet.  This is fine given that the test case is technically
> undefined.

I am seeing a regression with this patch:

$ make test t=elf/order2-cmp
[...]
FAIL: elf/order2-cmp
original exit status 1
/home/azanella/Projects/glibc/build/x86_64-linux-gnu/elf/order2.out - differ: byte 2, line 1
$ cat elf/order2.out 
13245

It seems to be the expected out, since:

$ ldd elf/order2mod1.so
        linux-vdso.so.1 (0x00007ffccdb88000)
        [...]elf/order2mod4.so (0x00007f415e4bd000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f415e278000)
        [...]order2mod3.so (0x00007f415e273000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f415e4c9000)
$ ldd elf/order2mod4.so
        linux-vdso.so.1 (0x00007ffdc75fe000)
        [...]/elf/order2mod3.so (0x00007f2870f30000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f2870ceb000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f2870f3c000)

The full tree is:
                             destructor output
  elf/order2mod1.so          '1'
  \_ elf/order2mod4.so       '3'
     \_ elf/order2mod3.so    '4'
  \_ elf/order2mod3.so       '4'

  elf/order2mod2.so          '2'
  \_ elf/order2mod3.so       '4'
  
So the fini order should be processed as:

  1. elf/order2mod1.so
  2. elf/order2mod4.so
  3. elf/order2mod2.so
  4. elf/order2mod3.so
  5. application

Since now the new algorithm uses a depth breadth search

I am not sure why you haven't see this failure.

> ---
> v2: Rebase.
> 
>  elf/dl-close.c             |   2 +-
>  elf/dl-deps.c              |   3 +-
>  elf/dl-fini.c              |   2 +-
>  elf/dl-sort-maps.c         | 105 ++++---------------------------------
>  elf/dso-sort-tests-1.def   |   6 +--
>  sysdeps/generic/ldsodefs.h |   2 +-
>  6 files changed, 15 insertions(+), 105 deletions(-)
> 
> diff --git a/elf/dl-close.c b/elf/dl-close.c
> index bcd6e206e9..a161b95d00 100644
> --- a/elf/dl-close.c
> +++ b/elf/dl-close.c
> @@ -258,7 +258,7 @@ _dl_close_worker (struct link_map *map, bool force)
>  
>    /* Sort the entries.  We can skip looking for the binary itself which is
>       at the front of the search list for the main namespace.  */
> -  _dl_sort_maps (maps, nloaded, (nsid == LM_ID_BASE), true);
> +  _dl_sort_maps (maps, nloaded, (nsid == LM_ID_BASE));
>  
>    /* Call all termination functions at once.  */
>    bool unload_any = false;
> diff --git a/elf/dl-deps.c b/elf/dl-deps.c
> index c8bab5cad5..ef19e7c390 100644
> --- a/elf/dl-deps.c
> +++ b/elf/dl-deps.c
> @@ -614,8 +614,7 @@ Filters not supported with LD_TRACE_PRELINKING"));
>    /* If libc.so.6 is the main map, it participates in the sort, so
>       that the relocation order is correct regarding libc.so.6.  */
>    _dl_sort_maps (l_initfini, nlist,
> -		 (l_initfini[0] != GL (dl_ns)[l_initfini[0]->l_ns].libc_map),
> -		 false);
> +		 (l_initfini[0] != GL (dl_ns)[l_initfini[0]->l_ns].libc_map));
>  
>    /* Terminate the list of dependencies.  */
>    l_initfini[nlist] = NULL;
> diff --git a/elf/dl-fini.c b/elf/dl-fini.c
> index 030b1fcbcd..f841868cdb 100644
> --- a/elf/dl-fini.c
> +++ b/elf/dl-fini.c
> @@ -96,7 +96,7 @@ _dl_fini (void)
>  	  /* Now we have to do the sorting.  We can skip looking for the
>  	     binary itself which is at the front of the search list for
>  	     the main namespace.  */
> -	  _dl_sort_maps (maps, nmaps, (ns == LM_ID_BASE), true);
> +	  _dl_sort_maps (maps, nmaps, (ns == LM_ID_BASE));
>  
>  	  /* We do not rely on the linked list of loaded object anymore
>  	     from this point on.  We have our own list here (maps).  The
> diff --git a/elf/dl-sort-maps.c b/elf/dl-sort-maps.c
> index 9e9d53ec47..95938c4a52 100644
> --- a/elf/dl-sort-maps.c
> +++ b/elf/dl-sort-maps.c
> @@ -23,11 +23,10 @@
>  /* Note: this is the older, "original" sorting algorithm, being used as
>     default up to 2.35.
>  
> -   Sort array MAPS according to dependencies of the contained objects.
> -   If FOR_FINI is true, this is called for finishing an object.  */
> +   Sort array MAPS according to dependencies of the contained objects.  */
>  static void
>  _dl_sort_maps_original (struct link_map **maps, unsigned int nmaps,
> -			unsigned int skip, bool for_fini)
> +			unsigned int skip)
>  {
>    /* Allows caller to do the common optimization of skipping the first map,
>       usually the main binary.  */
> @@ -47,14 +46,6 @@ _dl_sort_maps_original (struct link_map **maps, unsigned int nmaps,
>        ++seen[i];
>        struct link_map *thisp = maps[i];
>  
> -      if (__glibc_unlikely (for_fini))
> -	{
> -	  /* Do not handle ld.so in secondary namespaces and objects which
> -	     are not removed.  */
> -	  if (thisp != thisp->l_real || thisp->l_idx == -1)
> -	    goto skip;
> -	}
> -
>        /* Find the last object in the list for which the current one is
>  	 a dependency and move the current object behind the object
>  	 with the dependency.  */
> @@ -67,7 +58,6 @@ _dl_sort_maps_original (struct link_map **maps, unsigned int nmaps,
>  	    while (*runp != NULL)
>  	      if (__glibc_unlikely (*runp++ == thisp))
>  		{
> -		move:
>  		  /* Move the current object to the back past the last
>  		     object with it as the dependency.  */
>  		  memmove (&maps[i], &maps[i + 1],
> @@ -87,31 +77,9 @@ _dl_sort_maps_original (struct link_map **maps, unsigned int nmaps,
>  		  goto next;
>  		}
>  
> -	  if (__glibc_unlikely (for_fini && maps[k]->l_reldeps != NULL))
> -	    {
> -	      unsigned int m = maps[k]->l_reldeps->act;
> -	      struct link_map **relmaps = &maps[k]->l_reldeps->list[0];
> -
> -	      /* Look through the relocation dependencies of the object.  */
> -	      while (m-- > 0)
> -		if (__glibc_unlikely (relmaps[m] == thisp))
> -		  {
> -		    /* If a cycle exists with a link time dependency,
> -		       preserve the latter.  */
> -		    struct link_map **runp = thisp->l_initfini;
> -		    if (runp != NULL)
> -		      while (*runp != NULL)
> -			if (__glibc_unlikely (*runp++ == maps[k]))
> -			  goto ignore;
> -		    goto move;
> -		  }
> -	    ignore:;
> -	    }
> -
>  	  --k;
>  	}
>  
> -    skip:
>        if (++i == nmaps)
>  	break;
>      next_clear:
> @@ -137,8 +105,7 @@ strong_alias (_dl_sort_maps_original, _dl_sort_maps);
>     decremented before storing the current map at each level.  */
>  
>  static void
> -dfs_traversal (struct link_map ***rpo, struct link_map *map,
> -	       bool *do_reldeps)
> +dfs_traversal (struct link_map ***rpo, struct link_map *map)
>  {
>    if (map->l_visited)
>      return;
> @@ -152,22 +119,7 @@ dfs_traversal (struct link_map ***rpo, struct link_map *map,
>  	  struct link_map *dep = map->l_initfini[i];
>  	  if (dep->l_visited == 0
>  	      && dep->l_main_map == 0)
> -	    dfs_traversal (rpo, dep, do_reldeps);
> -	}
> -    }
> -
> -  if (__glibc_unlikely (do_reldeps != NULL && map->l_reldeps != NULL))
> -    {
> -      /* Indicate that we encountered relocation dependencies during
> -	 traversal.  */
> -      *do_reldeps = true;
> -
> -      for (int m = map->l_reldeps->act - 1; m >= 0; m--)
> -	{
> -	  struct link_map *dep = map->l_reldeps->list[m];
> -	  if (dep->l_visited == 0
> -	      && dep->l_main_map == 0)
> -	    dfs_traversal (rpo, dep, do_reldeps);
> +	    dfs_traversal (rpo, dep);
>  	}
>      }
>  
> @@ -180,7 +132,7 @@ dfs_traversal (struct link_map ***rpo, struct link_map *map,
>  
>  static void
>  _dl_sort_maps_dfs (struct link_map **maps, unsigned int nmaps,
> -		   unsigned int skip __attribute__ ((unused)), bool for_fini)
> +		   unsigned int skip __attribute__ ((unused)))
>  {
>    for (int i = nmaps - 1; i >= 0; i--)
>      maps[i]->l_visited = 0;
> @@ -225,52 +177,15 @@ _dl_sort_maps_dfs (struct link_map **maps, unsigned int nmaps,
>       bottom of above dfs_traversal() routine).  */
>    struct link_map **rpo_head = &rpo[nmaps];
>  
> -  bool do_reldeps = false;
> -  bool *do_reldeps_ref = (for_fini ? &do_reldeps : NULL);
> -
>    for (int i = nmaps - 1; i >= 0; i--)
>      {
> -      dfs_traversal (&rpo_head, maps[i], do_reldeps_ref);
> -
> +      dfs_traversal (&rpo_head, maps[i]);
>        /* We can break early if all objects are already placed.  */
>        if (rpo_head == rpo)
> -	goto end;
> +	break;
>      }
>    assert (rpo_head == rpo);
>  
> - end:
> -  /* Here we may do a second pass of sorting, using only l_initfini[]
> -     static dependency links. This is avoided if !FOR_FINI or if we didn't
> -     find any reldeps in the first DFS traversal.
> -
> -     The reason we do this is: while it is unspecified how circular
> -     dependencies should be handled, the presumed reasonable behavior is to
> -     have destructors to respect static dependency links as much as possible,
> -     overriding reldeps if needed. And the first sorting pass, which takes
> -     l_initfini/l_reldeps links equally, may not preserve this priority.
> -
> -     Hence we do a 2nd sorting pass, taking only DT_NEEDED links into account
> -     (see how the do_reldeps argument to dfs_traversal() is NULL below).  */
> -  if (do_reldeps)
> -    {
> -      for (int i = nmaps - 1; i >= 0; i--)
> -	rpo[i]->l_visited = 0;
> -
> -      struct link_map **maps_head = &maps[nmaps];
> -      for (int i = nmaps - 1; i >= 0; i--)
> -	{
> -	  dfs_traversal (&maps_head, rpo[i], NULL);
> -
> -	  /* We can break early if all objects are already placed.
> -	     The below memcpy is not needed in the do_reldeps case here,
> -	     since we wrote back to maps[] during DFS traversal.  */
> -	  if (maps_head == maps)
> -	    return;
> -	}
> -      assert (maps_head == maps);
> -      return;
> -    }
> -
>    memcpy (maps, rpo, sizeof (struct link_map *) * nmaps);
>  }
>  
> @@ -284,7 +199,7 @@ _dl_sort_maps_init (void)
>  
>  void
>  _dl_sort_maps (struct link_map **maps, unsigned int nmaps,
> -	       unsigned int skip, bool for_fini)
> +	       unsigned int skip)
>  {
>    /* It can be tempting to use a static function pointer to store and call
>       the current selected sorting algorithm routine, but experimentation
> @@ -294,9 +209,9 @@ _dl_sort_maps (struct link_map **maps, unsigned int nmaps,
>       input cases. A simple if-case with direct function calls appears to
>       be the fastest.  */
>    if (__glibc_likely (GLRO(dl_dso_sort_algo) == dso_sort_algorithm_original))
> -    _dl_sort_maps_original (maps, nmaps, skip, for_fini);
> +    _dl_sort_maps_original (maps, nmaps, skip);
>    else
> -    _dl_sort_maps_dfs (maps, nmaps, skip, for_fini);
> +    _dl_sort_maps_dfs (maps, nmaps, skip);
>  }
>  
>  #endif /* HAVE_TUNABLES.  */
> diff --git a/elf/dso-sort-tests-1.def b/elf/dso-sort-tests-1.def
> index 5f7f18ef27..c2398408cd 100644
> --- a/elf/dso-sort-tests-1.def
> +++ b/elf/dso-sort-tests-1.def
> @@ -56,11 +56,7 @@ output: b>a>{}<a<b
>  # relocation(dynamic) dependencies. While this is technically unspecified, the
>  # presumed reasonable practical behavior is for the destructor order to respect
>  # the static DT_NEEDED links (here this means the a->b->c->d order).
> -# The older dynamic_sort=1 algorithm does not achieve this, while the DFS-based
> -# dynamic_sort=2 algorithm does, although it is still arguable whether going
> -# beyond spec to do this is the right thing to do.
>  # The below expected outputs are what the two algorithms currently produce
>  # respectively, for regression testing purposes.
>  tst-bz15311: {+a;+e;+f;+g;+d;%d;-d;-g;-f;-e;-a};a->b->c->d;d=>[ba];c=>a;b=>e=>a;c=>f=>b;d=>g=>c
> -output(glibc.rtld.dynamic_sort=1): {+a[d>c>b>a>];+e[e>];+f[f>];+g[g>];+d[];%d(b(e(a()))a()g(c(a()f(b(e(a()))))));-d[];-g[];-f[];-e[];-a[<a<c<d<g<f<b<e];}
> -output(glibc.rtld.dynamic_sort=2): {+a[d>c>b>a>];+e[e>];+f[f>];+g[g>];+d[];%d(b(e(a()))a()g(c(a()f(b(e(a()))))));-d[];-g[];-f[];-e[];-a[<g<f<a<b<c<d<e];}
> +output: {+a[d>c>b>a>];+e[e>];+f[f>];+g[g>];+d[];%d(b(e(a()))a()g(c(a()f(b(e(a()))))));-d[];-g[];-f[];-e[];-a[<a<b<c<d<e<f<g];}
> diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
> index 2ebe7901c0..4d679510e3 100644
> --- a/sysdeps/generic/ldsodefs.h
> +++ b/sysdeps/generic/ldsodefs.h
> @@ -1123,7 +1123,7 @@ extern void _dl_fini (void) attribute_hidden;
>  
>  /* Sort array MAPS according to dependencies of the contained objects.  */
>  extern void _dl_sort_maps (struct link_map **maps, unsigned int nmaps,
> -			   unsigned int skip, bool for_fini) attribute_hidden;
> +			   unsigned int skip) attribute_hidden;
>  
>  /* The dynamic linker calls this function before and having changing
>     any shared object mappings.  The `r_state' member of `struct r_debug'
diff mbox series

Patch

diff --git a/elf/dl-close.c b/elf/dl-close.c
index bcd6e206e9..a161b95d00 100644
--- a/elf/dl-close.c
+++ b/elf/dl-close.c
@@ -258,7 +258,7 @@  _dl_close_worker (struct link_map *map, bool force)
 
   /* Sort the entries.  We can skip looking for the binary itself which is
      at the front of the search list for the main namespace.  */
-  _dl_sort_maps (maps, nloaded, (nsid == LM_ID_BASE), true);
+  _dl_sort_maps (maps, nloaded, (nsid == LM_ID_BASE));
 
   /* Call all termination functions at once.  */
   bool unload_any = false;
diff --git a/elf/dl-deps.c b/elf/dl-deps.c
index c8bab5cad5..ef19e7c390 100644
--- a/elf/dl-deps.c
+++ b/elf/dl-deps.c
@@ -614,8 +614,7 @@  Filters not supported with LD_TRACE_PRELINKING"));
   /* If libc.so.6 is the main map, it participates in the sort, so
      that the relocation order is correct regarding libc.so.6.  */
   _dl_sort_maps (l_initfini, nlist,
-		 (l_initfini[0] != GL (dl_ns)[l_initfini[0]->l_ns].libc_map),
-		 false);
+		 (l_initfini[0] != GL (dl_ns)[l_initfini[0]->l_ns].libc_map));
 
   /* Terminate the list of dependencies.  */
   l_initfini[nlist] = NULL;
diff --git a/elf/dl-fini.c b/elf/dl-fini.c
index 030b1fcbcd..f841868cdb 100644
--- a/elf/dl-fini.c
+++ b/elf/dl-fini.c
@@ -96,7 +96,7 @@  _dl_fini (void)
 	  /* Now we have to do the sorting.  We can skip looking for the
 	     binary itself which is at the front of the search list for
 	     the main namespace.  */
-	  _dl_sort_maps (maps, nmaps, (ns == LM_ID_BASE), true);
+	  _dl_sort_maps (maps, nmaps, (ns == LM_ID_BASE));
 
 	  /* We do not rely on the linked list of loaded object anymore
 	     from this point on.  We have our own list here (maps).  The
diff --git a/elf/dl-sort-maps.c b/elf/dl-sort-maps.c
index 9e9d53ec47..95938c4a52 100644
--- a/elf/dl-sort-maps.c
+++ b/elf/dl-sort-maps.c
@@ -23,11 +23,10 @@ 
 /* Note: this is the older, "original" sorting algorithm, being used as
    default up to 2.35.
 
-   Sort array MAPS according to dependencies of the contained objects.
-   If FOR_FINI is true, this is called for finishing an object.  */
+   Sort array MAPS according to dependencies of the contained objects.  */
 static void
 _dl_sort_maps_original (struct link_map **maps, unsigned int nmaps,
-			unsigned int skip, bool for_fini)
+			unsigned int skip)
 {
   /* Allows caller to do the common optimization of skipping the first map,
      usually the main binary.  */
@@ -47,14 +46,6 @@  _dl_sort_maps_original (struct link_map **maps, unsigned int nmaps,
       ++seen[i];
       struct link_map *thisp = maps[i];
 
-      if (__glibc_unlikely (for_fini))
-	{
-	  /* Do not handle ld.so in secondary namespaces and objects which
-	     are not removed.  */
-	  if (thisp != thisp->l_real || thisp->l_idx == -1)
-	    goto skip;
-	}
-
       /* Find the last object in the list for which the current one is
 	 a dependency and move the current object behind the object
 	 with the dependency.  */
@@ -67,7 +58,6 @@  _dl_sort_maps_original (struct link_map **maps, unsigned int nmaps,
 	    while (*runp != NULL)
 	      if (__glibc_unlikely (*runp++ == thisp))
 		{
-		move:
 		  /* Move the current object to the back past the last
 		     object with it as the dependency.  */
 		  memmove (&maps[i], &maps[i + 1],
@@ -87,31 +77,9 @@  _dl_sort_maps_original (struct link_map **maps, unsigned int nmaps,
 		  goto next;
 		}
 
-	  if (__glibc_unlikely (for_fini && maps[k]->l_reldeps != NULL))
-	    {
-	      unsigned int m = maps[k]->l_reldeps->act;
-	      struct link_map **relmaps = &maps[k]->l_reldeps->list[0];
-
-	      /* Look through the relocation dependencies of the object.  */
-	      while (m-- > 0)
-		if (__glibc_unlikely (relmaps[m] == thisp))
-		  {
-		    /* If a cycle exists with a link time dependency,
-		       preserve the latter.  */
-		    struct link_map **runp = thisp->l_initfini;
-		    if (runp != NULL)
-		      while (*runp != NULL)
-			if (__glibc_unlikely (*runp++ == maps[k]))
-			  goto ignore;
-		    goto move;
-		  }
-	    ignore:;
-	    }
-
 	  --k;
 	}
 
-    skip:
       if (++i == nmaps)
 	break;
     next_clear:
@@ -137,8 +105,7 @@  strong_alias (_dl_sort_maps_original, _dl_sort_maps);
    decremented before storing the current map at each level.  */
 
 static void
-dfs_traversal (struct link_map ***rpo, struct link_map *map,
-	       bool *do_reldeps)
+dfs_traversal (struct link_map ***rpo, struct link_map *map)
 {
   if (map->l_visited)
     return;
@@ -152,22 +119,7 @@  dfs_traversal (struct link_map ***rpo, struct link_map *map,
 	  struct link_map *dep = map->l_initfini[i];
 	  if (dep->l_visited == 0
 	      && dep->l_main_map == 0)
-	    dfs_traversal (rpo, dep, do_reldeps);
-	}
-    }
-
-  if (__glibc_unlikely (do_reldeps != NULL && map->l_reldeps != NULL))
-    {
-      /* Indicate that we encountered relocation dependencies during
-	 traversal.  */
-      *do_reldeps = true;
-
-      for (int m = map->l_reldeps->act - 1; m >= 0; m--)
-	{
-	  struct link_map *dep = map->l_reldeps->list[m];
-	  if (dep->l_visited == 0
-	      && dep->l_main_map == 0)
-	    dfs_traversal (rpo, dep, do_reldeps);
+	    dfs_traversal (rpo, dep);
 	}
     }
 
@@ -180,7 +132,7 @@  dfs_traversal (struct link_map ***rpo, struct link_map *map,
 
 static void
 _dl_sort_maps_dfs (struct link_map **maps, unsigned int nmaps,
-		   unsigned int skip __attribute__ ((unused)), bool for_fini)
+		   unsigned int skip __attribute__ ((unused)))
 {
   for (int i = nmaps - 1; i >= 0; i--)
     maps[i]->l_visited = 0;
@@ -225,52 +177,15 @@  _dl_sort_maps_dfs (struct link_map **maps, unsigned int nmaps,
      bottom of above dfs_traversal() routine).  */
   struct link_map **rpo_head = &rpo[nmaps];
 
-  bool do_reldeps = false;
-  bool *do_reldeps_ref = (for_fini ? &do_reldeps : NULL);
-
   for (int i = nmaps - 1; i >= 0; i--)
     {
-      dfs_traversal (&rpo_head, maps[i], do_reldeps_ref);
-
+      dfs_traversal (&rpo_head, maps[i]);
       /* We can break early if all objects are already placed.  */
       if (rpo_head == rpo)
-	goto end;
+	break;
     }
   assert (rpo_head == rpo);
 
- end:
-  /* Here we may do a second pass of sorting, using only l_initfini[]
-     static dependency links. This is avoided if !FOR_FINI or if we didn't
-     find any reldeps in the first DFS traversal.
-
-     The reason we do this is: while it is unspecified how circular
-     dependencies should be handled, the presumed reasonable behavior is to
-     have destructors to respect static dependency links as much as possible,
-     overriding reldeps if needed. And the first sorting pass, which takes
-     l_initfini/l_reldeps links equally, may not preserve this priority.
-
-     Hence we do a 2nd sorting pass, taking only DT_NEEDED links into account
-     (see how the do_reldeps argument to dfs_traversal() is NULL below).  */
-  if (do_reldeps)
-    {
-      for (int i = nmaps - 1; i >= 0; i--)
-	rpo[i]->l_visited = 0;
-
-      struct link_map **maps_head = &maps[nmaps];
-      for (int i = nmaps - 1; i >= 0; i--)
-	{
-	  dfs_traversal (&maps_head, rpo[i], NULL);
-
-	  /* We can break early if all objects are already placed.
-	     The below memcpy is not needed in the do_reldeps case here,
-	     since we wrote back to maps[] during DFS traversal.  */
-	  if (maps_head == maps)
-	    return;
-	}
-      assert (maps_head == maps);
-      return;
-    }
-
   memcpy (maps, rpo, sizeof (struct link_map *) * nmaps);
 }
 
@@ -284,7 +199,7 @@  _dl_sort_maps_init (void)
 
 void
 _dl_sort_maps (struct link_map **maps, unsigned int nmaps,
-	       unsigned int skip, bool for_fini)
+	       unsigned int skip)
 {
   /* It can be tempting to use a static function pointer to store and call
      the current selected sorting algorithm routine, but experimentation
@@ -294,9 +209,9 @@  _dl_sort_maps (struct link_map **maps, unsigned int nmaps,
      input cases. A simple if-case with direct function calls appears to
      be the fastest.  */
   if (__glibc_likely (GLRO(dl_dso_sort_algo) == dso_sort_algorithm_original))
-    _dl_sort_maps_original (maps, nmaps, skip, for_fini);
+    _dl_sort_maps_original (maps, nmaps, skip);
   else
-    _dl_sort_maps_dfs (maps, nmaps, skip, for_fini);
+    _dl_sort_maps_dfs (maps, nmaps, skip);
 }
 
 #endif /* HAVE_TUNABLES.  */
diff --git a/elf/dso-sort-tests-1.def b/elf/dso-sort-tests-1.def
index 5f7f18ef27..c2398408cd 100644
--- a/elf/dso-sort-tests-1.def
+++ b/elf/dso-sort-tests-1.def
@@ -56,11 +56,7 @@  output: b>a>{}<a<b
 # relocation(dynamic) dependencies. While this is technically unspecified, the
 # presumed reasonable practical behavior is for the destructor order to respect
 # the static DT_NEEDED links (here this means the a->b->c->d order).
-# The older dynamic_sort=1 algorithm does not achieve this, while the DFS-based
-# dynamic_sort=2 algorithm does, although it is still arguable whether going
-# beyond spec to do this is the right thing to do.
 # The below expected outputs are what the two algorithms currently produce
 # respectively, for regression testing purposes.
 tst-bz15311: {+a;+e;+f;+g;+d;%d;-d;-g;-f;-e;-a};a->b->c->d;d=>[ba];c=>a;b=>e=>a;c=>f=>b;d=>g=>c
-output(glibc.rtld.dynamic_sort=1): {+a[d>c>b>a>];+e[e>];+f[f>];+g[g>];+d[];%d(b(e(a()))a()g(c(a()f(b(e(a()))))));-d[];-g[];-f[];-e[];-a[<a<c<d<g<f<b<e];}
-output(glibc.rtld.dynamic_sort=2): {+a[d>c>b>a>];+e[e>];+f[f>];+g[g>];+d[];%d(b(e(a()))a()g(c(a()f(b(e(a()))))));-d[];-g[];-f[];-e[];-a[<g<f<a<b<c<d<e];}
+output: {+a[d>c>b>a>];+e[e>];+f[f>];+g[g>];+d[];%d(b(e(a()))a()g(c(a()f(b(e(a()))))));-d[];-g[];-f[];-e[];-a[<a<b<c<d<e<f<g];}
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 2ebe7901c0..4d679510e3 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -1123,7 +1123,7 @@  extern void _dl_fini (void) attribute_hidden;
 
 /* Sort array MAPS according to dependencies of the contained objects.  */
 extern void _dl_sort_maps (struct link_map **maps, unsigned int nmaps,
-			   unsigned int skip, bool for_fini) attribute_hidden;
+			   unsigned int skip) attribute_hidden;
 
 /* The dynamic linker calls this function before and having changing
    any shared object mappings.  The `r_state' member of `struct r_debug'