]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - bfd/elflink.c
2004-07-27 H.J. Lu <hongjiu.lu@intel.com>
[thirdparty/binutils-gdb.git] / bfd / elflink.c
index cee59096a522f79d07c95841371829c7b303ca0c..e3411826f962961a0f1235a10e97019a4cb0cf07 100644 (file)
@@ -98,7 +98,7 @@ _bfd_elf_create_got_section (bfd *abfd, struct bfd_link_info *info)
     }
 
   /* The first bit of the global offset table is the header.  */
-  s->_raw_size += bed->got_header_size + bed->got_symbol_offset;
+  s->size += bed->got_header_size + bed->got_symbol_offset;
 
   return TRUE;
 }
@@ -608,6 +608,43 @@ elf_link_renumber_hash_table_dynsyms (struct elf_link_hash_entry *h,
   return TRUE;
 }
 
+/* Return true if the dynamic symbol for a given section should be
+   omitted when creating a shared library.  */
+bfd_boolean
+_bfd_elf_link_omit_section_dynsym (bfd *output_bfd ATTRIBUTE_UNUSED,
+                                  struct bfd_link_info *info,
+                                  asection *p)
+{
+  switch (elf_section_data (p)->this_hdr.sh_type)
+    {
+    case SHT_PROGBITS:
+    case SHT_NOBITS:
+      /* If sh_type is yet undecided, assume it could be
+        SHT_PROGBITS/SHT_NOBITS.  */
+    case SHT_NULL:
+      if (strcmp (p->name, ".got") == 0
+         || strcmp (p->name, ".got.plt") == 0
+         || strcmp (p->name, ".plt") == 0)
+       {
+         asection *ip;
+         bfd *dynobj = elf_hash_table (info)->dynobj;
+
+         if (dynobj != NULL
+             && (ip = bfd_get_section_by_name (dynobj, p->name))
+             != NULL
+             && (ip->flags & SEC_LINKER_CREATED)
+             && ip->output_section == p)
+           return TRUE;
+       }
+      return FALSE;
+
+      /* There shouldn't be section relative relocations
+        against any other section.  */
+    default:
+      return TRUE;
+    }
+}
+
 /* Assign dynsym indices.  In a shared library we generate a section
    symbol for each output section, which come first.  Next come all of
    the back-end allocated local dynamic syms, followed by the rest of
@@ -620,38 +657,13 @@ _bfd_elf_link_renumber_dynsyms (bfd *output_bfd, struct bfd_link_info *info)
 
   if (info->shared)
     {
+      const struct elf_backend_data *bed = get_elf_backend_data (output_bfd);
       asection *p;
       for (p = output_bfd->sections; p ; p = p->next)
        if ((p->flags & SEC_EXCLUDE) == 0
-           && (p->flags & SEC_ALLOC) != 0)
-         switch (elf_section_data (p)->this_hdr.sh_type)
-           {
-           case SHT_PROGBITS:
-           case SHT_NOBITS:
-             /* If sh_type is yet undecided, assume it could be
-                SHT_PROGBITS/SHT_NOBITS.  */
-           case SHT_NULL:
-             if (strcmp (p->name, ".got") == 0
-                 || strcmp (p->name, ".got.plt") == 0
-                 || strcmp (p->name, ".plt") == 0)
-               {
-                 asection *ip;
-                 bfd *dynobj = elf_hash_table (info)->dynobj;
-
-                 if (dynobj != NULL
-                     && (ip = bfd_get_section_by_name (dynobj, p->name))
-                        != NULL
-                     && (ip->flags & SEC_LINKER_CREATED)
-                     && ip->output_section == p)
-                   continue;
-               }
-             elf_section_data (p)->dynindx = ++dynsymcount;
-             break;
-             /* There shouldn't be section relative relocations
-                against any other section.  */
-           default:
-             break;
-           }
+           && (p->flags & SEC_ALLOC) != 0
+           && !(*bed->elf_backend_omit_section_dynsym) (output_bfd, info, p))
+         elf_section_data (p)->dynindx = ++dynsymcount;
     }
 
   if (elf_hash_table (info)->dynlocal)
@@ -1847,10 +1859,14 @@ elf_link_read_relocs_from_section (bfd *abfd,
        r_symndx >>= 24;
       if ((size_t) r_symndx >= nsyms)
        {
+         char *sec_name = bfd_get_section_ident (sec);
          (*_bfd_error_handler)
            (_("%s: bad reloc symbol index (0x%lx >= 0x%lx) for offset 0x%lx in section `%s'"),
             bfd_archive_filename (abfd), (unsigned long) r_symndx,
-            (unsigned long) nsyms, irela->r_offset, sec->name);
+            (unsigned long) nsyms, irela->r_offset,
+            sec_name ? sec_name : sec->name);
+         if (sec_name)
+           free (sec_name);
          bfd_set_error (bfd_error_bad_value);
          return FALSE;
        }
@@ -2036,11 +2052,14 @@ _bfd_elf_link_output_relocs (bfd *output_bfd,
     }
   else
     {
+      char *sec_name = bfd_get_section_ident (input_section);
       (*_bfd_error_handler)
        (_("%s: relocation size mismatch in %s section %s"),
         bfd_get_filename (output_bfd),
         bfd_archive_filename (input_section->owner),
-        input_section->name);
+        sec_name ? sec_name : input_section->name);
+      if (sec_name)
+       free (sec_name);
       bfd_set_error (bfd_error_wrong_object_format);
       return FALSE;
     }
