[01/25] _dl_fini: Rewrite to use VLA instead of extend_alloca

Message ID 4640427f7b0c3de94ac8792a85a7d3c5ef2d5084.1425285061.git.fweimer@redhat.com
State Committed
Headers

Commit Message

Florian Weimer March 1, 2015, 1:49 p.m. UTC
  In this case, extend_alloca is used to work around the lack of
deallocation on scope exit.  A VLA is automatically deallocated in this
way, so it is the more fitting approach.

To implement this, it is necessary to eliminate the goto.  In addition,
this change eliminates the trivially-true assert; the assert is always
skipped if nloaded > 0.
---
 elf/dl-fini.c | 193 +++++++++++++++++++++++++++-------------------------------
 1 file changed, 89 insertions(+), 104 deletions(-)
  

Comments

Carlos O'Donell Oct. 28, 2015, 5:11 a.m. UTC | #1
On 03/01/2015 08:49 AM, Florian Weimer wrote:
> In this case, extend_alloca is used to work around the lack of
> deallocation on scope exit.  A VLA is automatically deallocated in this
> way, so it is the more fitting approach.
> 
> To implement this, it is necessary to eliminate the goto.  In addition,
> this change eliminates the trivially-true assert; the assert is always
> skipped if nloaded > 0.

This looks good to me.

It's a good use of VLA.

Please check this in.

