#include "bfdlink.h"
#include "ld.h"
+#include "getopt.h"
#include "ldmain.h"
#include "ldmisc.h"
#include "ldexp.h"
#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
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. */
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;
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;
/* 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 =
{
${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},
${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},
--- /dev/null
+# 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