@@ -2362,7 +2381,7 @@ _bfd_elf_link_sec_merge_syms (struct elf_link_hash_entry *h, void *data)
        _bfd_merged_section_offset (output_bfd,
                                    &h->root.u.def.section,
                                    elf_section_data (sec)->sec_info,
-                                   h->root.u.def.value, 0);
+                                   h->root.u.def.value);
     }
 
   return TRUE;
@@ -2437,9 +2456,13 @@ _bfd_elf_symbol_refs_local_p (struct elf_link_hash_entry *h,
   if (h == NULL)
     return TRUE;
 
+  /* Common symbols that become definitions don't get the DEF_REGULAR
+     flag set, so test it first, and don't bail out.  */
+  if (ELF_COMMON_DEF_P (h))
+    /* Do nothing.  */;
   /* If we don't have a definition in a regular file, then we can't
      resolve locally.  The sym is either undefined or dynamic.  */
-  if ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
+  else if ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
     return FALSE;
 
   /* Forced local symbols resolve locally.  */
@@ -2641,16 +2664,16 @@ _bfd_elf_add_dynamic_entry (struct bfd_link_info *info,
   s = bfd_get_section_by_name (hash_table->dynobj, ".dynamic");
   BFD_ASSERT (s != NULL);
 
-  newsize = s->_raw_size + bed->s->sizeof_dyn;
+  newsize = s->size + bed->s->sizeof_dyn;
   newcontents = bfd_realloc (s->contents, newsize);
   if (newcontents == NULL)
     return FALSE;
 
   dyn.d_tag = tag;
   dyn.d_un.d_val = val;
-  bed->s->swap_dyn_out (hash_table->dynobj, &dyn, newcontents + s->_raw_size);
+  bed->s->swap_dyn_out (hash_table->dynobj, &dyn, newcontents + s->size);
 
-  s->_raw_size = newsize;
+  s->size = newsize;
   s->contents = newcontents;
 
   return TRUE;
@@ -2686,7 +2709,7 @@ elf_add_dt_needed_tag (struct bfd_link_info *info,
       BFD_ASSERT (sdyn != NULL);
 
       for (extdyn = sdyn->contents;
-          extdyn < sdyn->contents + sdyn->_raw_size;
+          extdyn < sdyn->contents + sdyn->size;
           extdyn += bed->s->sizeof_dyn)
        {
          Elf_Internal_Dyn dyn;
@@ -2728,7 +2751,7 @@ elf_sort_symbol (const void *arg1, const void *arg2)
     return vdiff > 0 ? 1 : -1;
   else
     {
-      long sdiff = h1->root.u.def.section - h2->root.u.def.section;
+      long sdiff = h1->root.u.def.section->id - h2->root.u.def.section->id;
       if (sdiff != 0)
        return sdiff > 0 ? 1 : -1;
     }
@@ -2775,7 +2798,7 @@ elf_finalize_dynstr (bfd *output_bfd, struct bfd_link_info *info)
 
   /* Update all .dynamic entries referencing .dynstr strings.  */
   for (extdyn = sdyn->contents;
-       extdyn < sdyn->contents + sdyn->_raw_size;
+       extdyn < sdyn->contents + sdyn->size;
        extdyn += bed->s->sizeof_dyn)
     {
       Elf_Internal_Dyn dyn;
@@ -2885,6 +2908,8 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
      const char **, flagword *, asection **, bfd_vma *);
   bfd_boolean (*check_relocs)
     (bfd *, struct bfd_link_info *, asection *, const Elf_Internal_Rela *);
+  bfd_boolean (*check_directives)
+    (bfd *, struct bfd_link_info *);
   bfd_boolean collect;
   Elf_Internal_Shdr *hdr;
   bfd_size_type symcount;
@@ -2975,12 +3000,12 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
                      /* We don't want to issue this warning.  Clobber
                         the section size so that the warning does not
                         get copied into the output file.  */
-                     s->_raw_size = 0;
+                     s->size = 0;
                      continue;
                    }
                }
 
-             sz = bfd_section_size (abfd, s);
+             sz = s->size;
              prefix_len = strlen (gnu_warning_prefix);
              msg = bfd_alloc (abfd, prefix_len + sz + 1);
              if (msg == NULL)
@@ -3001,7 +3026,7 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
                {
                  /* Clobber the section size so that the warning does
                     not get copied into the output file.  */
-                 s->_raw_size = 0;
+                 s->size = 0;
                }
            }
        }
@@ -3043,8 +3068,13 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
       /* If this dynamic lib was specified on the command line with
         --as-needed in effect, then we don't want to add a DT_NEEDED
         tag unless the lib is actually used.  Similary for libs brought
-        in by another lib's DT_NEEDED.  */
-      add_needed = elf_dyn_lib_class (abfd) == DYN_NORMAL;
+        in by another lib's DT_NEEDED.  When --no-add-needed is used
+        on a dynamic lib, we don't want to add a DT_NEEDED entry for
+        any dynamic library in DT_NEEDED tags in the dynamic lib at
+        all.  */
+      add_needed = (elf_dyn_lib_class (abfd)
+                   & (DYN_AS_NEEDED | DYN_DT_NEEDED
+                      | DYN_NO_NEEDED)) == 0;
 
       s = bfd_get_section_by_name (abfd, ".dynamic");
       if (s != NULL)
@@ -3054,11 +3084,7 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
          int elfsec;
          unsigned long shlink;
 
-         dynbuf = bfd_malloc (s->_raw_size);
-         if (dynbuf == NULL)
-           goto error_return;
-
-         if (! bfd_get_section_contents (abfd, s, dynbuf, 0, s->_raw_size))
+         if (!bfd_malloc_and_get_section (abfd, s, &dynbuf))
            goto error_free_dyn;
 
          elfsec = _bfd_elf_section_from_bfd_section (abfd, s);
