[PATCH 2/4][MSP430] Change .either section placement to be performed after allocation

Jozef Lawrynowicz jozef.l@somniumtech.com
Wed Jul 19 17:22:00 GMT 2017


This patch adds ".either" section placement functionality in the
"after_allocation" stage of the linker. At present, ".either" section
placement is performed only in place_orphan, and at this stage the sizes
of sections have not been finalised, as there is still relaxation to be
performed. This can lead to ROM/RAM overflow, despite there being space
in the upper regions for code/data to be placed. The modified
after_allocation stage relaxes sections just before attempting to move
them, so the section sizes can be assumed accurate. This new placement
stage operates in two passes, firstly to move .either sections in .lower
to .upper if they cause .lower to overflow, and secondly to move .either
sections in .upper to .lower if they fit in .lower and .upper is
overflowing.
-------------- next part --------------
From 4a3c924adcd6e3d9a5adce0a8fe5d414aa320077 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@somniumtech.com>
Date: Mon, 10 Jul 2017 15:12:18 +0000
Subject: [PATCH 2/4] MSP430: Change .either section placement to be performed
 after allocation

---
 ld/emultempl/msp430.em                        | 309 +++++++++++++++++++++++++-
 ld/testsuite/ld-msp430-elf/msp430-elf.exp     |  29 +++
 ld/testsuite/ld-msp430-elf/msp430-tiny-ram.ld |  49 ++++
 ld/testsuite/ld-msp430-elf/msp430-tiny-rom.ld |  48 ++++
 4 files changed, 434 insertions(+), 1 deletion(-)
 create mode 100644 ld/testsuite/ld-msp430-elf/msp430-tiny-ram.ld
 create mode 100644 ld/testsuite/ld-msp430-elf/msp430-tiny-rom.ld

diff --git a/ld/emultempl/msp430.em b/ld/emultempl/msp430.em
index 04f8a2b..15c60a7 100644
--- a/ld/emultempl/msp430.em
+++ b/ld/emultempl/msp430.em
@@ -139,6 +139,32 @@ fi
 if test x"$LDEMUL_PLACE_ORPHAN" != xgld"$EMULATION_NAME"_place_orphan; then
 fragment <<EOF
 
+static unsigned int
+data_statement_size (lang_data_statement_type *d)
+{
+  unsigned int size = 0;
+  switch (d->type)
+    {
+    case QUAD:
+    case SQUAD:
+      size = QUAD_SIZE;
+      break;
+    case LONG:
+      size = LONG_SIZE;
+      break;
+    case SHORT:
+      size = SHORT_SIZE;
+      break;
+    case BYTE:
+      size = BYTE_SIZE;
+      break;
+    default:
+      einfo ("%P: error: unhandled data_statement size\n");
+      FAIL ();
+    }
+  return size;
+}
+
 /* Helper function for place_orphan that computes the size
    of sections already mapped to the given statement.  */
 
@@ -158,12 +184,17 @@ scan_children (lang_statement_union_type * l)
 
 	case lang_constructors_statement_enum:
 	case lang_assignment_statement_enum:
+	case lang_padding_statement_enum:
 	  break;
 
 	case lang_wild_statement_enum:
 	  amount += scan_children (l->wild_statement.children.head);	  
 	  break;
 
+	case lang_data_statement_enum:
+	  amount += data_statement_size (&l->data_statement);
+	  break;
+
 	default:
 	  fprintf (stderr, "msp430 orphan placer: unhandled lang type %d\n", l->header.type);
 	  break;
@@ -536,6 +567,282 @@ gld${EMULATION_NAME}_handle_option (int optc)
   return TRUE;
 }
 
