PR13616, linker should pad executable sections with nops, not zeros

Message ID 20191016124408.GY10114@bubble.grove.modra.org
State New
Headers show
Series
  • PR13616, linker should pad executable sections with nops, not zeros
Related show

Commit Message

Alan Modra Oct. 16, 2019, 12:44 p.m.
This implements padding of orphan executable sections for PowerPC.
Of course, the simple implementation of bfd_arch_ppc_nop_fill and
removing the NOP definition didn't work, with powerpc64 hitting a
testsuite failure linking to S-records.  That's because the srec
target is BFD_ENDIAN_UNKNOWN so the test of bfd_big_endian (abfd) in
default_data_link_order therefore returned false, resulting in a
little-endian nop pattern.  The rest of the patch fixes that problem
by adding a new field to bfd_link_info that can be used to determine
actual endianness on targets like srec.

	PR 13616
include/
	* bfdlink.h (struct bfd_link_info <big_endian>): New field.
bfd/
	* cpu-powerpc.c (bfd_arch_ppc_nop_fill): New function, use it
	for all ppc arch info.
	* linker.c (default_data_link_order): Pass info->big_endian to
	arch_info->fill function.
ld/
	* emulparams/elf64lppc.sh (NOP): Don't define.
	* emulparams/elf64ppc.sh (NOP): Don't define.
	* ldwrite.c (build_link_order): Use link_info.big_endian.  Move
	code determining endian to use for data_statement to..
	* ldemul.c (after_open_default): ..here.  Set link_info.big_endian.


-- 
Alan Modra
Australia Development Lab, IBM

Patch

diff --git a/bfd/cpu-powerpc.c b/bfd/cpu-powerpc.c
index 49594678d3..6c54388a82 100644
--- a/bfd/cpu-powerpc.c
+++ b/bfd/cpu-powerpc.c
@@ -48,6 +48,43 @@  powerpc_compatible (const bfd_arch_info_type *a,
   /*NOTREACHED*/
 }
 
