* cache.c (cache_bread): Set bfd_error_file_truncated if EOF
[deliverable/binutils-gdb.git] / bfd / elflink.c
index cac22b7c720af62e4cbc6fc506ea3a1038ded691..c658b140d7ba740e635a773c6f5bee8ef12123de 100644 (file)
@@ -1,6 +1,6 @@
 /* ELF linking support for BFD.
    Copyright 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
-   2005, 2006, 2007 Free Software Foundation, Inc.
+   2005, 2006, 2007, 2008 Free Software Foundation, Inc.
 
    This file is part of BFD, the Binary File Descriptor library.
 
@@ -452,7 +452,7 @@ bfd_elf_link_mark_dynamic_symbol (struct bfd_link_info *info,
        && (h->type == STT_OBJECT
           || (sym != NULL
               && ELF_ST_TYPE (sym->st_info) == STT_OBJECT)))
-      || (d != NULL 
+      || (d != NULL
          && h->root.type == bfd_link_hash_new
          && (*d->match) (&d->head, NULL, h->root.root.string)))
     h->dynamic = 1;
@@ -501,7 +501,7 @@ bfd_elf_record_link_assignment (bfd *output_bfd,
       break;
     case bfd_link_hash_indirect:
       /* We had a versioned symbol in a dynamic library.  We make the
-         the versioned symbol point to this one.  */
+        the versioned symbol point to this one.  */
       bed = get_elf_backend_data (output_bfd);
       hv = h;
       while (hv->root.type == bfd_link_hash_indirect
@@ -2321,7 +2321,7 @@ _bfd_elf_link_output_relocs (bfd *output_bfd,
       (*_bfd_error_handler)
        (_("%B: relocation size mismatch in %B section %A"),
         output_bfd, input_section->owner, input_section);
-      bfd_set_error (bfd_error_wrong_object_format);
+      bfd_set_error (bfd_error_wrong_format);
       return FALSE;
     }
 
@@ -3252,6 +3252,40 @@ elf_finalize_dynstr (bfd *output_bfd, struct bfd_link_info *info)
   return TRUE;
 }
 \f
+/* Return TRUE iff relocations for INPUT are compatible with OUTPUT.
+   The default is to only match when the INPUT and OUTPUT are exactly
+   the same target.  */
+
+bfd_boolean
+_bfd_elf_default_relocs_compatible (const bfd_target *input,
+                                   const bfd_target *output)
+{
+  return input == output;
+}
+
+/* Return TRUE iff relocations for INPUT are compatible with OUTPUT.
+   This version is used when different targets for the same architecture
+   are virtually identical.  */
+
+bfd_boolean
+_bfd_elf_relocs_compatible (const bfd_target *input,
+                           const bfd_target *output)
+{
+  const struct elf_backend_data *obed, *ibed;
+
+  if (input == output)
+    return TRUE;
+
+  ibed = xvec_get_elf_backend_data (input);
+  obed = xvec_get_elf_backend_data (output);
+
+  if (ibed->arch != obed->arch)
+    return FALSE;
+
+  /* If both backends are using this function, deem them compatible.  */
+  return ibed->relocs_compatible == obed->relocs_compatible;
+}
+
 /* Add symbols from an ELF object file to the linker hash table.  */
 
 static bfd_boolean
@@ -4639,8 +4673,8 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
      different format.  It probably can't be done.  */
   if (! dynamic
       && is_elf_hash_table (htab)
-      && htab->root.creator == abfd->xvec
-      && bed->check_relocs != NULL)
+      && bed->check_relocs != NULL
+      && (*bed->relocs_compatible) (abfd->xvec, htab->root.creator))
     {
       asection *o;
 
@@ -4997,7 +5031,7 @@ struct hash_codes_info
   unsigned long *hashcodes;
   bfd_boolean error;
 };
-  
+
 /* This function will be called though elf_link_hash_traverse to store
    all hash value of the exported symbols in an array.  */
 
@@ -6889,7 +6923,7 @@ elf_create_symbuf (bfd_size_type symcount, Elf_Internal_Sym *isymbuf)
 /* Check if 2 sections define the same set of local and global
    symbols.  */
 