Make sure you follow GNU Changelog format (you didn't post one for review).

> ---
>  elf/dl-fini.c | 193 +++++++++++++++++++++++++++-------------------------------
>  1 file changed, 89 insertions(+), 104 deletions(-)
> 
> diff --git a/elf/dl-fini.c b/elf/dl-fini.c
> index 6cfe651..0790b04 100644
> --- a/elf/dl-fini.c
> +++ b/elf/dl-fini.c
> @@ -16,7 +16,6 @@
>     License along with the GNU C Library; if not, see
>     <http://www.gnu.org/licenses/>.  */
>  
> -#include <alloca.h>
>  #include <assert.h>
>  #include <string.h>
>  #include <ldsodefs.h>
> @@ -140,8 +139,6 @@ _dl_fini (void)
>       using `dlopen' there are possibly several other modules with its
>       dependencies to be taken into account.  Therefore we have to start
>       determining the order of the modules once again from the beginning.  */
> -  struct link_map **maps = NULL;
> -  size_t maps_size = 0;
>  
>    /* We run the destructors of the main namespaces last.  As for the
>       other namespaces, we pick run the destructors in them in reverse
> @@ -155,7 +152,6 @@ _dl_fini (void)
>        /* Protect against concurrent loads and unloads.  */
>        __rtld_lock_lock_recursive (GL(dl_load_lock));
>  
> -      unsigned int nmaps = 0;
>        unsigned int nloaded = GL(dl_ns)[ns]._ns_nloaded;
>        /* No need to do anything for empty namespaces or those used for
>  	 auditing DSOs.  */
> @@ -164,118 +160,107 @@ _dl_fini (void)
>  	  || GL(dl_ns)[ns]._ns_loaded->l_auditing != do_audit
>  #endif
>  	  )
> -	goto out;
> -
> -      /* XXX Could it be (in static binaries) that there is no object
> -	 loaded?  */
> -      assert (ns != LM_ID_BASE || nloaded > 0);
> -
> -      /* Now we can allocate an array to hold all the pointers and copy
> -	 the pointers in.  */
> -      if (maps_size < nloaded * sizeof (struct link_map *))
> +	__rtld_lock_unlock_recursive (GL(dl_load_lock));
> +      else
>  	{
> -	  if (maps_size == 0)
> +	  /* Now we can allocate an array to hold all the pointers and
> +	     copy the pointers in.  */
> +	  struct link_map *maps[nloaded];
> +
> +	  unsigned int i;
> +	  struct link_map *l;
> +	  assert (nloaded != 0 || GL(dl_ns)[ns]._ns_loaded == NULL);
> +	  for (l = GL(dl_ns)[ns]._ns_loaded, i = 0; l != NULL; l = l->l_next)
> +	    /* Do not handle ld.so in secondary namespaces.  */
> +	    if (l == l->l_real)
> +	      {
> +		assert (i < nloaded);
> +
> +		maps[i] = l;
> +		l->l_idx = i;
> +		++i;
> +
> +		/* Bump l_direct_opencount of all objects so that they
> +		   are not dlclose()ed from underneath us.  */
> +		++l->l_direct_opencount;
> +	      }
> +	  assert (ns != LM_ID_BASE || i == nloaded);
> +	  assert (ns == LM_ID_BASE || i == nloaded || i == nloaded - 1);
> +	  unsigned int nmaps = i;
> +
> +	  /* Now we have to do the sorting.  */
> +	  _dl_sort_fini (maps, nmaps, NULL, ns);
> +
> +	  /* We do not rely on the linked list of loaded object anymore
> +	     from this point on.  We have our own list here (maps).  The
> +	     various members of this list cannot vanish since the open
> +	     count is too high and will be decremented in this loop.  So
> +	     we release the lock so that some code which might be called
> +	     from a destructor can directly or indirectly access the
> +	     lock.  */
> +	  __rtld_lock_unlock_recursive (GL(dl_load_lock));
> +
> +	  /* 'maps' now contains the objects in the right order.  Now
> +	     call the destructors.  We have to process this array from
> +	     the front.  */
> +	  for (i = 0; i < nmaps; ++i)
>  	    {
> -	      maps_size = nloaded * sizeof (struct link_map *);
> -	      maps = (struct link_map **) alloca (maps_size);
> -	    }
> -	  else
> -	    maps = (struct link_map **)
> -	      extend_alloca (maps, maps_size,
> -			     nloaded * sizeof (struct link_map *));
> -	}
> +	      struct link_map *l = maps[i];
>  
> -      unsigned int i;
> -      struct link_map *l;
> -      assert (nloaded != 0 || GL(dl_ns)[ns]._ns_loaded == NULL);
> -      for (l = GL(dl_ns)[ns]._ns_loaded, i = 0; l != NULL; l = l->l_next)
> -	/* Do not handle ld.so in secondary namespaces.  */
> -	if (l == l->l_real)
> -	  {
> -	    assert (i < nloaded);
> -
> -	    maps[i] = l;
> -	    l->l_idx = i;
> -	    ++i;
> -
> -	    /* Bump l_direct_opencount of all objects so that they are
> -	       not dlclose()ed from underneath us.  */
> -	    ++l->l_direct_opencount;
> -	  }
> -      assert (ns != LM_ID_BASE || i == nloaded);
> -      assert (ns == LM_ID_BASE || i == nloaded || i == nloaded - 1);
> -      nmaps = i;
> -
> -      /* Now we have to do the sorting.  */
> -      _dl_sort_fini (maps, nmaps, NULL, ns);
> -
> -      /* We do not rely on the linked list of loaded object anymore from
> -	 this point on.  We have our own list here (maps).  The various
> -	 members of this list cannot vanish since the open count is too
> -	 high and will be decremented in this loop.  So we release the
> -	 lock so that some code which might be called from a destructor
> -	 can directly or indirectly access the lock.  */
> -    out:
> -      __rtld_lock_unlock_recursive (GL(dl_load_lock));
> -
> -      /* 'maps' now contains the objects in the right order.  Now call the
> -	 destructors.  We have to process this array from the front.  */
> -      for (i = 0; i < nmaps; ++i)
> -	{
> -	  l = maps[i];
> -
> -	  if (l->l_init_called)
> -	    {
> -	      /* Make sure nothing happens if we are called twice.  */
> -	      l->l_init_called = 0;
> -
> -	      /* Is there a destructor function?  */
> -	      if (l->l_info[DT_FINI_ARRAY] != NULL
> -		  || l->l_info[DT_FINI] != NULL)
> +	      if (l->l_init_called)
>  		{
> -		  /* When debugging print a message first.  */
> -		  if (__builtin_expect (GLRO(dl_debug_mask)
> -					& DL_DEBUG_IMPCALLS, 0))
> -		    _dl_debug_printf ("\ncalling fini: %s [%lu]\n\n",
> -				      DSO_FILENAME (l->l_name),
> -				      ns);
> -
> -		  /* First see whether an array is given.  */
> -		  if (l->l_info[DT_FINI_ARRAY] != NULL)
> +		  /* Make sure nothing happens if we are called twice.  */
> +		  l->l_init_called = 0;
> +
> +		  /* Is there a destructor function?  */
> +		  if (l->l_info[DT_FINI_ARRAY] != NULL
> +		      || l->l_info[DT_FINI] != NULL)
>  		    {
> -		      ElfW(Addr) *array =
> -			(ElfW(Addr) *) (l->l_addr
> -					+ l->l_info[DT_FINI_ARRAY]->d_un.d_ptr);
> -		      unsigned int i = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val
> -					/ sizeof (ElfW(Addr)));
> -		      while (i-- > 0)
> -			((fini_t) array[i]) ();
> +		      /* When debugging print a message first.  */
> +		      if (__builtin_expect (GLRO(dl_debug_mask)
> +					    & DL_DEBUG_IMPCALLS, 0))
> +			_dl_debug_printf ("\ncalling fini: %s [%lu]\n\n",
> +					  DSO_FILENAME (l->l_name),
> +					  ns);
> +
> +		      /* First see whether an array is given.  */
> +		      if (l->l_info[DT_FINI_ARRAY] != NULL)
> +			{
> +			  ElfW(Addr) *array =
> +			    (ElfW(Addr) *) (l->l_addr
> +					    + l->l_info[DT_FINI_ARRAY]->d_un.d_ptr);
> +			  unsigned int i = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val
> +					    / sizeof (ElfW(Addr)));
> +			  while (i-- > 0)
> +			    ((fini_t) array[i]) ();
> +			}
> +
> +		      /* Next try the old-style destructor.  */
> +		      if (l->l_info[DT_FINI] != NULL)
> +			DL_CALL_DT_FINI
> +			  (l, l->l_addr + l->l_info[DT_FINI]->d_un.d_ptr);
>  		    }
>  
> -		  /* Next try the old-style destructor.  */
> -		  if (l->l_info[DT_FINI] != NULL)
> -		     DL_CALL_DT_FINI(l, l->l_addr + l->l_info[DT_FINI]->d_un.d_ptr);
> -		}
> -
>  #ifdef SHARED
> -	      /* Auditing checkpoint: another object closed.  */
> -	      if (!do_audit && __builtin_expect (GLRO(dl_naudit) > 0, 0))
> -		{
> -		  struct audit_ifaces *afct = GLRO(dl_audit);
> -		  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
> +		  /* Auditing checkpoint: another object closed.  */
> +		  if (!do_audit && __builtin_expect (GLRO(dl_naudit) > 0, 0))
>  		    {
> -		      if (afct->objclose != NULL)
> -			/* Return value is ignored.  */
> -			(void) afct->objclose (&l->l_audit[cnt].cookie);
> -
> -		      afct = afct->next;
> +		      struct audit_ifaces *afct = GLRO(dl_audit);
> +		      for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
> +			{
> +			  if (afct->objclose != NULL)
> +			    /* Return value is ignored.  */
> +			    (void) afct->objclose (&l->l_audit[cnt].cookie);
> +
> +			  afct = afct->next;
> +			}
>  		    }
> -		}
>  #endif
> -	    }
> +		}
>  
> -	  /* Correct the previous increment.  */
> -	  --l->l_direct_opencount;
> +	      /* Correct the previous increment.  */
> +	      --l->l_direct_opencount;
> +	    }
>  	}
>      }
>  
>
  
Florian Weimer Oct. 29, 2015, 9:04 a.m. UTC | #2
On 10/28/2015 06:11 AM, Carlos O'Donell wrote:
> On 03/01/2015 08:49 AM, Florian Weimer wrote:
>> In this case, extend_alloca is used to work around the lack of
>> deallocation on scope exit.  A VLA is automatically deallocated in this
>> way, so it is the more fitting approach.
>>
>> To implement this, it is necessary to eliminate the goto.  In addition,
>> this change eliminates the trivially-true assert; the assert is always
>> skipped if nloaded > 0.
> 
> This looks good to me.
> 
> It's a good use of VLA.
> 
> Please check this in.

Thanks, committed

> Make sure you follow GNU Changelog format (you didn't post one for review).

The ChangeLog was posted in 00/25:

  <https://sourceware.org/ml/libc-alpha/2015-03/msg00056.html>

Florian
  

Patch

diff --git a/elf/dl-fini.c b/elf/dl-fini.c
index 6cfe651..0790b04 100644
--- a/elf/dl-fini.c
+++ b/elf/dl-fini.c
@@ -16,7 +16,6 @@ 
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
-#include <alloca.h>
 #include <assert.h>
 #include <string.h>
 #include <ldsodefs.h>
@@ -140,8 +139,6 @@  _dl_fini (void)
      using `dlopen' there are possibly several other modules with its
      dependencies to be taken into account.  Therefore we have to start
      determining the order of the modules once again from the beginning.  */
-  struct link_map **maps = NULL;
-  size_t maps_size = 0;
 
   /* We run the destructors of the main namespaces last.  As for the
      other namespaces, we pick run the destructors in them in reverse
@@ -155,7 +152,6 @@  _dl_fini (void)
       /* Protect against concurrent loads and unloads.  */
       __rtld_lock_lock_recursive (GL(dl_load_lock));
 
-      unsigned int nmaps = 0;
       unsigned int nloaded = GL(dl_ns)[ns]._ns_nloaded;
       /* No need to do anything for empty namespaces or those used for
 	 auditing DSOs.  */
@@ -164,118 +160,107 @@  _dl_fini (void)
 	  || GL(dl_ns)[ns]._ns_loaded->l_auditing != do_audit
 #endif
 	  )