+enum { ROM, RAM };
+
+static void
+eval_upper_either_sections (bfd *abfd, asection *s, void *data)
+{
+  if ((s->flags & SEC_ALLOC) == 0)
+    return;
+  if (bfd_link_relocatable (&link_info))
+    return;
+
+  char * base_sec_name = (char *) data;
+  const char *curr_name = bfd_get_section_name (abfd, s);
+
+  /* Only concerned with .either input sections in the upper output section.
+   * */
+  char * either_name = concat (".either", base_sec_name, NULL);
+  if (strncmp (curr_name, either_name, strlen (either_name)) != 0
+      || strncmp (s->output_section->name, ".upper", 6) != 0)
+    goto end;
+
+  lang_output_section_statement_type * lower
+    = lang_output_section_find (concat (".lower", base_sec_name, NULL));
+  lang_output_section_statement_type * upper
+    = lang_output_section_find (concat (".upper", base_sec_name, NULL));
+
+
+  if (upper == NULL)
+    goto end;
+  else if (lower == NULL)
+    lower = lang_output_section_find (base_sec_name);
+
+  int curr_region;
+  if (strcmp (base_sec_name, ".text") == 0
+      || strcmp (base_sec_name, ".rodata") == 0)
+    curr_region = ROM;
+  else
+    curr_region = RAM;
+
+  static bfd_size_type *lower_size = 0;
+  static bfd_size_type *upper_size = 0;
+  static bfd_size_type lower_size_rom = 0;
+  static bfd_size_type lower_size_ram = 0;
+  static bfd_size_type upper_size_rom = 0;
+  static bfd_size_type upper_size_ram = 0;
+  if (curr_region == ROM)
+    {
+      if (lower_size_rom == 0)
+	{
+	  lower_size_rom = lower->region->current - lower->region->origin;
+	  upper_size_rom = upper->region->current - upper->region->origin;
+	}
+      lower_size = &lower_size_rom;
+      upper_size = &upper_size_rom;
+    }
+  else if (curr_region == RAM)
+    {
+      if (lower_size_ram == 0)
+	{
+	  lower_size_ram = lower->region->current - lower->region->origin;
+	  upper_size_ram = upper->region->current - upper->region->origin;
+	}
+      lower_size = &lower_size_ram;
+      upper_size = &upper_size_ram;
+    }
+
+  /* Move sections in the upper region that would fit in the lower
+   * region to the lower region if the upper region is overflowing.  */
+  if (*upper_size > upper->region->length
+      && *lower_size + s->size < lower->region->length)
+    {
+      if (change_output_section (&(upper->children.head),
+				 s, lower))
+	{
+	  *upper_size -= s->size;
+	  *lower_size += s->size;
+	}
+    }
+ end:
+  free (either_name);
+}
+
+static void
+eval_lower_either_sections (bfd *abfd, asection *s, void *data)
+{
+  if ((s->flags & SEC_ALLOC) == 0)
+    return;
+  if (bfd_link_relocatable (&link_info))
+    return;
+
+  char * base_sec_name = (char *) data;
+  const char *curr_name = bfd_get_section_name (abfd, s);
+
+  /* Only concerned with .either input sections in the lower or "default"
+   * output section i.e. not in the upper output section.  */
+  char * either_name = concat (".either", base_sec_name, NULL);
+  if (strncmp (curr_name, either_name, strlen (either_name)) != 0
+      || strncmp (s->output_section->name, ".upper", 6) == 0)
+    return;
+
+  int curr_region;
+  if (strcmp (base_sec_name, ".text") == 0
+      || strcmp (base_sec_name, ".rodata") == 0)
+    curr_region = ROM;
+  else
+    curr_region = RAM;
+
+  lang_output_section_statement_type * output_sec
+    = lang_output_section_find (s->output_section->name);
+
+  /* If the output_section doesn't exist, this has already been reported in
+   * place_orphan, so don't need to warn again.  */
+  if (output_sec == NULL)
+    return;
+
+  /* lower and output_sec might be the same, but in some cases an .either
+   * section can end up in base_sec_name if it hasn't been placed by
+   * place_orphan.  */
+  lang_output_section_statement_type * lower
+    = lang_output_section_find (concat (".lower", base_sec_name, NULL));
+  lang_output_section_statement_type * upper
+    = lang_output_section_find (concat (".upper", base_sec_name, NULL));
+  if (upper == NULL)
+    goto end;
+
+  static bfd_size_type *lower_size = 0;
+  static bfd_size_type lower_size_rom = 0;
+  static bfd_size_type lower_size_ram = 0;
+  if (curr_region == ROM)
+    {
+      if (lower_size_rom == 0)
+	{
+	  /* Get the size of other items in the lower region that aren't the
+	   * sections to be moved around.  */
+	  lower_size_rom
+	    = (output_sec->region->current - output_sec->region->origin)
+	    - scan_children (output_sec->children.head);
+	  if (output_sec != lower && lower != NULL)
+	    lower_size_rom -= scan_children (lower->children.head);
+	}
+      lower_size = &lower_size_rom;
+    }
+  else if (curr_region == RAM)
+    {
+      if (lower_size_ram == 0)
+	{
+	  lower_size_ram
+	    = (output_sec->region->current - output_sec->region->origin)
+	    - scan_children (output_sec->children.head);
+	  if (output_sec != lower && lower != NULL)
+	    lower_size_ram -= scan_children (lower->children.head);
+	}
+      lower_size = &lower_size_ram;
+    }
+  /* Move sections that cause the lower region to overflow to the upper region.
+   * */
+  if (*lower_size + s->size > output_sec->region->length)
+    change_output_section (&(output_sec->children.head), s, upper);
+  else
+    *lower_size += s->size;
+ end:
+  free (either_name);
+}
+
+/* This function is similar to lang_relax_sections, but without the size
+ * evaluation code that is always executed after relaxation.  */
+static void
+intermediate_relax_sections (void)
+{
+  int i = link_info.relax_pass;
+
+  /* The backend can use it to determine the current pass.  */
+  link_info.relax_pass = 0;
+
+  while (i--)
+    {
+      bfd_boolean relax_again;
+
+      link_info.relax_trip = -1;
+      do
+	{
+	  link_info.relax_trip++;
+
+	  lang_do_assignments (lang_assigning_phase_enum);
+
+	  lang_reset_memory_regions ();
+
+	  relax_again = FALSE;
+	  lang_size_sections (&relax_again, FALSE);
+	}
+      while (relax_again);
+
+      link_info.relax_pass++;
+    }
+}
+
+enum either_placement_stage
+{
+  LOWER_TO_UPPER,
+  UPPER_TO_LOWER,
+};
+
+static void
+msp430_elf_after_allocation (void)
+{
+  int relax_count = 0;
+  int i;
+  /* Go over each section twice, once to place either sections that don't fit
+   * in lower into upper, and then again to move any sections in upper that fit
+   * in lower into lower.  */
+  for (i = 0; i < 8; i++)
+    {
+      int placement_stage = (i < 4) ? LOWER_TO_UPPER : UPPER_TO_LOWER;
+      char * base_sec_name;
+      switch (i % 4)
+	{
+	case 0:
+	  base_sec_name = concat (".text", NULL);
+	  break;
+	case 1:
+	  base_sec_name = concat (".data", NULL);
+	  break;
+	case 2:
+	  base_sec_name = concat (".bss", NULL);
+	  break;
+	case 3:
+	  base_sec_name = concat (".rodata", NULL);
+	  break;
+	}
+      lang_output_section_statement_type * upper
+	= lang_output_section_find (concat (".upper", base_sec_name, NULL));
+      if (upper != NULL)
+	{
+	  /* Can't just use one iteration over the all the sections to make
+	   * both lower->upper and upper->lower transformations because the
+	   * iterator encounters upper sections before all lower sections have
+	   * been examined.  */
+	  bfd *abfd;
+	  if (placement_stage == LOWER_TO_UPPER)
+	    {
+	      /* Perform relaxation and get the final size of sections before
+	       * trying to fit .either sections in the correct ouput sections.
+	       */
+	      if (relax_count == 0)
+		{
+		  intermediate_relax_sections ();
+		  relax_count++;
+		}
+	      for (abfd = link_info.input_bfds; abfd != NULL;
+		   abfd = abfd->link.next)
+		{
+		  bfd_map_over_sections (abfd, eval_lower_either_sections,
+					 base_sec_name);
+		}
+	    }
+	  else if (placement_stage == UPPER_TO_LOWER)
+	    {
+	      /* Relax again before moving upper->lower.  */
+	      if (relax_count == 1)
+		{
+		  intermediate_relax_sections ();
+		  relax_count++;
+		}
+	      for (abfd = link_info.input_bfds; abfd != NULL;
+		   abfd = abfd->link.next)
+		{
+		  bfd_map_over_sections (abfd, eval_upper_either_sections,
+					 base_sec_name);
+		}
+	    }
+
+	}
+      free (base_sec_name);
+    }
+  gld${EMULATION_NAME}_after_allocation ();
+}
+
 struct ld_emulation_xfer_struct ld_${EMULATION_NAME}_emulation =
 {
   ${LDEMUL_BEFORE_PARSE-gld${EMULATION_NAME}_before_parse},
@@ -543,7 +850,7 @@ struct ld_emulation_xfer_struct ld_${EMULATION_NAME}_emulation =
   ${LDEMUL_HLL-hll_default},
   ${LDEMUL_AFTER_PARSE-after_parse_default},
   msp430_elf_after_open,
-  ${LDEMUL_AFTER_ALLOCATION-after_allocation_default},
+  msp430_elf_after_allocation,
   ${LDEMUL_SET_OUTPUT_ARCH-set_output_arch_default},
   ${LDEMUL_CHOOSE_TARGET-ldemul_default_target},
   ${LDEMUL_BEFORE_ALLOCATION-before_allocation_default},
diff --git a/ld/testsuite/ld-msp430-elf/msp430-elf.exp b/ld/testsuite/ld-msp430-elf/msp430-elf.exp
index bed2ed8..fa396aa 100644
--- a/ld/testsuite/ld-msp430-elf/msp430-elf.exp
+++ b/ld/testsuite/ld-msp430-elf/msp430-elf.exp
@@ -108,5 +108,34 @@ set msp430regionprefixuniquesectiontests {
     "" "" {main-with-text-rodata-unique-sec.s} {{objdump -D main-const-lower.d}} "main-const-lower"}
 }
 
