* elf32-ppc.c (ppc_elf_info_to_howto): Check for invalid relocation
[deliverable/binutils-gdb.git] / bfd / elflink.c
index 63cac8e052ff1d1619ee018541860008faf5f5c6..dfdbade6353c337bf2459d07f52550cfd6ca7acc 100644 (file)
@@ -1,6 +1,6 @@
 /* ELF linking support for BFD.
    Copyright 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
-   2005, 2006 Free Software Foundation, Inc.
+   2005, 2006, 2007 Free Software Foundation, Inc.
 
    This file is part of BFD, the Binary File Descriptor library.
 
@@ -18,8 +18,8 @@
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
 
-#include "bfd.h"
 #include "sysdep.h"
+#include "bfd.h"
 #include "bfdlink.h"
 #include "libbfd.h"
 #define ARCH_SIZE 0
@@ -185,16 +185,6 @@ _bfd_elf_link_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
        return FALSE;
     }
 
-  if (! info->traditional_format)
-    {
-      s = bfd_make_section_with_flags (abfd, ".eh_frame_hdr",
-                                      flags | SEC_READONLY);
-      if (s == NULL
-         || ! bfd_set_section_alignment (abfd, s, 2))
-       return FALSE;
-      elf_hash_table (info)->eh_info.hdr_sec = s;
-    }
-
   /* Create sections to hold version informations.  These are removed
      if they are not needed.  */
   s = bfd_make_section_with_flags (abfd, ".gnu.version_d",
@@ -448,14 +438,22 @@ bfd_elf_link_record_dynamic_symbol (struct bfd_link_info *info,
 
 void
 bfd_elf_link_mark_dynamic_symbol (struct bfd_link_info *info,
-                                 struct elf_link_hash_entry *h)
+                                 struct elf_link_hash_entry *h,
+                                 Elf_Internal_Sym *sym)
 {
-  struct bfd_elf_dynamic_list *d = info->dynamic;
+  struct bfd_elf_dynamic_list *d = info->dynamic_list;
 
-  if (d == NULL || info->relocatable)
+  /* It may be called more than once on the same H.  */
+  if(h->dynamic || info->relocatable)
     return;
 
-  if ((*d->match) (&d->head, NULL, h->root.root.string))
+  if ((info->dynamic_data
+       && (h->type == STT_OBJECT
+          || (sym != NULL
+              && ELF_ST_TYPE (sym->st_info) == STT_OBJECT)))
+      || (d != NULL 
+         && h->root.type == bfd_link_hash_new
+         && (*d->match) (&d->head, NULL, h->root.root.string)))
     h->dynamic = 1;
 }
 
@@ -493,7 +491,7 @@ bfd_elf_record_link_assignment (bfd *output_bfd,
 
   if (h->root.type == bfd_link_hash_new)
     {
-      bfd_elf_link_mark_dynamic_symbol (info, h);
+      bfd_elf_link_mark_dynamic_symbol (info, h, NULL);
       h->non_elf = 0;
     }
 
@@ -871,13 +869,17 @@ _bfd_elf_merge_symbol (bfd *abfd,
         || h->root.type == bfd_link_hash_warning)
     h = (struct elf_link_hash_entry *) h->root.u.i.link;
 
+  /* We have to check it for every instance since the first few may be
+     refereences and not all compilers emit symbol type for undefined
+     symbols.  */
+  bfd_elf_link_mark_dynamic_symbol (info, h, sym);
+
   /* If we just created the symbol, mark it as being an ELF symbol.
      Other than that, there is nothing to do--there is no merge issue
      with a newly defined symbol--so we just return.  */
 
   if (h->root.type == bfd_link_hash_new)
     {
-      bfd_elf_link_mark_dynamic_symbol (info, h);
       h->non_elf = 0;
       return TRUE;
     }
@@ -945,6 +947,7 @@ _bfd_elf_merge_symbol (bfd *abfd,
            && h->root.type != bfd_link_hash_undefweak
            && h->root.type != bfd_link_hash_common);
 
+  bed = get_elf_backend_data (abfd);
   /* When we try to create a default indirect symbol from the dynamic
      definition with the default version, we skip it if its type and
      the type of existing regular definition mismatch.  We only do it
@@ -959,7 +962,9 @@ _bfd_elf_merge_symbol (bfd *abfd,
       && (olddef || h->root.type == bfd_link_hash_common)
       && ELF_ST_TYPE (sym->st_info) != h->type
       && ELF_ST_TYPE (sym->st_info) != STT_NOTYPE
-      && h->type != STT_NOTYPE)
+      && h->type != STT_NOTYPE
+      && !(bed->is_function_type (ELF_ST_TYPE (sym->st_info))
+          && bed->is_function_type (h->type)))
     {
       *skip = TRUE;
       return TRUE;
@@ -1150,6 +1155,11 @@ _bfd_elf_merge_symbol (bfd *abfd,
   if (olddef && newdyn)
     oldweak = FALSE;
 
+  /* Allow changes between different types of funciton symbol.  */
+  if (bed->is_function_type (ELF_ST_TYPE (sym->st_info))
+      && bed->is_function_type (h->type))
+    *type_change_ok = TRUE;
+
   /* It's OK to change the type if either the existing symbol or the
      new symbol is weak.  A type change is also OK if the old symbol
      is undefined and the new symbol is defined.  */
@@ -1196,7 +1206,7 @@ _bfd_elf_merge_symbol (bfd *abfd,
       && (sec->flags & SEC_ALLOC) != 0
       && (sec->flags & SEC_LOAD) == 0
       && sym->st_size > 0
-      && ELF_ST_TYPE (sym->st_info) != STT_FUNC)
+      && !bed->is_function_type (ELF_ST_TYPE (sym->st_info)))
     newdyncommon = TRUE;
   else
     newdyncommon = FALSE;
@@ -1208,14 +1218,13 @@ _bfd_elf_merge_symbol (bfd *abfd,
       && (h->root.u.def.section->flags & SEC_ALLOC) != 0
       && (h->root.u.def.section->flags & SEC_LOAD) == 0
       && h->size > 0
-      && h->type != STT_FUNC)
+      && !bed->is_function_type (h->type))
     olddyncommon = TRUE;
   else
     olddyncommon = FALSE;
 
   /* We now know everything about the old and new symbols.  We ask the
      backend to check if we can merge them.  */
-  bed = get_elf_backend_data (abfd);
   if (bed->merge_symbol
       && !bed->merge_symbol (info, sym_hash, h, sym, psec, pvalue,
                             pold_alignment, skip, override,
@@ -1270,7 +1279,7 @@ _bfd_elf_merge_symbol (bfd *abfd,
       && (olddef
          || (h->root.type == bfd_link_hash_common
              && (newweak
-                 || ELF_ST_TYPE (sym->st_info) == STT_FUNC))))
+                 || bed->is_function_type (ELF_ST_TYPE (sym->st_info))))))
     {
       *override = TRUE;
       newdef = FALSE;
@@ -1325,7 +1334,7 @@ _bfd_elf_merge_symbol (bfd *abfd,
       && (newdef
          || (bfd_is_com_section (sec)
              && (oldweak
-                 || h->type == STT_FUNC)))
+                 || bed->is_function_type (h->type))))
       && olddyn
       && olddef
       && h->def_dynamic)
@@ -1554,6 +1563,10 @@ _bfd_elf_add_default_symbol (bfd *abfd,
       hi = h;
     }
 
+  /* Check if HI is a warning symbol.  */
+  if (hi->root.type == bfd_link_hash_warning)
+    hi = (struct elf_link_hash_entry *) hi->root.u.i.link;
+
   /* If there is a duplicate definition somewhere, then HI may not
      point to an indirect symbol.  We will have reported an error to
      the user in that case.  */
@@ -2596,6 +2609,52 @@ _bfd_elf_adjust_dynamic_symbol (struct elf_link_hash_entry *h, void *data)
   return TRUE;
 }
 
+/* Adjust the dynamic symbol, H, for copy in the dynamic bss section,
+   DYNBSS.  */
+
+bfd_boolean
+_bfd_elf_adjust_dynamic_copy (struct elf_link_hash_entry *h,
+                             asection *dynbss)
+{
+  unsigned int power_of_two;
+  bfd_vma mask;
+  asection *sec = h->root.u.def.section;
+
+  /* The section aligment of definition is the maximum alignment
+     requirement of symbols defined in the section.  Since we don't
+     know the symbol alignment requirement, we start with the
+     maximum alignment and check low bits of the symbol address
+     for the minimum alignment.  */
+  power_of_two = bfd_get_section_alignment (sec->owner, sec);
+  mask = ((bfd_vma) 1 << power_of_two) - 1;
+  while ((h->root.u.def.value & mask) != 0)
+    {
+       mask >>= 1;
+       --power_of_two;
+    }
+
+  if (power_of_two > bfd_get_section_alignment (dynbss->owner,
+                                               dynbss))
+    {
+      /* Adjust the section alignment if needed.  */
+      if (! bfd_set_section_alignment (dynbss->owner, dynbss,
+                                      power_of_two))
+       return FALSE;
+    }
+
+  /* We make sure that the symbol will be aligned properly.  */
+  dynbss->size = BFD_ALIGN (dynbss->size, mask + 1);
+
+  /* Define the symbol as being at this point in DYNBSS.  */
+  h->root.u.def.section = dynbss;
+  h->root.u.def.value = dynbss->size;
+
+  /* Increment the size of DYNBSS to make room for the symbol.  */
+  dynbss->size += h->size;
+
+  return TRUE;
+}
+
 /* Adjust all external symbols pointing into SEC_MERGE sections
    to reflect the object merging within the sections.  */
 