-bfd_boolean
+static bfd_boolean
 bfd_elf_match_symbols_in_sections (asection *sec1, asection *sec2,
                                   struct bfd_link_info *info)
 {
@@ -6908,13 +6942,6 @@ bfd_elf_match_symbols_in_sections (asection *sec1, asection *sec2,
   bfd1 = sec1->owner;
   bfd2 = sec2->owner;
 
-  /* If both are .gnu.linkonce sections, they have to have the same
-     section name.  */
-  if (CONST_STRNEQ (sec1->name, ".gnu.linkonce")
-      && CONST_STRNEQ (sec2->name, ".gnu.linkonce"))
-    return strcmp (sec1->name + sizeof ".gnu.linkonce",
-                  sec2->name + sizeof ".gnu.linkonce") == 0;
-
   /* Both sections have to be in ELF.  */
   if (bfd_get_flavour (bfd1) != bfd_target_elf_flavour
       || bfd_get_flavour (bfd2) != bfd_target_elf_flavour)
@@ -6923,15 +6950,6 @@ bfd_elf_match_symbols_in_sections (asection *sec1, asection *sec2,
   if (elf_section_type (sec1) != elf_section_type (sec2))
     return FALSE;
 
-  if ((elf_section_flags (sec1) & SHF_GROUP) != 0
-      && (elf_section_flags (sec2) & SHF_GROUP) != 0)
-    {
-      /* If both are members of section groups, they have to have the
-        same group name.  */
-      if (strcmp (elf_group_name (sec1), elf_group_name (sec2)) != 0)
-       return FALSE;
-    }
-
   shndx1 = _bfd_elf_section_from_bfd_section (bfd1, sec1);
   shndx2 = _bfd_elf_section_from_bfd_section (bfd2, sec2);
   if (shndx1 == -1 || shndx2 == -1)
@@ -7203,7 +7221,7 @@ struct elf_outext_info
 
    Complex relocations are generalized, self-describing relocations.  The
    implementation of them consists of two parts: complex symbols, and the
-   relocations themselves. 
+   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
@@ -7229,11 +7247,11 @@ struct elf_outext_info
    <unary-operator> := as in C, plus "0-" for unambiguous negation.  */
 
 static void
-set_symbol_value (bfd *                         bfd_with_globals,
-                 Elf_Internal_Sym *            isymbuf,
-                 size_t                        locsymcount,
-                 size_t                        symidx,
-                 bfd_vma                       val)
+set_symbol_value (bfd *bfd_with_globals,
+                 Elf_Internal_Sym *isymbuf,
+                 size_t locsymcount,
+                 size_t symidx,
+                 bfd_vma val)
 {
   struct elf_link_hash_entry **sym_hashes;
   struct elf_link_hash_entry *h;
@@ -7269,20 +7287,20 @@ set_symbol_value (bfd *                         bfd_with_globals,
   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,
-               Elf_Internal_Sym *            isymbuf,
-               size_t                        locsymcount)
-{
-  Elf_Internal_Sym *            sym;
-  struct bfd_link_hash_entry *  global_entry;
-  const char *                  candidate = NULL;
-  Elf_Internal_Shdr *           symtab_hdr;
-  size_t                        i;
-  
+static bfd_boolean
+resolve_symbol (const char *name,
+               bfd *input_bfd,
+               struct elf_final_link_info *finfo,
+               bfd_vma *result,
+               Elf_Internal_Sym *isymbuf,
+               size_t locsymcount)
+{
+  Elf_Internal_Sym *sym;
+  struct bfd_link_hash_entry *global_entry;
+  const char *candidate = NULL;
+  Elf_Internal_Shdr *symtab_hdr;
+  size_t i;
+
   symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
 
   for (i = 0; i < locsymcount; ++ i)
@@ -7314,35 +7332,36 @@ resolve_symbol (const char *                  name,
     }
 
   /* Hmm, haven't found it yet. perhaps it is a global.  */
-  global_entry = bfd_link_hash_lookup (finfo->info->hash, name, FALSE, FALSE, TRUE);
+  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;
+      *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.8lx\n",
              global_entry->root.string, (unsigned long) *result);
 #endif
       return TRUE;
-    } 
+    }
 
   return FALSE;
 }
 
 static bfd_boolean
-resolve_section (const char *  name,
-                asection *    sections,
-                bfd_vma *     result)
+resolve_section (const char *name,
+                asection *sections,
+                bfd_vma *result)
 {
-  asection *    curr;
-  unsigned int  len;
+  asection *curr;
+  unsigned int len;
 
-  for (curr = sections; curr; curr = curr->next)    
+  for (curr = sections; curr; curr = curr->next)
     if (strcmp (curr->name, name) == 0)
       {
        *result = curr->vma;
@@ -7350,10 +7369,10 @@ resolve_section (const char *  name,
       }
 
   /* Hmm. still haven't found it. try pseudo-section names.  */
-  for (curr = sections; curr; curr = curr->next)    
+  for (curr = sections; curr; curr = curr->next)
     {
       len = strlen (curr->name);
-      if (len > strlen (name)) 
+      if (len > strlen (name))
        continue;
 
       if (strncmp (curr->name, name, len) == 0)
@@ -7367,46 +7386,45 @@ resolve_section (const char *  name,
          /* Insert more pseudo-section names here, if you like.  */
        }
     }
-  
+
   return FALSE;
 }
 
 static void
-undefined_reference (const char *  reftype,
-                    const char *  name)
+undefined_reference (const char *reftype, const char *name)
 {
-  _bfd_error_handler (_("undefined %s reference in complex symbol: %s"), reftype, name);
+  _bfd_error_handler (_("undefined %s reference in complex symbol: %s"),
+                     reftype, name);
 }
 
 static bfd_boolean
-eval_symbol (bfd_vma *                     result,
-            const char **                 symp,
-            bfd *                         input_bfd,
-            struct elf_final_link_info *  finfo,
-            bfd_vma                       dot,
-            Elf_Internal_Sym *            isymbuf,
-            size_t                        locsymcount,
-            int                           signed_p)
-{
-  int           len;
-  int           symlen;
-  bfd_vma       a;
-  bfd_vma       b;
-  const int     bufsz = 4096;
-  char          symbuf [bufsz];
+eval_symbol (bfd_vma *result,
+            const char **symp,
+            bfd *input_bfd,
+            struct elf_final_link_info *finfo,
+            bfd_vma dot,
+            Elf_Internal_Sym *isymbuf,
+            size_t locsymcount,
+            int signed_p)
+{
+  size_t len;
+  size_t symlen;
+  bfd_vma a;
+  bfd_vma b;
+  char symbuf[4096];
   const char *sym = *symp;
-  const char *  symend;
-  bfd_boolean   symbol_is_section = FALSE;
+  const char *symend;
+  bfd_boolean symbol_is_section = FALSE;
 
   len = strlen (sym);
   symend = sym + len;
 
-  if (len < 1 || len > bufsz)
+  if (len < 1 || len > sizeof (symbuf))
     {
       bfd_set_error (bfd_error_invalid_operation);
       return FALSE;
     }
-  
+
   switch (* sym)
     {
     case '.':
@@ -7421,27 +7439,27 @@ eval_symbol (bfd_vma *                     result,
 
     case 'S':
       symbol_is_section = TRUE;
-    case 's':      
+    case 's':
       ++sym;
       symlen = strtol (sym, (char **) symp, 10);
       sym = *symp + 1; /* Skip the trailing ':'.  */
 
-      if ((symend < sym) || ((symlen + 1) > bufsz))
+      if (symend < sym || symlen + 1 > sizeof (symbuf))
        {
          bfd_set_error (bfd_error_invalid_operation);
          return FALSE;
        }
 
       memcpy (symbuf, sym, symlen);
-      symbuf [symlen] = '\0';
+      symbuf[symlen] = '\0';
       *symp = sym + symlen;
-      
-      /* Is it always possible, with complex symbols, that gas "mis-guessed" 
+
+      /* 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 (symbol_is_section)
        {
          if (!resolve_section (symbuf, finfo->output_bfd->sections, result)
              && !resolve_symbol (symbuf, input_bfd, finfo, result,
@@ -7450,8 +7468,8 @@ eval_symbol (bfd_vma *                     result,
              undefined_reference ("section", symbuf);
              return FALSE;
            }
-       } 
-      else 
+       }
+      else
        {
          if (!resolve_symbol (symbuf, input_bfd, finfo, result,
                               isymbuf, locsymcount)
@@ -7464,23 +7482,23 @@ eval_symbol (bfd_vma *                     result,
        }
 
       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 (*sym == ':')                                         \
+       ++sym;                                                  \
       *symp = sym;                                             \
       if (!eval_symbol (&a, symp, input_bfd, finfo, dot,       \
                        isymbuf, locsymcount, signed_p))        \
-        return FALSE;                                          \
-      if (signed_p)                                             \
+       return FALSE;                                           \
+      if (signed_p)                                            \
        *result = op ((bfd_signed_vma) a);                      \
-      else                                                      \
-        * result = op a;                                        \
+      else                                                     \
+       *result = op a;                                         \
       return TRUE;                                             \
     }
 
@@ -7488,20 +7506,20 @@ eval_symbol (bfd_vma *                     result,
   if (strncmp (sym, #op, strlen (#op)) == 0)                   \
     {                                                          \
       sym += strlen (#op);                                     \
-      if (* sym == ':')                                                \
-        ++ sym;                                                        \
+      if (*sym == ':')                                         \
+       ++sym;                                                  \
       *symp = sym;                                             \
       if (!eval_symbol (&a, symp, input_bfd, finfo, dot,       \
                        isymbuf, locsymcount, signed_p))        \
-        return FALSE;                                          \
+       return FALSE;                                           \
       ++*symp;                                                 \
       if (!eval_symbol (&b, symp, input_bfd, finfo, dot,       \
                        isymbuf, locsymcount, signed_p))        \
-        return FALSE;                                          \
-      if (signed_p)                                             \
+       return FALSE;                                           \
+      if (signed_p)                                            \
        *result = ((bfd_signed_vma) a) op ((bfd_signed_vma) b); \
-      else                                                      \
-        * result = a op b;                                      \
+      else                                                     \
+       *result = a op b;                                       \
       return TRUE;                                             \
     }
 
@@ -7536,15 +7554,15 @@ eval_symbol (bfd_vma *                     result,
 }
 
 static void
-put_value (bfd_vma        size,
-          unsigned long  chunksz,
-          bfd *          input_bfd,
-          bfd_vma        x,
-          bfd_byte *     location)
+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)) 
+  for (; size; size -= chunksz, location -= chunksz, x >>= (chunksz * 8))
     {
       switch (chunksz)
        {
@@ -7571,15 +7589,15 @@ put_value (bfd_vma        size,
     }
 }
 
-static bfd_vma 
-get_value (bfd_vma        size,
-          unsigned long  chunksz,
-          bfd *          input_bfd,
-          bfd_byte *     location)
+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) 
+  for (; size; size -= chunksz, location += chunksz)
     {
       switch (chunksz)
        {
@@ -7607,17 +7625,16 @@ get_value (bfd_vma        size,
   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)
+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;
@@ -7629,25 +7646,26 @@ decode_complex_addend
   * trunc_p   = (encoded >> 29) & 1;
 }
 
-void
+bfd_reloc_status_type
 bfd_elf_perform_complex_relocation (bfd *input_bfd,
-                                   asection *input_section,
+                                   asection *input_section ATTRIBUTE_UNUSED,
                                    bfd_byte *contents,
                                    Elf_Internal_Rela *rel,
                                    bfd_vma relocation)
 {
   bfd_vma shift, x, mask;
   unsigned long start, oplen, len, wordsz, chunksz, lsb0_p, signed_p, trunc_p;
+  bfd_reloc_status_type r;
 
   /*  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.).  */ 
+      word size, etc) encoded within it.).  */
 
-  decode_complex_addend (& start, & oplen, & len, & wordsz, 
-                        & chunksz, & lsb0_p, & signed_p, 
-                        & trunc_p, rel->r_addend);
+  decode_complex_addend (&start, &oplen, &len, &wordsz,
+                        &chunksz, &lsb0_p, &signed_p,
+                        &trunc_p, rel->r_addend);
 
   mask = (((1L << (len - 1)) - 1) << 1) | 1;
 
@@ -7656,7 +7674,7 @@ bfd_elf_perform_complex_relocation (bfd *input_bfd,
   else
     shift = (8 * wordsz) - (start + len);
 
-  x = get_value (wordsz, chunksz, input_bfd, contents + rel->r_offset);          
+  x = get_value (wordsz, chunksz, input_bfd, contents + rel->r_offset);
 
 #ifdef DEBUG
   printf ("Doing complex reloc: "
@@ -7667,21 +7685,15 @@ bfd_elf_perform_complex_relocation (bfd *input_bfd,
          oplen, x, mask,  relocation);
 #endif
 
+  r = bfd_reloc_ok;
   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);
-    }
-         
+    /* Now do an overflow check.  */
+    r = bfd_check_overflow ((signed_p
+                            ? complain_overflow_signed
+                            : complain_overflow_unsigned),
+                           len, 0, (8 * wordsz),
+                           relocation);
+
   /* Do the deed.  */
   x = (x & ~(mask << shift)) | ((relocation & mask) << shift);
 
@@ -7690,10 +7702,11 @@ bfd_elf_perform_complex_relocation (bfd *input_bfd,
          "         shifted mask: %8.8lx\n"
          " shifted/masked reloc: %8.8lx\n"
          "               result: %8.8lx\n",
-         relocation, (mask << shift), 
+         relocation, (mask << shift),
          ((relocation & mask) << shift), x);
 #endif
   put_value (wordsz, chunksz, input_bfd, x, contents + rel->r_offset);
+  return r;
 }
 
 /* When performing a relocatable link, the input relocations are
@@ -8189,7 +8202,7 @@ check_dynsym (bfd *abfd, Elf_Internal_Sym *sym)
   if (sym->st_shndx > SHN_HIRESERVE)
     {
       /* The gABI doesn't support dynamic symbols in output sections
-         beyond 64k.  */
+        beyond 64k.  */
       (*_bfd_error_handler)
        (_("%B: Too many sections: %d (>= %d)"),
         abfd, bfd_count_sections (abfd), SHN_LORESERVE);
@@ -8523,10 +8536,15 @@ elf_link_output_extsym (struct elf_link_hash_entry *h, void *data)
                sym.st_value += input_sec->output_section->vma;
                if (h->type == STT_TLS)
                  {
-                   /* STT_TLS symbols are relative to PT_TLS segment
-                      base.  */
-                   BFD_ASSERT (elf_hash_table (finfo->info)->tls_sec != NULL);
-                   sym.st_value -= elf_hash_table (finfo->info)->tls_sec->vma;
+                   asection *tls_sec = elf_hash_table (finfo->info)->tls_sec;
+                   if (tls_sec != NULL)
+                     sym.st_value -= tls_sec->vma;
+                   else
+                     {
+                       /* The TLS section may have been garbage collected.  */
+                       BFD_ASSERT (finfo->info->gc_sections
+                                   && !input_sec->gc_mark);
+                     }
                  }
              }
          }
@@ -8787,7 +8805,9 @@ _bfd_elf_check_kept_section (asection *sec, struct bfd_link_info *info)
     {
       if ((kept->flags & SEC_GROUP) != 0)
        kept = match_group_member (sec, kept, info);
-      if (kept != NULL && sec->size != kept->size)
+      if (kept != NULL
+         && ((sec->rawsize != 0 ? sec->rawsize : sec->size)
+             != (kept->rawsize != 0 ? kept->rawsize : kept->size)))
        kept = NULL;
       sec->kept_section = kept;
     }
@@ -9491,6 +9511,7 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
        default:
          {
            if (! (o->flags & SEC_EXCLUDE)
+               && ! (o->output_section->flags & SEC_NEVER_LOAD)
                && ! bfd_set_section_contents (output_bfd, o->output_section,
                                               contents,
                                               (file_ptr) o->output_offset,
@@ -9917,7 +9938,7 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
                 elf_link_input_bfd ignores this section.  */
              input_section->flags &= ~SEC_HAS_CONTENTS;
            }
-           
+
          attr_size = bfd_elf_obj_attr_size (abfd);
          if (attr_size)
            {
@@ -10776,7 +10797,7 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
 
              if (dyn.d_tag == DT_TEXTREL)
                {
-                info->callbacks->einfo 
+                info->callbacks->einfo
                    (_("%P: warning: creating a DT_TEXTREL in a shared object.\n"));
                  break;
                }
@@ -10920,6 +10941,139 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
   return FALSE;
 }
 \f
+/* Initialize COOKIE for input bfd ABFD.  */
+
+static bfd_boolean
+init_reloc_cookie (struct elf_reloc_cookie *cookie,
+                  struct bfd_link_info *info, bfd *abfd)
+{
+  Elf_Internal_Shdr *symtab_hdr;
+  const struct elf_backend_data *bed;
+
+  bed = get_elf_backend_data (abfd);
+  symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+
+  cookie->abfd = abfd;
+  cookie->sym_hashes = elf_sym_hashes (abfd);
+  cookie->bad_symtab = elf_bad_symtab (abfd);
+  if (cookie->bad_symtab)
+    {
+      cookie->locsymcount = symtab_hdr->sh_size / bed->s->sizeof_sym;
+      cookie->extsymoff = 0;
+    }
+  else
+    {
+      cookie->locsymcount = symtab_hdr->sh_info;
+      cookie->extsymoff = symtab_hdr->sh_info;
+    }
+
+  if (bed->s->arch_size == 32)
+    cookie->r_sym_shift = 8;
+  else
+    cookie->r_sym_shift = 32;
+
+  cookie->locsyms = (Elf_Internal_Sym *) symtab_hdr->contents;
+  if (cookie->locsyms == NULL && cookie->locsymcount != 0)
+    {
+      cookie->locsyms = bfd_elf_get_elf_syms (abfd, symtab_hdr,
+                                             cookie->locsymcount, 0,
+                                             NULL, NULL, NULL);
+      if (cookie->locsyms == NULL)
+       {
+         info->callbacks->einfo (_("%P%X: can not read symbols: %E\n"));
+         return FALSE;
+       }
+      if (info->keep_memory)
+       symtab_hdr->contents = (bfd_byte *) cookie->locsyms;
+    }
+  return TRUE;
+}
+
+/* Free the memory allocated by init_reloc_cookie, if appropriate.  */
+
+static void
+fini_reloc_cookie (struct elf_reloc_cookie *cookie, bfd *abfd)
+{
+  Elf_Internal_Shdr *symtab_hdr;
+
+  symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+  if (cookie->locsyms != NULL
+      && symtab_hdr->contents != (unsigned char *) cookie->locsyms)
+    free (cookie->locsyms);
+}
+
+/* Initialize the relocation information in COOKIE for input section SEC
+   of input bfd ABFD.  */
+
+static bfd_boolean
+init_reloc_cookie_rels (struct elf_reloc_cookie *cookie,
+                       struct bfd_link_info *info, bfd *abfd,
+                       asection *sec)
+{
+  const struct elf_backend_data *bed;
+
+  if (sec->reloc_count == 0)
+    {
+      cookie->rels = NULL;
+      cookie->relend = NULL;
+    }
+  else
+    {
+      bed = get_elf_backend_data (abfd);
+
+      cookie->rels = _bfd_elf_link_read_relocs (abfd, sec, NULL, NULL,
+                                               info->keep_memory);
+      if (cookie->rels == NULL)
+       return FALSE;
+      cookie->rel = cookie->rels;
+      cookie->relend = (cookie->rels
+                       + sec->reloc_count * bed->s->int_rels_per_ext_rel);
+    }
+  cookie->rel = cookie->rels;
+  return TRUE;
+}
+
+/* Free the memory allocated by init_reloc_cookie_rels,
+   if appropriate.  */
+
+static void
+fini_reloc_cookie_rels (struct elf_reloc_cookie *cookie,
+                       asection *sec)
+{
+  if (cookie->rels && elf_section_data (sec)->relocs != cookie->rels)
+    free (cookie->rels);
+}
+
+/* Initialize the whole of COOKIE for input section SEC.  */
+
+static bfd_boolean
+init_reloc_cookie_for_section (struct elf_reloc_cookie *cookie,
+                              struct bfd_link_info *info,
+                              asection *sec)
+{
+  if (!init_reloc_cookie (cookie, info, sec->owner))
+    goto error1;
+  if (!init_reloc_cookie_rels (cookie, info, sec->owner, sec))
+    goto error2;
+  return TRUE;
+
+ error2:
+  fini_reloc_cookie (cookie, sec->owner);
+ error1:
+  return FALSE;
+}
+
+/* Free the memory allocated by init_reloc_cookie_for_section,
+   if appropriate.  */
+
+static void
+fini_reloc_cookie_for_section (struct elf_reloc_cookie *cookie,
+                              asection *sec)
+{
+  fini_reloc_cookie_rels (cookie, sec);
+  fini_reloc_cookie (cookie, sec->owner);
+}
+\f
 /* Garbage collect unused sections.  */
 
 /* Default gc_mark_hook.  */
@@ -10952,6 +11106,59 @@ _bfd_elf_gc_mark_hook (asection *sec,
   return NULL;
 }
 
+/* COOKIE->rel describes a relocation against section SEC, which is
+   a section we've decided to keep.  Return the section that contains
+   the relocation symbol, or NULL if no section contains it.  */
+
+asection *
+_bfd_elf_gc_mark_rsec (struct bfd_link_info *info, asection *sec,
+                      elf_gc_mark_hook_fn gc_mark_hook,
+                      struct elf_reloc_cookie *cookie)
+{
+  unsigned long r_symndx;
+  struct elf_link_hash_entry *h;
+
+  r_symndx = cookie->rel->r_info >> cookie->r_sym_shift;
+  if (r_symndx == 0)
+    return NULL;
+
+  if (r_symndx >= cookie->locsymcount
+      || ELF_ST_BIND (cookie->locsyms[r_symndx].st_info) != STB_LOCAL)
+    {
+      h = cookie->sym_hashes[r_symndx - cookie->extsymoff];
+      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;
+      return (*gc_mark_hook) (sec, info, cookie->rel, h, NULL);
+    }
+
+  return (*gc_mark_hook) (sec, info, cookie->rel, NULL,
+                         &cookie->locsyms[r_symndx]);
+}
+
+/* COOKIE->rel describes a relocation against section SEC, which is
+   a section we've decided to keep.  Mark the section that contains
+   the relocation symbol.  */
+
+bfd_boolean
+_bfd_elf_gc_mark_reloc (struct bfd_link_info *info,
+                       asection *sec,
+                       elf_gc_mark_hook_fn gc_mark_hook,
+                       struct elf_reloc_cookie *cookie)
+{
+  asection *rsec;
+
+  rsec = _bfd_elf_gc_mark_rsec (info, sec, gc_mark_hook, cookie);
+  if (rsec && !rsec->gc_mark)
+    {
+      if (bfd_get_flavour (rsec->owner) != bfd_target_elf_flavour)
+       rsec->gc_mark = 1;
+      else if (!_bfd_elf_gc_mark (info, rsec, gc_mark_hook))
+       return FALSE;
+    }
+  return TRUE;
+}
+
 /* The mark phase of garbage collection.  For a given section, mark
    it and any sections in this section's group, and all the sections
    which define symbols to which it refers.  */
@@ -10962,8 +11169,7 @@ _bfd_elf_gc_mark (struct bfd_link_info *info,
                  elf_gc_mark_hook_fn gc_mark_hook)
 {
   bfd_boolean ret;
-  bfd_boolean is_eh;
-  asection *group_sec;
+  asection *group_sec, *eh_frame;
 
   sec->gc_mark = 1;
 
@@ -10975,103 +11181,39 @@ _bfd_elf_gc_mark (struct bfd_link_info *info,
 
   /* Look through the section relocs.  */
   ret = TRUE;
-  is_eh = strcmp (sec->name, ".eh_frame") == 0;
-  if ((sec->flags & SEC_RELOC) != 0 && sec->reloc_count > 0)
+  eh_frame = elf_eh_frame_section (sec->owner);
+  if ((sec->flags & SEC_RELOC) != 0
+      && sec->reloc_count > 0
+      && sec != eh_frame)
     {
-      Elf_Internal_Rela *relstart, *rel, *relend;
-      Elf_Internal_Shdr *symtab_hdr;
-      struct elf_link_hash_entry **sym_hashes;
-      size_t nlocsyms;
-      size_t extsymoff;
-      bfd *input_bfd = sec->owner;
-      const struct elf_backend_data *bed = get_elf_backend_data (input_bfd);
-      Elf_Internal_Sym *isym = NULL;
-      int r_sym_shift;
-
-      symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
-      sym_hashes = elf_sym_hashes (input_bfd);
-
-      /* Read the local symbols.  */
-      if (elf_bad_symtab (input_bfd))
-       {
-         nlocsyms = symtab_hdr->sh_size / bed->s->sizeof_sym;
-         extsymoff = 0;
-       }
-      else
-       extsymoff = nlocsyms = symtab_hdr->sh_info;
+      struct elf_reloc_cookie cookie;
 
-      isym = (Elf_Internal_Sym *) symtab_hdr->contents;
-      if (isym == NULL && nlocsyms != 0)
+      if (!init_reloc_cookie_for_section (&cookie, info, sec))
+       ret = FALSE;
+      else
        {
-         isym = bfd_elf_get_elf_syms (input_bfd, symtab_hdr, nlocsyms, 0,
-                                      NULL, NULL, NULL);
-         if (isym == NULL)
-           return FALSE;
+         for (; cookie.rel < cookie.relend; cookie.rel++)
+           if (!_bfd_elf_gc_mark_reloc (info, sec, gc_mark_hook, &cookie))
+             {
+               ret = FALSE;
+               break;
+             }
+         fini_reloc_cookie_for_section (&cookie, sec);
        }
+    }
 
-      /* Read the relocations.  */
-      relstart = _bfd_elf_link_read_relocs (input_bfd, sec, NULL, NULL,
-                                           info->keep_memory);
-      if (relstart == NULL)
-       {
-         ret = FALSE;
-         goto out1;
-       }
-      relend = relstart + sec->reloc_count * bed->s->int_rels_per_ext_rel;
+  if (ret && eh_frame && elf_fde_list (sec))
+    {
+      struct elf_reloc_cookie cookie;
 
-      if (bed->s->arch_size == 32)
-       r_sym_shift = 8;
+      if (!init_reloc_cookie_for_section (&cookie, info, eh_frame))
+       ret = FALSE;
       else
-       r_sym_shift = 32;
-
-      for (rel = relstart; rel < relend; rel++)
-       {
-         unsigned long r_symndx;
-         asection *rsec;
-         struct elf_link_hash_entry *h;
-
-         r_symndx = rel->r_info >> r_sym_shift;
-         if (r_symndx == 0)
-           continue;
-
-         if (r_symndx >= nlocsyms
-             || ELF_ST_BIND (isym[r_symndx].st_info) != STB_LOCAL)
-           {
-             h = sym_hashes[r_symndx - extsymoff];
-             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;
-             rsec = (*gc_mark_hook) (sec, info, rel, h, NULL);
-           }
-         else
-           {
-             rsec = (*gc_mark_hook) (sec, info, rel, NULL, &isym[r_symndx]);
-           }
-
-         if (rsec && !rsec->gc_mark)
-           {
-             if (bfd_get_flavour (rsec->owner) != bfd_target_elf_flavour)
-               rsec->gc_mark = 1;
-             else if (is_eh)
-               rsec->gc_mark_from_eh = 1;
-             else if (!_bfd_elf_gc_mark (info, rsec, gc_mark_hook))
-               {
-                 ret = FALSE;
-                 goto out2;
-               }
-           }
-       }
-
-    out2:
-      if (elf_section_data (sec)->relocs != relstart)
-       free (relstart);
-    out1:
-      if (isym != NULL && symtab_hdr->contents != (unsigned char *) isym)
        {
-         if (! info->keep_memory)
-           free (isym);
-         else
-           symtab_hdr->contents = (unsigned char *) isym;
+         if (!_bfd_elf_gc_mark_fdes (info, sec, eh_frame,
+                                     gc_mark_hook, &cookie))
+           ret = FALSE;
+         fini_reloc_cookie_for_section (&cookie, eh_frame);
        }
     }
 
@@ -11321,6 +11463,29 @@ bfd_elf_gc_mark_dynamic_ref_symbol (struct elf_link_hash_entry *h, void *inf)
   return TRUE;
 }
 
+/* Keep all sections containing symbols undefined on the command-line,
+   and the section containing the entry symbol.  */
+
+void
+_bfd_elf_gc_keep (struct bfd_link_info *info)
+{
+  struct bfd_sym_chain *sym;
+
+  for (sym = info->gc_sym_list; sym != NULL; sym = sym->next)
+    {
+      struct elf_link_hash_entry *h;
+
+      h = elf_link_hash_lookup (elf_hash_table (info), sym->name,
+                               FALSE, FALSE, FALSE);
+
+      if (h != NULL
+         && (h->root.type == bfd_link_hash_defined
+             || h->root.type == bfd_link_hash_defweak)
+         && !bfd_is_abs_section (h->root.u.def.section))
+       h->root.u.def.section->flags |= SEC_KEEP;
+    }
+}
+
 /* Do mark and sweep of unused sections.  */
 
 bfd_boolean
@@ -11332,14 +11497,33 @@ bfd_elf_gc_sections (bfd *abfd, struct bfd_link_info *info)
   const struct elf_backend_data *bed = get_elf_backend_data (abfd);
 
   if (!bed->can_gc_sections
-      || info->relocatable
-      || info->emitrelocations
       || !is_elf_hash_table (info->hash))
     {
       (*_bfd_error_handler)(_("Warning: gc-sections option ignored"));
       return TRUE;
     }
 
+  bed->gc_keep (info);
+
+  /* Try to parse each bfd's .eh_frame section.  Point elf_eh_frame_section
+     at the .eh_frame section if we can mark the FDEs individually.  */
+  _bfd_elf_begin_eh_frame_parsing (info);
+  for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
+    {
+      asection *sec;
+      struct elf_reloc_cookie cookie;
+
+      sec = bfd_get_section_by_name (sub, ".eh_frame");
+      if (sec && init_reloc_cookie_for_section (&cookie, info, sec))
+       {
+         _bfd_elf_parse_eh_frame (sub, info, sec, &cookie);
+         if (elf_section_data (sec)->sec_info)
+           elf_eh_frame_section (sub) = sec;
+         fini_reloc_cookie_for_section (&cookie, sec);
+       }
+    }
+  _bfd_elf_end_eh_frame_parsing (info);
+
   /* Apply transitive closure to the vtable entry usage info.  */
   elf_link_hash_traverse (elf_hash_table (info),
                          elf_gc_propagate_vtable_entries_used,
@@ -11377,69 +11561,7 @@ bfd_elf_gc_sections (bfd *abfd, struct bfd_link_info *info)
 
   /* 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)
-    {
-      asection *o;
-
-      if (bfd_get_flavour (sub) != bfd_target_elf_flavour)
-       continue;
-
-      /* 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)
-         {
-           if (CONST_STRNEQ (o->name, GCC_EXCEPT_TABLE_PREFIX))
-             {
-               char *fn_name;
-               const char *sec_name;
-               asection *fn_text;
-               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_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;
-             }
-
-           /* If not using specially named exception table section,
-              then keep whatever we are using.  */
-           if (!_bfd_elf_gc_mark (info, o, gc_mark_hook))
-             return FALSE;
-         }
-    }
+    bed->gc_mark_extra_sections (info, gc_mark_hook);
 
   /* ... and mark SEC_EXCLUDE for those that go.  */
   return elf_gc_sweep (abfd, info);
@@ -11757,16 +11879,15 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
 {
   struct elf_reloc_cookie cookie;
   asection *stab, *eh;
-  Elf_Internal_Shdr *symtab_hdr;
   const struct elf_backend_data *bed;
   bfd *abfd;
-  unsigned int count;
   bfd_boolean ret = FALSE;
 
   if (info->traditional_format
       || !is_elf_hash_table (info->hash))
     return FALSE;
 
+  _bfd_elf_begin_eh_frame_parsing (info);
   for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link_next)
     {
       if (bfd_get_flavour (abfd) != bfd_target_elf_flavour)
@@ -11799,96 +11920,39 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
          && bed->elf_backend_discard_info == NULL)
        continue;
 
-      symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
-      cookie.abfd = abfd;
-      cookie.sym_hashes = elf_sym_hashes (abfd);
-      cookie.bad_symtab = elf_bad_symtab (abfd);
-      if (cookie.bad_symtab)
-       {
-         cookie.locsymcount = symtab_hdr->sh_size / bed->s->sizeof_sym;
-         cookie.extsymoff = 0;
-       }
-      else
-       {
-         cookie.locsymcount = symtab_hdr->sh_info;
-         cookie.extsymoff = symtab_hdr->sh_info;
-       }
-
-      if (bed->s->arch_size == 32)
-       cookie.r_sym_shift = 8;
-      else
-       cookie.r_sym_shift = 32;
-
-      cookie.locsyms = (Elf_Internal_Sym *) symtab_hdr->contents;
-      if (cookie.locsyms == NULL && cookie.locsymcount != 0)
-       {
-         cookie.locsyms = bfd_elf_get_elf_syms (abfd, symtab_hdr,
-                                                cookie.locsymcount, 0,
-                                                NULL, NULL, NULL);
-         if (cookie.locsyms == NULL)
-           {
-             info->callbacks->einfo (_("%P%X: can not read symbols: %E\n"));
-             return FALSE;
-           }
-       }
+      if (!init_reloc_cookie (&cookie, info, abfd))
+       return FALSE;
 
-      if (stab != NULL)
+      if (stab != NULL
+         && stab->reloc_count > 0
+         && init_reloc_cookie_rels (&cookie, info, abfd, stab))
        {
-         cookie.rels = NULL;
-         count = stab->reloc_count;
-         if (count != 0)
-           cookie.rels = _bfd_elf_link_read_relocs (abfd, stab, NULL, NULL,
-                                                    info->keep_memory);
-         if (cookie.rels != NULL)
-           {
-             cookie.rel = cookie.rels;
-             cookie.relend = cookie.rels;
-             cookie.relend += count * bed->s->int_rels_per_ext_rel;
-             if (_bfd_discard_section_stabs (abfd, stab,
-                                             elf_section_data (stab)->sec_info,
-                                             bfd_elf_reloc_symbol_deleted_p,
-                                             &cookie))
-               ret = TRUE;
-             if (elf_section_data (stab)->relocs != cookie.rels)
-               free (cookie.rels);
-           }
+         if (_bfd_discard_section_stabs (abfd, stab,
+                                         elf_section_data (stab)->sec_info,
+                                         bfd_elf_reloc_symbol_deleted_p,
+                                         &cookie))
+           ret = TRUE;
+         fini_reloc_cookie_rels (&cookie, stab);
        }
 
-      if (eh != NULL)
+      if (eh != NULL
+         && init_reloc_cookie_rels (&cookie, info, abfd, eh))
        {
-         cookie.rels = NULL;
-         count = eh->reloc_count;
-         if (count != 0)
-           cookie.rels = _bfd_elf_link_read_relocs (abfd, eh, NULL, NULL,
-                                                    info->keep_memory);
-         cookie.rel = cookie.rels;
-         cookie.relend = cookie.rels;
-         if (cookie.rels != NULL)
-           cookie.relend += count * bed->s->int_rels_per_ext_rel;
-
+         _bfd_elf_parse_eh_frame (abfd, info, eh, &cookie);
          if (_bfd_elf_discard_section_eh_frame (abfd, info, eh,
                                                 bfd_elf_reloc_symbol_deleted_p,
                                                 &cookie))
            ret = TRUE;
-
-         if (cookie.rels != NULL
-             && elf_section_data (eh)->relocs != cookie.rels)
-           free (cookie.rels);
+         fini_reloc_cookie_rels (&cookie, eh);
        }
 
       if (bed->elf_backend_discard_info != NULL
          && (*bed->elf_backend_discard_info) (abfd, &cookie, info))
        ret = TRUE;
 
-      if (cookie.locsyms != NULL
-         && symtab_hdr->contents != (unsigned char *) cookie.locsyms)
-       {
-         if (! info->keep_memory)
-           free (cookie.locsyms);
-         else
-           symtab_hdr->contents = (unsigned char *) cookie.locsyms;
-       }
+      fini_reloc_cookie (&cookie, abfd);
     }
+  _bfd_elf_end_eh_frame_parsing (info);
 
   if (info->eh_frame_hdr
       && !info->relocatable
This page took 0.041246 seconds and 4 git commands to generate.