@@ -3067,7 +3093,7 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
          shlink = elf_elfsections (abfd)[elfsec]->sh_link;
 
          for (extdyn = dynbuf;
-              extdyn < dynbuf + s->_raw_size;
+              extdyn < dynbuf + s->size;
               extdyn += bed->s->sizeof_dyn)
            {
              Elf_Internal_Dyn dyn;
@@ -3829,7 +3855,17 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
 
              /* A symbol from a library loaded via DT_NEEDED of some
                 other library is referenced by a regular object.
-                Add a DT_NEEDED entry for it.  */
+                Add a DT_NEEDED entry for it.  Issue an error if
+                --no-add-needed is used.  */
+             if ((elf_dyn_lib_class (abfd) & DYN_NO_NEEDED) != 0)
+               {
+                 (*_bfd_error_handler)
+                   (_("%s: invalid DSO for symbol `%s' definition"),
+                    bfd_archive_filename (abfd), name);
+                 bfd_set_error (bfd_error_bad_value);
+                 goto error_free_vers;
+               }
+
              add_needed = TRUE;
              ret = elf_add_dt_needed_tag (info, soname, add_needed);
              if (ret < 0)
@@ -3982,7 +4018,7 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
                i = idx + 1;
              else
                {
-                 long sdiff = slook - h->root.u.def.section;
+                 long sdiff = slook->id - h->root.u.def.section->id;
                  if (sdiff < 0)
                    j = idx;
                  else if (sdiff > 0)
@@ -4038,6 +4074,10 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
       free (sorted_sym_hash);
     }
 
+  check_directives = get_elf_backend_data (abfd)->check_directives;
+  if (check_directives)
+    check_directives (abfd, info);
+
   /* If this object is the same format as the output object, and it is
      not a shared library, then let the backend look through the
      relocs.
@@ -4116,7 +4156,7 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
 
                secdata = elf_section_data (stab);
                if (! _bfd_link_section_stabs (abfd,
-                                              & hash_table->stab_info,
+                                              &hash_table->stab_info,
                                               stab, stabstr,
                                               &secdata->sec_info,
                                               &string_offset))
@@ -4127,28 +4167,6 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
        }
     }
 
-  if (! info->relocatable
-      && ! dynamic
-      && is_elf_hash_table (hash_table))
-    {
-      asection *s;
-
-      for (s = abfd->sections; s != NULL; s = s->next)
-       if ((s->flags & SEC_MERGE) != 0
-           && !bfd_is_abs_section (s->output_section))
-         {
-           struct bfd_elf_section_data *secdata;
-
-           secdata = elf_section_data (s);
-           if (! _bfd_merge_section (abfd,
-                                     & hash_table->merge_info,
-                                     s, &secdata->sec_info))
-             goto error_return;
-           else if (secdata->sec_info)
-             s->sec_info_type = ELF_INFO_TYPE_MERGE;
-         }
-    }
-
   if (is_elf_hash_table (hash_table))
     {
       /* Add this bfd to the loaded list.  */
@@ -5037,9 +5055,9 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
                size += sizeof (Elf_External_Verdaux);
            }
 
-         s->_raw_size = size;
-         s->contents = bfd_alloc (output_bfd, s->_raw_size);
-         if (s->contents == NULL && s->_raw_size != 0)
+         s->size = size;
+         s->contents = bfd_alloc (output_bfd, s->size);
+         if (s->contents == NULL && s->size != 0)
            return FALSE;
 
          /* Fill in the version definition section.  */
@@ -5236,8 +5254,8 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
                  size += sizeof (Elf_External_Vernaux);
              }
 
-           s->_raw_size = size;
-           s->contents = bfd_alloc (output_bfd, s->_raw_size);
+           s->size = size;
+           s->contents = bfd_alloc (output_bfd, s->size);
            if (s->contents == NULL)
              return FALSE;
 