+set msp430eithershuffletests {
+  {"Move \"either\" main() to .upper.text when it doesn\'t fit in .lower.text"
+    "-T msp430-tiny-rom.ld --code-region=either --data-region=either" "" "" {main-with-text-rodata.s}
+    {{objdump -d main-text-upper.d}} "either-to-upper-text"}
+  {"Move \"either\" glob_var_array to .upper.data when it doesn\'t fit in .lower.data"
+    "-T msp430-tiny-ram.ld --data-region=either" "" "" {main-with-data-bss.s}
+    {{objdump -D main-var-upper.d}} "either-to-upper-data"}
+  {"Move \"either\" glob_bss_array to .upper.bss when it doesn\'t fit in .lower.bss"
+    "-T msp430-tiny-ram.ld --data-region=either" "" "" {main-with-data-bss.s}
+    {{objdump -D main-bss-upper.d}} "either-to-upper-bss"}
+  {"Move \"either\" glob_const_array to .upper.rodata when it doesn\'t fit in .lower.rodata"
+    "-T msp430-tiny-rom.ld --code-region=either --data-region=either" "" "" {main-with-text-rodata.s}
+    {{objdump -D main-const-upper.d}} "either-to-upper-const"}
+
+  {"Move \"either\" main() to .upper.text when it doesn\'t fit in .lower.text, with -ffunction/data-sections"
+    "-T msp430-tiny-rom.ld --code-region=either --data-region=either" "" "" {main-with-text-rodata-unique-sec.s}
+    {{objdump -d main-text-upper.d}} "either-to-upper-text-unique-sec"}
+  {"Move \"either\" glob_var_array to .upper.data when it doesn\'t fit in .lower.data, with -ffunction/data-sections"
+    "-T msp430-tiny-ram.ld --data-region=either" "" "" {main-with-data-bss-unique-sec.s}
+    {{objdump -D main-var-upper.d}} "either-to-upper-data-unique-sec"}
+  {"Move \"either\" glob_bss_array to .upper.bss when it doesn\'t fit in .lower.bss, with -ffunction/data-sections"
+    "-T msp430-tiny-ram.ld --data-region=either" "" "" {main-with-data-bss-unique-sec.s}
+    {{objdump -D main-bss-upper.d}} "either-to-upper-bss-unique-sec"}
+  {"Move \"either\" glob_const_array to .upper.rodata when it doesn\'t fit in .lower.rodata, with -ffunction/data-sections"
+    "-T msp430-tiny-rom.ld --code-region=either --data-region=either" "" "" {main-with-text-rodata-unique-sec.s}
+    {{objdump -D main-const-upper.d}} "either-to-upper-const-unique-sec"}
+}
+
 run_ld_link_tests $msp430regionprefixtests
 run_ld_link_tests $msp430regionprefixuniquesectiontests
