SORT_BY_INIT_PRIORITY

Message ID 20190925144402.GG19242@bubble.grove.modra.org
State New
Headers show
Series
  • SORT_BY_INIT_PRIORITY
Related show

Commit Message

Alan Modra Sept. 25, 2019, 2:44 p.m.
I was looking at the implementation of this script keyword today and
couldn't remember why we do what we do in get_init_priority, because
the comments explain how the init_priority is encoded but don't say
why it is necessary to extract the priority and sort on that.  So
after figuring out why (again), I wrote some more comments.

Then I simplified get_init_priority a little, adding some sanity
checking on the strtoul result.  This actually makes get_init_priority
support sorting by numerical suffix more generally, but I figure this
feature would be better as a new keyword (without the .ctors/.dtors
special case), so haven't documented the extension.

	* ld.texi (SORT_BY_ALIGNMENT): Reword slightly.
	(SORT_BY_INIT_PRIORITY): Elucidate.
	* ldlang.c: Include limits.h.
	(get_init_priority): Comment.  Change param to a section,
	return an int.  Sanity check priority digits.  Support sorting
	more sections with trailing digits.  Return -1 on error.
	(compare_section): Adjust.


-- 
Alan Modra
Australia Development Lab, IBM

Patch

diff --git a/ld/ld.texi b/ld/ld.texi
index 8d5c7bc140..95fa6a0348 100644
--- a/ld/ld.texi
+++ b/ld/ld.texi
@@ -4630,17 +4630,20 @@  pattern in parentheses (e.g., @code{SORT_BY_NAME(.text*)}).  When the
 into ascending order by name before placing them in the output file.
 
 @cindex SORT_BY_ALIGNMENT
-@code{SORT_BY_ALIGNMENT} is very similar to @code{SORT_BY_NAME}. The
-difference is @code{SORT_BY_ALIGNMENT} will sort sections into
-descending order by alignment before placing them in the output file.
-Larger alignments are placed before smaller alignments in order to
-reduce the amount of padding necessary.
+@code{SORT_BY_ALIGNMENT} is similar to @code{SORT_BY_NAME}.
+@code{SORT_BY_ALIGNMENT} will sort sections into descending order of
+alignment before placing them in the output file.  Placing larger
+alignments before smaller alignments can reduce the amount of padding
+needed.
 
 @cindex SORT_BY_INIT_PRIORITY
-@code{SORT_BY_INIT_PRIORITY} is very similar to @code{SORT_BY_NAME}. The
-difference is @code{SORT_BY_INIT_PRIORITY} will sort sections into
-ascending order by numerical value of the GCC init_priority attribute
-encoded in the section name before placing them in the output file.
+@code{SORT_BY_INIT_PRIORITY} is also similar to @code{SORT_BY_NAME}.
+@code{SORT_BY_INIT_PRIORITY} will sort sections into ascending
+numerical order of the GCC init_priority attribute encoded in the
+section name before placing them in the output file.  In
+@code{.init_array.NNNNN} and @code{.fini_array.NNNNN}, @code{NNNNN} is
+the init_priority.  In @code{.ctors.NNNNN} and @code{.dtors.NNNNN},
+@code{NNNNN} is 65535 minus the init_priority.
 
 @cindex SORT
 @code{SORT} is an alias for @code{SORT_BY_NAME}.
diff --git a/ld/ldlang.c b/ld/ldlang.c
index 1a49f69d90..a28e9a0453 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -19,6 +19,7 @@ 
    MA 02110-1301, USA.  */
 
 #include "sysdep.h"
+#include <limits.h>
 #include "bfd.h"
 #include "libiberty.h"
 #include "filenames.h"
@@ -403,39 +404,50 @@  match_simple_wild (const char *pattern, const char *name)
 /* Return the numerical value of the init_priority attribute from
    section name NAME.  */
 
-static unsigned long
-get_init_priority (const char *name)
+static int
+get_init_priority (const asection *sec)
 {
-  char *end;
-  unsigned long init_priority;
+  const char *name = bfd_section_name (sec);
+  const char *dot;
 
   /* GCC uses the following section names for the init_priority
-     attribute with numerical values 101 and 65535 inclusive. A
+     attribute with numerical values 101 to 65535 inclusive. A
      lower value means a higher priority.
 
-     1: .init_array.NNNN/.fini_array.NNNN: Where NNNN is the
+     1: .init_array.NNNNN/.fini_array.NNNNN: Where NNNNN is the
 	decimal numerical value of the init_priority attribute.
 	The order of execution in .init_array is forward and
 	.fini_array is backward.
-     2: .ctors.NNNN/.dtors.NNNN: Where NNNN is 65535 minus the
+     2: .ctors.NNNNN/.dtors.NNNNN: Where NNNNN is 65535 minus the
 	decimal numerical value of the init_priority attribute.
 	The order of execution in .ctors is backward and .dtors
 	is forward.
-   */
-  if (strncmp (name, ".init_array.", 12) == 0
-      || strncmp (name, ".fini_array.", 12) == 0)
-    {
-      init_priority = strtoul (name + 12, &end, 10);
-      return *end ? 0 : init_priority;
-    }
-  else if (strncmp (name, ".ctors.", 7) == 0
-	   || strncmp (name, ".dtors.", 7) == 0)
-    {
-      init_priority = strtoul (name + 7, &end, 10);
-      return *end ? 0 : 65535 - init_priority;
-    }
 
-  return 0;
+     .init_array.NNNNN sections would normally be placed in an output
+     .init_array section, .fini_array.NNNNN in .fini_array,
+     .ctors.NNNNN in .ctors, and .dtors.NNNNN in .dtors.  This means
+     we should sort by increasing number (and could just use
+     SORT_BY_NAME in scripts).  However if .ctors.NNNNN sections are
+     being placed in .init_array (which may also contain
+     .init_array.NNNNN sections) or .dtors.NNNNN sections are being
+     placed in .fini_array then we need to extract the init_priority
+     attribute and sort on that.  */
+  dot = strrchr (name, '.');
+  if (dot != NULL && ISDIGIT (dot[1]))
+    {
+      char *end;
+      unsigned long init_priority = strtoul (dot + 1, &end, 10);
+      if (*end == 0)
+	{
+	  if (dot == name + 6
+	      && (strncmp (name, ".ctors", 6) == 0
+		  || strncmp (name, ".dtors", 6) == 0))
+	    init_priority = 65535 - init_priority;
+	  if (init_priority <= INT_MAX)
+	    return init_priority;
+	}
+    }
+  return -1;
 }
 
 /* Compare sections ASEC and BSEC according to SORT.  */
@@ -444,7 +456,7 @@  static int
 compare_section (sort_type sort, asection *asec, asection *bsec)
 {
   int ret;
-  unsigned long ainit_priority, binit_priority;
+  int a_priority, b_priority;
 
   switch (sort)
     {
@@ -452,11 +464,11 @@  compare_section (sort_type sort, asection *asec, asection *bsec)
       abort ();
 
     case by_init_priority:
-      ainit_priority = get_init_priority (bfd_section_name (asec));
-      binit_priority = get_init_priority (bfd_section_name (bsec));
-      if (ainit_priority == 0 || binit_priority == 0)
+      a_priority = get_init_priority (asec);
+      b_priority = get_init_priority (bsec);
+      if (a_priority < 0 || b_priority < 0)
 	goto sort_by_name;
-      ret = ainit_priority - binit_priority;
+      ret = a_priority - b_priority;
       if (ret)
 	break;
       else