@@ -5322,8 +5340,8 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
        }
       else
        {
-         s->_raw_size = dynsymcount * sizeof (Elf_External_Versym);
-         s->contents = bfd_zalloc (output_bfd, s->_raw_size);
+         s->size = dynsymcount * sizeof (Elf_External_Versym);
+         s->contents = bfd_zalloc (output_bfd, s->size);
          if (s->contents == NULL)
            return FALSE;
 
@@ -5339,9 +5357,9 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
         section as we went along in elf_link_add_object_symbols.  */
       s = bfd_get_section_by_name (dynobj, ".dynsym");
       BFD_ASSERT (s != NULL);
-      s->_raw_size = dynsymcount * bed->s->sizeof_sym;
-      s->contents = bfd_alloc (output_bfd, s->_raw_size);
-      if (s->contents == NULL && s->_raw_size != 0)
+      s->size = dynsymcount * bed->s->sizeof_sym;
+      s->contents = bfd_alloc (output_bfd, s->size);
+      if (s->contents == NULL && s->size != 0)
        return FALSE;
 
       if (dynsymcount != 0)
@@ -5365,8 +5383,8 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
       s = bfd_get_section_by_name (dynobj, ".hash");
       BFD_ASSERT (s != NULL);
       hash_entry_size = elf_section_data (s)->this_hdr.sh_entsize;
-      s->_raw_size = ((2 + bucketcount + dynsymcount) * hash_entry_size);
-      s->contents = bfd_zalloc (output_bfd, s->_raw_size);
+      s->size = ((2 + bucketcount + dynsymcount) * hash_entry_size);
+      s->contents = bfd_zalloc (output_bfd, s->size);
       if (s->contents == NULL)
        return FALSE;
 
@@ -5381,7 +5399,7 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
 
       elf_finalize_dynstr (output_bfd, info);
 
-      s->_raw_size = _bfd_elf_strtab_size (elf_hash_table (info)->dynstr);
+      s->size = _bfd_elf_strtab_size (elf_hash_table (info)->dynstr);
 
       for (dtagcount = 0; dtagcount <= info->spare_dynamic_tags; ++dtagcount)
        if (!_bfd_elf_add_dynamic_entry (info, DT_NULL, 0))
@@ -5591,10 +5609,10 @@ elf_link_sort_relocs (bfd *abfd, struct bfd_link_info *info, asection **psec)
   bfd_vma r_sym_mask;
 
   reldyn = bfd_get_section_by_name (abfd, ".rela.dyn");
-  if (reldyn == NULL || reldyn->_raw_size == 0)
+  if (reldyn == NULL || reldyn->size == 0)
     {
       reldyn = bfd_get_section_by_name (abfd, ".rel.dyn");
-      if (reldyn == NULL || reldyn->_raw_size == 0)
+      if (reldyn == NULL || reldyn->size == 0)
        return 0;
       ext_size = bed->s->sizeof_rel;
       swap_in = bed->s->swap_reloc_in;
@@ -5606,17 +5624,17 @@ elf_link_sort_relocs (bfd *abfd, struct bfd_link_info *info, asection **psec)
       swap_in = bed->s->swap_reloca_in;
       swap_out = bed->s->swap_reloca_out;
     }
-  count = reldyn->_raw_size / ext_size;
+  count = reldyn->size / ext_size;
 
   size = 0;
   for (lo = reldyn->link_order_head; lo != NULL; lo = lo->next)
     if (lo->type == bfd_indirect_link_order)
       {
        asection *o = lo->u.indirect.section;
-       size += o->_raw_size;
+       size += o->size;
       }
 
-  if (size != reldyn->_raw_size)
+  if (size != reldyn->size)
     return 0;
 
   sort_elt = (sizeof (struct elf_link_sort_rela)
@@ -5641,7 +5659,7 @@ elf_link_sort_relocs (bfd *abfd, struct bfd_link_info *info, asection **psec)
        asection *o = lo->u.indirect.section;
 
        erel = o->contents;
-       erelend = o->contents + o->_raw_size;
+       erelend = o->contents + o->size;
        p = sort + o->output_offset / ext_size * sort_elt;
        while (erel < erelend)
          {
@@ -5683,7 +5701,7 @@ elf_link_sort_relocs (bfd *abfd, struct bfd_link_info *info, asection **psec)
        asection *o = lo->u.indirect.section;
 
        erel = o->contents;
-       erelend = o->contents + o->_raw_size;
+       erelend = o->contents + o->size;
        p = sort + o->output_offset / ext_size * sort_elt;
        while (erel < erelend)
          {
@@ -5819,7 +5837,7 @@ elf_link_check_versioned_symbol (struct bfd_link_info *info,
     case bfd_link_hash_undefweak:
       abfd = h->root.u.undef.abfd;
       if ((abfd->flags & DYNAMIC) == 0
-         || elf_dyn_lib_class (abfd) != DYN_DT_NEEDED)
+         || (elf_dyn_lib_class (abfd) & DYN_DT_NEEDED) == 0)
        return FALSE;
       break;
 
@@ -6087,11 +6105,14 @@ elf_link_output_extsym (struct elf_link_hash_entry *h, void *data)
                                                 input_sec->output_section);
            if (sym.st_shndx == SHN_BAD)
              {
+               char *sec_name = bfd_get_section_ident (input_sec);
                (*_bfd_error_handler)
                  (_("%s: could not find output section %s for input section %s"),
                   bfd_get_filename (finfo->output_bfd),
                   input_sec->output_section->name,
-                  input_sec->name);
+                  sec_name ? sec_name : input_sec->name);
+               if (sec_name)
+                 free (sec_name);
                eoinfo->failed = TRUE;
                return FALSE;
              }
@@ -6272,6 +6293,9 @@ elf_link_output_extsym (struct elf_link_hash_entry *h, void *data)
   return TRUE;
 }
 
+/* Return TRUE if special handling is done for relocs in SEC against
+   symbols defined in discarded sections.  */
+
 static bfd_boolean
 elf_section_ignore_discarded_relocs (asection *sec)
 {
@@ -6294,6 +6318,49 @@ elf_section_ignore_discarded_relocs (asection *sec)
   return FALSE;
 }
 
+/* Return TRUE if we should complain about a reloc in SEC against a
+   symbol defined in a discarded section.  */
+
+static bfd_boolean
+elf_section_complain_discarded (asection *sec)
+{
+  if (strncmp (".stab", sec->name, 5) == 0
+      && (!sec->name[5] ||
+         (sec->name[5] == '.' && ISDIGIT (sec->name[6]))))
+    return FALSE;
+
+  if (strcmp (".eh_frame", sec->name) == 0)
+    return FALSE;
+
+  if (strcmp (".gcc_except_table", sec->name) == 0)
+    return FALSE;
+
+  if (strcmp (".PARISC.unwind", sec->name) == 0)
+    return FALSE;
+
+  return TRUE;
+}
+
+/* Find a match between a section and a member of a section group.  */
+
+static asection *
+match_group_member (asection *sec, asection *group)
+{
+  asection *first = elf_next_in_group (group);
+  asection *s = first;
+
+  while (s != NULL)
+    {
+      if (bfd_elf_match_symbols_in_sections (s, sec))
+       return s;
+
+      if (s == first)
+       break;
+    }
+
+  return NULL;
+}
+
 /* Link an input file into the linker output file.  This function
    handles all the sections and relocations of the input file at once.
    This is so that we only have to read the local symbols once, and
@@ -6392,7 +6459,7 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
            isym->st_value =
              _bfd_merged_section_offset (output_bfd, &isec,
                                          elf_section_data (isec)->sec_info,
-                                         isym->st_value, 0);
+                                         isym->st_value);
        }
       else if (isym->st_shndx == SHN_ABS)
        isec = bfd_abs_section_ptr;
@@ -6507,7 +6574,7 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
        }
 
       if ((o->flags & SEC_HAS_CONTENTS) == 0
-         || (o->_raw_size == 0 && (o->flags & SEC_RELOC) == 0))
+         || (o->size == 0 && (o->flags & SEC_RELOC) == 0))
        continue;
 
       if ((o->flags & SEC_LINKER_CREATED) != 0)
@@ -6525,9 +6592,10 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
        contents = elf_section_data (o)->this_hdr.contents;
       else
        {
+         bfd_size_type amt = o->rawsize ? o->rawsize : o->size;
+
          contents = finfo->contents;
-         if (! bfd_get_section_contents (input_bfd, o, contents, 0,
-                                         o->_raw_size))
+         if (! bfd_get_section_contents (input_bfd, o, contents, 0, amt))
            return FALSE;
        }
 
@@ -6565,95 +6633,109 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
          if (!elf_section_ignore_discarded_relocs (o))
            {
              Elf_Internal_Rela *rel, *relend;
+             bfd_boolean complain = elf_section_complain_discarded (o);
 
              rel = internal_relocs;
              relend = rel + o->reloc_count * bed->s->int_rels_per_ext_rel;
              for ( ; rel < relend; rel++)
                {
                  unsigned long r_symndx = rel->r_info >> r_sym_shift;
-                 asection *sec;
+                 asection **ps, *sec;
+                 struct elf_link_hash_entry *h = NULL;
+                 const char *sym_name;
 
                  if (r_symndx >= locsymcount
                      || (elf_bad_symtab (input_bfd)
                          && finfo->sections[r_symndx] == NULL))
                    {
-                     struct elf_link_hash_entry *h;
-
                      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;
 
-                     /* Complain if the definition comes from a
-                        discarded section.  */
-                     sec = h->root.u.def.section;
-                     if ((h->root.type == bfd_link_hash_defined
-                          || h->root.type == bfd_link_hash_defweak)
-                         && elf_discarded_section (sec))
-                       {
-                         if ((o->flags & SEC_DEBUGGING) != 0)
-                           {
-                             BFD_ASSERT (r_symndx != 0);
-                             /* Try to preserve debug information.  */
-                             if ((o->flags & SEC_DEBUGGING) != 0
-                                 && sec->kept_section != NULL
-                                 && sec->_raw_size == sec->kept_section->_raw_size)
-                               h->root.u.def.section
-                                 = sec->kept_section;
-                             else
-                               memset (rel, 0, sizeof (*rel));
-                           }
-                         else
-                           finfo->info->callbacks->error_handler
-                             (LD_DEFINITION_IN_DISCARDED_SECTION,
-                              _("%T: discarded in section `%s' from %s\n"),
-                              h->root.root.string,
-                              h->root.root.string,
-                              h->root.u.def.section->name,
-                              bfd_archive_filename (h->root.u.def.section->owner));
-                       }
+                     if (h->root.type != bfd_link_hash_defined
+                         && h->root.type != bfd_link_hash_defweak)
+                       continue;
+
+                     ps = &h->root.u.def.section;
+                     sym_name = h->root.root.string;
                    }
                  else
                    {
-                     sec = finfo->sections[r_symndx];
+                     Elf_Internal_Sym *sym = isymbuf + r_symndx;
+                     ps = &finfo->sections[r_symndx];
+                     sym_name = bfd_elf_local_sym_name (input_bfd, sym);
+                   }
 
