This is the mail archive of the binutils-cvs@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[binutils-gdb] Improve MSP430 section placement.


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=7ef3addbe195b6283d0baf59e081146dfb211c97

commit 7ef3addbe195b6283d0baf59e081146dfb211c97
Author: Jozef Lawrynowicz <jozef.l@somniumtech.com>
Date:   Tue Aug 29 17:18:43 2017 +0100

    Improve MSP430 section placement.
    
    ld	* emultempl/msp430.em (change_output_section): New function.
    	(move_prefixed_section): New function.
    	(add_region_prefix): New function.
    	(msp430_elf_after_open): New function.
    	(gld${EMULATION_NAME}_add_options): Implement.
    	(gld${EMULATION_NAME}_list_options): Implement.
    	(gld${EMULATION_NAME}_handle_option): Implement.
    	* ld.texinfo: Document new options.
    	* testsuite/ld-msp430-elf/main-bss-lower.d: New.
    	* testsuite/ld-msp430-elf/main-bss-upper.d: New.
    	* testsuite/ld-msp430-elf/main-const-lower.d: New.
    	* testsuite/ld-msp430-elf/main-const-upper.d: New.
    	* testsuite/ld-msp430-elf/main-text-lower.d: New.
    	* testsuite/ld-msp430-elf/main-text-upper.d: New.
    	* testsuite/ld-msp430-elf/main-var-lower.d: New.
    	* testsuite/ld-msp430-elf/main-var-upper.d: New.
    	* testsuite/ld-msp430-elf/main-with-data-bss-unique-sec.s: New.
    	* testsuite/ld-msp430-elf/main-with-data-bss.s: New.
    	* testsuite/ld-msp430-elf/main-with-text-rodata-unique-sec.s: New.
    	* testsuite/ld-msp430-elf/main-with-text-rodata.s: New.
    	* testsuite/ld-msp430-elf/msp430-elf.exp: New.
    	* testsuite/ld-msp430-elf/msp430-no-lower.ld: New.
    	* testsuite/ld-msp430-elf/msp430.ld: New.
    	* emultempl/msp430.em (data_statement_size): New.
    	(eval_upper_either_sections): New.
    	(eval_lower_either_sections): New.
    	(intermediate_relax_sections): New.
    	(msp430_elf_after_allocation): New.
    	* emultempl/msp430.em (gld${EMULATION_NAME}_place_orphan): Always
    	place sections in the lower region.
    
    gas	* config/tc-msp430.c (md_parse_option): Define high data and high
    	bss symbols if -mdata-region is passed.
    	Define -mdata-region open.
    	* doc/c-msp430.texi: Document -mdata-region.
    	* testsuite/gas/msp430/high-data-bss-sym.d: New test.
    	* testsuite/gas/msp430/high-data-bss-sym.s: New.
    	* testsuite/gas/msp430/msp430.exp: Add -mdata-region tests.

Diff:
---
 gas/ChangeLog                                      |  10 +
 gas/config/tc-msp430.c                             |  23 +-
 gas/doc/c-msp430.texi                              |  13 +
 gas/testsuite/gas/msp430/high-data-bss-sym.d       |   6 +
 gas/testsuite/gas/msp430/high-data-bss-sym.s       |  19 +
 gas/testsuite/gas/msp430/msp430.exp                |   2 +
 ld/ChangeLog                                       |  33 ++
 ld/emultempl/msp430.em                             | 653 +++++++++++++++++++--
 ld/ld.texinfo                                      |  23 +
 ld/testsuite/ld-msp430-elf/main-bss-lower.d        |   3 +
 ld/testsuite/ld-msp430-elf/main-bss-upper.d        |   3 +
 ld/testsuite/ld-msp430-elf/main-const-lower.d      |   3 +
 ld/testsuite/ld-msp430-elf/main-const-upper.d      |   3 +
 ld/testsuite/ld-msp430-elf/main-text-lower.d       |   3 +
 ld/testsuite/ld-msp430-elf/main-text-upper.d       |   6 +
 ld/testsuite/ld-msp430-elf/main-var-lower.d        |   3 +
 ld/testsuite/ld-msp430-elf/main-var-upper.d        |   3 +
 .../ld-msp430-elf/main-with-data-bss-unique-sec.s  |  78 +++
 ld/testsuite/ld-msp430-elf/main-with-data-bss.s    |  74 +++
 .../main-with-text-rodata-unique-sec.s             |  59 ++
 ld/testsuite/ld-msp430-elf/main-with-text-rodata.s |  59 ++
 ld/testsuite/ld-msp430-elf/msp430-elf.exp          | 141 +++++
 ld/testsuite/ld-msp430-elf/msp430-no-lower.ld      |  54 ++
 ld/testsuite/ld-msp430-elf/msp430-tiny-ram.ld      |  49 ++
 ld/testsuite/ld-msp430-elf/msp430-tiny-rom.ld      |  48 ++
 ld/testsuite/ld-msp430-elf/msp430.ld               |  78 +++
 26 files changed, 1401 insertions(+), 48 deletions(-)

diff --git a/gas/ChangeLog b/gas/ChangeLog
index 8bea1df..3e250d3 100644
--- a/gas/ChangeLog
+++ b/gas/ChangeLog
@@ -1,3 +1,13 @@
+2017-08-29  Jozef Lawrynowicz  <jozef.l@somniumtech.com>
+
+	* config/tc-msp430.c (md_parse_option): Define high data and high
+	bss symbols if -mdata-region is passed.
+	Define -mdata-region open.
+	* doc/c-msp430.texi: Document -mdata-region.
+	* testsuite/gas/msp430/high-data-bss-sym.d: New test.
+	* testsuite/gas/msp430/high-data-bss-sym.s: New.
+	* testsuite/gas/msp430/msp430.exp: Add -mdata-region tests.
+
 2017-08-23  Alexander Fedotov <alexander.fedotov@nxp.com>
 	    Edmar Wienskoski <edmar.wienskoski@nxp.com
 
diff --git a/gas/config/tc-msp430.c b/gas/config/tc-msp430.c
index 91e0a73..62a34ed 100644
--- a/gas/config/tc-msp430.c
+++ b/gas/config/tc-msp430.c
@@ -676,6 +676,8 @@ static bfd_boolean warn_interrupt_nops = TRUE;
 #define OPTION_MCPU 'c'
 #define OPTION_MOVE_DATA 'd'
 static bfd_boolean move_data = FALSE;
+#define OPTION_DATA_REGION 'r'
+static bfd_boolean upper_data_region_in_use = FALSE;
 
 enum
 {
@@ -1448,6 +1450,12 @@ md_parse_option (int c, const char * arg)
     case OPTION_MOVE_DATA:
       move_data = TRUE;
       return 1;
+
+    case OPTION_DATA_REGION:
+      if (strcmp (arg, "upper") == 0
+	  || strcmp (arg, "either") == 0)
+	upper_data_region_in_use = TRUE;
+      return 1;
     }
 
   return 0;
