]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - ld/emultempl/msp430.em
Update year range in copyright notice of binutils files
[thirdparty/binutils-gdb.git] / ld / emultempl / msp430.em
index 0eff3f063fc4b0194e114d10e75da2461a2848ac..e3ea3c69a909b48b8b0bcbd76196da3e37b0b284 100644 (file)
@@ -4,10 +4,10 @@ fragment <<EOF
 /* This file is is generated by a shell script.  DO NOT EDIT! */
 
 /* Emulate the original gld for the given ${EMULATION_NAME}
-   Copyright (C) 2014-2015 Free Software Foundation, Inc.
+   Copyright (C) 2014-2021 Free Software Foundation, Inc.
    Written by Steve Chamberlain steve@cygnus.com
    Extended for the MSP430 by Nick Clifton  nickc@redhat.com
-   
+
    This file is part of the GNU Binutils.
 
    This program is free software; you can redistribute it and/or modify
@@ -30,8 +30,10 @@ fragment <<EOF
 #include "sysdep.h"
 #include "bfd.h"
 #include "bfdlink.h"
+#include "ctf-api.h"
 
 #include "ld.h"
+#include "getopt.h"
 #include "ldmain.h"
 #include "ldmisc.h"
 #include "ldexp.h"
@@ -39,6 +41,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
 
@@ -60,7 +85,7 @@ gld${EMULATION_NAME}_before_parse (void)
 
   /* The MSP430 port *needs* linker relaxtion in order to cope with large
      functions where conditional branches do not fit into a +/- 1024 byte range.  */
-  if (! link_info.relocatable)
+  if (!bfd_link_relocatable (&link_info))
     TARGET_ENABLE_RELAXATION;
 }
 
@@ -85,11 +110,11 @@ fragment <<EOF
 {
   *isfile = 0;
 
-  if (link_info.relocatable && config.build_constructors)
+  if (bfd_link_relocatable (&link_info) && config.build_constructors)
     return
 EOF
 sed $sc ldscripts/${EMULATION_NAME}.xu                 >> e${EMULATION_NAME}.c
-echo '  ; else if (link_info.relocatable) return'      >> e${EMULATION_NAME}.c
+echo '  ; else if (bfd_link_relocatable (&link_info)) return' >> e${EMULATION_NAME}.c
 sed $sc ldscripts/${EMULATION_NAME}.xr                 >> e${EMULATION_NAME}.c
 echo '  ; else if (!config.text_read_only) return'     >> e${EMULATION_NAME}.c
 sed $sc ldscripts/${EMULATION_NAME}.xbn                >> e${EMULATION_NAME}.c
@@ -106,9 +131,9 @@ fragment <<EOF
 {
   *isfile = 1;
 
-  if (link_info.relocatable && config.build_constructors)
+  if (bfd_link_relocatable (&link_info) && config.build_constructors)
     return "ldscripts/${EMULATION_NAME}.xu";
-  else if (link_info.relocatable)
+  else if (bfd_link_relocatable (&link_info))
     return "ldscripts/${EMULATION_NAME}.xr";
   else if (!config.text_read_only)
     return "ldscripts/${EMULATION_NAME}.xbn";
@@ -124,6 +149,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,10 +194,15 @@ 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);      
+         amount += scan_children (l->wild_statement.children.head);
+         break;
+
+       case lang_data_statement_enum:
+         amount += data_statement_size (&l->data_statement);
          break;
 
        default:
@@ -159,7 +215,41 @@ scan_children (lang_statement_union_type * l)
 
   return amount;
 }
-  
+
+#define WARN_UPPER 0
+#define WARN_LOWER 1
+#define WARN_TEXT 0
+#define WARN_DATA 1
+#define WARN_BSS 2
+#define WARN_RODATA 3
+
+/* Warn only once per output section.
+ * NAME starts with ".upper." or ".lower.".  */
+static void
+warn_no_output_section (const char *name)
+{
+  static bfd_boolean warned[2][4] = {{FALSE, FALSE, FALSE, FALSE},
+                                    {FALSE, FALSE, FALSE, FALSE}};
+  int i = WARN_LOWER;
+
+  if (strncmp (name, ".upper.", 7) == 0)
+    i = WARN_UPPER;
+
+  if (!warned[i][WARN_TEXT] && strcmp (name + 6, ".text") == 0)
+    warned[i][WARN_TEXT] = TRUE;
+  else if (!warned[i][WARN_DATA] && strcmp (name + 6, ".data") == 0)
+    warned[i][WARN_DATA] = TRUE;
+  else if (!warned[i][WARN_BSS] && strcmp (name + 6, ".bss") == 0)
+    warned[i][WARN_BSS] = TRUE;
+  else if (!warned[i][WARN_RODATA] && strcmp (name + 6, ".rodata") == 0)
+    warned[i][WARN_RODATA] = TRUE;
+  else
+    return;
+  einfo ("%P: warning: no input section rule matches %s in linker script\n",
+        name);
+}
+
+
 /* Place an orphan section.  We use this to put .either sections
    into either their lower or their upper equivalents.  */
 