-                     if (sec != NULL && elf_discarded_section (sec))
+                 /* Complain if the definition comes from a
+                    discarded section.  */
+                 if ((sec = *ps) != NULL && elf_discarded_section (sec))
+                   {
+                     if ((o->flags & SEC_DEBUGGING) != 0)
                        {
-                         if ((o->flags & SEC_DEBUGGING) != 0
-                             || (sec->flags & SEC_LINK_ONCE) != 0)
+                         BFD_ASSERT (r_symndx != 0);
+
+                         /* Try to preserve debug information.
+                            FIXME: This is quite broken.  Modifying
+                            the symbol here means we will be changing
+                            all uses of the symbol, not just those in
+                            debug sections.  The only thing that makes
+                            this half reasonable is that debug sections
+                            tend to come after other sections.  Of
+                            course, that doesn't help with globals.
+                            ??? All link-once sections of the same name
+                            ought to define the same set of symbols, so
+                            it would seem that globals ought to always
+                            be defined in the kept section.  */
+                         if (sec->kept_section != NULL)
                            {
-                             BFD_ASSERT (r_symndx != 0);
-                             /* Try to preserve debug information.  */
-                             if ((o->flags & SEC_DEBUGGING) != 0
-                                 && sec->kept_section != NULL
-                                 && sec->_raw_size == sec->kept_section->_raw_size)
-                               finfo->sections[r_symndx]
-                                 = sec->kept_section;
-                             else
+                             asection *member;
+
+                             /* Check if it is a linkonce section or
+                                member of a comdat group.  */
+                             if (elf_sec_group (sec) == NULL
+                                 && sec->size == sec->kept_section->size)
                                {
-                                 rel->r_info &= r_type_mask;
-                                 rel->r_addend = 0;
+                                 *ps = sec->kept_section;
+                                 continue;
+                               }
+                             else if (elf_sec_group (sec) != NULL
+                                      && (member = match_group_member (sec, sec->kept_section))
+                                      && sec->size == member->size)
+                               {
+                                 *ps = member;
+                                 continue;
                                }
-                           }
-                         else
-                           {
-                             static int count;
-                             int ok;
-                             char *buf;
-
-                             ok = asprintf (&buf, "local symbol %d",
-                                            count++);
-                             if (ok <= 0)
-                               buf = (char *) "local symbol";
-                             finfo->info->callbacks->error_handler
-                               (LD_DEFINITION_IN_DISCARDED_SECTION,
-                                _("%T: discarded in section `%s' from %s\n"),
-                                buf, buf, sec->name,
-                                bfd_archive_filename (input_bfd));
-                             if (ok != -1)
-                               free (buf);
                            }
                        }
+                     else if (complain)
+                       {
+                         char *r_sec
+                           = bfd_get_section_ident (o);
+                         char *d_sec
+                           = bfd_get_section_ident (sec);
+                         finfo->info->callbacks->error_handler
+                           (LD_DEFINITION_IN_DISCARDED_SECTION,
+                            _("`%T' referenced in section `%s' of %B: "
+                              "defined in discarded section `%s' of %B\n"),
+                            sym_name, sym_name,
+                            r_sec ? r_sec : o->name, input_bfd,
+                            d_sec ? d_sec : sec->name, sec->owner);
+                         if (r_sec)
+                           free (r_sec);
+                         if (d_sec)
+                           free (d_sec);
+                       }
+
+                     /* 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;
                    }
                }
            }
@@ -6944,14 +7026,11 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
          break;
        default:
          {
-           bfd_size_type sec_size;
-
-           sec_size = (o->_cooked_size != 0 ? o->_cooked_size : o->_raw_size);
            if (! (o->flags & SEC_EXCLUDE)
                && ! bfd_set_section_contents (output_bfd, o->output_section,
                                               contents,
                                               (file_ptr) o->output_offset,
-                                              sec_size))
+                                              o->size))
              return FALSE;
          }
          break;
@@ -7127,6 +7206,120 @@ elf_reloc_link_order (bfd *output_bfd,
   return TRUE;
 }
 
+
+/* Get the output vma of the section pointed to by the sh_link field.  */
+
+static bfd_vma
+elf_get_linked_section_vma (struct bfd_link_order *p)
+{
+  Elf_Internal_Shdr **elf_shdrp;
+  asection *s;
+  int elfsec;
+
+  s = p->u.indirect.section;
+  elf_shdrp = elf_elfsections (s->owner);
+  elfsec = _bfd_elf_section_from_bfd_section (s->owner, s);
+  elfsec = elf_shdrp[elfsec]->sh_link;
+  s = elf_shdrp[elfsec]->bfd_section;
+  return s->output_section->vma + s->output_offset;
+}
+
+
+/* Compare two sections based on the locations of the sections they are
+   linked to.  Used by elf_fixup_link_order.  */
+
+static int
+compare_link_order (const void * a, const void * b)
+{
+  bfd_vma apos;
+  bfd_vma bpos;
+
+  apos = elf_get_linked_section_vma (*(struct bfd_link_order **)a);
+  bpos = elf_get_linked_section_vma (*(struct bfd_link_order **)b);
+  if (apos < bpos)
+    return -1;
+  return apos > bpos;
+}
+
+
+/* Looks for sections with SHF_LINK_ORDER set.  Rearranges them into the same
+   order as their linked sections.  Returns false if this could not be done
+   because an output section includes both ordered and unordered
+   sections.  Ideally we'd do this in the linker proper.  */
+
+static bfd_boolean
+elf_fixup_link_order (bfd *abfd, asection *o)
+{
+  int seen_linkorder;
+  int seen_other;
+  int n;
+  struct bfd_link_order *p;
+  bfd *sub;
+  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+  int elfsec;
+  struct bfd_link_order **sections;
+  asection *s;
+  bfd_vma offset;
+  
+  seen_other = 0;
+  seen_linkorder = 0;
+  for (p = o->link_order_head; p != NULL; p = p->next)
+    {
+      if (p->type == bfd_indirect_link_order
+         && (bfd_get_flavour ((sub = p->u.indirect.section->owner))
+             == bfd_target_elf_flavour)
+         && elf_elfheader (sub)->e_ident[EI_CLASS] == bed->s->elfclass)
+       {
+         s = p->u.indirect.section;
+         elfsec = _bfd_elf_section_from_bfd_section (sub, s);
+         if (elfsec != -1
+             && elf_elfsections (sub)[elfsec]->sh_flags & SHF_LINK_ORDER)
+           seen_linkorder++;
+         else
+           seen_other++;
+       }
+      else
+       seen_other++;
+    }
+
+  if (!seen_linkorder)
+    return TRUE;
+
+  if (seen_other && seen_linkorder)
+    {
+      (*_bfd_error_handler) (_("%s: has both ordered and unordered sections"),
+                            o->name);
+      bfd_set_error (bfd_error_bad_value);
+      return FALSE;
+    }
+  
+  sections = (struct bfd_link_order **)
+    xmalloc (seen_linkorder * sizeof (struct bfd_link_order *));
+  seen_linkorder = 0;
+  
+  for (p = o->link_order_head; p != NULL; p = p->next)
+    {
+      sections[seen_linkorder++] = p;
+    }
+  /* Sort the input sections in the order of their linked section.  */
+  qsort (sections, seen_linkorder, sizeof (struct bfd_link_order *),
+        compare_link_order);
+
+  /* Change the offsets of the sections.  */
+  offset = 0;
+  for (n = 0; n < seen_linkorder; n++)
+    {
+      s = sections[n]->u.indirect.section;
+      offset &= ~(bfd_vma)((1 << s->alignment_power) - 1);
+      s->output_offset = offset;
+      sections[n]->offset = offset;
+      offset += sections[n]->size;
+    }
+
+  return TRUE;
+}
+
+
 /* Do the final step of an ELF link.  */
 
 bfd_boolean
@@ -7258,10 +7451,10 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
                    free (relocs);
                }
 