@@ -1478,14 +1486,19 @@ msp430_make_init_symbols (const char * name)
 
   /* Note - data assigned to the .either.data section may end up being
      placed in the .upper.data section if the .lower.data section is
-     full.  Hence the need to define the crt0 symbol.  */
+     full.  Hence the need to define the crt0 symbol.
+     The linker may create upper or either data sections, even when none exist
+     at the moment, so use the value of the data-region flag to determine if
+     the symbol is needed.  */
   if (strncmp (name, ".either.data", 12) == 0
-      || strncmp (name, ".upper.data", 11) == 0)
+      || strncmp (name, ".upper.data", 11) == 0
+      || upper_data_region_in_use)
     (void) symbol_find_or_make ("__crt0_move_highdata");
 
   /* See note about .either.data above.  */
   if (strncmp (name, ".upper.bss", 10) == 0
-      || strncmp (name, ".either.bss", 11) == 0)
+      || strncmp (name, ".either.bss", 11) == 0
+      || upper_data_region_in_use)
     (void) symbol_find_or_make ("__crt0_init_highbss");
 }
 
@@ -1570,6 +1583,7 @@ struct option md_longopts[] =
   {"mY", no_argument, NULL, OPTION_NO_WARN_INTR_NOPS},
   {"my", no_argument, NULL, OPTION_WARN_INTR_NOPS},
   {"md", no_argument, NULL, OPTION_MOVE_DATA},
+  {"mdata-region", required_argument, NULL, OPTION_DATA_REGION},
   {NULL, no_argument, NULL, 0}
 };
 
@@ -1601,6 +1615,9 @@ md_show_usage (FILE * stream)
 	   _("  -my - warn about missing NOPs after changing interrupts (default)\n"));
   fprintf (stream,
 	   _("  -md - Force copying of data from ROM to RAM at startup\n"));
+  fprintf (stream,
+	   _("  -mdata-region={none|lower|upper|either} - select region data will be\n"
+	     "    placed in.\n"));
 }
 
 symbolS *
diff --git a/gas/doc/c-msp430.texi b/gas/doc/c-msp430.texi
index eb0e757..d80f540 100644
--- a/gas/doc/c-msp430.texi
+++ b/gas/doc/c-msp430.texi
@@ -107,6 +107,19 @@ disables warnings about missing NOP instructions.
 mark the object file as one that requires data to copied from ROM to
 RAM at execution startup.  Disabled by default.
 
+@item -mdata-region=@var{region}
+Select the region data will be placed in.
+Region placement is performed by the compiler and linker.  The only effect this
+option will have on the assembler is that if @var{upper} or @var{either} is
+selected, then the symbols to initialise high data and bss will be defined.
+Valid @var{region} values are:
+@table @code
+@item none
+@item lower
+@item upper
+@item either
+@end table
+
 @end table
 
 @node MSP430 Syntax
diff --git a/gas/testsuite/gas/msp430/high-data-bss-sym.d b/gas/testsuite/gas/msp430/high-data-bss-sym.d
new file mode 100644
index 0000000..1c8d95b
--- /dev/null
+++ b/gas/testsuite/gas/msp430/high-data-bss-sym.d
@@ -0,0 +1,6 @@
+#objdump: -t
+#name: Check symbols to initialise high data and high bss have been defined
+#...
+.*__crt0_move_highdata.*
+.*__crt0_init_highbss.*
+#pass
diff --git a/gas/testsuite/gas/msp430/high-data-bss-sym.s b/gas/testsuite/gas/msp430/high-data-bss-sym.s
new file mode 100644
index 0000000..1e6bf72
--- /dev/null
+++ b/gas/testsuite/gas/msp430/high-data-bss-sym.s
@@ -0,0 +1,19 @@
+	.file	"main.c"
+.text
+	.balign 2
+	.global	main
+	.type	main, @function
+main:
+; start of function
+; framesize_regs:     0
+; framesize_locals:   0
+; framesize_outgoing: 0
+; framesize:          0
+; elim ap -> fp       2
+; elim fp -> sp       0
+; saved regs:(none)
+	; start of prologue
+	; end of prologue
+.L2:
+	BR	#.L2
+	.size	main, .-main
diff --git a/gas/testsuite/gas/msp430/msp430.exp b/gas/testsuite/gas/msp430/msp430.exp
index b83e1ac..0dfb271 100644
--- a/gas/testsuite/gas/msp430/msp430.exp
+++ b/gas/testsuite/gas/msp430/msp430.exp
@@ -24,4 +24,6 @@ if [expr [istarget "msp430-*-*"]]  then {
     run_dump_test "bad"
     run_dump_test "errata_warns"
     run_dump_test "errata_fixes"
+    run_dump_test "high-data-bss-sym" { { as "-mdata-region=upper" } }
+    run_dump_test "high-data-bss-sym" { { as "-mdata-region=either" } }
 }
diff --git a/ld/ChangeLog b/ld/ChangeLog
index 06b8c25..8569569 100644
--- a/ld/ChangeLog
+++ b/ld/ChangeLog
@@ -1,3 +1,36 @@
+2017-08-29  Jozef Lawrynowicz  <jozef.l@somniumtech.com>
+
+	* emultempl/msp430.em (change_output_section): New function.
+	(move_prefixed_section): New function.
+	(add_region_prefix): New function.
+	(msp430_elf_after_open): New function.
+	(gld${EMULATION_NAME}_add_options): Implement.
+	(gld${EMULATION_NAME}_list_options): Implement.
+	(gld${EMULATION_NAME}_handle_option): Implement.
+	* ld.texinfo: Document new options.
+	* testsuite/ld-msp430-elf/main-bss-lower.d: New.
+	* testsuite/ld-msp430-elf/main-bss-upper.d: New.
+	* testsuite/ld-msp430-elf/main-const-lower.d: New.
+	* testsuite/ld-msp430-elf/main-const-upper.d: New.
+	* testsuite/ld-msp430-elf/main-text-lower.d: New.
+	* testsuite/ld-msp430-elf/main-text-upper.d: New.
+	* testsuite/ld-msp430-elf/main-var-lower.d: New.
+	* testsuite/ld-msp430-elf/main-var-upper.d: New.
+	* testsuite/ld-msp430-elf/main-with-data-bss-unique-sec.s: New.
+	* testsuite/ld-msp430-elf/main-with-data-bss.s: New.
+	* testsuite/ld-msp430-elf/main-with-text-rodata-unique-sec.s: New.
+	* testsuite/ld-msp430-elf/main-with-text-rodata.s: New.
+	* testsuite/ld-msp430-elf/msp430-elf.exp: New.
+	* testsuite/ld-msp430-elf/msp430-no-lower.ld: New.
+	* testsuite/ld-msp430-elf/msp430.ld: New.
+	* emultempl/msp430.em (data_statement_size): New.
+	(eval_upper_either_sections): New.
+	(eval_lower_either_sections): New.
+	(intermediate_relax_sections): New.
+	(msp430_elf_after_allocation): New.
+	* emultempl/msp430.em (gld${EMULATION_NAME}_place_orphan): Always
+	place sections in the lower region.
+
 2017-08-26  H.J. Lu  <hongjiu.lu@intel.com>
 
 	PR ld/21997
diff --git a/ld/emultempl/msp430.em b/ld/emultempl/msp430.em
index 949fea0..f1bbc85 100644
--- a/ld/emultempl/msp430.em
+++ b/ld/emultempl/msp430.em
@@ -32,6 +32,7 @@ fragment <<EOF
 #include "bfdlink.h"
 
 #include "ld.h"