@@ -2634,6 +2693,8 @@ _bfd_elf_dynamic_symbol_p (struct elf_link_hash_entry *h,
                           bfd_boolean ignore_protected)
 {
   bfd_boolean binding_stays_local_p;
+  const struct elf_backend_data *bed;
+  struct elf_link_hash_table *hash_table;
 
   if (h == NULL)
     return FALSE;
@@ -2659,10 +2720,16 @@ _bfd_elf_dynamic_symbol_p (struct elf_link_hash_entry *h,
       return FALSE;
 
     case STV_PROTECTED:
+      hash_table = elf_hash_table (info);
+      if (!is_elf_hash_table (hash_table))
+       return FALSE;
+
+      bed = get_elf_backend_data (hash_table->dynobj);
+
       /* Proper resolution for function pointer equality may require
         that these symbols perhaps be resolved dynamically, even though
         we should be resolving them to the current module.  */
-      if (!ignore_protected || h->type != STT_FUNC)
+      if (!ignore_protected || !bed->is_function_type (h->type))
        binding_stays_local_p = TRUE;
       break;
 
@@ -2689,6 +2756,9 @@ _bfd_elf_symbol_refs_local_p (struct elf_link_hash_entry *h,
                              struct bfd_link_info *info,
                              bfd_boolean local_protected)
 {
+  const struct elf_backend_data *bed;
+  struct elf_link_hash_table *hash_table;
+
   /* If it's a local sym, of course we resolve locally.  */
   if (h == NULL)
     return TRUE;
@@ -2725,8 +2795,14 @@ _bfd_elf_symbol_refs_local_p (struct elf_link_hash_entry *h,
   if (ELF_ST_VISIBILITY (h->other) != STV_PROTECTED)
     return TRUE;
 
+  hash_table = elf_hash_table (info);
+  if (!is_elf_hash_table (hash_table))
+    return TRUE;
+
+  bed = get_elf_backend_data (hash_table->dynobj);
+
   /* STV_PROTECTED non-function symbols are local.  */
-  if (h->type != STT_FUNC)
+  if (!bed->is_function_type (h->type))
     return TRUE;
 
   /* Function pointer equality tests may require that STV_PROTECTED
@@ -2775,8 +2851,9 @@ is_global_data_symbol_definition (bfd *abfd ATTRIBUTE_UNUSED,
       && ELF_ST_BIND (sym->st_info) < STB_LOOS)
     return FALSE;
 
+  bed = get_elf_backend_data (abfd);
   /* Function symbols do not count.  */
-  if (ELF_ST_TYPE (sym->st_info) == STT_FUNC)
+  if (bed->is_function_type (ELF_ST_TYPE (sym->st_info)))
     return FALSE;
 
   /* If the section is undefined, then so is the symbol.  */
@@ -2785,7 +2862,6 @@ is_global_data_symbol_definition (bfd *abfd ATTRIBUTE_UNUSED,
 
   /* If the symbol is defined in the common section, then
      it is a common definition and so does not count.  */
-  bed = get_elf_backend_data (abfd);
   if (bed->common_definition (sym))
     return FALSE;
 
@@ -3692,12 +3768,10 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
            sec = bfd_abs_section_ptr;
          else if (sec->kept_section)
            {
-             /* Symbols from discarded section are undefined, and have
-                default visibility.  */
+             /* Symbols from discarded section are undefined.  We keep
+                its visibility.  */
              sec = bfd_und_section_ptr;
              isym->st_shndx = SHN_UNDEF;
-             isym->st_other = (STV_DEFAULT
-                               | (isym->st_other & ~ ELF_ST_VISIBILITY (-1)));
            }
          else if ((abfd->flags & (EXEC_P | DYNAMIC)) != 0)
            value -= sec->vma;
@@ -3795,8 +3869,9 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
             if it is not a function, because it might be the version
             symbol itself.  FIXME: What if it isn't?  */
          if ((iver.vs_vers & VERSYM_HIDDEN) != 0
-             || (vernum > 1 && (! bfd_is_abs_section (sec)
-                                || ELF_ST_TYPE (isym->st_info) == STT_FUNC)))
+             || (vernum > 1
+                 && (!bfd_is_abs_section (sec)
+                     || bed->is_function_type (ELF_ST_TYPE (isym->st_info)))))
            {
              const char *verstr;
              size_t namelen, verlen, newlen;
@@ -3943,7 +4018,7 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
       if (dynamic
          && definition
          && (flags & BSF_WEAK) != 0
-         && ELF_ST_TYPE (isym->st_info) != STT_FUNC
+         && !bed->is_function_type (ELF_ST_TYPE (isym->st_info))
          && is_elf_hash_table (htab)
          && h->u.weakdef == NULL)
        {
@@ -4047,11 +4122,13 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
                }
            }
 
-         /* Remember the symbol size and type.  */
-         if (isym->st_size != 0
+         /* Remember the symbol size if it isn't undefined.  */
+         if ((isym->st_size != 0 && isym->st_shndx != SHN_UNDEF)
              && (definition || h->size == 0))
            {
-             if (h->size != 0 && h->size != isym->st_size && ! size_change_ok)
+             if (h->size != 0
+                 && h->size != isym->st_size
+                 && ! size_change_ok)
                (*_bfd_error_handler)
                  (_("Warning: size of symbol `%s' changed"
                     " from %lu in %B to %lu in %B"),
@@ -4066,7 +4143,8 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
             to be the size of the common symbol.  The code just above
             won't fix the size if a common symbol becomes larger.  We
             don't warn about a size change here, because that is
-            covered by --warn-common.  */
+            covered by --warn-common.  Allow changed between different
+            function types.  */
          if (h->root.type == bfd_link_hash_common)
            h->size = h->root.u.c.size;
 
@@ -4156,6 +4234,13 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
                dynsym = TRUE;
            }
 
+         if (definition && (sec->flags & SEC_DEBUGGING))
+           {
+             /* We don't want to make debug symbol dynamic.  */
+             (*bed->elf_backend_hide_symbol) (info, h, TRUE);
+             dynsym = FALSE;
+           }
+
          /* Check to see if we need to add an indirect symbol for
             the default name.  */
          if (definition || h->root.type == bfd_link_hash_common)
@@ -4256,6 +4341,8 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
       unsigned int i;
 
       /* Restore the symbol table.  */
+      if (bed->as_needed_cleanup)
+       (*bed->as_needed_cleanup) (abfd, info);
       old_hash = (char *) old_tab + tabsize;
       old_ent = (char *) old_hash + hashsize;
       sym_hash = elf_sym_hashes (abfd);
@@ -4399,7 +4486,7 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
          h = *hpp;
          if (h != NULL
              && h->root.type == bfd_link_hash_defined
-             && h->type != STT_FUNC)
+             && !bed->is_function_type (h->type))
            {
              *sym_hash = h;
              sym_hash++;
@@ -5222,6 +5309,7 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
   if (!is_elf_hash_table (info->hash))
     return TRUE;
 
+  bed = get_elf_backend_data (output_bfd);
   elf_tdata (output_bfd)->relro = info->relro;
   if (info->execstack)
     elf_tdata (output_bfd)->stack_flags = PF_R | PF_W | PF_X;
@@ -5248,7 +5336,7 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
                exec = PF_X;
              notesec = s;
            }
-         else
+         else if (bed->default_execstack)
            exec = PF_X;
        }
       if (notesec)
@@ -5269,11 +5357,13 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
 
   /* The backend may have to create some sections regardless of whether
      we're dynamic or not.  */
-  bed = get_elf_backend_data (output_bfd);
   if (bed->elf_backend_always_size_sections
       && ! (*bed->elf_backend_always_size_sections) (output_bfd, info))
     return FALSE;
 
+  if (! _bfd_elf_maybe_strip_eh_frame_hdr (info))
+    return FALSE;
+
   dynobj = elf_hash_table (info)->dynobj;
 
   /* If there were no dynamic objects in the link, there is nothing to
@@ -5281,9 +5371,6 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
   if (dynobj == NULL)
     return TRUE;
 
-  if (! _bfd_elf_maybe_strip_eh_frame_hdr (info))
-    return FALSE;
-
   if (elf_hash_table (info)->dynamic_sections_created)
     {
       struct elf_info_failed eif;
@@ -5507,15 +5594,16 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
 
              for (sub = info->input_bfds; sub != NULL;
                   sub = sub->link_next)
-               for (o = sub->sections; o != NULL; o = o->next)
-                 if (elf_section_data (o)->this_hdr.sh_type
-                     == SHT_PREINIT_ARRAY)
-                   {
-                     (*_bfd_error_handler)
-                       (_("%B: .preinit_array section is not allowed in DSO"),
-                        sub);
-                     break;
-                   }
+               if (bfd_get_flavour (sub) == bfd_target_elf_flavour)
+                 for (o = sub->sections; o != NULL; o = o->next)
+                   if (elf_section_data (o)->this_hdr.sh_type
+                       == SHT_PREINIT_ARRAY)
+                     {
+                       (*_bfd_error_handler)
+                         (_("%B: .preinit_array section is not allowed in DSO"),
+                          sub);
+                       break;
+                     }
 
              bfd_set_error (bfd_error_nonrepresentable_section);
              return FALSE;
@@ -6337,6 +6425,719 @@ struct elf_outext_info
   struct elf_final_link_info *finfo;
 };
 
+
+/* Support for evaluating a complex relocation.
+
+   Complex relocations are generalized, self-describing relocations.  The
+   implementation of them consists of two parts: complex symbols, and the
+   relocations themselves. 
+
+   The relocations are use a reserved elf-wide relocation type code (R_RELC
+   external / BFD_RELOC_RELC internal) and an encoding of relocation field
+   information (start bit, end bit, word width, etc) into the addend.  This
+   information is extracted from CGEN-generated operand tables within gas.
+
+   Complex symbols are mangled symbols (BSF_RELC external / STT_RELC
+   internal) representing prefix-notation expressions, including but not
+   limited to those sorts of expressions normally encoded as addends in the
+   addend field.  The symbol mangling format is:
+
+   <node> := <literal>
+          |  <unary-operator> ':' <node>
+          |  <binary-operator> ':' <node> ':' <node>
+         ;
+
+   <literal> := 's' <digits=N> ':' <N character symbol name>
+             |  'S' <digits=N> ':' <N character section name>
+            |  '#' <hexdigits>
+            ;
+
+   <binary-operator> := as in C
+   <unary-operator> := as in C, plus "0-" for unambiguous negation.  */
+
+static void
+set_symbol_value (bfd *                         bfd_with_globals,
+                 struct elf_final_link_info *  finfo,    
+                 int                           symidx,
+                 bfd_vma                       val)
+{
+  bfd_boolean                    is_local;
+  Elf_Internal_Sym *             sym;
+  struct elf_link_hash_entry **  sym_hashes;
+  struct elf_link_hash_entry *   h;
+
+  sym_hashes = elf_sym_hashes (bfd_with_globals);
+  sym = finfo->internal_syms + symidx;  
+  is_local = ELF_ST_BIND(sym->st_info) == STB_LOCAL;
+  
+  if (is_local)
+    {
+      /* It is a local symbol: move it to the
+        "absolute" section and give it a value.  */
+      sym->st_shndx = SHN_ABS;
+      sym->st_value = val;
+    }
+  else 
+    {
+      /* It is a global symbol: set its link type
+        to "defined" and give it a value.  */
+      h = sym_hashes [symidx];   
+      while (h->root.type == bfd_link_hash_indirect
+            || h->root.type == bfd_link_hash_warning)
+       h = (struct elf_link_hash_entry *) h->root.u.i.link;
+      h->root.type = bfd_link_hash_defined;
+      h->root.u.def.value = val;
+      h->root.u.def.section = bfd_abs_section_ptr;
+    }
+}
+
+static bfd_boolean 
+resolve_symbol (const char *                  name,
+               bfd *                         input_bfd,
+               struct elf_final_link_info *  finfo,
+               bfd_vma *                     result,
+               size_t                        locsymcount)
+{
+  Elf_Internal_Sym *            sym;
+  struct bfd_link_hash_entry *  global_entry;
+  const char *                  candidate = NULL;
+  Elf_Internal_Shdr *           symtab_hdr;
+  asection *                    sec = NULL;
+  size_t                        i;
+  
+  symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
+
+  for (i = 0; i < locsymcount; ++ i)
+    {
+      sym = finfo->internal_syms + i;
+      sec = finfo->sections [i];
+
+      if (ELF_ST_BIND (sym->st_info) != STB_LOCAL)
+       continue;
+
+      candidate = bfd_elf_string_from_elf_section (input_bfd,
+                                                  symtab_hdr->sh_link,
+                                                  sym->st_name);
+#ifdef DEBUG
+      printf ("Comparing string: '%s' vs. '%s' = 0x%x\n", 
+             name, candidate, (unsigned int)sym->st_value);
+#endif
+      if (candidate && strcmp (candidate, name) == 0)
+       {
+         * result = sym->st_value;
+
+         if (sym->st_shndx > SHN_UNDEF && 
+             sym->st_shndx < SHN_LORESERVE)
+           {
+#ifdef DEBUG
+             printf ("adjusting for sec '%s' @ 0x%x + 0x%x\n",
+                     sec->output_section->name, 
+                     (unsigned int)sec->output_section->vma, 
+                     (unsigned int)sec->output_offset);
+#endif
+             * result += sec->output_offset + sec->output_section->vma;
+           }
+#ifdef DEBUG
+         printf ("Found symbol with effective value %8.8x\n", (unsigned int)* result);
+#endif
+         return TRUE;
+       }
+    }
+
+  /* Hmm, haven't found it yet. perhaps it is a global.  */
+  global_entry = bfd_link_hash_lookup (finfo->info->hash, name, FALSE, FALSE, TRUE);
+  if (!global_entry)
+    return FALSE;
+  
+  if (global_entry->type == bfd_link_hash_defined
+      || global_entry->type == bfd_link_hash_defweak)
+    {
+      * result = global_entry->u.def.value 
+       + global_entry->u.def.section->output_section->vma 
+       + global_entry->u.def.section->output_offset;
+#ifdef DEBUG
+      printf ("Found GLOBAL symbol '%s' with value %8.8x\n",
+             global_entry->root.string, (unsigned int)*result);
+#endif
+      return TRUE;
+    } 
+
+  if (global_entry->type == bfd_link_hash_common)
+    {
+      *result = global_entry->u.def.value +
+       bfd_com_section_ptr->output_section->vma +
+       bfd_com_section_ptr->output_offset;
+#ifdef DEBUG
+      printf ("Found COMMON symbol '%s' with value %8.8x\n",
+             global_entry->root.string, (unsigned int)*result);
+#endif
+      return TRUE;
+    }
+  
+  return FALSE;
+}
+
+static bfd_boolean
+resolve_section (const char *  name,
+                asection *    sections,
+                bfd_vma *     result)
+{
+  asection *    curr;
+  unsigned int  len;
+
+  for (curr = sections; curr; curr = curr->next)    
+    if (strcmp (curr->name, name) == 0)
+      {
+       *result = curr->vma;
+       return TRUE;
+      }
+
+  /* Hmm. still haven't found it. try pseudo-section names.  */
+  for (curr = sections; curr; curr = curr->next)    
+    {
+      len = strlen (curr->name);
+      if (len > strlen (name)) 
+       continue;
+
+      if (strncmp (curr->name, name, len) == 0)
+       {
+         if (strncmp (".end", name + len, 4) == 0)
+           {
+             *result = curr->vma + curr->size;
+             return TRUE;
+           }
+
+         /* Insert more pseudo-section names here, if you like.  */
+       }
+    }
+  
+  return FALSE;
+}
+
+static void
+undefined_reference (const char *  reftype,
+                    const char *  name)
+{
+  _bfd_error_handler (_("undefined %s reference in complex symbol: %s"), reftype, name);
+}
+
+static bfd_boolean
+eval_symbol (bfd_vma *                     result,
+            char *                        sym,
+            char **                       advanced,
+            bfd *                         input_bfd,
+            struct elf_final_link_info *  finfo,
+            bfd_vma                       addr,
+            bfd_vma                       section_offset,
+            size_t                        locsymcount,
+            int                           signed_p)
+{
+  int           len;
+  int           symlen;
+  bfd_vma       a;
+  bfd_vma       b;
+  const int     bufsz = 4096;
+  char          symbuf [bufsz];
+  const char *  symend;
+  bfd_boolean   symbol_is_section = FALSE;
+
+  len = strlen (sym);
+  symend = sym + len;
+
+  if (len < 1 || len > bufsz)
+    {
+      bfd_set_error (bfd_error_invalid_operation);
+      return FALSE;
+    }
+  
+  switch (* sym)
+    {
+    case '.':
+      * result = addr + section_offset;
+      * advanced = sym + 1;
+      return TRUE;
+
+    case '#':
+      ++ sym;
+      * result = strtoul (sym, advanced, 16);
+      return TRUE;
+
+    case 'S':
+      symbol_is_section = TRUE;
+    case 's':      
+      ++ sym;
+      symlen = strtol (sym, &sym, 10);
+      ++ sym; /* Skip the trailing ':'.  */
+
+      if ((symend < sym) || ((symlen + 1) > bufsz))
+       {
+         bfd_set_error (bfd_error_invalid_operation);
+         return FALSE;
+       }
+
+      memcpy (symbuf, sym, symlen);
+      symbuf [symlen] = '\0';
+      * advanced = sym + symlen;
+      
+      /* Is it always possible, with complex symbols, that gas "mis-guessed" 
+        the symbol as a section, or vice-versa. so we're pretty liberal in our
+        interpretation here; section means "try section first", not "must be a
+        section", and likewise with symbol.  */
+
+      if (symbol_is_section) 
+       {
+         if ((resolve_section (symbuf, finfo->output_bfd->sections, result) != TRUE)
+             && (resolve_symbol (symbuf, input_bfd, finfo, result, locsymcount) != TRUE))
+           {
+             undefined_reference ("section", symbuf);
+             return FALSE;
+           }
+       } 
+      else 
+       {
+         if ((resolve_symbol (symbuf, input_bfd, finfo, result, locsymcount) != TRUE)
+             && (resolve_section (symbuf, finfo->output_bfd->sections,
+                                  result) != TRUE))
+           {
+             undefined_reference ("symbol", symbuf);
+             return FALSE;
+           }
+       }
+
+      return TRUE;
+      
+      /* All that remains are operators.  */
+
+#define UNARY_OP(op)                                           \
+  if (strncmp (sym, #op, strlen (#op)) == 0)                   \
+    {                                                          \
+      sym += strlen (#op);                                     \
+      if (* sym == ':')                                                \
+        ++ sym;                                                        \
+      if (eval_symbol (& a, sym, & sym, input_bfd, finfo, addr, \
+                       section_offset, locsymcount, signed_p)   \
+                                                    != TRUE)   \
+        return FALSE;                                          \
+      if (signed_p)                                             \
+        * result = op ((signed)a);                             \
+      else                                                      \
+        * result = op a;                                        \
+      * advanced = sym;                                        \
+      return TRUE;                                             \
+    }
+
+#define BINARY_OP(op)                                          \
+  if (strncmp (sym, #op, strlen (#op)) == 0)                   \
+    {                                                          \
+      sym += strlen (#op);                                     \
+      if (* sym == ':')                                                \
+        ++ sym;                                                        \
+      if (eval_symbol (& a, sym, & sym, input_bfd, finfo, addr, \
+                       section_offset, locsymcount, signed_p)   \
+                                                     != TRUE)  \
+        return FALSE;                                          \
+      ++ sym;                                                  \
+      if (eval_symbol (& b, sym, & sym, input_bfd, finfo, addr, \
+                       section_offset, locsymcount, signed_p)   \
+                                                     != TRUE)  \
+        return FALSE;                                          \
+      if (signed_p)                                             \
+        * result = ((signed) a) op ((signed) b);               \
+      else                                                      \
+        * result = a op b;                                      \
+      * advanced = sym;                                                \
+      return TRUE;                                             \
+    }
+
+    default:
+      UNARY_OP  (0-);
+      BINARY_OP (<<);
+      BINARY_OP (>>);
+      BINARY_OP (==);
+      BINARY_OP (!=);
+      BINARY_OP (<=);
+      BINARY_OP (>=);
+      BINARY_OP (&&);
+      BINARY_OP (||);
+      UNARY_OP  (~);
+      UNARY_OP  (!);
+      BINARY_OP (*);
+      BINARY_OP (/);
+      BINARY_OP (%);
+      BINARY_OP (^);
+      BINARY_OP (|);
+      BINARY_OP (&);
+      BINARY_OP (+);
+      BINARY_OP (-);
+      BINARY_OP (<);
+      BINARY_OP (>);
+#undef UNARY_OP
+#undef BINARY_OP
+      _bfd_error_handler (_("unknown operator '%c' in complex symbol"), * sym);
+      bfd_set_error (bfd_error_invalid_operation);
+      return FALSE;
+    }
+}
+
+/* Entry point to evaluator, called from elf_link_input_bfd.  */
+
+static bfd_boolean
+evaluate_complex_relocation_symbols (bfd * input_bfd,
+                                    struct elf_final_link_info * finfo,
+                                    size_t locsymcount)
+{
+  const struct elf_backend_data * bed;
+  Elf_Internal_Shdr *             symtab_hdr;
+  struct elf_link_hash_entry **   sym_hashes;
+  asection *                      reloc_sec;
+  bfd_boolean                     result = TRUE;
+
+  /* For each section, we're going to check and see if it has any
+     complex relocations, and we're going to evaluate any of them
+     we can.  */
+
+  if (finfo->info->relocatable)
+    return TRUE;
+
+  symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
+  sym_hashes = elf_sym_hashes (input_bfd);
+  bed = get_elf_backend_data (input_bfd);
+
+  for (reloc_sec = input_bfd->sections; reloc_sec; reloc_sec = reloc_sec->next)
+    {
+      Elf_Internal_Rela * internal_relocs;
+      unsigned long i;
+
+      /* This section was omitted from the link.  */
+      if (! reloc_sec->linker_mark)
+       continue;
+
+      /* Only process sections containing relocs.  */
+      if ((reloc_sec->flags & SEC_RELOC) == 0)
+       continue;
+
+      if (reloc_sec->reloc_count == 0)
+       continue;
+
+      /* Read in the relocs for this section.  */
+      internal_relocs
+       = _bfd_elf_link_read_relocs (input_bfd, reloc_sec, NULL,
+                                    (Elf_Internal_Rela *) NULL,
+                                    FALSE);
+      if (internal_relocs == NULL)
+       continue;
+
+      for (i = reloc_sec->reloc_count; i--;)
+       {
+         Elf_Internal_Rela * rel;
+         char * sym_name;
+         bfd_vma index;
+         Elf_Internal_Sym * sym;
+         bfd_vma result;
+         bfd_vma section_offset;
+         bfd_vma addr;
+         int signed_p = 0;
+
+         rel = internal_relocs + i;
+         section_offset = reloc_sec->output_section->vma
+           + reloc_sec->output_offset;
+         addr = rel->r_offset;
+
+         index = ELF32_R_SYM (rel->r_info);
+         if (bed->s->arch_size == 64)
+           index >>= 24;
+
+         if (index == STN_UNDEF)
+           continue;
+
+         if (index < locsymcount)
+           {
+             /* The symbol is local.  */
+             sym = finfo->internal_syms + index;
+
+             /* We're only processing STT_RELC or STT_SRELC type symbols.  */
+             if ((ELF_ST_TYPE (sym->st_info) != STT_RELC) &&
+                 (ELF_ST_TYPE (sym->st_info) != STT_SRELC))
+               continue;
+
+             sym_name = bfd_elf_string_from_elf_section
+               (input_bfd, symtab_hdr->sh_link, sym->st_name);
+
+             signed_p = (ELF_ST_TYPE (sym->st_info) == STT_SRELC);
+           }
+         else
+           {
+             /* The symbol is global.  */
+             struct elf_link_hash_entry * h;
+
+             if (elf_bad_symtab (input_bfd))
+               continue;
+
+             h = sym_hashes [index - locsymcount];
+             while (   h->root.type == bfd_link_hash_indirect
+                    || h->root.type == bfd_link_hash_warning)
+               h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+             if (h->type != STT_RELC && h->type != STT_SRELC)
+               continue;
+
+             signed_p = (h->type == STT_SRELC);
+             sym_name = (char *) h->root.root.string;
+           }
+#ifdef DEBUG
+         printf ("Encountered a complex symbol!");
+         printf (" (input_bfd %s, section %s, reloc %ld\n",
+                 input_bfd->filename, reloc_sec->name, i);
+         printf (" symbol: idx  %8.8lx, name %s\n",
+                 index, sym_name);
+         printf (" reloc : info %8.8lx, addr %8.8lx\n",
+                 rel->r_info, addr);
+         printf (" Evaluating '%s' ...\n ", sym_name);
+#endif
+         if (eval_symbol (& result, sym_name, & sym_name, input_bfd, 
+                          finfo, addr, section_offset, locsymcount,
+                          signed_p))
+           /* Symbol evaluated OK.  Update to absolute value.  */
+           set_symbol_value (input_bfd, finfo, index, result);
+
+         else
+           result = FALSE;
+       }
+
+      if (internal_relocs != elf_section_data (reloc_sec)->relocs)
+       free (internal_relocs);
+    }
+
+  /* If nothing went wrong, then we adjusted 
+     everything we wanted to adjust.  */
+  return result;
+}
+
+static void
+put_value (bfd_vma        size,
+          unsigned long  chunksz,
+          bfd *          input_bfd,
+          bfd_vma        x,
+          bfd_byte *     location)
+{
+  location += (size - chunksz);
+
+  for (; size; size -= chunksz, location -= chunksz, x >>= (chunksz * 8)) 
+    {
+      switch (chunksz)
+       {
+       default:
+       case 0:
+         abort ();
+       case 1:
+         bfd_put_8 (input_bfd, x, location);
+         break;
+       case 2:
+         bfd_put_16 (input_bfd, x, location);
+         break;
+       case 4:
+         bfd_put_32 (input_bfd, x, location);
+         break;
+       case 8:
+#ifdef BFD64
+         bfd_put_64 (input_bfd, x, location);
+#else
+         abort ();
+#endif
+         break;
+       }
+    }
+}
+
+static bfd_vma 
+get_value (bfd_vma        size,
+          unsigned long  chunksz,
+          bfd *          input_bfd,
+          bfd_byte *     location)
+{
+  bfd_vma x = 0;
+
+  for (; size; size -= chunksz, location += chunksz) 
+    {
+      switch (chunksz)
+       {
+       default:
+       case 0:
+         abort ();
+       case 1:
+         x = (x << (8 * chunksz)) | bfd_get_8 (input_bfd, location);
+         break;
+       case 2:
+         x = (x << (8 * chunksz)) | bfd_get_16 (input_bfd, location);
+         break;
+       case 4:
+         x = (x << (8 * chunksz)) | bfd_get_32 (input_bfd, location);
+         break;
+       case 8:
+#ifdef BFD64
+         x = (x << (8 * chunksz)) | bfd_get_64 (input_bfd, location);
+#else
+         abort ();
+#endif
+         break;
+       }
+    }
+  return x;
+}
+
+static void 
+decode_complex_addend
+    (unsigned long * start,   /* in bits */
+     unsigned long * oplen,   /* in bits */
+     unsigned long * len,     /* in bits */
+     unsigned long * wordsz,  /* in bytes */
+     unsigned long * chunksz,  /* in bytes */
+     unsigned long * lsb0_p,
+     unsigned long * signed_p,
+     unsigned long * trunc_p,
+     unsigned long encoded)
+{
+  * start     =  encoded        & 0x3F;
+  * len       = (encoded >>  6) & 0x3F;
+  * oplen     = (encoded >> 12) & 0x3F;
+  * wordsz    = (encoded >> 18) & 0xF;
+  * chunksz   = (encoded >> 22) & 0xF;
+  * lsb0_p    = (encoded >> 27) & 1;
+  * signed_p  = (encoded >> 28) & 1;
+  * trunc_p   = (encoded >> 29) & 1;
+}
+
+void
+bfd_elf_perform_complex_relocation
+    (bfd *                   output_bfd ATTRIBUTE_UNUSED,
+     struct bfd_link_info *  info,
+     bfd *                   input_bfd,
+     asection *              input_section,
+     bfd_byte *              contents,
+     Elf_Internal_Rela *     rel,
+     Elf_Internal_Sym *      local_syms,
+     asection **             local_sections)
+{
+  const struct elf_backend_data * bed;
+  Elf_Internal_Shdr * symtab_hdr;
+  asection * sec;
+  bfd_vma relocation = 0, shift, x;
+  bfd_vma r_symndx;
+  bfd_vma mask;
+  unsigned long start, oplen, len, wordsz, 
+    chunksz, lsb0_p, signed_p, trunc_p;
+
+  /*  Perform this reloc, since it is complex.
+      (this is not to say that it necessarily refers to a complex
+      symbol; merely that it is a self-describing CGEN based reloc.
+      i.e. the addend has the complete reloc information (bit start, end,
+      word size, etc) encoded within it.).  */ 
+  r_symndx = ELF32_R_SYM (rel->r_info);
+  bed = get_elf_backend_data (input_bfd);
+  if (bed->s->arch_size == 64)
+    r_symndx >>= 24;
+
+#ifdef DEBUG
+  printf ("Performing complex relocation %ld...\n", r_symndx);
+#endif
+
+  symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
+  if (r_symndx < symtab_hdr->sh_info)
+    {
+      /* The symbol is local.  */
+      Elf_Internal_Sym * sym;
+
+      sym = local_syms + r_symndx;
+      sec = local_sections [r_symndx];
+      relocation = sym->st_value;
+      if (sym->st_shndx > SHN_UNDEF && 
+         sym->st_shndx < SHN_LORESERVE)
+       relocation += (sec->output_offset +
+                      sec->output_section->vma);
+    }
+  else
+    {
+      /* The symbol is global.  */
+      struct elf_link_hash_entry **sym_hashes;
+      struct elf_link_hash_entry * h;
+
+      sym_hashes = elf_sym_hashes (input_bfd);
+      h = sym_hashes [r_symndx];
+
+      while (h->root.type == bfd_link_hash_indirect
+            || h->root.type == bfd_link_hash_warning)
+       h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+      if (h->root.type == bfd_link_hash_defined
+         || h->root.type == bfd_link_hash_defweak)
+       {
+         sec = h->root.u.def.section;
+         relocation = h->root.u.def.value;
+
+         if (! bfd_is_abs_section (sec))
+           relocation += (sec->output_section->vma 
+                          + sec->output_offset); 
+       }
+      if (h->root.type == bfd_link_hash_undefined
+         && !((*info->callbacks->undefined_symbol)
+              (info, h->root.root.string, input_bfd,
+               input_section, rel->r_offset,
+               info->unresolved_syms_in_objects == RM_GENERATE_ERROR
+               || ELF_ST_VISIBILITY (h->other))))
+       return;
+    }
+
+  decode_complex_addend (& start, & oplen, & len, & wordsz, 
+                        & chunksz, & lsb0_p, & signed_p, 
+                        & trunc_p, rel->r_addend);
+
+  mask = (((1L << (len - 1)) - 1) << 1) | 1;
+
+  if (lsb0_p)
+    shift = (start + 1) - len;
+  else
+    shift = (8 * wordsz) - (start + len);
+
+  x = get_value (wordsz, chunksz, input_bfd, contents + rel->r_offset);          
+
+#ifdef DEBUG
+  printf ("Doing complex reloc: "
+         "lsb0? %ld, signed? %ld, trunc? %ld, wordsz %ld, "
+         "chunksz %ld, start %ld, len %ld, oplen %ld\n"
+         "    dest: %8.8lx, mask: %8.8lx, reloc: %8.8lx\n",
+         lsb0_p, signed_p, trunc_p, wordsz, chunksz, start, len,
+         oplen, x, mask,  relocation);
+#endif
+
+  if (! trunc_p)
+    {
+      /* Now do an overflow check.  */
+      if (bfd_check_overflow ((signed_p ? 
+                              complain_overflow_signed : 
+                              complain_overflow_unsigned),
+                             len, 0, (8 * wordsz), 
+                             relocation) == bfd_reloc_overflow)
+       (*_bfd_error_handler) 
+         ("%s (%s + 0x%lx): relocation overflow: 0x%lx %sdoes not fit "
+          "within 0x%lx", 
+          input_bfd->filename, input_section->name, rel->r_offset,
+          relocation, (signed_p ? "(signed) " : ""), mask);
+    }
+         
+  /* Do the deed.  */
+  x = (x & ~(mask << shift)) | ((relocation & mask) << shift);
+
+#ifdef DEBUG
+  printf ("           relocation: %8.8lx\n"
+         "         shifted mask: %8.8lx\n"
+         " shifted/masked reloc: %8.8lx\n"
+         "               result: %8.8lx\n",
+         relocation, (mask << shift), 
+         ((relocation & mask) << shift), x);
+#endif
+  put_value (wordsz, chunksz, input_bfd, x, contents + rel->r_offset);
+}
+
 /* When performing a relocatable link, the input relocations are
    preserved.  But, if they reference global symbols, the indices
    referenced must be updated.  Update all the relocations in
@@ -6465,7 +7266,9 @@ elf_link_sort_cmp2 (const void *A, const void *B)
 static size_t
 elf_link_sort_relocs (bfd *abfd, struct bfd_link_info *info, asection **psec)
 {
-  asection *reldyn;
+  asection *dynamic_relocs;
+  asection *rela_dyn;
+  asection *rel_dyn;
   bfd_size_type count, size;
   size_t i, ret, sort_elt, ext_size;
   bfd_byte *sort, *s_non_relative, *p;
@@ -6476,39 +7279,170 @@ elf_link_sort_relocs (bfd *abfd, struct bfd_link_info *info, asection **psec)
   void (*swap_out) (bfd *, const Elf_Internal_Rela *, bfd_byte *);
   struct bfd_link_order *lo;
   bfd_vma r_sym_mask;
+  bfd_boolean use_rela;
 
-  reldyn = bfd_get_section_by_name (abfd, ".rela.dyn");
-  if (reldyn == NULL || reldyn->size == 0)
+  /* Find a dynamic reloc section.  */
+  rela_dyn = bfd_get_section_by_name (abfd, ".rela.dyn");
+  rel_dyn  = bfd_get_section_by_name (abfd, ".rel.dyn");
+  if (rela_dyn != NULL && rela_dyn->size > 0
+      && rel_dyn != NULL && rel_dyn->size > 0)
     {
-      reldyn = bfd_get_section_by_name (abfd, ".rel.dyn");
-      if (reldyn == NULL || reldyn->size == 0)
-       return 0;
-      ext_size = bed->s->sizeof_rel;
-      swap_in = bed->s->swap_reloc_in;
-      swap_out = bed->s->swap_reloc_out;
+      bfd_boolean use_rela_initialised = FALSE;
+
+      /* This is just here to stop gcc from complaining.
+        It's initialization checking code is not perfect.  */
+      use_rela = TRUE;
+
+      /* Both sections are present.  Examine the sizes
+        of the indirect sections to help us choose.  */
+      for (lo = rela_dyn->map_head.link_order; lo != NULL; lo = lo->next)
+       if (lo->type == bfd_indirect_link_order)
+         {
+           asection *o = lo->u.indirect.section;
+
+           if ((o->size % bed->s->sizeof_rela) == 0)
+             {
+               if ((o->size % bed->s->sizeof_rel) == 0)
+                 /* Section size is divisible by both rel and rela sizes.
+                    It is of no help to us.  */
+                 ;
+               else
+                 {
+                   /* Section size is only divisible by rela.  */
+                   if (use_rela_initialised && (use_rela == FALSE))
+                     {
+                       _bfd_error_handler
+                         (_("%B: Unable to sort relocs - they are in more than one size"), abfd);
+                       bfd_set_error (bfd_error_invalid_operation);
+                       return 0;
+                     }
+                   else
+                     {
+                       use_rela = TRUE;
+                       use_rela_initialised = TRUE;
+                     }
+                 }
+             }
+           else if ((o->size % bed->s->sizeof_rel) == 0)
+             {
+               /* Section size is only divisible by rel.  */
+               if (use_rela_initialised && (use_rela == TRUE))
+                 {
+                   _bfd_error_handler
+                     (_("%B: Unable to sort relocs - they are in more than one size"), abfd);
+                   bfd_set_error (bfd_error_invalid_operation);
+                   return 0;
+                 }
+               else
+                 {
+                   use_rela = FALSE;
+                   use_rela_initialised = TRUE;
+                 }
+             }
+           else
+             {
+               /* The section size is not divisible by either - something is wrong.  */
+               _bfd_error_handler
+                 (_("%B: Unable to sort relocs - they are of an unknown size"), abfd);
+               bfd_set_error (bfd_error_invalid_operation);
+               return 0;
+             }
+         }
+
+      for (lo = rel_dyn->map_head.link_order; lo != NULL; lo = lo->next)
+       if (lo->type == bfd_indirect_link_order)
+         {
+           asection *o = lo->u.indirect.section;
+
+           if ((o->size % bed->s->sizeof_rela) == 0)
+             {
+               if ((o->size % bed->s->sizeof_rel) == 0)
+                 /* Section size is divisible by both rel and rela sizes.
+                    It is of no help to us.  */
+                 ;
+               else
+                 {
+                   /* Section size is only divisible by rela.  */
+                   if (use_rela_initialised && (use_rela == FALSE))
+                     {
+                       _bfd_error_handler
+                         (_("%B: Unable to sort relocs - they are in more than one size"), abfd);
+                       bfd_set_error (bfd_error_invalid_operation);
+                       return 0;
+                     }
+                   else
+                     {
+                       use_rela = TRUE;
+                       use_rela_initialised = TRUE;
+                     }
+                 }
+             }
+           else if ((o->size % bed->s->sizeof_rel) == 0)
+             {
+               /* Section size is only divisible by rel.  */
+               if (use_rela_initialised && (use_rela == TRUE))
+                 {
+                   _bfd_error_handler
+                     (_("%B: Unable to sort relocs - they are in more than one size"), abfd);
+                   bfd_set_error (bfd_error_invalid_operation);
+                   return 0;
+                 }
+               else
+                 {
+                   use_rela = FALSE;
+                   use_rela_initialised = TRUE;
+                 }
+             }
+           else
+             {
+               /* The section size is not divisible by either - something is wrong.  */
+               _bfd_error_handler
+                 (_("%B: Unable to sort relocs - they are of an unknown size"), abfd);
+               bfd_set_error (bfd_error_invalid_operation);
+               return 0;
+             }
+         }
+
+      if (! use_rela_initialised)
+       /* Make a guess.  */
+       use_rela = TRUE;
     }
+  else if (rela_dyn != NULL && rela_dyn->size > 0)
+    use_rela = TRUE;
+  else if (rel_dyn != NULL && rel_dyn->size > 0)
+    use_rela = FALSE;
   else
+    return 0;
+
+  if (use_rela)
     {
+      dynamic_relocs = rela_dyn;
       ext_size = bed->s->sizeof_rela;
       swap_in = bed->s->swap_reloca_in;
       swap_out = bed->s->swap_reloca_out;
     }
-  count = reldyn->size / ext_size;
+  else
+    {
+      dynamic_relocs = rel_dyn;
+      ext_size = bed->s->sizeof_rel;
+      swap_in = bed->s->swap_reloc_in;
+      swap_out = bed->s->swap_reloc_out;
+    }
 
   size = 0;
-  for (lo = reldyn->map_head.link_order; lo != NULL; lo = lo->next)
+  for (lo = dynamic_relocs->map_head.link_order; lo != NULL; lo = lo->next)
     if (lo->type == bfd_indirect_link_order)
-      {
-       asection *o = lo->u.indirect.section;
-       size += o->size;
-      }
+      size += lo->u.indirect.section->size;
 
-  if (size != reldyn->size)
+  if (size != dynamic_relocs->size)
     return 0;
 
   sort_elt = (sizeof (struct elf_link_sort_rela)
              + (i2e - 1) * sizeof (Elf_Internal_Rela));
+
+  count = dynamic_relocs->size / ext_size;
   sort = bfd_zmalloc (sort_elt * count);
+
   if (sort == NULL)
     {
       (*info->callbacks->warning)
@@ -6521,7 +7455,7 @@ elf_link_sort_relocs (bfd *abfd, struct bfd_link_info *info, asection **psec)
   else
     r_sym_mask = ~(bfd_vma) 0xffffffff;
 
-  for (lo = reldyn->map_head.link_order; lo != NULL; lo = lo->next)
+  for (lo = dynamic_relocs->map_head.link_order; lo != NULL; lo = lo->next)
     if (lo->type == bfd_indirect_link_order)
       {
        bfd_byte *erel, *erelend;
@@ -6538,9 +7472,11 @@ elf_link_sort_relocs (bfd *abfd, struct bfd_link_info *info, asection **psec)
        erel = o->contents;
        erelend = o->contents + o->size;
        p = sort + o->output_offset / ext_size * sort_elt;
+
        while (erel < erelend)
          {
            struct elf_link_sort_rela *s = (struct elf_link_sort_rela *) p;
+
            (*swap_in) (abfd, erel, s->rela);
            s->type = (*bed->elf_backend_reloc_type_class) (s->rela);
            s->u.sym_mask = r_sym_mask;
@@ -6571,7 +7507,7 @@ elf_link_sort_relocs (bfd *abfd, struct bfd_link_info *info, asection **psec)
 
   qsort (s_non_relative, count - ret, sort_elt, elf_link_sort_cmp2);
 
-  for (lo = reldyn->map_head.link_order; lo != NULL; lo = lo->next)
+  for (lo = dynamic_relocs->map_head.link_order; lo != NULL; lo = lo->next)
     if (lo->type == bfd_indirect_link_order)
       {
        bfd_byte *erel, *erelend;
@@ -6590,7 +7526,7 @@ elf_link_sort_relocs (bfd *abfd, struct bfd_link_info *info, asection **psec)
       }
 
   free (sort);
-  *psec = reldyn;
+  *psec = dynamic_relocs;
   return ret;
 }
 
@@ -7260,14 +8196,15 @@ _bfd_elf_default_action_discarded (asection *sec)
 /* Find a match between a section and a member of a section group.  */
 
 static asection *
-match_group_member (asection *sec, asection *group)
+match_group_member (asection *sec, asection *group,
+                   struct bfd_link_info *info)
 {
   asection *first = elf_next_in_group (group);
   asection *s = first;
 
   while (s != NULL)
     {
-      if (bfd_elf_match_symbols_in_sections (s, sec))
+      if (bfd_elf_match_symbols_in_sections (s, sec, info))
        return s;
 
       s = elf_next_in_group (s);
@@ -7279,21 +8216,22 @@ match_group_member (asection *sec, asection *group)
 }
 
 /* Check if the kept section of a discarded section SEC can be used
-   to replace it. Return the replacement if it is OK. Otherwise return
-   NULL. */
+   to replace it.  Return the replacement if it is OK.  Otherwise return
+   NULL.  */
 
 asection *
-_bfd_elf_check_kept_section (asection *sec)
+_bfd_elf_check_kept_section (asection *sec, struct bfd_link_info *info)
 {
   asection *kept;
 
   kept = sec->kept_section;
   if (kept != NULL)
     {
-      if (elf_sec_group (sec) != NULL)
-       kept = match_group_member (sec, kept);
+      if ((kept->flags & SEC_GROUP) != 0)
+       kept = match_group_member (sec, kept, info);
       if (kept != NULL && sec->size != kept->size)
        kept = NULL;
+      sec->kept_section = kept;
     }
   return kept;
 }
@@ -7306,7 +8244,7 @@ _bfd_elf_check_kept_section (asection *sec)
 static bfd_boolean
 elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
 {
-  bfd_boolean (*relocate_section)
+  int (*relocate_section)
     (bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
      Elf_Internal_Rela *, Elf_Internal_Sym *, asection **);
   bfd *output_bfd;
@@ -7320,7 +8258,6 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
   asection **ppsection;
   asection *o;
   const struct elf_backend_data *bed;
-  bfd_boolean emit_relocs;
   struct elf_link_hash_entry **sym_hashes;
 
   output_bfd = finfo->output_bfd;
@@ -7333,9 +8270,6 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
   if ((input_bfd->flags & DYNAMIC) != 0)
     return TRUE;
 
-  emit_relocs = (finfo->info->relocatable
-                || finfo->info->emitrelocations);
-
   symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
   if (elf_bad_symtab (input_bfd))
     {
@@ -7359,6 +8293,15 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
       if (isymbuf == NULL)
        return FALSE;
     }
+  /* evaluate_complex_relocation_symbols looks for symbols in
+     finfo->internal_syms.  */
+  else if (isymbuf != NULL && locsymcount != 0)
+    {
+      bfd_elf_get_elf_syms (input_bfd, symtab_hdr, locsymcount, 0,
+                           finfo->internal_syms,
+                           finfo->external_syms,
+                           finfo->locsym_shndx);
+    }
 
   /* Find local symbol sections and adjust values of symbols in
      SEC_MERGE sections.  Write out those local symbols we know are
@@ -7496,6 +8439,9 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
        return FALSE;
     }
 
+  if (! evaluate_complex_relocation_symbols (input_bfd, finfo, locsymcount))
+    return FALSE;
+
   /* Relocate the contents of each section.  */
   sym_hashes = elf_sym_hashes (input_bfd);
   for (o = input_bfd->sections; o != NULL; o = o->next)
@@ -7539,6 +8485,7 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
          Elf_Internal_Rela *internal_relocs;
          bfd_vma r_type_mask;
          int r_sym_shift;
+         int ret;
 
          /* Get the swapped relocs.  */
          internal_relocs
@@ -7644,23 +8591,14 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
                        {
                          asection *kept;
 
-                         kept = _bfd_elf_check_kept_section (sec);
+                         kept = _bfd_elf_check_kept_section (sec,
+                                                             finfo->info);
                          if (kept != NULL)
                            {
                              *ps = kept;
                              continue;
                            }
                        }
-
-                     /* Remove the symbol reference from the reloc, but
-                        don't kill the reloc completely.  This is so that
-                        a zero value will be written into the section,
-                        which may have non-zero contents put there by the
-                        assembler.  Zero in things like an eh_frame fde
-                        pc_begin allows stack unwinders to recognize the
-                        fde as bogus.  */
-                     rel->r_info &= r_type_mask;
-                     rel->r_addend = 0;
                    }
                }
            }
@@ -7685,14 +8623,17 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
             corresponding to the output section, which will require
             the addend to be adjusted.  */
 
-         if (! (*relocate_section) (output_bfd, finfo->info,
+         ret = (*relocate_section) (output_bfd, finfo->info,
                                     input_bfd, o, contents,
                                     internal_relocs,
                                     isymbuf,
-                                    finfo->sections))
+                                    finfo->sections);
+         if (!ret)
            return FALSE;
 
-         if (emit_relocs)
+         if (ret == 2
+             || finfo->info->relocatable
+             || finfo->info->emitrelocations)
            {
              Elf_Internal_Rela *irela;
              Elf_Internal_Rela *irelaend;
@@ -7814,8 +8755,10 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
 
                          /* If we have discarded a section, the output
                             section will be the absolute section.  In
-                            case of discarded link-once and discarded
-                            SEC_MERGE sections, use the kept section.  */
+                            case of discarded SEC_MERGE sections, use
+                            the kept section.  relocate_section should
+                            have already handled discarded linkonce
+                            sections.  */
                          if (bfd_is_abs_section (osec)
                              && sec->kept_section != NULL
                              && sec->kept_section->output_section != NULL)
@@ -7940,7 +8883,8 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
 
       /* Write out the modified section contents.  */
       if (bed->elf_backend_write_section
-         && (*bed->elf_backend_write_section) (output_bfd, o, contents))
+         && (*bed->elf_backend_write_section) (output_bfd, finfo->info, o,
+                                               contents))
        {
          /* Section written out.  */
        }
@@ -8414,13 +9358,18 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
                {
                  Elf_Internal_Rela * relocs;
 
-                 relocs = _bfd_elf_link_read_relocs (abfd, sec, NULL, NULL,
+                 relocs = _bfd_elf_link_read_relocs (sec->owner, sec,
+                                                     NULL, NULL,
                                                      info->keep_memory);
 
-                 reloc_count = (*bed->elf_backend_count_relocs) (sec, relocs);
+                 if (relocs != NULL)
+                   {
+                     reloc_count
+                       = (*bed->elf_backend_count_relocs) (sec, relocs);
 
-                 if (elf_section_data (o)->relocs != relocs)
-                   free (relocs);
+                     if (elf_section_data (sec)->relocs != relocs)
+                       free (relocs);
+                   }
                }
 
              if (sec->rawsize > max_contents_size)
@@ -8793,6 +9742,18 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
        }
     }
 
+  /* Free symbol buffer if needed.  */
+  if (!info->reduce_memory_overheads)
+    {
+      for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
+       if (bfd_get_flavour (sub) == bfd_target_elf_flavour
+           && elf_tdata (sub)->symbuf)
+         {
+           free (elf_tdata (sub)->symbuf);
+           elf_tdata (sub)->symbuf = NULL;
+         }
+    }
+
   /* Output any global symbols that got converted to local in a
      version script or due to symbol visibility.  We do this in a
      separate step since ELF requires all local symbols to appear
@@ -9197,8 +10158,8 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
 
              if (dyn.d_tag == DT_TEXTREL)
                {
-                 _bfd_error_handler
-                   (_("warning: creating a DT_TEXTREL in a shared object."));
+                info->callbacks->einfo 
+                   (_("%P: warning: creating a DT_TEXTREL in a shared object.\n"));
                  break;
                }
            }
@@ -9333,10 +10294,6 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
 \f
 /* Garbage collect unused sections.  */
 
-typedef asection * (*gc_mark_hook_fn)
-  (asection *, struct bfd_link_info *, Elf_Internal_Rela *,
-   struct elf_link_hash_entry *, Elf_Internal_Sym *);
-
 /* Default gc_mark_hook.  */
 
 asection *
@@ -9374,7 +10331,7 @@ _bfd_elf_gc_mark_hook (asection *sec,
 bfd_boolean
 _bfd_elf_gc_mark (struct bfd_link_info *info,
                  asection *sec,
-                 gc_mark_hook_fn gc_mark_hook)
+                 elf_gc_mark_hook_fn gc_mark_hook)
 {
   bfd_boolean ret;
   bfd_boolean is_eh;
@@ -9743,9 +10700,7 @@ bfd_elf_gc_sections (bfd *abfd, struct bfd_link_info *info)
 {
   bfd_boolean ok = TRUE;
   bfd *sub;
-  asection * (*gc_mark_hook)
-    (asection *, struct bfd_link_info *, Elf_Internal_Rela *,
-     struct elf_link_hash_entry *h, Elf_Internal_Sym *);
+  elf_gc_mark_hook_fn gc_mark_hook;
   const struct elf_backend_data *bed = get_elf_backend_data (abfd);
 
   if (!bed->can_gc_sections
@@ -9787,11 +10742,15 @@ bfd_elf_gc_sections (bfd *abfd, struct bfd_link_info *info)
        continue;
 
       for (o = sub->sections; o != NULL; o = o->next)
-       if ((o->flags & SEC_KEEP) != 0 && !o->gc_mark)
+       if ((o->flags & (SEC_EXCLUDE | SEC_KEEP)) == SEC_KEEP && !o->gc_mark)
          if (!_bfd_elf_gc_mark (info, o, gc_mark_hook))
            return FALSE;
     }
 
+  /* Allow the backend to mark additional target specific sections.  */
+  if (bed->gc_mark_extra_sections)
+    bed->gc_mark_extra_sections(info, gc_mark_hook);
+
   /* ... again for sections marked from eh_frame.  */
   for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
     {
@@ -9800,13 +10759,15 @@ bfd_elf_gc_sections (bfd *abfd, struct bfd_link_info *info)
       if (bfd_get_flavour (sub) != bfd_target_elf_flavour)
        continue;
 
-      /* Keep .gcc_except_table.* if the associated .text.* is
+      /* Keep .gcc_except_table.* if the associated .text.* (or the
+        associated .gnu.linkonce.t.* if .text.* doesn't exist) is
         marked.  This isn't very nice, but the proper solution,
         splitting .eh_frame up and using comdat doesn't pan out
         easily due to needing special relocs to handle the
         difference of two symbols in separate sections.
         Don't keep code sections referenced by .eh_frame.  */
 #define TEXT_PREFIX                    ".text."
+#define TEXT_PREFIX2                   ".gnu.linkonce.t."
 #define GCC_EXCEPT_TABLE_PREFIX                ".gcc_except_table."
       for (o = sub->sections; o != NULL; o = o->next)
        if (!o->gc_mark && o->gc_mark_from_eh && (o->flags & SEC_CODE) == 0)
@@ -9816,15 +10777,30 @@ bfd_elf_gc_sections (bfd *abfd, struct bfd_link_info *info)
                char *fn_name;
                const char *sec_name;
                asection *fn_text;
-               unsigned o_name_prefix_len  = strlen (GCC_EXCEPT_TABLE_PREFIX);
-               unsigned fn_name_prefix_len = strlen (TEXT_PREFIX);
+               unsigned o_name_prefix_len , fn_name_prefix_len, tmp;
 
+               o_name_prefix_len = strlen (GCC_EXCEPT_TABLE_PREFIX);
                sec_name = o->name + o_name_prefix_len;
-               fn_name = bfd_malloc (strlen (sec_name) + fn_name_prefix_len + 1);
+               fn_name_prefix_len = strlen (TEXT_PREFIX);
+               tmp = strlen (TEXT_PREFIX2);
+               if (tmp > fn_name_prefix_len)
+                 fn_name_prefix_len = tmp;
+               fn_name
+                 = bfd_malloc (fn_name_prefix_len + strlen (sec_name) + 1);
                if (fn_name == NULL)
                  return FALSE;
+
+               /* Try the first prefix.  */
                sprintf (fn_name, "%s%s", TEXT_PREFIX, sec_name);
                fn_text = bfd_get_section_by_name (sub, fn_name);
+
+               /* Try the second prefix.  */
+               if (fn_text == NULL)
+                 {
+                   sprintf (fn_name, "%s%s", TEXT_PREFIX2, sec_name);
+                   fn_text = bfd_get_section_by_name (sub, fn_name);
+                 }
+
                free (fn_name);
                if (fn_text == NULL || !fn_text->gc_mark)
                  continue;
@@ -10173,12 +11149,15 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
       if ((abfd->flags & DYNAMIC) != 0)
        continue;
 
-      eh = bfd_get_section_by_name (abfd, ".eh_frame");
-      if (info->relocatable
-         || (eh != NULL
+      eh = NULL;
+      if (!info->relocatable)
+       {
+         eh = bfd_get_section_by_name (abfd, ".eh_frame");
+         if (eh != NULL
              && (eh->size == 0
-                 || bfd_is_abs_section (eh->output_section))))
-       eh = NULL;
+                 || bfd_is_abs_section (eh->output_section)))
+           eh = NULL;
+       }
 
       stab = bfd_get_section_by_name (abfd, ".stab");
       if (stab != NULL
@@ -10219,7 +11198,10 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
                                                 cookie.locsymcount, 0,
                                                 NULL, NULL, NULL);
          if (cookie.locsyms == NULL)
-           return FALSE;
+           {
+             info->callbacks->einfo (_("%P%X: can not read symbols: %E\n"));
+             return FALSE;
+           }
        }
 
       if (stab != NULL)
@@ -10289,39 +11271,28 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
 }
 
 void
-_bfd_elf_section_already_linked (bfd *abfd, struct bfd_section * sec)
+_bfd_elf_section_already_linked (bfd *abfd, struct bfd_section *sec,
+                                struct bfd_link_info *info)
 {
   flagword flags;
   const char *name, *p;
   struct bfd_section_already_linked *l;
   struct bfd_section_already_linked_hash_entry *already_linked_list;
-  asection *group;
 
-  /* A single member comdat group section may be discarded by a
-     linkonce section. See below.  */
   if (sec->output_section == bfd_abs_section_ptr)
     return;
 
   flags = sec->flags;
 
-  /* Check if it belongs to a section group.  */
-  group = elf_sec_group (sec);
-
-  /* Return if it isn't a linkonce section nor a member of a group.  A
-     comdat group section also has SEC_LINK_ONCE set.  */
-  if ((flags & SEC_LINK_ONCE) == 0 && group == NULL)
+  /* Return if it isn't a linkonce section.  A comdat group section
+     also has SEC_LINK_ONCE set.  */
+  if ((flags & SEC_LINK_ONCE) == 0)
     return;
 
-  if (group)
-    {
-      /* If this is the member of a single member comdat group, check if
-        the group should be discarded.  */
-      if (elf_next_in_group (sec) == sec
-         && (group->flags & SEC_LINK_ONCE) != 0)
-       sec = group;
-      else
-       return;
-    }
+  /* Don't put group member sections on our list of already linked
+     sections.  They are handled as a group via their group section.  */
+  if (elf_sec_group (sec) != NULL)
+    return;
 
   /* FIXME: When doing a relocatable link, we may have trouble
      copying relocations in other sections that refer to local symbols
@@ -10352,11 +11323,8 @@ _bfd_elf_section_already_linked (bfd *abfd, struct bfd_section * sec)
 
   for (l = already_linked_list->entry; l != NULL; l = l->next)
     {
-      /* We may have 3 different sections on the list: group section,
-        comdat section and linkonce section. SEC may be a linkonce or
-        group section. We match a group section with a group section,
-        a linkonce section with a linkonce section, and ignore comdat
-        section.  */
+      /* We may have 2 different types of sections on the list: group
+        sections and linkonce sections.  Match like sections.  */
       if ((flags & SEC_GROUP) == (l->sec->flags & SEC_GROUP)
          && strcmp (name, l->sec->name) == 0
          && bfd_coff_get_comdat_section (l->sec->owner, l->sec) == NULL)
@@ -10444,31 +11412,28 @@ _bfd_elf_section_already_linked (bfd *abfd, struct bfd_section * sec)
        }
     }
 
-  if (group)
+  /* A single member comdat group section may be discarded by a
+     linkonce section and vice versa.  */
+
+  if ((flags & SEC_GROUP) != 0)
     {
-      /* If this is the member of a single member comdat group and the
-        group hasn't be discarded, we check if it matches a linkonce
-        section. We only record the discarded comdat group. Otherwise
-        the undiscarded group will be discarded incorrectly later since
-        itself has been recorded.  */
-      for (l = already_linked_list->entry; l != NULL; l = l->next)
-       if ((l->sec->flags & SEC_GROUP) == 0
-           && bfd_coff_get_comdat_section (l->sec->owner, l->sec) == NULL
-           && bfd_elf_match_symbols_in_sections (l->sec,
-                                                 elf_next_in_group (sec)))
-         {
-           elf_next_in_group (sec)->output_section = bfd_abs_section_ptr;
-           elf_next_in_group (sec)->kept_section = l->sec;
-           group->output_section = bfd_abs_section_ptr;
-           break;
-         }
-      if (l == NULL)
-       return;
+      asection *first = elf_next_in_group (sec);
+
+      if (first != NULL && elf_next_in_group (first) == first)
+       /* Check this single member group against linkonce sections.  */
+       for (l = already_linked_list->entry; l != NULL; l = l->next)
+         if ((l->sec->flags & SEC_GROUP) == 0
+             && bfd_coff_get_comdat_section (l->sec->owner, l->sec) == NULL
+             && bfd_elf_match_symbols_in_sections (l->sec, first, info))
+           {
+             first->output_section = bfd_abs_section_ptr;
+             first->kept_section = l->sec;
+             sec->output_section = bfd_abs_section_ptr;
+             break;
+           }
     }
   else
-    /* There is no direct match. But for linkonce section, we should
-       check if there is a match with comdat group member. We always
-       record the linkonce section, discarded or not.  */
+    /* Check this linkonce section against single member groups.  */
     for (l = already_linked_list->entry; l != NULL; l = l->next)
       if (l->sec->flags & SEC_GROUP)
        {
@@ -10476,10 +11441,10 @@ _bfd_elf_section_already_linked (bfd *abfd, struct bfd_section * sec)
 
          if (first != NULL
              && elf_next_in_group (first) == first
-             && bfd_elf_match_symbols_in_sections (first, sec))
+             && bfd_elf_match_symbols_in_sections (first, sec, info))
            {
              sec->output_section = bfd_abs_section_ptr;
-             sec->kept_section = l->sec;
+             sec->kept_section = first;
              break;
            }
        }
This page took 0.047265 seconds and 4 git commands to generate.