-             if (sec->_raw_size > max_contents_size)
-               max_contents_size = sec->_raw_size;
-             if (sec->_cooked_size > max_contents_size)
-               max_contents_size = sec->_cooked_size;
+             if (sec->rawsize > max_contents_size)
+               max_contents_size = sec->rawsize;
+             if (sec->size > max_contents_size)
+               max_contents_size = sec->size;
 
              /* We are interested in just local symbols, not all
                 symbols.  */
@@ -7569,7 +7762,7 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
           sec && (sec->flags & SEC_THREAD_LOCAL);
           sec = sec->next)
        {
-         bfd_vma size = sec->_raw_size;
+         bfd_vma size = sec->size;
 
          if (size == 0 && (sec->flags & SEC_HAS_CONTENTS) == 0)
            {
@@ -7586,6 +7779,13 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
       elf_hash_table (info)->tls_size = end - base;
     }
 
+  /* Reorder SHF_LINK_ORDER sections.  */
+  for (o = abfd->sections; o != NULL; o = o->next)
+    {
+      if (!elf_fixup_link_order (abfd, o))
+       return FALSE;
+    }
+
   /* Since ELF permits relocations to be against local symbols, we
      must have the local symbols available when we do the relocations.
      Since we would rather only read the local symbols once, and we
@@ -7845,7 +8045,7 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
       BFD_ASSERT (o != NULL);
 
       dyncon = o->contents;
-      dynconend = o->contents + o->_raw_size;
+      dynconend = o->contents + o->size;
       for (; dyncon < dynconend; dyncon += bed->s->sizeof_dyn)
        {
          Elf_Internal_Dyn dyn;
@@ -7921,10 +8121,10 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
                     bfd_get_filename (abfd), name);
                  goto error_return;
                }
-             if (o->_raw_size == 0)
+             if (o->size == 0)
                (*_bfd_error_handler)
                  (_("warning: %s section has zero size"), name);
-             dyn.d_un.d_val = o->_raw_size;
+             dyn.d_un.d_val = o->size;
              break;
 
            case DT_PREINIT_ARRAY:
@@ -8008,7 +8208,7 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
       for (o = dynobj->sections; o != NULL; o = o->next)
        {
          if ((o->flags & SEC_HAS_CONTENTS) == 0
-             || o->_raw_size == 0
+             || o->size == 0
              || o->output_section == bfd_abs_section_ptr)
            continue;
          if ((o->flags & SEC_LINKER_CREATED) == 0)
@@ -8017,6 +8217,10 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
                 created by _bfd_elf_link_create_dynamic_sections.  */
              continue;
            }