+#include "getopt.h"
 #include "ldmain.h"
 #include "ldmisc.h"
 #include "ldexp.h"
@@ -39,6 +40,29 @@ fragment <<EOF
 #include "ldfile.h"
 #include "ldemul.h"
 #include "libiberty.h"
+#include <ldgram.h>
+
+enum regions
+{
+  REGION_NONE = 0,
+  REGION_LOWER,
+  REGION_UPPER,
+  REGION_EITHER = 3,
+};
+
+enum either_placement_stage
+{
+  LOWER_TO_UPPER,
+  UPPER_TO_LOWER,
+};
+
+enum { ROM, RAM };
+
+static int data_region = REGION_NONE;
+static int code_region = REGION_NONE;
+static bfd_boolean disable_sec_transformation = FALSE;
+
+#define MAX_PREFIX_LENGTH 7
 
 EOF
 
@@ -124,6 +148,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.  */
 
@@ -143,12 +193,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;
@@ -174,7 +229,6 @@ gld${EMULATION_NAME}_place_orphan (asection * s,
   char * buf = NULL;
   lang_output_section_statement_type * lower;
   lang_output_section_statement_type * upper;
-  lang_output_section_statement_type * os;
 
   if ((s->flags & SEC_ALLOC) == 0)
     return NULL;
@@ -211,62 +265,571 @@ gld${EMULATION_NAME}_place_orphan (asection * s,
   /* Find the corresponding lower and upper sections.  */
   lower = lang_output_section_find (lower_name);
   upper = lang_output_section_find (upper_name);
-  /* If the upper section does not exist, try again without the suffix.  */
-  if (upper == NULL)
-    upper = lang_output_section_find (name);
 
-  if (lower == NULL)
+  if (lower == NULL && upper == NULL)
+    {
+      einfo ("%P: error: no section named %s or %s in linker script\n",
+	     lower_name, upper_name);
+      goto end;
+    }
+  else if (lower == NULL)
     {
-      os = upper;
-      if (upper == NULL)
-        {
-          einfo ("%P: error: no section named %s or %s in linker script\n", lower_name, upper_name);
+      lower = lang_output_section_find (name);
+      if (lower == NULL)
+	{
+	  einfo ("%P: error: no section named %s in linker script\n", name);
 	  goto end;
 	}
     }
-  else if (upper == NULL)
-    os = lower;
-  else if (lower->region == NULL)
-    os = lower;
-  /* If the section is too big for the region containing
-     the lower section then do not even try to use it.  */
-  else if (lower->region->length < s->size)
-    os = upper;
+
+  /* Always place orphaned sections in lower.  Optimal placement of either
+     sections is performed later, once section sizes have been finalized.  */
+  lang_add_section (& lower->children, s, NULL, lower);
+ end:
+  free (upper_name);
+  free (lower_name);
+  if (buf)
+    free (buf);
+  return lower;
+}
+EOF
+fi
+
+fragment <<EOF
+
+static bfd_boolean
+change_output_section (lang_statement_union_type ** head,
+		       asection *s,
+		       lang_output_section_statement_type * new_output_section)
+{
+  asection *is;
+  lang_statement_union_type * prev = NULL;
+  lang_statement_union_type * curr;
+
+  curr = *head;
+  while (curr != NULL)
+    {
+      switch (curr->header.type)
+	{
+	case lang_input_section_enum:
+	  is = curr->input_section.section;
+	  if (is == s)
+	    {
+	      s->output_section = NULL;
+	      lang_add_section (& (new_output_section->children), s, NULL,
+				new_output_section);
+	      /* Remove the section from the old output section.  */
+	      if (prev == NULL)
+		*head = curr->header.next;
+	      else
+		prev->header.next = curr->header.next;
+	      return TRUE;
+	    }
+	  break;
+	case lang_wild_statement_enum:
+	  if (change_output_section (&(curr->wild_statement.children.head),
+				     s, new_output_section))
+	    return TRUE;
+	  break;
+	default:
+	  break;
+	}
+      prev = curr;
+      curr = curr->header.next;
+    }
+  return FALSE;
+}
+
+static void
+move_prefixed_section (asection *s, char *new_name,
+		       lang_output_section_statement_type * new_output_sec)
+{
+  s->name = new_name;
+  if (s->output_section == NULL)
+    lang_add_section (& (new_output_sec->children), s, NULL, new_output_sec);
   else
     {
-      bfd_size_type amount = 0;
-      struct lang_output_section_statement_struct * p;
+      lang_output_section_statement_type * curr_output_sec
+	= lang_output_section_find (s->output_section->name);
+      change_output_section (&(curr_output_sec->children.head), s,
+			     new_output_sec);
+    }
+}
+
+static void
+add_region_prefix (bfd *abfd, asection *s,
+		   ATTRIBUTE_UNUSED void *unused)
+{
+  const char *curr_name = bfd_get_section_name (abfd, s);
+  char * base_name;
+  char * new_input_sec_name = NULL;
+  char * new_output_sec_name = NULL;
+  int region = REGION_NONE;
+
+  if (strncmp (curr_name, ".text", 5) == 0)
+    {
+      region = code_region;
+      base_name = concat (".text", NULL);
+    }
+  else if (strncmp (curr_name, ".data", 5) == 0)
+    {
+      region = data_region;
+      base_name = concat (".data", NULL);
+    }
+  else if (strncmp (curr_name, ".bss", 4) == 0)
+    {
+      region = data_region;
+      base_name = concat (".bss", NULL);
+    }
+  else if (strncmp (curr_name, ".rodata", 7) == 0)
+    {
+      region = data_region;
+      base_name = concat (".rodata", NULL);
+    }
+  else
+    return;
+
+  switch (region)
+    {
+    case REGION_NONE:
+      break;
+    case REGION_UPPER:
+      new_input_sec_name = concat (".upper", curr_name, NULL);
+      new_output_sec_name = concat (".upper", base_name, NULL);
+      lang_output_section_statement_type * upper
+	= lang_output_section_find (new_output_sec_name);
+      if (upper != NULL)
+	{
+	  move_prefixed_section (s, new_input_sec_name, upper);
+	}
+      else
+	einfo ("%P: error: no section named %s in linker script\n",
+	       new_output_sec_name);
+      break;
+    case REGION_LOWER:
+      new_input_sec_name = concat (".lower", curr_name, NULL);
+      new_output_sec_name = concat (".lower", base_name, NULL);
+      lang_output_section_statement_type * lower
+	= lang_output_section_find (new_output_sec_name);
+      if (lower != NULL)
+	{
+	  move_prefixed_section (s, new_input_sec_name, lower);
+	}
+      else
+	einfo ("%P: error: no section named %s in linker script\n",
+	       new_output_sec_name);
+      break;
+    case REGION_EITHER:
+      s->name = concat (".either", curr_name, NULL);
+      break;
+    default:
+      /* Unreachable.  */
+      FAIL ();
+      break;
+    }
+  free (base_name);
+  if (new_input_sec_name)
+    {
+      free (new_input_sec_name);
+      free (new_output_sec_name);
+    }
+}
+
+static void
+msp430_elf_after_open (void)
+{
+  bfd *abfd;
+
+  gld${EMULATION_NAME}_after_open ();
 
-      amount += scan_children (lower->children.head);
+  /* If neither --code-region or --data-region have been passed, do not
+     transform sections names.  */
+  if ((code_region == REGION_NONE && data_region == REGION_NONE)
+      || disable_sec_transformation)
+    return;
+
+  for (abfd = link_info.input_bfds; abfd != NULL; abfd = abfd->link.next)
+    bfd_map_over_sections (abfd, add_region_prefix, NULL);
+}
+
+#define OPTION_CODE_REGION		321
+#define OPTION_DATA_REGION		(OPTION_CODE_REGION + 1)
+#define OPTION_DISABLE_TRANS		(OPTION_CODE_REGION + 2)
+
+static void
+gld${EMULATION_NAME}_add_options
+  (int ns, char **shortopts, int nl, struct option **longopts,
+   int nrl ATTRIBUTE_UNUSED, struct option **really_longopts ATTRIBUTE_UNUSED)
+{
+  static const char xtra_short[] = { };
 
-      /* Also check forwards for other statements assigned to the same region.  */
-      for (p = lower->next; p != NULL; p = p->next)
-	if (p->region == lower->region)
-	  amount += scan_children (p->children.head);
+  static const struct option xtra_long[] =
+    {
+      { "code-region", required_argument, NULL, OPTION_CODE_REGION },
+      { "data-region", required_argument, NULL, OPTION_DATA_REGION },
+      { "disable-sec-transformation", no_argument, NULL,
+	OPTION_DISABLE_TRANS },
+      { NULL, no_argument, NULL, 0 }
+    };
+
+  *shortopts = (char *) xrealloc (*shortopts, ns + sizeof (xtra_short));
+  memcpy (*shortopts + ns, &xtra_short, sizeof (xtra_short));
+  *longopts = (struct option *)
+    xrealloc (*longopts, nl * sizeof (struct option) + sizeof (xtra_long));
+  memcpy (*longopts + nl, &xtra_long, sizeof (xtra_long));
+}
 
-      /* Scan backwards as well.  */      
-      for (p = lower->prev; p != NULL; p = p->prev)
-	if (p->region == lower->region)
-	  amount += scan_children (p->children.head);
+static void
+gld${EMULATION_NAME}_list_options (FILE * file)
+{
+  fprintf (file, _("\
+  --code-region={either,lower,upper,none}\n\
+  \tTransform .text* sections to {either,lower,upper,none}.text* sections.\n\
+  --data-region={either,lower,upper,none}\n\
+  \tTransform .data*, .rodata* and .bss* sections to\n\
+  {either,lower,upper,none}.{bss,data,rodata}* sections\n\
+  --disable-sec-transformation\n\
+  \tDisable transformation of .{text,data,bss,rodata}* sections to\n\
+  \tadd the {either,lower,upper,none} prefixes\n"));
+}
 
-      if (amount + s->size >= lower->region->length)
-	os = upper;
+static bfd_boolean
+gld${EMULATION_NAME}_handle_option (int optc)
+{
+  switch (optc)
+    {
+    case OPTION_CODE_REGION:
+      if (strcmp (optarg, "upper") == 0)
+	code_region = REGION_UPPER;
+      else if (strcmp (optarg, "lower") == 0)
+	code_region = REGION_LOWER;
+      else if (strcmp (optarg, "either") == 0)
+	code_region = REGION_EITHER;
+      else if (strcmp (optarg, "none") == 0)
+	code_region = REGION_NONE;
+      else if (strlen (optarg) == 0)
+	{
+	  einfo (_("%P: --code-region requires an argument: \
+		   {upper,lower,either,none}\n"));
+	  return FALSE;
+	}
       else
-	os = lower;
+	{
+	  einfo (_("%P: error: unrecognized argument to --code-region= option: \
+		   \"%s\"\n"), optarg);
+	  return FALSE;
+	}
+      break;
+
+    case OPTION_DATA_REGION:
+      if (strcmp (optarg, "upper") == 0)
+	data_region = REGION_UPPER;
+      else if (strcmp (optarg, "lower") == 0)
+	data_region = REGION_LOWER;
+      else if (strcmp (optarg, "either") == 0)
+	data_region = REGION_EITHER;
+      else if (strcmp (optarg, "none") == 0)
+	data_region = REGION_NONE;
+      else if (strlen (optarg) == 0)
+	{
+	  einfo (_("%P: --data-region requires an argument: \
+		   {upper,lower,either,none}\n"));
+	  return FALSE;
+	}
+      else
+	{
+	  einfo (_("%P: error: unrecognized argument to --data-region= option: \
+		   \"%s\"\n"), optarg);
+	  return FALSE;
+	}
+      break;
+
+    case OPTION_DISABLE_TRANS:
+      disable_sec_transformation = TRUE;
+      break;
+
+    default:
+      return FALSE;
+    }
+  return TRUE;
+}
+
+static void
+eval_upper_either_sections (bfd *abfd, asection *s, void *data)
+{
+  char * base_sec_name;
+  const char * curr_name;
+  char * either_name;
+  int curr_region;
+
+  lang_output_section_statement_type * lower;
+  lang_output_section_statement_type * upper;
+  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 ((s->flags & SEC_ALLOC) == 0)
+    return;
+  if (bfd_link_relocatable (&link_info))
+    return;
+
+  base_sec_name = (char *) data;
+  curr_name = bfd_get_section_name (abfd, s);
+
+  /* Only concerned with .either input sections in the upper output section.  */
+  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;
+
+  lower = lang_output_section_find (concat (".lower", base_sec_name, NULL));
+  upper = lang_output_section_find (concat (".upper", base_sec_name, NULL));
+
+  if (upper == NULL || upper->region == NULL)
+    goto end;
+  else if (lower == NULL)
+    lower = lang_output_section_find (base_sec_name);
+  if (lower == NULL || lower->region == NULL)
+    goto end;
+
+  if (strcmp (base_sec_name, ".text") == 0
+      || strcmp (base_sec_name, ".rodata") == 0)
+    curr_region = ROM;
+  else
+    curr_region = RAM;
+
+  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;
     }
 
-  lang_add_section (& os->children, s, NULL, os);
+  /* Move sections in the upper region that would fit in the lower
+     region to the lower region.  */
+  if (*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 (upper_name);
-  free (lower_name);
-  if (buf)
-    free (buf);
-  return os;
+  free (either_name);
 }
-EOF
-fi
 
-fragment <<EOF
+static void
+eval_lower_either_sections (bfd *abfd, asection *s, void *data)
+{
+  char * base_sec_name;
+  const char * curr_name;
+  char * either_name;
+  int curr_region;
+  lang_output_section_statement_type * output_sec;
+  lang_output_section_statement_type * lower;
+  lang_output_section_statement_type * upper;
+
+  static bfd_size_type *lower_size = 0;
+  static bfd_size_type lower_size_rom = 0;
+  static bfd_size_type lower_size_ram = 0;
+
+  if ((s->flags & SEC_ALLOC) == 0)
+    return;
+  if (bfd_link_relocatable (&link_info))
+    return;
+
+  base_sec_name = (char *) data;
+  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.  */
+  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;
+
+  if (strcmp (base_sec_name, ".text") == 0
+      || strcmp (base_sec_name, ".rodata") == 0)
+    curr_region = ROM;
+  else
+    curr_region = RAM;
+
+  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 || output_sec->region == NULL)
+    goto end;
+
+  /* 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.  */
+  lower = lang_output_section_find (concat (".lower", base_sec_name, NULL));
+  upper = lang_output_section_find (concat (".upper", base_sec_name, NULL));
+  if (upper == NULL)
+    goto end;
+
+  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++;
+    }
+}
+
+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;
+      lang_output_section_statement_type * upper;
+
+      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;
+	}
+      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 =
 {
@@ -274,8 +837,8 @@ struct ld_emulation_xfer_struct ld_${EMULATION_NAME}_emulation =
   ${LDEMUL_SYSLIB-syslib_default},
   ${LDEMUL_HLL-hll_default},
   ${LDEMUL_AFTER_PARSE-after_parse_default},
-  ${LDEMUL_AFTER_OPEN-after_open_default},
-  ${LDEMUL_AFTER_ALLOCATION-after_allocation_default},
+  msp430_elf_after_open,
+  msp430_elf_after_allocation,
   ${LDEMUL_SET_OUTPUT_ARCH-set_output_arch_default},
   ${LDEMUL_CHOOSE_TARGET-ldemul_default_target},
   ${LDEMUL_BEFORE_ALLOCATION-before_allocation_default},
@@ -288,10 +851,10 @@ struct ld_emulation_xfer_struct ld_${EMULATION_NAME}_emulation =
   ${LDEMUL_PLACE_ORPHAN-gld${EMULATION_NAME}_place_orphan},
   ${LDEMUL_SET_SYMBOLS-NULL},
   ${LDEMUL_PARSE_ARGS-NULL},
-  ${LDEMUL_ADD_OPTIONS-NULL},
-  ${LDEMUL_HANDLE_OPTION-NULL},
+  gld${EMULATION_NAME}_add_options,
+  gld${EMULATION_NAME}_handle_option,
   ${LDEMUL_UNRECOGNIZED_FILE-NULL},
-  ${LDEMUL_LIST_OPTIONS-NULL},
+  gld${EMULATION_NAME}_list_options,
   ${LDEMUL_RECOGNIZED_FILE-NULL},
   ${LDEMUL_FIND_POTENTIAL_LIBRARIES-NULL},
   ${LDEMUL_NEW_VERS_PATTERN-NULL},
diff --git a/ld/ld.texinfo b/ld/ld.texinfo
index bbfa9fd..ba19cd7 100644
--- a/ld/ld.texinfo
+++ b/ld/ld.texinfo
@@ -7260,6 +7260,29 @@ Denotes a portion of RAM located above @samp{.bss} section.
 The last two sections are used by gcc.
 @end table
 
+@table @option
+@cindex MSP430 Options
+@kindex --code-region
+@item --code-region=[either,lower,upper,none]
+This will transform .text* sections to [either,lower,upper].text* sections. The
+argument passed to GCC for -mcode-region is propagated to the linker
+using this option.
+
+@kindex --data-region
+@item --data-region=[either,lower,upper,none]
+This will transform .data*, .bss* and .rodata* sections to
+[either,lower,upper].[data,bss,rodata]* sections. The argument passed to GCC
+for -mdata-region is propagated to the linker using this option.
+
+@kindex --disable-sec-transformation
+@item --disable-sec-transformation
+Prevent the transformation of sections as specified by the @code{--code-region}
+and @code{--data-region} options.
+This is useful if you are compiling and linking using a single call to the GCC
+wrapper, and want to compile the source files using -m[code,data]-region but
+not transform the sections for prebuilt libraries and objects.
+@end table
+
 @ifclear GENERIC
 @lowersections
 @end ifclear
diff --git a/ld/testsuite/ld-msp430-elf/main-bss-lower.d b/ld/testsuite/ld-msp430-elf/main-bss-lower.d
new file mode 100644
index 0000000..6007420
--- /dev/null
+++ b/ld/testsuite/ld-msp430-elf/main-bss-lower.d
@@ -0,0 +1,3 @@
+#...
+Disassembly of section .lower.bss:
+#pass
diff --git a/ld/testsuite/ld-msp430-elf/main-bss-upper.d b/ld/testsuite/ld-msp430-elf/main-bss-upper.d
new file mode 100644
index 0000000..2f6376a7
--- /dev/null
+++ b/ld/testsuite/ld-msp430-elf/main-bss-upper.d
@@ -0,0 +1,3 @@
+#...
+Disassembly of section .upper.bss:
+#pass
diff --git a/ld/testsuite/ld-msp430-elf/main-const-lower.d b/ld/testsuite/ld-msp430-elf/main-const-lower.d
new file mode 100644
index 0000000..8549961
--- /dev/null
+++ b/ld/testsuite/ld-msp430-elf/main-const-lower.d
@@ -0,0 +1,3 @@
+#...
+Disassembly of section .lower.rodata:
+#pass
diff --git a/ld/testsuite/ld-msp430-elf/main-const-upper.d b/ld/testsuite/ld-msp430-elf/main-const-upper.d
new file mode 100644
index 0000000..c84d649
--- /dev/null
+++ b/ld/testsuite/ld-msp430-elf/main-const-upper.d
@@ -0,0 +1,3 @@
+#...
+Disassembly of section .upper.rodata:
+#pass
diff --git a/ld/testsuite/ld-msp430-elf/main-text-lower.d b/ld/testsuite/ld-msp430-elf/main-text-lower.d
new file mode 100644
index 0000000..446a305
--- /dev/null
+++ b/ld/testsuite/ld-msp430-elf/main-text-lower.d
@@ -0,0 +1,3 @@
+#...
+Disassembly of section .lower.text:
+#pass
diff --git a/ld/testsuite/ld-msp430-elf/main-text-upper.d b/ld/testsuite/ld-msp430-elf/main-text-upper.d
new file mode 100644
index 0000000..f7ae6af
--- /dev/null
+++ b/ld/testsuite/ld-msp430-elf/main-text-upper.d
@@ -0,0 +1,6 @@
+
+.*:     file format.*msp430.*
+
+
+Disassembly of section .upper.text:
+#...
diff --git a/ld/testsuite/ld-msp430-elf/main-var-lower.d b/ld/testsuite/ld-msp430-elf/main-var-lower.d
new file mode 100644
index 0000000..f520cf5
--- /dev/null
+++ b/ld/testsuite/ld-msp430-elf/main-var-lower.d
@@ -0,0 +1,3 @@
+#...
+Disassembly of section .lower.data:
+#pass
diff --git a/ld/testsuite/ld-msp430-elf/main-var-upper.d b/ld/testsuite/ld-msp430-elf/main-var-upper.d
new file mode 100644
index 0000000..fc3d712
--- /dev/null
+++ b/ld/testsuite/ld-msp430-elf/main-var-upper.d
@@ -0,0 +1,3 @@
+#...
+Disassembly of section .upper.data:
+#pass
diff --git a/ld/testsuite/ld-msp430-elf/main-with-data-bss-unique-sec.s b/ld/testsuite/ld-msp430-elf/main-with-data-bss-unique-sec.s
new file mode 100644
index 0000000..7774804
--- /dev/null
+++ b/ld/testsuite/ld-msp430-elf/main-with-data-bss-unique-sec.s
@@ -0,0 +1,78 @@
+	.file	"main-with-data-bss.c"
+	.global	glob_var_array
+	.section	.data.glob_var_array,"aw",@progbits
+	.balign 2
+	.type	glob_var_array, @object
+	.size	glob_var_array, 20
+glob_var_array:
+	.short	0
+	.short	1
+	.short	2
+	.short	3
+	.short	4
+	.short	5
+	.short	6
+	.short	7
+	.short	8
+	.short	9
+	.section	.bss.glob_bss_array,"aw",@nobits
+	.balign 2
+	.type	glob_bss_array, @object
+	.size	glob_bss_array, 20
+glob_bss_array:
+	.zero	20
+	.section	.text.main,"ax",@progbits
+	.balign 2
+	.global	main
+	.type	main, @function
+main:
+; start of function
+; framesize_regs:     0
+; framesize_locals:   2
+; framesize_outgoing: 0
+; framesize:          2
+; elim ap -> fp       2
+; elim fp -> sp       2
+; saved regs:(none)
+	; start of prologue
+	SUB.W	#2, R1
+	; end of prologue
+	MOV.W	#0, @R1
+	BR	#.L2
+.L7:
+	MOV.W	@R1, R12
+	ADD.W	R12, R12
+	ADD.W	#glob_var_array, R12
+	MOV.W	@R12, R13
+	MOV.W	R13, R12
+	ADD.W	R12, R12
+	ADD.W	R13, R12
+	rpt	#2 { rlax.w	R12
+	SUB.W	R13, R12
+	CMP.W	#110, R12 { JNE	.L3
+.L4:
+	BR	#.L4
+.L3:
+	MOV.W	@R1, R12
+	ADD.W	R12, R12
+	ADD.W	#glob_bss_array, R12
+	MOV.W	@R12, R13
+	MOV.W	R13, R12
+	ADD.W	R12, R12
+	ADD.W	R13, R12
+	rpt	#2 { rlax.w	R12
+	SUB.W	R13, R12
+	CMP.W	#110, R12 { JNE	.L5
+.L6:
+	BR	#.L6
+.L5:
+	ADD.W	#1, @R1
+.L2:
+	MOV.B	#9, R12
+	CMP.W	@R1, R12 { JGE	.L7
+	MOV.B	#0, R12
+	; start of epilogue
+	.refsym	__crt0_call_exit
+	ADD.W	#2, R1
+	RET
+	.size	main, .-main
diff --git a/ld/testsuite/ld-msp430-elf/main-with-data-bss.s b/ld/testsuite/ld-msp430-elf/main-with-data-bss.s
new file mode 100644
index 0000000..a406b64
--- /dev/null
+++ b/ld/testsuite/ld-msp430-elf/main-with-data-bss.s
@@ -0,0 +1,74 @@
+	.file	"main-with-data-bss.c"
+	.global	glob_var_array
+.data
+	.balign 2
+	.type	glob_var_array, @object
+	.size	glob_var_array, 20
+glob_var_array:
+	.short	0
+	.short	1
+	.short	2
+	.short	3
+	.short	4
+	.short	5
+	.short	6
+	.short	7
+	.short	8
+	.short	9
+	.local	glob_bss_array
+	.comm	glob_bss_array,20,2
+.text
+	.balign 2
+	.global	main
+	.type	main, @function
+main:
+; start of function
+; framesize_regs:     0
+; framesize_locals:   2
+; framesize_outgoing: 0
+; framesize:          2
+; elim ap -> fp       2
+; elim fp -> sp       2
+; saved regs:(none)
+	; start of prologue
+	SUB.W	#2, R1
+	; end of prologue
+	MOV.W	#0, @R1
+	BR	#.L2
+.L7:
+	MOV.W	@R1, R12
+	ADD.W	R12, R12
+	ADD.W	#glob_var_array, R12
+	MOV.W	@R12, R13
+	MOV.W	R13, R12
+	ADD.W	R12, R12
+	ADD.W	R13, R12
+	rpt	#2 { rlax.w	R12
+	SUB.W	R13, R12
+	CMP.W	#110, R12 { JNE	.L3
+.L4:
+	BR	#.L4
+.L3:
+	MOV.W	@R1, R12
+	ADD.W	R12, R12
+	ADD.W	#glob_bss_array, R12
+	MOV.W	@R12, R13
+	MOV.W	R13, R12
+	ADD.W	R12, R12
+	ADD.W	R13, R12
+	rpt	#2 { rlax.w	R12
+	SUB.W	R13, R12
+	CMP.W	#110, R12 { JNE	.L5
+.L6:
+	BR	#.L6
+.L5:
+	ADD.W	#1, @R1
+.L2:
+	MOV.B	#9, R12
+	CMP.W	@R1, R12 { JGE	.L7
+	MOV.B	#0, R12
+	; start of epilogue
+	.refsym	__crt0_call_exit
+	ADD.W	#2, R1
+	RET
+	.size	main, .-main
diff --git a/ld/testsuite/ld-msp430-elf/main-with-text-rodata-unique-sec.s b/ld/testsuite/ld-msp430-elf/main-with-text-rodata-unique-sec.s
new file mode 100644
index 0000000..398cf74
--- /dev/null
+++ b/ld/testsuite/ld-msp430-elf/main-with-text-rodata-unique-sec.s
@@ -0,0 +1,59 @@
+	.file	"main-with-text-rodata.c"
+	.global	glob_const_array
+	.section	.rodata.glob_const_array,"a",@progbits
+	.balign 2
+	.type	glob_const_array, @object
+	.size	glob_const_array, 20
+glob_const_array:
+	.short	0
+	.short	1
+	.short	2
+	.short	3
+	.short	4
+	.short	5
+	.short	6
+	.short	7
+	.short	8
+	.short	9
+	.section	.text.main,"ax",@progbits
+	.balign 2
+	.global	main
+	.type	main, @function
+main:
+; start of function
+; framesize_regs:     0
+; framesize_locals:   2
+; framesize_outgoing: 0
+; framesize:          2
+; elim ap -> fp       2
+; elim fp -> sp       2
+; saved regs:(none)
+	; start of prologue
+	SUB.W	#2, R1
+	; end of prologue
+	MOV.W	#0, @R1
+	BR	#.L2
+.L5:
+	MOV.W	@R1, R12
+	ADD.W	R12, R12
+	ADD.W	#glob_const_array, R12
+	MOV.W	@R12, R13
+	MOV.W	R13, R12
+	ADD.W	R12, R12
+	ADD.W	R13, R12
+	rpt	#2 { rlax.w	R12
+	SUB.W	R13, R12
+	CMP.W	#110, R12 { JNE	.L3
+.L4:
+	BR	#.L4
+.L3:
+	ADD.W	#1, @R1
+.L2:
+	MOV.B	#9, R12
+	CMP.W	@R1, R12 { JGE	.L5
+	MOV.B	#0, R12
+	; start of epilogue
+	.refsym	__crt0_call_exit
+	ADD.W	#2, R1
+	RET
+	.size	main, .-main
diff --git a/ld/testsuite/ld-msp430-elf/main-with-text-rodata.s b/ld/testsuite/ld-msp430-elf/main-with-text-rodata.s
new file mode 100644
index 0000000..225b5d4
--- /dev/null
+++ b/ld/testsuite/ld-msp430-elf/main-with-text-rodata.s
@@ -0,0 +1,59 @@
+	.file	"main-with-text-rodata.c"
+	.global	glob_const_array
+	.section	.rodata
+	.balign 2
+	.type	glob_const_array, @object
+	.size	glob_const_array, 20
+glob_const_array:
+	.short	0
+	.short	1
+	.short	2
+	.short	3
+	.short	4
+	.short	5
+	.short	6
+	.short	7
+	.short	8
+	.short	9
+.text
+	.balign 2
+	.global	main
+	.type	main, @function
+main:
+; start of function
+; framesize_regs:     0
+; framesize_locals:   2
+; framesize_outgoing: 0
+; framesize:          2
+; elim ap -> fp       2
+; elim fp -> sp       2
+; saved regs:(none)
+	; start of prologue
+	SUB.W	#2, R1
+	; end of prologue
+	MOV.W	#0, @R1
+	BR	#.L2
+.L5:
+	MOV.W	@R1, R12
+	ADD.W	R12, R12
+	ADD.W	#glob_const_array, R12
+	MOV.W	@R12, R13
+	MOV.W	R13, R12
+	ADD.W	R12, R12
+	ADD.W	R13, R12
+	rpt	#2 { rlax.w	R12
+	SUB.W	R13, R12
+	CMP.W	#110, R12 { JNE	.L3
+.L4:
+	BR	#.L4
+.L3:
+	ADD.W	#1, @R1
+.L2:
+	MOV.B	#9, R12
+	CMP.W	@R1, R12 { JGE	.L5
+	MOV.B	#0, R12
+	; start of epilogue
+	.refsym	__crt0_call_exit
+	ADD.W	#2, R1
+	RET
+	.size	main, .-main
diff --git a/ld/testsuite/ld-msp430-elf/msp430-elf.exp b/ld/testsuite/ld-msp430-elf/msp430-elf.exp
new file mode 100644
index 0000000..fa396aa
--- /dev/null
+++ b/ld/testsuite/ld-msp430-elf/msp430-elf.exp
@@ -0,0 +1,141 @@
+# Expect script for various MSP430 ELF tests.
+#   Copyright (C) 2002-2015 Free Software Foundation, Inc.
+#
+# This file is part of the GNU Binutils.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+# MA 02110-1301, USA.
+#
+
+if { ![istarget "msp430*elf*"] } {
+    return
+}
+
+# List contains test-items with 3 items followed by 2 lists and one more item:
+# 0:name 1:ld early options 2:ld late options 3:assembler options
+# 4:filenames of assembler files 5: action and options. 6: name of output file
+
+# Actions:
+# objdump: Apply objdump options on result.  Compare with regex (last arg).
+# nm: Apply nm options on result.  Compare with regex (last arg).
+# readelf: Apply readelf options on result.  Compare with regex (last arg).
+
+set msp430regionprefixtests {
+  {"Move main() to .upper.text" "-T msp430.ld --code-region=upper"
+    "" "" {main-with-text-rodata.s} {{objdump -d main-text-upper.d}} "main-upper"}
+  {"Move main() to .upper.text. No .lower.text in ld script" "-T msp430-no-lower.ld --code-region=upper"
+    "" "" {main-with-text-rodata.s} {{objdump -d main-text-upper.d}} "main-upper"}
+  {"Move main() to .lower.text" "-T msp430.ld --code-region=lower"
+    "" "" {main-with-text-rodata.s} {{objdump -d main-text-lower.d}} "main-lower"}
+  {"Move \"either\" main() to .lower.text" "-T msp430.ld --code-region=either"
+    "" "" {main-with-text-rodata.s} {{objdump -d main-text-lower.d}} "main-either"}
+
+  {"Move glob_var to .upper.data" "-T msp430.ld --data-region=upper"
+    "" "" {main-with-data-bss.s} {{objdump -D main-var-upper.d}} "main-var-upper"}
+  {"Move glob_var to .upper.data. No .lower.data in ld script" "-T msp430-no-lower.ld --data-region=upper"
+    "" "" {main-with-data-bss.s} {{objdump -D main-var-upper.d}} "main-var-upper"}
+  {"Move glob_var to .lower.data" "-T msp430.ld --data-region=lower"
+    "" "" {main-with-data-bss.s} {{objdump -D main-var-lower.d}} "main-var-lower"}
+  {"Move \"either\" glob_var to .lower.data" "-T msp430.ld --data-region=lower"
+    "" "" {main-with-data-bss.s} {{objdump -D main-var-lower.d}} "main-var-lower"}
+
+  {"Move glob_zero to .upper.bss" "-T msp430.ld --data-region=upper"
+    "" "" {main-with-data-bss.s} {{objdump -D main-bss-upper.d}} "main-bss-upper"}
+  {"Move glob_zero to .upper.bss. No .lower.bss in ld script." "-T msp430-no-lower.ld --data-region=upper"
+    "" "" {main-with-data-bss.s} {{objdump -D main-bss-upper.d}} "main-bss-upper"}
+  {"Move glob_zero to .lower.bss" "-T msp430.ld --data-region=lower"
+    "" "" {main-with-data-bss.s} {{objdump -D main-bss-lower.d}} "main-bss-lower"}
+  {"Move \"either\" glob_zero to .lower.bss" "-T msp430.ld --data-region=lower"
+    "" "" {main-with-data-bss.s} {{objdump -D main-bss-lower.d}} "main-bss-lower"}
+
+  {"Move glob_const to .upper.rodata" "-T msp430.ld --data-region=upper"
+    "" "" {main-with-text-rodata.s} {{objdump -D main-const-upper.d}} "main-const-upper"}
+  {"Move glob_const to .upper.rodata. No .lower.rodata in ld script." "-T msp430-no-lower.ld --data-region=upper"
+    "" "" {main-with-text-rodata.s} {{objdump -D main-const-upper.d}} "main-const-upper"}
+  {"Move glob_const to .lower.rodata" "-T msp430.ld --data-region=lower"
+    "" "" {main-with-text-rodata.s} {{objdump -D main-const-lower.d}} "main-const-lower"}
+  {"Move \"either\" glob_const to .lower.rodata" "-T msp430.ld --data-region=lower"
+    "" "" {main-with-text-rodata.s} {{objdump -D main-const-lower.d}} "main-const-lower"}
+}
+
+set msp430regionprefixuniquesectiontests {
+  {"Move main() to .upper.text, with -ffunction/data-sections" "-T msp430.ld --code-region=upper"
+    "" "" {main-with-text-rodata-unique-sec.s} {{objdump -d main-text-upper.d}} "main-upper"}
+  {"Move main() to .upper.text. No .lower.text in ld script, with -ffunction/data-sections" "-T msp430-no-lower.ld --code-region=upper"
+    "" "" {main-with-text-rodata-unique-sec.s} {{objdump -d main-text-upper.d}} "main-upper"}
+  {"Move main() to .lower.text, with -ffunction/data-sections" "-T msp430.ld --code-region=lower"
+    "" "" {main-with-text-rodata-unique-sec.s} {{objdump -d main-text-lower.d}} "main-lower"}
+  {"Move \"either\" main() to .lower.text, with -ffunction/data-sections" "-T msp430.ld --code-region=either"
+    "" "" {main-with-text-rodata-unique-sec.s} {{objdump -d main-text-lower.d}} "main-either"}
+
+  {"Move glob_var to .upper.data, with -ffunction/data-sections" "-T msp430.ld --data-region=upper"
+    "" "" {main-with-data-bss-unique-sec.s} {{objdump -D main-var-upper.d}} "main-var-upper"}
+  {"Move glob_var to .upper.data. No .lower.data in ld script, with -ffunction/data-sections" "-T msp430-no-lower.ld --data-region=upper"
+    "" "" {main-with-data-bss-unique-sec.s} {{objdump -D main-var-upper.d}} "main-var-upper"}
+  {"Move glob_var to .lower.data, with -ffunction/data-sections" "-T msp430.ld --data-region=lower"
+    "" "" {main-with-data-bss-unique-sec.s} {{objdump -D main-var-lower.d}} "main-var-lower"}
+  {"Move \"either\" glob_var to .lower.data, with -ffunction/data-sections" "-T msp430.ld --data-region=lower"
+    "" "" {main-with-data-bss-unique-sec.s} {{objdump -D main-var-lower.d}} "main-var-lower"}
+
+  {"Move glob_zero to .upper.bss, with -ffunction/data-sections" "-T msp430.ld --data-region=upper"
+    "" "" {main-with-data-bss-unique-sec.s} {{objdump -D main-bss-upper.d}} "main-bss-upper"}
+  {"Move glob_zero to .upper.bss. No .lower.bss in ld script., with -ffunction/data-sections" "-T msp430-no-lower.ld --data-region=upper"
+    "" "" {main-with-data-bss-unique-sec.s} {{objdump -D main-bss-upper.d}} "main-bss-upper"}
+  {"Move glob_zero to .lower.bss, with -ffunction/data-sections" "-T msp430.ld --data-region=lower"
+    "" "" {main-with-data-bss-unique-sec.s} {{objdump -D main-bss-lower.d}} "main-bss-lower"}
+  {"Move \"either\" glob_zero to .lower.bss, with -ffunction/data-sections" "-T msp430.ld --data-region=lower"
+    "" "" {main-with-data-bss-unique-sec.s} {{objdump -D main-bss-lower.d}} "main-bss-lower"}
+
+  {"Move glob_const to .upper.rodata, with -ffunction/data-sections" "-T msp430.ld --data-region=upper"
+    "" "" {main-with-text-rodata-unique-sec.s} {{objdump -D main-const-upper.d}} "main-const-upper"}
+  {"Move glob_const to .upper.rodata. No .lower.rodata in ld script., with -ffunction/data-sections" "-T msp430-no-lower.ld --data-region=upper"
+    "" "" {main-with-text-rodata-unique-sec.s} {{objdump -D main-const-upper.d}} "main-const-upper"}
+  {"Move glob_const to .lower.rodata, with -ffunction/data-sections" "-T msp430.ld --data-region=lower"
+    "" "" {main-with-text-rodata-unique-sec.s} {{objdump -D main-const-lower.d}} "main-const-lower"}
+  {"Move \"either\" glob_const to .lower.rodata, with -ffunction/data-sections" "-T msp430.ld --data-region=lower"
+    "" "" {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-no-lower.ld b/ld/testsuite/ld-msp430-elf/msp430-no-lower.ld
new file mode 100644
index 0000000..f9a2847
--- /dev/null
+++ b/ld/testsuite/ld-msp430-elf/msp430-no-lower.ld
@@ -0,0 +1,54 @@
+/* Script for ld testsuite */
+OUTPUT_ARCH(msp430)
+ENTRY(_start)
+
+SECTIONS
+{
+  .text :
+  {
+    PROVIDE (_start = .);
+    . = ALIGN(2);
+    *(.text .stub .text.* .gnu.linkonce.t.* .text:*)
+  }
+
+  .rodata :
+  {
+    *(.rodata.* .rodata)
+  }
+
+  .data :
+  {
+    . = ALIGN(2);
+    *(.data.* .data)
+  }
+
+  .bss :
+  {
+    . = ALIGN(2);
+    *(.bss.* .bss)
+  }
+
+  .upper.text :
+  {
+    . = ALIGN(2);
+    *(.upper.text.* .upper.text)
+  }
+
+  .upper.rodata :
+  {
+    . = ALIGN(2);
+    *(.upper.rodata .upper.rodata.*)
+  }
+
+  .upper.data :
+  {
+    . = ALIGN(2);
+    *(.upper.data .upper.data.*)
+  }
+
+  .upper.bss :
+  {
+    . = ALIGN(2);
+    *(.upper.bss .upper.bss.*)
+  }
+}
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
+}
diff --git a/ld/testsuite/ld-msp430-elf/msp430.ld b/ld/testsuite/ld-msp430-elf/msp430.ld
new file mode 100644
index 0000000..9c30836
--- /dev/null
+++ b/ld/testsuite/ld-msp430-elf/msp430.ld
@@ -0,0 +1,78 @@
+/* Script for ld testsuite */
+OUTPUT_ARCH(msp430)
+ENTRY(_start)
+
+SECTIONS
+{
+  .lower.data :
+  {
+    . = ALIGN(2);
+    *(.lower.data .lower.data.*)
+  }
+
+  .lower.bss :
+  {
+    . = ALIGN(2);
+    *(.lower.bss .lower.bss.*)
+  }
+
+  .lower.text :
+  {
+    PROVIDE (_start = .);
+    . = ALIGN(2);
+    *(.lower.text.* .lower.text)
+  }
+
+  .lower.rodata :
+  {
+    . = ALIGN(2);
+    *(.lower.rodata .lower.rodata.*)
+  }
+
+  .text :
+  {
+    . = ALIGN(2);
+    *(.text .stub .text.* .gnu.linkonce.t.* .text:*)
+  }
+
+  .rodata :
+  {
+    *(.rodata.* .rodata)
+  }
+
+  .data :
+  {
+    . = ALIGN(2);
+    *(.data.* .data)
+  }
+
+  .bss :
+  {
+    . = ALIGN(2);
+    *(.bss.* .bss)
+  }
+
+  .upper.text :
+  {
+    . = ALIGN(2);
+    *(.upper.text.* .upper.text)
+  }
+
+  .upper.rodata :
+  {
+    . = ALIGN(2);
+    *(.upper.rodata .upper.rodata.*)
+  }
+
+  .upper.data :
+  {
+    . = ALIGN(2);
+    *(.upper.data .upper.data.*)
+  }
+
+  .upper.bss :
+  {
+    . = ALIGN(2);
+    *(.upper.bss .upper.bss.*)
+  }
+}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]