@@ -173,18 +263,24 @@ gld${EMULATION_NAME}_place_orphan (asection * s,
   char * name;
   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;
 
-  if (link_info.relocatable)
+  if (bfd_link_relocatable (&link_info))
     return NULL;
 
   /* If constraints are involved let the linker handle the placement normally.  */
   if (constraint != 0)
     return NULL;
 
+  if (strncmp (secname, ".upper.", 7) == 0
+      || strncmp (secname, ".lower.", 7) == 0)
+    {
+      warn_no_output_section (secname);
+      return NULL;
+    }
+
   /* We only need special handling for .either sections.  */
   if (strncmp (secname, ".either.", 8) != 0)
     return NULL;
@@ -197,70 +293,617 @@ gld${EMULATION_NAME}_place_orphan (asection * s,
      only use the part of the name before the second dot.  */
   if (strchr (secname + 1, '.') != NULL)
     {
-      name = ACONCAT ((secname, NULL));
+      name = xstrdup (secname);
 
       * strchr (name + 1, '.') = 0;
     }
   else
     name = (char *) secname;
-  
-  lower_name = ACONCAT ((".lower", name, NULL));
-  upper_name = ACONCAT ((".upper", name, NULL));
+
+  lower_name = concat (".lower", name, NULL);
+  upper_name = concat (".upper", name, 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);
-         return NULL;
+      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);
+  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_os,
+                      lang_output_section_statement_type *old_os)
+{
+  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)
+           {
+             lang_statement_list_type *old_list
+               = (lang_statement_list_type *) &old_os->children;
+             s->output_section = NULL;
+             lang_add_section (&new_os->children, s, NULL, new_os);
+
+             /* Remove the section from the old output section.  */
+             if (prev == NULL)
+               *head = curr->header.next;
+             else
+               prev->header.next = curr->header.next;
+             /* If the input section we just moved is the tail of the old
+                output section, then we also need to adjust that tail.  */
+             if (old_list->tail == (lang_statement_union_type **) curr)
+               old_list->tail = (lang_statement_union_type **) prev;
+
+             return TRUE;
+           }
+         break;
+       case lang_wild_statement_enum:
+         if (change_output_section (&(curr->wild_statement.children.head),
+                                    s, new_os, old_os))
+           return TRUE;
+         break;
+       default:
+         break;
+       }
+      prev = curr;
+      curr = curr->header.next;
+    }
+  return FALSE;
+}
+
+static void
+add_region_prefix (bfd *abfd ATTRIBUTE_UNUSED, asection *s,
+                  void *unused ATTRIBUTE_UNUSED)
+{
+  const char *curr_name = bfd_section_name (s);
+  int region = REGION_NONE;
+
+  if (strncmp (curr_name, ".text", 5) == 0)
+    region = code_region;
+  else if (strncmp (curr_name, ".data", 5) == 0)
+    region = data_region;
+  else if (strncmp (curr_name, ".bss", 4) == 0)
+    region = data_region;
+  else if (strncmp (curr_name, ".rodata", 7) == 0)
+    region = data_region;
   else
+    return;
+
+  switch (region)
     {
-      bfd_size_type amount = 0;
-      struct lang_output_section_statement_struct * p;
+    case REGION_NONE:
+      break;
+    case REGION_UPPER:
+      bfd_rename_section (s, concat (".upper", curr_name, NULL));
+      break;
+    case REGION_LOWER:
+      bfd_rename_section (s, concat (".lower", curr_name, NULL));
+      break;
+    case REGION_EITHER:
+      s->name = concat (".either", curr_name, NULL);
+      break;
+    default:
+      /* Unreachable.  */
+      FAIL ();
+      break;
+    }
+}
+
+static void
+msp430_elf_after_open (void)
+{
+  bfd *abfd;
 
-      amount += scan_children (lower->children.head);
+  gld${EMULATION_NAME}_after_open ();
 
-      /* 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);
+  /* 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;
 
-      /* Scan backwards as well.  */      
-      for (p = lower->prev; p != NULL; p = p->prev)
-       if (p->region == lower->region)
-         amount += scan_children (p->children.head);
+  for (abfd = link_info.input_bfds; abfd != NULL; abfd = abfd->link.next)
+    bfd_map_over_sections (abfd, add_region_prefix, NULL);
+}
 