-	goto out;
-
-      /* XXX Could it be (in static binaries) that there is no object
-	 loaded?  */
-      assert (ns != LM_ID_BASE || nloaded > 0);
-
-      /* Now we can allocate an array to hold all the pointers and copy
-	 the pointers in.  */
-      if (maps_size < nloaded * sizeof (struct link_map *))
+	__rtld_lock_unlock_recursive (GL(dl_load_lock));
+      else
 	{
-	  if (maps_size == 0)
+	  /* Now we can allocate an array to hold all the pointers and
+	     copy the pointers in.  */
+	  struct link_map *maps[nloaded];
+
+	  unsigned int i;
+	  struct link_map *l;
+	  assert (nloaded != 0 || GL(dl_ns)[ns]._ns_loaded == NULL);
+	  for (l = GL(dl_ns)[ns]._ns_loaded, i = 0; l != NULL; l = l->l_next)
+	    /* Do not handle ld.so in secondary namespaces.  */
+	    if (l == l->l_real)
+	      {
+		assert (i < nloaded);
+
+		maps[i] = l;
+		l->l_idx = i;
+		++i;
+
+		/* Bump l_direct_opencount of all objects so that they
+		   are not dlclose()ed from underneath us.  */
+		++l->l_direct_opencount;
+	      }
+	  assert (ns != LM_ID_BASE || i == nloaded);
+	  assert (ns == LM_ID_BASE || i == nloaded || i == nloaded - 1);
+	  unsigned int nmaps = i;
+
+	  /* Now we have to do the sorting.  */
+	  _dl_sort_fini (maps, nmaps, NULL, ns);
+
+	  /* We do not rely on the linked list of loaded object anymore
+	     from this point on.  We have our own list here (maps).  The
+	     various members of this list cannot vanish since the open
+	     count is too high and will be decremented in this loop.  So
+	     we release the lock so that some code which might be called
+	     from a destructor can directly or indirectly access the
+	     lock.  */
+	  __rtld_lock_unlock_recursive (GL(dl_load_lock));
+
+	  /* 'maps' now contains the objects in the right order.  Now
+	     call the destructors.  We have to process this array from
+	     the front.  */
+	  for (i = 0; i < nmaps; ++i)
 	    {
-	      maps_size = nloaded * sizeof (struct link_map *);
-	      maps = (struct link_map **) alloca (maps_size);
-	    }
-	  else
-	    maps = (struct link_map **)
-	      extend_alloca (maps, maps_size,
-			     nloaded * sizeof (struct link_map *));
-	}
+	      struct link_map *l = maps[i];
 