+/* Return a COUNT sized buffer filled with nops (if CODE is TRUE) or
+   zeros (if CODE is FALSE).  This is the fill used between input
+   sections for alignment.  It won't normally be executed.   */
+
+static void *
+bfd_arch_ppc_nop_fill (bfd_size_type count,
+		       bfd_boolean is_bigendian,
+		       bfd_boolean code)
+{
+  bfd_byte *fill;
+
+  if (count == 0)
+    return NULL;
+  fill = bfd_malloc (count);
+  if (fill == NULL)
+    return fill;
+
+  if (code && (count & 3) == 0)
+    {
+      static const char nop_be[4] = {0x60, 0, 0, 0};
+      static const char nop_le[4] = {0, 0, 0, 0x60};
+      const char *nop = is_bigendian ? nop_be : nop_le;
+      bfd_byte *p = fill;
+
+      while (count != 0)
+	{
+	  memcpy (p, nop, 4);
+	  p += 4;
+	  count -= 4;
+	}
+    }
+  else
+    memset (fill, 0, count);
+
+  return fill;
+}
+
 #define N(BITS, NUMBER, PRINT, DEFAULT, NEXT)		\
   {							\
     BITS,      /* Bits in a word.  */			\
@@ -61,7 +98,7 @@  powerpc_compatible (const bfd_arch_info_type *a,
     DEFAULT,						\
     powerpc_compatible,					\
     bfd_default_scan,					\
-    bfd_arch_default_fill,				\
+    bfd_arch_ppc_nop_fill,				\
     NEXT,						\
     0 /* Maximum offset of a reloc from the start of an insn.  */ \
   }
diff --git a/bfd/linker.c b/bfd/linker.c
index 143b8eb950..382b69d8c3 100644
--- a/bfd/linker.c
+++ b/bfd/linker.c
@@ -2469,7 +2469,7 @@  _bfd_default_link_order (bfd *abfd,
 
 static bfd_boolean
 default_data_link_order (bfd *abfd,
-			 struct bfd_link_info *info ATTRIBUTE_UNUSED,
+			 struct bfd_link_info *info,
 			 asection *sec,
 			 struct bfd_link_order *link_order)
 {
@@ -2489,7 +2489,7 @@  default_data_link_order (bfd *abfd,
   fill_size = link_order->u.data.size;
   if (fill_size == 0)
     {
-      fill = abfd->arch_info->fill (size, bfd_big_endian (abfd),
+      fill = abfd->arch_info->fill (size, info->big_endian,
 				    (sec->flags & SEC_CODE) != 0);
       if (fill == NULL)
 	return FALSE;
diff --git a/include/bfdlink.h b/include/bfdlink.h
index 76355a3b95..32d15129ab 100644
--- a/include/bfdlink.h
+++ b/include/bfdlink.h
@@ -359,6 +359,10 @@  struct bfd_link_info
   /* TRUE if section groups should be resolved.  */
   unsigned int resolve_section_groups: 1;
 
+  /* Set if output file is big-endian, or if that is unknown, from
+     the command line or first input file endianness.  */
+  unsigned int big_endian : 1;
+
   /* Which symbols to strip.  */
   ENUM_BITFIELD (bfd_link_strip) strip : 2;
 
diff --git a/ld/emulparams/elf64lppc.sh b/ld/emulparams/elf64lppc.sh
index 71ef0d3efd..341629184b 100644
--- a/ld/emulparams/elf64lppc.sh
+++ b/ld/emulparams/elf64lppc.sh
@@ -1,3 +1,2 @@ 
 source_sh ${srcdir}/emulparams/elf64ppc.sh
 OUTPUT_FORMAT="elf64-powerpcle"
-NOP=0x00000060
diff --git a/ld/emulparams/elf64ppc.sh b/ld/emulparams/elf64ppc.sh
index 0dd42cf432..15221b8222 100644
--- a/ld/emulparams/elf64ppc.sh
+++ b/ld/emulparams/elf64ppc.sh
@@ -6,7 +6,6 @@  OUTPUT_FORMAT="elf64-powerpc"
 TEXT_START_ADDR=0x10000000
 #SEGMENT_SIZE=0x10000000
 ARCH=powerpc:common64
-NOP=0x60000000
 unset EXECUTABLE_SYMBOLS
 unset SDATA_START_SYMBOLS
 unset SDATA2_START_SYMBOLS
diff --git a/ld/ldemul.c b/ld/ldemul.c
index ab23dee41b..090f1ebfa7 100644
--- a/ld/ldemul.c
+++ b/ld/ldemul.c
@@ -236,6 +236,29 @@  after_parse_default (void)
 void
 after_open_default (void)
 {
+  link_info.big_endian = TRUE;
+
+  if (bfd_big_endian (link_info.output_bfd))
+    ;
+  else if (bfd_little_endian (link_info.output_bfd))
+    link_info.big_endian = FALSE;
+  else
+    {
+      if (command_line.endian == ENDIAN_BIG)
+	;
+      else if (command_line.endian == ENDIAN_LITTLE)
+	link_info.big_endian = FALSE;
+      else if (command_line.endian == ENDIAN_UNSET)
+	{
+	  LANG_FOR_EACH_INPUT_STATEMENT (s)
+	    if (s->the_bfd != NULL)
+	      {
+		if (bfd_little_endian (s->the_bfd))
+		  link_info.big_endian = FALSE;
+		break;
+	      }
+	}
+    }
 }
 
 void
diff --git a/ld/ldwrite.c b/ld/ldwrite.c
index f2d695063c..491a4e9d7f 100644
--- a/ld/ldwrite.c
+++ b/ld/ldwrite.c
@@ -46,7 +46,6 @@  build_link_order (lang_statement_union_type *statement)
 	asection *output_section;
 	struct bfd_link_order *link_order;
 	bfd_vma value;
-	bfd_boolean big_endian = FALSE;
 
 	output_section = statement->data_statement.output_section;
 	ASSERT (output_section->owner == link_info.output_bfd);
@@ -66,74 +65,38 @@  build_link_order (lang_statement_union_type *statement)
 
 	value = statement->data_statement.value;
 
-	/* If the endianness of the output BFD is not known, then we
-	   base the endianness of the data on the first input file.
-	   By convention, the bfd_put routines for an unknown
+	/* By convention, the bfd_put routines for an unknown
 	   endianness are big endian, so we must swap here if the
-	   input file is little endian.  */
-	if (bfd_big_endian (link_info.output_bfd))
-	  big_endian = TRUE;
-	else if (bfd_little_endian (link_info.output_bfd))
-	  big_endian = FALSE;
-	else
+	   input is little endian.  */
+	if (!bfd_big_endian (link_info.output_bfd)
+	    && !bfd_little_endian (link_info.output_bfd)
+	    && !link_info.big_endian)
 	  {
-	    bfd_boolean swap;
+	    bfd_byte buffer[8];
 
-	    swap = FALSE;
-	    if (command_line.endian == ENDIAN_BIG)
-	      big_endian = TRUE;
-	    else if (command_line.endian == ENDIAN_LITTLE)
-	      {
-		big_endian = FALSE;
-		swap = TRUE;
-	      }
-	    else if (command_line.endian == ENDIAN_UNSET)
+	    switch (statement->data_statement.type)
 	      {
-		big_endian = TRUE;
-		{
-		  LANG_FOR_EACH_INPUT_STATEMENT (s)
+	      case QUAD:
+	      case SQUAD:
+		if (sizeof (bfd_vma) >= QUAD_SIZE)
 		  {
-		    if (s->the_bfd != NULL)
-		      {
-			if (bfd_little_endian (s->the_bfd))
-			  {
-			    big_endian = FALSE;
-			    swap = TRUE;
-			  }
-			break;
-		      }
-		  }
-		}
-	      }
-
-	    if (swap)
-	      {
-		bfd_byte buffer[8];
-
-		switch (statement->data_statement.type)
-		  {
-		  case QUAD:
-		  case SQUAD:
-		    if (sizeof (bfd_vma) >= QUAD_SIZE)
-		      {
-			bfd_putl64 (value, buffer);
-			value = bfd_getb64 (buffer);
-			break;
-		      }
-		    /* Fall through.  */
-		  case LONG:
-		    bfd_putl32 (value, buffer);
-		    value = bfd_getb32 (buffer);
-		    break;
-		  case SHORT:
-		    bfd_putl16 (value, buffer);
-		    value = bfd_getb16 (buffer);
-		    break;
-		  case BYTE:
+		    bfd_putl64 (value, buffer);
+		    value = bfd_getb64 (buffer);
 		    break;
-		  default:
-		    abort ();
 		  }
+		/* Fall through.  */
+	      case LONG:
+		bfd_putl32 (value, buffer);
+		value = bfd_getb32 (buffer);
+		break;
+	      case SHORT:
+		bfd_putl16 (value, buffer);
+		value = bfd_getb16 (buffer);
+		break;
+	      case BYTE:
+		break;
+	      default:
+		abort ();
 	      }
 	  }
 
@@ -157,10 +120,10 @@  build_link_order (lang_statement_union_type *statement)
 		  high = (bfd_vma) -1;
 		bfd_put_32 (link_info.output_bfd, high,
 			    (link_order->u.data.contents
-			     + (big_endian ? 0 : 4)));
+			     + (link_info.big_endian ? 0 : 4)));
 		bfd_put_32 (link_info.output_bfd, value,
 			    (link_order->u.data.contents
-			     + (big_endian ? 4 : 0)));
+			     + (link_info.big_endian ? 4 : 0)));
 	      }
 	    link_order->size = QUAD_SIZE;
 	    break;