+static void
+eval_lower_either_sections (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *s, void *data)
+{
+ const 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 = (const char *) data;
+ curr_name = bfd_section_name (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;
+ unsigned 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;
+ const char * base_sec_name;
+ lang_output_section_statement_type * upper;
+
+ switch (i % 4)
+ {
+ default:
+ case 0:
+ base_sec_name = ".text";
+ break;
+ case 1:
+ base_sec_name = ".data";
+ break;
+ case 2:
+ base_sec_name = ".bss";
+ break;
+ case 3:
+ base_sec_name = ".rodata";
+ 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,
+ (void *) 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,
+ (void *) base_sec_name);
+ }
+ }
+
+ }
+ }
+ gld${EMULATION_NAME}_after_allocation ();
+}