-      unsigned int i;
-      struct link_map *l;
-      assert (nloaded != 0 || GL(dl_ns)[ns]._ns_loaded == NULL);
-      for (l = GL(dl_ns)[ns]._ns_loaded, i = 0; l != NULL; l = l->l_next)
-	/* Do not handle ld.so in secondary namespaces.  */
-	if (l == l->l_real)
-	  {
-	    assert (i < nloaded);
-
-	    maps[i] = l;
-	    l->l_idx = i;
-	    ++i;
-
-	    /* Bump l_direct_opencount of all objects so that they are
-	       not dlclose()ed from underneath us.  */
-	    ++l->l_direct_opencount;
-	  }
-      assert (ns != LM_ID_BASE || i == nloaded);
-      assert (ns == LM_ID_BASE || i == nloaded || i == nloaded - 1);
-      nmaps = i;
-
-      /* Now we have to do the sorting.  */
-      _dl_sort_fini (maps, nmaps, NULL, ns);
-
-      /* We do not rely on the linked list of loaded object anymore from
-	 this point on.  We have our own list here (maps).  The various
-	 members of this list cannot vanish since the open count is too
-	 high and will be decremented in this loop.  So we release the
-	 lock so that some code which might be called from a destructor
-	 can directly or indirectly access the lock.  */
-    out:
-      __rtld_lock_unlock_recursive (GL(dl_load_lock));
-
-      /* 'maps' now contains the objects in the right order.  Now call the
-	 destructors.  We have to process this array from the front.  */
-      for (i = 0; i < nmaps; ++i)
-	{
-	  l = maps[i];
-
-	  if (l->l_init_called)
-	    {
-	      /* Make sure nothing happens if we are called twice.  */
-	      l->l_init_called = 0;
-
-	      /* Is there a destructor function?  */
-	      if (l->l_info[DT_FINI_ARRAY] != NULL
-		  || l->l_info[DT_FINI] != NULL)
+	      if (l->l_init_called)
 		{
-		  /* When debugging print a message first.  */
-		  if (__builtin_expect (GLRO(dl_debug_mask)
-					& DL_DEBUG_IMPCALLS, 0))
-		    _dl_debug_printf ("\ncalling fini: %s [%lu]\n\n",
-				      DSO_FILENAME (l->l_name),
-				      ns);
-
-		  /* First see whether an array is given.  */
-		  if (l->l_info[DT_FINI_ARRAY] != NULL)
+		  /* Make sure nothing happens if we are called twice.  */
+		  l->l_init_called = 0;
+
+		  /* Is there a destructor function?  */
+		  if (l->l_info[DT_FINI_ARRAY] != NULL
+		      || l->l_info[DT_FINI] != NULL)
 		    {
-		      ElfW(Addr) *array =
-			(ElfW(Addr) *) (l->l_addr
-					+ l->l_info[DT_FINI_ARRAY]->d_un.d_ptr);
-		      unsigned int i = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val
-					/ sizeof (ElfW(Addr)));
-		      while (i-- > 0)
-			((fini_t) array[i]) ();
+		      /* When debugging print a message first.  */
+		      if (__builtin_expect (GLRO(dl_debug_mask)
+					    & DL_DEBUG_IMPCALLS, 0))
+			_dl_debug_printf ("\ncalling fini: %s [%lu]\n\n",
+					  DSO_FILENAME (l->l_name),
+					  ns);
+
+		      /* First see whether an array is given.  */
+		      if (l->l_info[DT_FINI_ARRAY] != NULL)
+			{
+			  ElfW(Addr) *array =
+			    (ElfW(Addr) *) (l->l_addr
+					    + l->l_info[DT_FINI_ARRAY]->d_un.d_ptr);
+			  unsigned int i = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val
+					    / sizeof (ElfW(Addr)));
+			  while (i-- > 0)
+			    ((fini_t) array[i]) ();
+			}
+
+		      /* Next try the old-style destructor.  */
+		      if (l->l_info[DT_FINI] != NULL)
+			DL_CALL_DT_FINI
+			  (l, l->l_addr + l->l_info[DT_FINI]->d_un.d_ptr);
 		    }
 
-		  /* Next try the old-style destructor.  */
-		  if (l->l_info[DT_FINI] != NULL)
-		     DL_CALL_DT_FINI(l, l->l_addr + l->l_info[DT_FINI]->d_un.d_ptr);
-		}
-
 #ifdef SHARED
-	      /* Auditing checkpoint: another object closed.  */
-	      if (!do_audit && __builtin_expect (GLRO(dl_naudit) > 0, 0))
-		{
-		  struct audit_ifaces *afct = GLRO(dl_audit);
-		  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+		  /* Auditing checkpoint: another object closed.  */
+		  if (!do_audit && __builtin_expect (GLRO(dl_naudit) > 0, 0))
 		    {
-		      if (afct->objclose != NULL)
-			/* Return value is ignored.  */
-			(void) afct->objclose (&l->l_audit[cnt].cookie);
-
-		      afct = afct->next;
+		      struct audit_ifaces *afct = GLRO(dl_audit);
+		      for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+			{
+			  if (afct->objclose != NULL)
+			    /* Return value is ignored.  */
+			    (void) afct->objclose (&l->l_audit[cnt].cookie);
+
+			  afct = afct->next;
+			}
 		    }
-		}
 #endif
-	    }
+		}
 
-	  /* Correct the previous increment.  */
-	  --l->l_direct_opencount;
+	      /* Correct the previous increment.  */
+	      --l->l_direct_opencount;
+	    }
 	}
     }