+         if (elf_hash_table (info)->stab_info.stabstr == o)
+           continue;
+         if (elf_hash_table (info)->eh_info.hdr_sec == o)
+           continue;
          if ((elf_section_data (o->output_section)->this_hdr.sh_type
               != SHT_STRTAB)
              || strcmp (bfd_get_section_name (abfd, o), ".dynstr") != 0)
@@ -8024,7 +8228,7 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
              if (! bfd_set_section_contents (abfd, o->output_section,
                                              o->contents,
                                              (file_ptr) o->output_offset,
-                                             o->_raw_size))
+                                             o->size))
                goto error_return;
            }
          else
@@ -8050,7 +8254,7 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
     }
 
   /* If we have optimized stabs strings, output them.  */
-  if (elf_hash_table (info)->stab_info != NULL)
+  if (elf_hash_table (info)->stab_info.stabstr != NULL)
     {
       if (! _bfd_write_stab_strings (abfd, &elf_hash_table (info)->stab_info))
        goto error_return;
@@ -8564,6 +8768,7 @@ bfd_elf_gc_record_vtinherit (bfd *abfd,
   struct elf_link_hash_entry **search, *child;
   bfd_size_type extsymcount;
   const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+  char *sec_name;
 
   /* The sh_info field of the symtab header tells us where the
      external symbols start.  We don't care about the local symbols at
@@ -8587,8 +8792,10 @@ bfd_elf_gc_record_vtinherit (bfd *abfd,
        goto win;
     }
 
+  sec_name = bfd_get_section_ident (sec);
   (*_bfd_error_handler) ("%s: %s+%lu: No symbol found for INHERIT",
-                        bfd_archive_filename (abfd), sec->name,
+                        bfd_archive_filename (abfd),
+                        sec_name ? sec_name : sec->name,
                         (unsigned long) offset);
   bfd_set_error (bfd_error_invalid_operation);
   return FALSE;
@@ -8875,13 +9082,13 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
       eh = bfd_get_section_by_name (abfd, ".eh_frame");
       if (info->relocatable
          || (eh != NULL
-             && (eh->_raw_size == 0
+             && (eh->size == 0
                  || bfd_is_abs_section (eh->output_section))))
        eh = NULL;
 
       stab = bfd_get_section_by_name (abfd, ".stab");
       if (stab != NULL
-         && (stab->_raw_size == 0
+         && (stab->size == 0
              || bfd_is_abs_section (stab->output_section)
              || stab->sec_info_type != ELF_INFO_TYPE_STABS))
        stab = NULL;
@@ -8986,3 +9193,219 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
 
   return ret;
 }
+
+struct already_linked_section
+{
+  asection *sec;
+  asection *linked;
+};
+
+/* Check if the member of a single member comdat group matches a
+   linkonce section and vice versa.  */
+static bfd_boolean
+try_match_symbols_in_sections
+  (struct bfd_section_already_linked_hash_entry *h, void *info)
+{
+  struct bfd_section_already_linked *l;
+  struct already_linked_section *s
+    = (struct already_linked_section *) info;
+
+  if (elf_sec_group (s->sec) == NULL)
+    {
+      /* It is a linkonce section. Try to match it with the member of a
+        single member comdat group. */
+      for (l = h->entry; l != NULL; l = l->next)
+       if ((l->sec->flags & SEC_GROUP))
+         {
+           asection *first = elf_next_in_group (l->sec);
+
+           if (first != NULL
+               && elf_next_in_group (first) == first
+               && bfd_elf_match_symbols_in_sections (first, s->sec))
+             {
+               s->linked = first;
+               return FALSE;
+             }
+         }
+    }
+  else
+    {
+      /* It is the member of a single member comdat group. Try to match
+        it with a linkonce section.  */
+      for (l = h->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, s->sec))
+         {
+           s->linked = l->sec;
+           return FALSE;
+         }
+    }
+
+  return TRUE;
+}
+
+static bfd_boolean
+already_linked (asection *sec, asection *group)
+{
+  struct already_linked_section result;
+
+  result.sec = sec;
+  result.linked = NULL;
+
+  bfd_section_already_linked_table_traverse
+    (try_match_symbols_in_sections, &result);
+
+  if (result.linked)
+    {
+      sec->output_section = bfd_abs_section_ptr;
+      sec->kept_section = result.linked;
+
+      /* Also discard the group section.  */
+      if (group)
+       group->output_section = bfd_abs_section_ptr;
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+void
+_bfd_elf_section_already_linked (bfd *abfd, struct bfd_section * sec)
+{
+  flagword flags;
+  const char *name;
+  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 (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;
+    }
+
+  /* FIXME: When doing a relocatable link, we may have trouble
+     copying relocations in other sections that refer to local symbols
+     in the section being discarded.  Those relocations will have to
+     be converted somehow; as of this writing I'm not sure that any of
+     the backends handle that correctly.
+
+     It is tempting to instead not discard link once sections when
+     doing a relocatable link (technically, they should be discarded
+     whenever we are building constructors).  However, that fails,
+     because the linker winds up combining all the link once sections
+     into a single large link once section, which defeats the purpose
+     of having link once sections in the first place.
+
+     Also, not merging link once sections in a relocatable link
+     causes trouble for MIPS ELF, which relies on link once semantics
+     to handle the .reginfo section correctly.  */
+
+  name = bfd_get_section_name (abfd, sec);
+
+  already_linked_list = bfd_section_already_linked_table_lookup (name);
+
+  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.  */
+      if ((flags & SEC_GROUP) == (l->sec->flags & SEC_GROUP)
+         && bfd_coff_get_comdat_section (l->sec->owner, l->sec) == NULL)
+       {
+         /* The section has already been linked.  See if we should
+             issue a warning.  */
+         switch (flags & SEC_LINK_DUPLICATES)
+           {
+           default:
+             abort ();
+
+           case SEC_LINK_DUPLICATES_DISCARD:
+             break;
+
+           case SEC_LINK_DUPLICATES_ONE_ONLY:
+             (*_bfd_error_handler)
+               (_("%s: %s: warning: ignoring duplicate section `%s'\n"),
+                bfd_archive_filename (abfd), name);
+             break;
+
+           case SEC_LINK_DUPLICATES_SAME_SIZE:
+             if (sec->size != l->sec->size)
+               (*_bfd_error_handler)
+                 (_("%s: %s: warning: duplicate section `%s' has different size\n"),
+                  bfd_archive_filename (abfd), name);
+             break;
+           }
+
+         /* Set the output_section field so that lang_add_section
+            does not create a lang_input_section structure for this
+            section.  Since there might be a symbol in the section
+            being discarded, we must retain a pointer to the section
+            which we are really going to use.  */
+         sec->output_section = bfd_abs_section_ptr;
+         sec->kept_section = l->sec;
+         
+         if (flags & SEC_GROUP)
+           {
+             asection *first = elf_next_in_group (sec);
+             asection *s = first;
+
+             while (s != NULL)
+               {
+                 s->output_section = bfd_abs_section_ptr;
+                 /* Record which group discards it.  */
+                 s->kept_section = l->sec;
+                 s = elf_next_in_group (s);
+                 /* These lists are circular.  */
+                 if (s == first)
+                   break;
+               }
+           }
+
+         return;
+       }
+    }
+
+  if (group)
+    {
+      /* 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.  */
+      if (! already_linked (elf_next_in_group (sec), group))
+       return;
+    }
+  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.  */
+    already_linked (sec, group);
+  
+  /* This is the first section with this name.  Record it.  */
+  bfd_section_already_linked_table_insert (already_linked_list, sec);
+}