-      if (amount + s->size >= lower->region->length)
-       os = upper;
+#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[] = { };
+
+  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));
+}
+
+static void
+gld${EMULATION_NAME}_list_options (FILE * file)
+{
+  fprintf (file, _("  --code-region={either,lower,upper,none}\n\
+        Transform .text* sections to {either,lower,upper,none}.text* sections\n"));
+  fprintf (file, _("  --data-region={either,lower,upper,none}\n\
+        Transform .data*, .rodata* and .bss* sections to\n\
+        {either,lower,upper,none}.{bss,data,rodata}* sections\n"));
+  fprintf (file, _("  --disable-sec-transformation\n\
+        Disable transformation of .{text,data,bss,rodata}* sections to\n\
+        add the {either,lower,upper,none} prefixes\n"));
+}
+
+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
+       {
+         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
-       os = lower;
+       {
+         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 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 * 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 = (const char *) data;
+  curr_name = bfd_section_name (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);
-  return os;
+  /* If the upper region is overflowing, try moving sections to the lower
+     region.
+     Note that there isn't any general benefit to using lower memory over upper
+     memory, so we only move sections around with the goal of making the program
+     fit.  */
+  if (*upper_size > upper->region->length
+      && *lower_size + s->size < lower->region->length)
+    {
+      if (change_output_section (&(upper->children.head), s, lower, upper))
+       {
+         *upper_size -= s->size;
+         *lower_size += s->size;
+       }
+    }
+ end:
+  free (either_name);
 }
-EOF
-fi
 
-fragment <<EOF
+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, output_sec);
+  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 ();
+}
+
+/* Return TRUE if a non-debug input section in L has positive size and matches
+   the given name.  */
+static int
+input_section_exists (lang_statement_union_type * l, const char * name)
+{
+  while (l != NULL)
+    {
+      switch (l->header.type)
+       {
+       case lang_input_section_enum:
+         if ((l->input_section.section->flags & SEC_ALLOC)
+             && l->input_section.section->size > 0
+             && !strcmp (l->input_section.section->name, name))
+           return TRUE;
+         break;
+
+       case lang_wild_statement_enum:
+         if (input_section_exists (l->wild_statement.children.head, name))
+           return TRUE;
+         break;
+
+       default:
+         break;
+       }
+      l = l->header.next;
+    }
+  return FALSE;
+}
+
+/* Some MSP430 linker scripts do not include ALIGN directives to ensure
+   __preinit_array_start, __init_array_start or __fini_array_start are word
+   aligned.
+   If __*_array_start symbols are not word aligned, the code in crt0 to run
+   through the array and call the functions will crash.
+   To avoid warning unnecessarily when the .*_array sections are not being
+   used for running constructors/destructors, only emit the warning if
+   the associated section exists and has size.  */
+static void
+check_array_section_alignment (void)
+{
+  int i;
+  lang_output_section_statement_type * rodata_sec;
+  lang_output_section_statement_type * rodata2_sec;
+  const char * array_names[3][2] = { { ".init_array", "__init_array_start" },
+       { ".preinit_array", "__preinit_array_start" },
+       { ".fini_array", "__fini_array_start" } };
+
+  /* .{preinit,init,fini}_array could be in either .rodata or .rodata2.  */
+  rodata_sec = lang_output_section_find (".rodata");
+  rodata2_sec = lang_output_section_find (".rodata2");
+  if (rodata_sec == NULL && rodata2_sec == NULL)
+    return;
+
+  /* There are 3 .*_array sections which must be checked for alignment.  */
+  for (i = 0; i < 3; i++)
+    {
+      struct bfd_link_hash_entry * sym;
+      if (((rodata_sec && input_section_exists (rodata_sec->children.head,
+                                               array_names[i][0]))
+          || (rodata2_sec && input_section_exists (rodata2_sec->children.head,
+                                                   array_names[i][0])))
+         && (sym = bfd_link_hash_lookup (link_info.hash, array_names[i][1],
+                                         FALSE, FALSE, TRUE))
+         && sym->type == bfd_link_hash_defined
+         && sym->u.def.value % 2)
+       {
+         einfo ("%P: warning: \"%s\" symbol (%pU) is not word aligned\n",
+                array_names[i][1], NULL);
+       }
+    }
+}
+
+static void
+gld${EMULATION_NAME}_finish (void)
+{
+  finish_default ();
+  check_array_section_alignment ();
+}
 
 struct ld_emulation_xfer_struct ld_${EMULATION_NAME}_emulation =
 {
@@ -268,28 +911,34 @@ 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,
+  after_check_relocs_default,
+  before_place_orphans_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},
   ${LDEMUL_GET_SCRIPT-gld${EMULATION_NAME}_get_script},
   "${EMULATION_NAME}",
   "${OUTPUT_FORMAT}",
-  ${LDEMUL_FINISH-finish_default},
+  gld${EMULATION_NAME}_finish,
   ${LDEMUL_CREATE_OUTPUT_SECTION_STATEMENTS-NULL},
   ${LDEMUL_OPEN_DYNAMIC_ARCHIVE-NULL},
   ${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},
-  ${LDEMUL_EXTRA_MAP_FILE_TEXT-NULL}
+  ${LDEMUL_EXTRA_MAP_FILE_TEXT-NULL},
+  ${LDEMUL_EMIT_CTF_EARLY-NULL},
+  ${LDEMUL_ACQUIRE_STRINGS_FOR_CTF-NULL},
+  ${LDEMUL_NEW_DYNSYM_FOR_CTF-NULL},
+  ${LDEMUL_PRINT_SYMBOL-NULL}
 };
 EOF
 # \f