+run_ld_link_tests $msp430eithershuffletests
diff --git a/ld/testsuite/ld-msp430-elf/msp430-tiny-ram.ld b/ld/testsuite/ld-msp430-elf/msp430-tiny-ram.ld
new file mode 100644
index 0000000..e2e6f2f
--- /dev/null
+++ b/ld/testsuite/ld-msp430-elf/msp430-tiny-ram.ld
@@ -0,0 +1,49 @@
+/* Script for ld testsuite */
+OUTPUT_ARCH(msp430)
+ENTRY(_start)
+
+MEMORY
+{
+  RAM : ORIGIN = 0x0, LENGTH = 0x2
+  ROM : ORIGIN = 0x2, LENGTH = 0x1fe
+  HIFRAM : ORIGIN = 0x200, LENGTH = 0x1000
+}
+
+SECTIONS
+{
+  .text :
+  {
+    PROVIDE (_start = .);
+    . = ALIGN(2);
+    *(.text .stub .text.* .gnu.linkonce.t.* .text:*)
+  } > ROM
+
+  .rodata :
+  {
+    *(.upper.rodata.* .rodata)
+  } > ROM
+
+  .data :
+  {
+    . = ALIGN(2);
+    *(.data.* .data)
+  } > RAM AT> ROM
+
+  .bss :
+  {
+    . = ALIGN(2);
+    *(.bss.* .bss)
+  } > RAM
+
+  .upper.data :
+  {
+    . = ALIGN(2);
+    *(.upper.data.* .upper.data)
+  } > HIFRAM AT> ROM
+
+  .upper.bss :
+  {
+    . = ALIGN(2);
+    *(.upper.bss.* .upper.bss)
+  } > HIFRAM
+}
diff --git a/ld/testsuite/ld-msp430-elf/msp430-tiny-rom.ld b/ld/testsuite/ld-msp430-elf/msp430-tiny-rom.ld
new file mode 100644
index 0000000..3e26379
--- /dev/null
+++ b/ld/testsuite/ld-msp430-elf/msp430-tiny-rom.ld
@@ -0,0 +1,48 @@
+/* Script for ld testsuite */
+OUTPUT_ARCH(msp430)
+ENTRY(_start)
+
+MEMORY
+{
+  ROM : ORIGIN = 0x0, LENGTH = 0x2
+  RAM : ORIGIN = 0x2, LENGTH = 0x1fe
+  HIROM : ORIGIN = 0x200, LENGTH = 0x1000
+}
+
+SECTIONS
+{
+  .text :
+  {
+    PROVIDE (_start = .);
+    . = ALIGN(2);
+    *(.text .stub .text.* .gnu.linkonce.t.* .text:*)
+  } > ROM
+
+  .rodata :
+  {
+    *(.rodata.* .rodata)
+  } > ROM
+
+  .data :
+  {
+    . = ALIGN(2);
+    *(.data.* .data)
+  } > RAM AT> ROM
+
+  .bss :
+  {
+    . = ALIGN(2);
+    *(.bss.* .bss)
+  } > RAM
+
+  .upper.text :
+  {
+    . = ALIGN(2);
+    *(.upper.text.* .upper.text)
+  } > HIROM
+
+  .upper.rodata :
+  {
+    *(.upper.rodata.* .upper.rodata)
+  } > HIROM
+}
-- 
1.8.3.1



More information about the Binutils mailing list