2007-04-12 H.J. Lu <hongjiu.lu@intel.com>
[deliverable/binutils-gdb.git] / binutils / objcopy.c
index 8796dd6e0208bb0d823a54c9e6b0caabae5bbbbc..d02b3e0e0b46c9eca35e65a2282c321cb992c287 100644 (file)
@@ -1,6 +1,6 @@
 /* objcopy.c -- copy object file from input to output, optionally massaging it.
    Copyright 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
-   2001, 2002, 2003
+   2001, 2002, 2003, 2004, 2005, 2006, 2007
    Free Software Foundation, Inc.
 
    This file is part of GNU Binutils.
@@ -17,8 +17,8 @@
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA.  */
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
 \f
 #include "bfd.h"
 #include "progress.h"
 #include "libiberty.h"
 #include "budbg.h"
 #include "filenames.h"
+#include "fnmatch.h"
+#include "elf-bfd.h"
 #include <sys/stat.h>
+#include "libbfd.h"
 
 /* A list of symbols to explicitly strip out, or to keep.  A linked
    list is good enough for a small number from the command line, but
@@ -144,8 +147,12 @@ static bfd_byte gap_fill = 0;
 static bfd_boolean pad_to_set = FALSE;
 static bfd_vma pad_to;
 
-/* Use alternate machine code?  */
-static int use_alt_mach_code = 0;
+/* Use alternative machine code?  */
+static unsigned long use_alt_mach_code = 0;
+
+/* Output BFD flags user wants to set or clear */
+static flagword bfd_flags_to_set;
+static flagword bfd_flags_to_clear;
 
 /* List of sections to add.  */
 struct section_add
@@ -180,11 +187,19 @@ static bfd_boolean change_leading_char = FALSE;
 /* Whether to remove the leading character from global symbol names.  */
 static bfd_boolean remove_leading_char = FALSE;
 
+/* Whether to permit wildcard in symbol comparison.  */
+static bfd_boolean wildcard = FALSE;
+
+/* True if --localize-hidden is in effect.  */
+static bfd_boolean localize_hidden = FALSE;
+
 /* List of symbols to strip, keep, localize, keep-global, weaken,
    or redefine.  */
 static struct symlist *strip_specific_list = NULL;
+static struct symlist *strip_unneeded_list = NULL;
 static struct symlist *keep_specific_list = NULL;
 static struct symlist *localize_specific_list = NULL;
+static struct symlist *globalize_specific_list = NULL;
 static struct symlist *keepglobal_specific_list = NULL;
 static struct symlist *weaken_specific_list = NULL;
 static struct redefine_node *redefine_sym_list = NULL;
@@ -192,11 +207,17 @@ static struct redefine_node *redefine_sym_list = NULL;
 /* If this is TRUE, we weaken global symbols (set BSF_WEAK).  */
 static bfd_boolean weaken = FALSE;
 
+/* If this is TRUE, we retain BSF_FILE symbols.  */
+static bfd_boolean keep_file_symbols = FALSE;
+
 /* Prefix symbols/sections.  */
 static char *prefix_symbols_string = 0;
 static char *prefix_sections_string = 0;
 static char *prefix_alloc_sections_string = 0;
 
+/* True if --extract-symbol was passed on the command line.  */
+static bfd_boolean extract_symbol = FALSE;
+
 /* 150 isn't special; it's just an arbitrary non-ASCII char value.  */
 enum command_line_switch
   {
@@ -222,8 +243,13 @@ enum command_line_switch
     OPTION_SREC_LEN,
     OPTION_SREC_FORCES3,
     OPTION_STRIP_SYMBOLS,
+    OPTION_STRIP_UNNEEDED_SYMBOL,
+    OPTION_STRIP_UNNEEDED_SYMBOLS,
     OPTION_KEEP_SYMBOLS,
+    OPTION_LOCALIZE_HIDDEN,
     OPTION_LOCALIZE_SYMBOLS,
+    OPTION_GLOBALIZE_SYMBOL,
+    OPTION_GLOBALIZE_SYMBOLS,
     OPTION_KEEPGLOBAL_SYMBOLS,
     OPTION_WEAKEN_SYMBOLS,
     OPTION_RENAME_SECTION,
@@ -233,7 +259,13 @@ enum command_line_switch
     OPTION_PREFIX_ALLOC_SECTIONS,
     OPTION_FORMATS_INFO,
     OPTION_ADD_GNU_DEBUGLINK,
-    OPTION_ONLY_KEEP_DEBUG
+    OPTION_ONLY_KEEP_DEBUG,
+    OPTION_KEEP_FILE_SYMBOLS,
+    OPTION_READONLY_TEXT,
+    OPTION_WRITABLE_TEXT,
+    OPTION_PURE,
+    OPTION_IMPURE,
+    OPTION_EXTRACT_SYMBOL
   };
 
 /* Options to handle if running as "strip".  */
@@ -247,6 +279,7 @@ static struct option strip_options[] =
   {"info", no_argument, 0, OPTION_FORMATS_INFO},
   {"input-format", required_argument, 0, 'I'}, /* Obsolete */
   {"input-target", required_argument, 0, 'I'},
+  {"keep-file-symbols", no_argument, 0, OPTION_KEEP_FILE_SYMBOLS},
   {"keep-symbol", required_argument, 0, 'K'},
   {"only-keep-debug", no_argument, 0, OPTION_ONLY_KEEP_DEBUG},
   {"output-format", required_argument, 0, 'O'},        /* Obsolete */
@@ -261,6 +294,7 @@ static struct option strip_options[] =
   {"target", required_argument, 0, 'F'},
   {"verbose", no_argument, 0, 'v'},
   {"version", no_argument, 0, 'V'},
+  {"wildcard", no_argument, 0, 'w'},
   {0, no_argument, 0, 0}
 };
 
@@ -287,17 +321,23 @@ static struct option copy_options[] =
   {"debugging", no_argument, 0, OPTION_DEBUGGING},
   {"discard-all", no_argument, 0, 'x'},
   {"discard-locals", no_argument, 0, 'X'},
+  {"extract-symbol", no_argument, 0, OPTION_EXTRACT_SYMBOL},
   {"format", required_argument, 0, 'F'}, /* Obsolete */
   {"gap-fill", required_argument, 0, OPTION_GAP_FILL},
+  {"globalize-symbol", required_argument, 0, OPTION_GLOBALIZE_SYMBOL},
+  {"globalize-symbols", required_argument, 0, OPTION_GLOBALIZE_SYMBOLS},
   {"help", no_argument, 0, 'h'},
+  {"impure", no_argument, 0, OPTION_IMPURE},
   {"info", no_argument, 0, OPTION_FORMATS_INFO},
   {"input-format", required_argument, 0, 'I'}, /* Obsolete */
   {"input-target", required_argument, 0, 'I'},
   {"interleave", required_argument, 0, 'i'},
+  {"keep-file-symbols", no_argument, 0, OPTION_KEEP_FILE_SYMBOLS},
   {"keep-global-symbol", required_argument, 0, 'G'},
   {"keep-global-symbols", required_argument, 0, OPTION_KEEPGLOBAL_SYMBOLS},
   {"keep-symbol", required_argument, 0, 'K'},
   {"keep-symbols", required_argument, 0, OPTION_KEEP_SYMBOLS},
+  {"localize-hidden", no_argument, 0, OPTION_LOCALIZE_HIDDEN},
   {"localize-symbol", required_argument, 0, 'L'},
   {"localize-symbols", required_argument, 0, OPTION_LOCALIZE_SYMBOLS},
   {"no-adjust-warnings", no_argument, 0, OPTION_NO_CHANGE_WARNINGS},
@@ -311,6 +351,8 @@ static struct option copy_options[] =
   {"prefix-sections", required_argument, 0, OPTION_PREFIX_SECTIONS},
   {"prefix-alloc-sections", required_argument, 0, OPTION_PREFIX_ALLOC_SECTIONS},
   {"preserve-dates", no_argument, 0, 'p'},
+  {"pure", no_argument, 0, OPTION_PURE},
+  {"readonly-text", no_argument, 0, OPTION_READONLY_TEXT},
   {"redefine-sym", required_argument, 0, OPTION_REDEFINE_SYM},
   {"redefine-syms", required_argument, 0, OPTION_REDEFINE_SYMS},
   {"remove-leading-char", no_argument, 0, OPTION_REMOVE_LEADING_CHAR},
@@ -323,6 +365,8 @@ static struct option copy_options[] =
   {"strip-all", no_argument, 0, 'S'},
   {"strip-debug", no_argument, 0, 'g'},
   {"strip-unneeded", no_argument, 0, OPTION_STRIP_UNNEEDED},
+  {"strip-unneeded-symbol", required_argument, 0, OPTION_STRIP_UNNEEDED_SYMBOL},
+  {"strip-unneeded-symbols", required_argument, 0, OPTION_STRIP_UNNEEDED_SYMBOLS},
   {"strip-symbol", required_argument, 0, 'N'},
   {"strip-symbols", required_argument, 0, OPTION_STRIP_SYMBOLS},
   {"target", required_argument, 0, 'F'},
@@ -331,6 +375,8 @@ static struct option copy_options[] =
   {"weaken", no_argument, 0, OPTION_WEAKEN},
   {"weaken-symbol", required_argument, 0, 'W'},
   {"weaken-symbols", required_argument, 0, OPTION_WEAKEN_SYMBOLS},
+  {"wildcard", no_argument, 0, 'w'},
+  {"writable-text", no_argument, 0, OPTION_WRITABLE_TEXT},
   {0, no_argument, 0, 0}
 };
 
@@ -358,6 +404,7 @@ extern unsigned long          bfd_external_machine;
 
 /* Forward declarations.  */
 static void setup_section (bfd *, asection *, void *);
+static void setup_bfd_headers (bfd *, bfd *);
 static void copy_section (bfd *, asection *, void *);
 static void get_sections (bfd *, asection *, void *);
 static int compare_section_lma (const void *, const void *);
@@ -385,11 +432,20 @@ copy_usage (FILE *stream, int exit_status)
   -g --strip-debug                 Remove all debugging symbols & sections\n\
      --strip-unneeded              Remove all symbols not needed by relocations\n\
   -N --strip-symbol <name>         Do not copy symbol <name>\n\
-  -K --keep-symbol <name>          Only copy symbol <name>\n\
+     --strip-unneeded-symbol <name>\n\
+                                   Do not copy symbol <name> unless needed by\n\
+                                     relocations\n\
+     --only-keep-debug             Strip everything but the debug information\n\
+     --extract-symbol              Remove section contents but keep symbols\n\
+  -K --keep-symbol <name>          Do not strip symbol <name>\n\
+     --keep-file-symbols           Do not strip file symbol(s)\n\
+     --localize-hidden             Turn all ELF hidden symbols into locals\n\
   -L --localize-symbol <name>      Force symbol <name> to be marked as a local\n\
+     --globalize-symbol <name>     Force symbol <name> to be marked as a global\n\
   -G --keep-global-symbol <name>   Localize all symbols except <name>\n\
   -W --weaken-symbol <name>        Force symbol <name> to be marked as a weak\n\
      --weaken                      Force all global symbols to be marked as weak\n\
+  -w --wildcard                    Permit wildcard in symbol comparison\n\
   -x --discard-all                 Remove all non-global symbols\n\
   -X --discard-locals              Remove any compiler-generated symbols\n\
   -i --interleave <number>         Only copy one out of every <number> bytes\n\
@@ -421,23 +477,32 @@ copy_usage (FILE *stream, int exit_status)
      --srec-len <number>           Restrict the length of generated Srecords\n\
      --srec-forceS3                Restrict the type of generated Srecords to S3\n\
      --strip-symbols <file>        -N for all symbols listed in <file>\n\
+     --strip-unneeded-symbols <file>\n\
+                                   --strip-unneeded-symbol for all symbols listed\n\
+                                     in <file>\n\
      --keep-symbols <file>         -K for all symbols listed in <file>\n\
      --localize-symbols <file>     -L for all symbols listed in <file>\n\
+     --globalize-symbols <file>    --globalize-symbol for all in <file>\n\
      --keep-global-symbols <file>  -G for all symbols listed in <file>\n\
      --weaken-symbols <file>       -W for all symbols listed in <file>\n\
-     --alt-machine-code <index>    Use alternate machine code for output\n\
+     --alt-machine-code <index>    Use the target's <index>'th alternative machine\n\
+     --writable-text               Mark the output text as writable\n\
+     --readonly-text               Make the output text write protected\n\
+     --pure                        Mark the output file as demand paged\n\
+     --impure                      Mark the output file as impure\n\
      --prefix-symbols <prefix>     Add <prefix> to start of every symbol name\n\
      --prefix-sections <prefix>    Add <prefix> to start of every section name\n\
      --prefix-alloc-sections <prefix>\n\
                                    Add <prefix> to start of every allocatable\n\
                                      section name\n\
   -v --verbose                     List all object files modified\n\
+  @<file>                          Read options from <file>\n\
   -V --version                     Display this program's version number\n\
   -h --help                        Display this output\n\
      --info                        List object formats & architectures supported\n\
 "));
   list_supported_targets (program_name, stream);
-  if (exit_status == 0)
+  if (REPORT_BUGS_TO[0] && exit_status == 0)
     fprintf (stream, _("Report bugs to %s\n"), REPORT_BUGS_TO);
   exit (exit_status);
 }
@@ -457,8 +522,11 @@ strip_usage (FILE *stream, int exit_status)
   -s --strip-all                   Remove all symbol and relocation information\n\
   -g -S -d --strip-debug           Remove all debugging symbols & sections\n\
      --strip-unneeded              Remove all symbols not needed by relocations\n\
+     --only-keep-debug             Strip everything but the debug information\n\
   -N --strip-symbol=<name>         Do not copy symbol <name>\n\
-  -K --keep-symbol=<name>          Only copy symbol <name>\n\
+  -K --keep-symbol=<name>          Do not strip symbol <name>\n\
+     --keep-file-symbols           Do not strip file symbol(s)\n\
+  -w --wildcard                    Permit wildcard in symbol comparison\n\
   -x --discard-all                 Remove all non-global symbols\n\
   -X --discard-locals              Remove any compiler-generated symbols\n\
   -v --verbose                     List all object files modified\n\
@@ -469,7 +537,7 @@ strip_usage (FILE *stream, int exit_status)
 "));
 
   list_supported_targets (program_name, stream);
-  if (exit_status == 0)
+  if (REPORT_BUGS_TO[0] && exit_status == 0)
     fprintf (stream, _("Report bugs to %s\n"), REPORT_BUGS_TO);
   exit (exit_status);
 }
@@ -508,7 +576,7 @@ parse_flags (const char *s)
       PARSE_FLAG ("code", SEC_CODE);
       PARSE_FLAG ("data", SEC_DATA);
       PARSE_FLAG ("rom", SEC_ROM);
-      PARSE_FLAG ("share", SEC_SHARED);
+      PARSE_FLAG ("share", SEC_COFF_SHARED);
       PARSE_FLAG ("contents", SEC_HAS_CONTENTS);
 #undef PARSE_FLAG
       else
@@ -591,7 +659,10 @@ add_specific_symbols (const char *filename, struct symlist **list)
 
   size = get_file_size (filename);
   if (size == 0)
-    return;
+    {
+      status = 1;
+      return;
+    }
 
   buffer = xmalloc (size + 2);
   f = fopen (filename, FOPEN_RT);
@@ -672,8 +743,8 @@ add_specific_symbols (const char *filename, struct symlist **list)
            ;
 
          if (! IS_LINE_TERMINATOR (* extra))
-           non_fatal (_("Ignoring rubbish found on line %d of %s"),
-                      line_count, filename);
+           non_fatal (_("%s:%d: Ignoring rubbish found on this line"),
+                      filename, line_count);
        }
 
       * name_end = '\0';
@@ -696,13 +767,54 @@ is_specified_symbol (const char *name, struct symlist *list)
 {
   struct symlist *tmp_list;
 
-  for (tmp_list = list; tmp_list; tmp_list = tmp_list->next)
-    if (strcmp (name, tmp_list->name) == 0)
-      return TRUE;
+  if (wildcard)
+    {
+      for (tmp_list = list; tmp_list; tmp_list = tmp_list->next)
+       if (*(tmp_list->name) != '!')
+         {
+           if (!fnmatch (tmp_list->name, name, 0))
+             return TRUE;
+         }
+       else
+         {
+           if (fnmatch (tmp_list->name + 1, name, 0))
+             return TRUE;
+         }
+    }
+  else
+    {
+      for (tmp_list = list; tmp_list; tmp_list = tmp_list->next)
+       if (strcmp (name, tmp_list->name) == 0)
+         return TRUE;
+    }
 
   return FALSE;
 }
 
+/* Return a pointer to the symbol used as a signature for GROUP.  */
+
+static asymbol *
+group_signature (asection *group)
+{
+  bfd *abfd = group->owner;
+  Elf_Internal_Shdr *ghdr;
+
+  if (bfd_get_flavour (abfd) != bfd_target_elf_flavour)
+    return NULL;
+
+  ghdr = &elf_section_data (group)->this_hdr;
+  if (ghdr->sh_link < elf_numsections (abfd))
+    {
+      const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+      Elf_Internal_Shdr *symhdr = elf_elfsections (abfd) [ghdr->sh_link];
+
+      if (symhdr->sh_type == SHT_SYMTAB
+         && ghdr->sh_info < symhdr->sh_size / bed->s->sizeof_sym)
+       return isympp[ghdr->sh_info - 1];
+    }
+  return NULL;
+}
+
 /* See if a section is being removed.  */
 
 static bfd_boolean
@@ -733,7 +845,50 @@ is_strip_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec)
        return FALSE;
     }
 
-  return strip_symbols == STRIP_NONDEBUG ? TRUE : FALSE;
+  if ((bfd_get_section_flags (abfd, sec) & SEC_GROUP) != 0)
+    {
+      asymbol *gsym;
+      const char *gname;
+
+      /* PR binutils/3166
+        Group sections look like debugging sections but they are not.
+        (They have a non-zero size but they are not ALLOCated).  */
+      if (strip_symbols == STRIP_NONDEBUG)
+       return TRUE;
+
+      /* PR binutils/3181
+        If we are going to strip the group signature symbol, then
+        strip the group section too.  */
+      gsym = group_signature (sec);
+      if (gsym != NULL)
+       gname = gsym->name;
+      else
+       gname = sec->name;
+      if ((strip_symbols == STRIP_ALL
+          && !is_specified_symbol (gname, keep_specific_list))
+         || is_specified_symbol (gname, strip_specific_list))
+       return TRUE;
+    }
+
+  return FALSE;
+}
+
+/* Return true if SYM is a hidden symbol.  */
+
+static bfd_boolean
+is_hidden_symbol (asymbol *sym)
+{
+  elf_symbol_type *elf_sym;
+
+  elf_sym = elf_symbol_from (sym->the_bfd, sym);
+  if (elf_sym != NULL)
+    switch (ELF_ST_VISIBILITY (elf_sym->internal_elf_sym.st_other))
+      {
+      case STV_HIDDEN:
+      case STV_INTERNAL:
+       return TRUE;
+      }
+  return FALSE;
 }
 
 /* Choose which symbol entries to copy; put the result in OSYMS.
@@ -846,7 +1001,7 @@ filter_symbols (bfd *abfd, bfd *obfd, asymbol **osyms,
        keep = (strip_symbols != STRIP_DEBUG
                && strip_symbols != STRIP_UNNEEDED
                && ! convert_debugging);
-      else if (bfd_get_section (sym)->comdat)
+      else if (bfd_coff_get_comdat_section (abfd, bfd_get_section (sym)))
        /* COMDAT sections store special information in local
           symbols, so we cannot risk stripping any of them.  */
        keep = 1;
@@ -858,28 +1013,47 @@ filter_symbols (bfd *abfd, bfd *obfd, asymbol **osyms,
 
       if (keep && is_specified_symbol (name, strip_specific_list))
        keep = 0;
-      if (!keep && is_specified_symbol (name, keep_specific_list))
+      if (keep
+         && !(flags & BSF_KEEP)
+         && is_specified_symbol (name, strip_unneeded_list))
+       keep = 0;
+      if (!keep
+         && ((keep_file_symbols && (flags & BSF_FILE))
+             || is_specified_symbol (name, keep_specific_list)))
        keep = 1;
       if (keep && is_strip_section (abfd, bfd_get_section (sym)))
        keep = 0;
 
-      if (keep && (flags & BSF_GLOBAL) != 0
-         && (weaken || is_specified_symbol (name, weaken_specific_list)))
-       {
-         sym->flags &=~ BSF_GLOBAL;
-         sym->flags |= BSF_WEAK;
-       }
-      if (keep && !undefined && (flags & (BSF_GLOBAL | BSF_WEAK))
-         && (is_specified_symbol (name, localize_specific_list)
-             || (keepglobal_specific_list != NULL
-                 && ! is_specified_symbol (name, keepglobal_specific_list))))
+      if (keep)
        {
-         sym->flags &= ~(BSF_GLOBAL | BSF_WEAK);
-         sym->flags |= BSF_LOCAL;
-       }
+         if ((flags & BSF_GLOBAL) != 0
+             && (weaken || is_specified_symbol (name, weaken_specific_list)))
+           {
+             sym->flags &= ~ BSF_GLOBAL;
+             sym->flags |= BSF_WEAK;
+           }
 
-      if (keep)
-       to[dst_count++] = sym;
+         if (!undefined
+             && (flags & (BSF_GLOBAL | BSF_WEAK))
+             && (is_specified_symbol (name, localize_specific_list)
+                 || (keepglobal_specific_list != NULL
+                     && ! is_specified_symbol (name, keepglobal_specific_list))
+                 || (localize_hidden && is_hidden_symbol (sym))))
+           {
+             sym->flags &= ~ (BSF_GLOBAL | BSF_WEAK);
+             sym->flags |= BSF_LOCAL;
+           }
+
+         if (!undefined
+             && (flags & BSF_LOCAL) 
+             && is_specified_symbol (name, globalize_specific_list))
+           {
+             sym->flags &= ~ BSF_LOCAL;
+             sym->flags |= BSF_GLOBAL;
+           }
+
+         to[dst_count++] = sym;
+       }
     }
 
   to[dst_count] = NULL;
@@ -1023,10 +1197,10 @@ add_redefine_syms_file (const char *filename)
          continue;
        }
       else
-       fatal (_("%s: garbage at end of line %d"), filename, lineno);
+       fatal (_("%s:%d: garbage found at end of line"), filename, lineno);
  comment:
       if (len != 0 && (outsym_off == 0 || outsym_off == len))
-       fatal (_("%s: missing new symbol name at line %d"), filename, lineno);
+       fatal (_("%s:%d: missing new symbol name"), filename, lineno);
       buf[len++] = '\0';
 
       /* Eat the rest of the line and finish it.  */
@@ -1036,31 +1210,83 @@ add_redefine_syms_file (const char *filename)
     }
 
   if (len != 0)
-    fatal (_("%s: premature end of file at line %d"), filename, lineno);
+    fatal (_("%s:%d: premature end of file"), filename, lineno);
 
   free (buf);
 }
 
-/* Keep only every `copy_byte'th byte in MEMHUNK, which is *SIZE bytes long.
-   Adjust *SIZE.  */
+/* Copy unkown object file IBFD onto OBFD.
+   Returns TRUE upon success, FALSE otherwise.  */
 
-static void
-filter_bytes (char *memhunk, bfd_size_type *size)
+static bfd_boolean
+copy_unknown_object (bfd *ibfd, bfd *obfd)
 {
-  char *from = memhunk + copy_byte, *to = memhunk, *end = memhunk + *size;
+  char *cbuf;
+  int tocopy;
+  long ncopied;
+  long size;
+  struct stat buf;
 
-  for (; from < end; from += interleave)
-    *to++ = *from;
+  if (bfd_stat_arch_elt (ibfd, &buf) != 0)
+    {
+      bfd_nonfatal (bfd_get_archive_filename (ibfd));
+      return FALSE;
+    }
 
-  if (*size % interleave > (bfd_size_type) copy_byte)
-    *size = (*size / interleave) + 1;
-  else
-    *size /= interleave;
+  size = buf.st_size;
+  if (size < 0)
+    {
+      non_fatal (_("stat returns negative size for `%s'"),
+                bfd_get_archive_filename (ibfd));
+      return FALSE;
+    }
+
+  if (bfd_seek (ibfd, (file_ptr) 0, SEEK_SET) != 0)
+    {
+      bfd_nonfatal (bfd_get_archive_filename (ibfd));
+      return FALSE;
+    }
+
+  if (verbose)
+    printf (_("copy from `%s' [unknown] to `%s' [unknown]\n"),
+           bfd_get_archive_filename (ibfd), bfd_get_filename (obfd));
+
+  cbuf = xmalloc (BUFSIZE);
+  ncopied = 0;
+  while (ncopied < size)
+    {
+      tocopy = size - ncopied;
+      if (tocopy > BUFSIZE)
+       tocopy = BUFSIZE;
+
+      if (bfd_bread (cbuf, (bfd_size_type) tocopy, ibfd)
+         != (bfd_size_type) tocopy)
+       {
+         bfd_nonfatal (bfd_get_archive_filename (ibfd));
+         free (cbuf);
+         return FALSE;
+       }
+
+      if (bfd_bwrite (cbuf, (bfd_size_type) tocopy, obfd)
+         != (bfd_size_type) tocopy)
+       {
+         bfd_nonfatal (bfd_get_filename (obfd));
+         free (cbuf);
+         return FALSE;
+       }
+
+      ncopied += tocopy;
+    }
+
+  chmod (bfd_get_filename (obfd), buf.st_mode);
+  free (cbuf);
+  return TRUE;
 }
 
-/* Copy object file IBFD onto OBFD.  */
+/* Copy object file IBFD onto OBFD.
+   Returns TRUE upon success, FALSE otherwise.  */
 
-static void
+static bfd_boolean
 copy_object (bfd *ibfd, bfd *obfd)
 {
   bfd_vma start;
@@ -1077,34 +1303,47 @@ copy_object (bfd *ibfd, bfd *obfd)
   if (ibfd->xvec->byteorder != obfd->xvec->byteorder
       && ibfd->xvec->byteorder != BFD_ENDIAN_UNKNOWN
       && obfd->xvec->byteorder != BFD_ENDIAN_UNKNOWN)
-    {
-      fatal (_("Unable to change endianness of input file(s)"));
-      return;
-    }
+    fatal (_("Unable to change endianness of input file(s)"));
 
   if (!bfd_set_format (obfd, bfd_get_format (ibfd)))
-    RETURN_NONFATAL (bfd_get_filename (obfd));
+    {
+      bfd_nonfatal (bfd_get_filename (obfd));
+      return FALSE;
+    }
 
   if (verbose)
-    printf (_("copy from %s(%s) to %s(%s)\n"),
-           bfd_get_filename (ibfd), bfd_get_target (ibfd),
+    printf (_("copy from `%s' [%s] to `%s' [%s]\n"),
+           bfd_get_archive_filename (ibfd), bfd_get_target (ibfd),
            bfd_get_filename (obfd), bfd_get_target (obfd));
 
-  if (set_start_set)
-    start = set_start;
+  if (extract_symbol)
+    start = 0;
   else
-    start = bfd_get_start_address (ibfd);
-  start += change_start;
+    {
+      if (set_start_set)
+       start = set_start;
+      else
+       start = bfd_get_start_address (ibfd);
+      start += change_start;
+    }
 
   /* Neither the start address nor the flags
      need to be set for a core file.  */
   if (bfd_get_format (obfd) != bfd_core)
     {
+      flagword flags;
+
+      flags = bfd_get_file_flags (ibfd);
+      flags |= bfd_flags_to_set;
+      flags &= ~bfd_flags_to_clear;
+      flags &= bfd_applicable_file_flags (obfd);
+
       if (!bfd_set_start_address (obfd, start)
-         || !bfd_set_file_flags (obfd,
-                                 (bfd_get_file_flags (ibfd)
-                                  & bfd_applicable_file_flags (obfd))))
-       RETURN_NONFATAL (bfd_get_filename (ibfd));
+         || !bfd_set_file_flags (obfd, flags))
+       {
+         bfd_nonfatal (bfd_get_archive_filename (ibfd));
+         return FALSE;
+       }
     }
 
   /* Copy architecture of input file to output file.  */
@@ -1113,12 +1352,22 @@ copy_object (bfd *ibfd, bfd *obfd)
   if (!bfd_set_arch_mach (obfd, iarch, imach)
       && (ibfd->target_defaulted
          || bfd_get_arch (ibfd) != bfd_get_arch (obfd)))
-    non_fatal (_("Warning: Output file cannot represent architecture %s"),
-              bfd_printable_arch_mach (bfd_get_arch (ibfd),
-                                       bfd_get_mach (ibfd)));
+    {
+      if (bfd_get_arch (ibfd) == bfd_arch_unknown)
+       non_fatal (_("Unable to recognise the format of the input file `%s'"),
+                  bfd_get_archive_filename (ibfd));
+      else
+       non_fatal (_("Warning: Output file cannot represent architecture `%s'"),
+                  bfd_printable_arch_mach (bfd_get_arch (ibfd),
+                                           bfd_get_mach (ibfd)));
+      return FALSE;
+    }
 
   if (!bfd_set_format (obfd, bfd_get_format (ibfd)))
-    RETURN_NONFATAL (bfd_get_filename (ibfd));
+    {
+      bfd_nonfatal (bfd_get_archive_filename (ibfd));
+      return FALSE;
+    }
 
   if (isympp)
     free (isympp);
@@ -1126,10 +1375,30 @@ copy_object (bfd *ibfd, bfd *obfd)
   if (osympp != isympp)
     free (osympp);
 
+  isympp = NULL;
+  osympp = NULL;
+
+  symsize = bfd_get_symtab_upper_bound (ibfd);
+  if (symsize < 0)
+    {
+      bfd_nonfatal (bfd_get_archive_filename (ibfd));
+      return FALSE;
+    }
+
+  osympp = isympp = xmalloc (symsize);
+  symcount = bfd_canonicalize_symtab (ibfd, isympp);
+  if (symcount < 0)
+    {
+      bfd_nonfatal (bfd_get_filename (ibfd));
+      return FALSE;
+    }
+
   /* BFD mandates that all output sections be created and sizes set before
      any output is done.  Thus, we traverse all sections multiple times.  */
   bfd_map_over_sections (ibfd, setup_section, obfd);
 
+  setup_bfd_headers (ibfd, obfd);
+
   if (add_sections != NULL)
     {
       struct section_add *padd;
@@ -1139,45 +1408,59 @@ copy_object (bfd *ibfd, bfd *obfd)
        {
          flagword flags;
 
-         padd->section = bfd_make_section (obfd, padd->name);
-         if (padd->section == NULL)
-           {
-             non_fatal (_("can't create section `%s': %s"),
-                      padd->name, bfd_errmsg (bfd_get_error ()));
-             status = 1;
-             return;
-           }
-
-         if (! bfd_set_section_size (obfd, padd->section, padd->size))
-           RETURN_NONFATAL (bfd_get_filename (obfd));
-
          pset = find_section_list (padd->name, FALSE);
          if (pset != NULL)
            pset->used = TRUE;
 
+         flags = SEC_HAS_CONTENTS | SEC_READONLY | SEC_DATA;
          if (pset != NULL && pset->set_flags)
            flags = pset->flags | SEC_HAS_CONTENTS;
+
+         /* bfd_make_section_with_flags() does not return very helpful
+            error codes, so check for the most likely user error first.  */
+         if (bfd_get_section_by_name (obfd, padd->name))
+           {
+             non_fatal (_("can't add section '%s' - it already exists!"), padd->name);
+             return FALSE;
+           }
          else
-           flags = SEC_HAS_CONTENTS | SEC_READONLY | SEC_DATA;
+           {
+             padd->section = bfd_make_section_with_flags (obfd, padd->name, flags);
+             if (padd->section == NULL)
+               {
+                 non_fatal (_("can't create section `%s': %s"),
+                            padd->name, bfd_errmsg (bfd_get_error ()));
+                 return FALSE;
+               }
+           }
 
-         if (! bfd_set_section_flags (obfd, padd->section, flags))
-           RETURN_NONFATAL (bfd_get_filename (obfd));
+         if (! bfd_set_section_size (obfd, padd->section, padd->size))
+           {
+             bfd_nonfatal (bfd_get_filename (obfd));
+             return FALSE;
+           }
 
          if (pset != NULL)
            {
              if (pset->change_vma != CHANGE_IGNORE)
                if (! bfd_set_section_vma (obfd, padd->section,
                                           pset->vma_val))
-                 RETURN_NONFATAL (bfd_get_filename (obfd));
+                 {
+                   bfd_nonfatal (bfd_get_filename (obfd));
+                   return FALSE;
+                 }
 
              if (pset->change_lma != CHANGE_IGNORE)
                {
                  padd->section->lma = pset->lma_val;
-                 
+
                  if (! bfd_set_section_alignment
                      (obfd, padd->section,
                       bfd_section_alignment (obfd, padd->section)))
-                   RETURN_NONFATAL (bfd_get_filename (obfd));
+                   {
+                     bfd_nonfatal (bfd_get_filename (obfd));
+                     return FALSE;
+                   }
                }
            }
        }
@@ -1189,10 +1472,57 @@ copy_object (bfd *ibfd, bfd *obfd)
        (obfd, gnu_debuglink_filename);
 
       if (gnu_debuglink_section == NULL)
-       RETURN_NONFATAL (gnu_debuglink_filename);
+       {
+         bfd_nonfatal (gnu_debuglink_filename);
+         return FALSE;
+       }
+
+      /* Special processing for PE format files.  We
+        have no way to distinguish PE from COFF here.  */
+      if (bfd_get_flavour (obfd) == bfd_target_coff_flavour)
+       {
+         bfd_vma debuglink_vma;
+         asection * highest_section;
+         asection * sec;
+
+         /* The PE spec requires that all sections be adjacent and sorted
+            in ascending order of VMA.  It also specifies that debug
+            sections should be last.  This is despite the fact that debug
+            sections are not loaded into memory and so in theory have no
+            use for a VMA.
+
+            This means that the debuglink section must be given a non-zero
+            VMA which makes it contiguous with other debug sections.  So
+            walk the current section list, find the section with the
+            highest VMA and start the debuglink section after that one.  */
+         for (sec = obfd->sections, highest_section = NULL;
+              sec != NULL;
+              sec = sec->next)
+           if (sec->vma > 0
+               && (highest_section == NULL
+                   || sec->vma > highest_section->vma))
+             highest_section = sec;
+
+         if (highest_section)
+           debuglink_vma = BFD_ALIGN (highest_section->vma
+                                      + highest_section->size,
+                                      /* FIXME: We ought to be using
+                                         COFF_PAGE_SIZE here or maybe
+                                         bfd_get_section_alignment() (if it
+                                         was set) but since this is for PE
+                                         and we know the required alignment
+                                         it is easier just to hard code it.  */
+                                      0x1000);
+         else
+           /* Umm, not sure what to do in this case.  */
+           debuglink_vma = 0x1000;
+
+         bfd_set_section_vma (obfd, gnu_debuglink_section, debuglink_vma);
+       }
     }
 
-  if (gap_fill_set || pad_to_set)
+  if (bfd_count_sections (obfd) != 0
+      && (gap_fill_set || pad_to_set))
     {
       asection **set;
       unsigned int c, i;
@@ -1277,15 +1607,6 @@ copy_object (bfd *ibfd, bfd *obfd)
   /* Symbol filtering must happen after the output sections
      have been created, but before their contents are set.  */
   dhandle = NULL;
-  symsize = bfd_get_symtab_upper_bound (ibfd);
-  if (symsize < 0)
-    RETURN_NONFATAL (bfd_get_filename (ibfd));
-
-  osympp = isympp = xmalloc (symsize);
-  symcount = bfd_canonicalize_symtab (ibfd, isympp);
-  if (symcount < 0)
-    RETURN_NONFATAL (bfd_get_filename (ibfd));
-
   if (convert_debugging)
     dhandle = read_debugging_info (ibfd, isympp, symcount);
 
@@ -1294,9 +1615,11 @@ copy_object (bfd *ibfd, bfd *obfd)
       || strip_symbols == STRIP_UNNEEDED
       || strip_symbols == STRIP_NONDEBUG
       || discard_locals != LOCALS_UNDEF
+      || localize_hidden
       || strip_specific_list != NULL
       || keep_specific_list != NULL
       || localize_specific_list != NULL
+      || globalize_specific_list != NULL
       || keepglobal_specific_list != NULL
       || weaken_specific_list != NULL
       || prefix_symbols_string
@@ -1329,7 +1652,7 @@ copy_object (bfd *ibfd, bfd *obfd)
       if (! write_debugging_info (obfd, dhandle, &symcount, &osympp))
        {
          status = 1;
-         return;
+         return FALSE;
        }
     }
 
@@ -1346,7 +1669,10 @@ copy_object (bfd *ibfd, bfd *obfd)
        {
          if (! bfd_set_section_contents (obfd, padd->section, padd->contents,
                                          0, padd->size))
-           RETURN_NONFATAL (bfd_get_filename (obfd));
+           {
+             bfd_nonfatal (bfd_get_filename (obfd));
+             return FALSE;
+           }
        }
     }
 
@@ -1354,7 +1680,10 @@ copy_object (bfd *ibfd, bfd *obfd)
     {
       if (! bfd_fill_in_gnu_debuglink_section
          (obfd, gnu_debuglink_section, gnu_debuglink_filename))
-       RETURN_NONFATAL (gnu_debuglink_filename);
+       {
+         bfd_nonfatal (gnu_debuglink_filename);
+         return FALSE;
+       }
     }
 
   if (gap_fill_set || pad_to_set)
@@ -1390,7 +1719,10 @@ copy_object (bfd *ibfd, bfd *obfd)
 
                  if (! bfd_set_section_contents (obfd, osections[i], buf,
                                                  off, now))
-                   RETURN_NONFATAL (bfd_get_filename (obfd));
+                   {
+                     bfd_nonfatal (bfd_get_filename (obfd));
+                     return FALSE;
+                   }
 
                  left -= now;
                  off += now;
@@ -1399,6 +1731,11 @@ copy_object (bfd *ibfd, bfd *obfd)
        }
     }
 
+  /* Do not copy backend data if --extract-symbol is passed; anything
+     that needs to look at the section contents will fail.  */
+  if (extract_symbol)
+    return TRUE;
+
   /* Allow the BFD backend to copy any private data it understands
      from the input BFD to the output BFD.  This is done last to
      permit the routine to look at the filtered symbol table, which is
@@ -1413,8 +1750,7 @@ copy_object (bfd *ibfd, bfd *obfd)
       non_fatal (_("%s: error copying private BFD data: %s"),
                 bfd_get_filename (obfd),
                 bfd_errmsg (bfd_get_error ()));
-      status = 1;
-      return;
+      return FALSE;
     }
 
   /* Switch to the alternate machine code.  We have to do this at the
@@ -1422,23 +1758,32 @@ copy_object (bfd *ibfd, bfd *obfd)
      the first section.  */
   if (use_alt_mach_code != 0)
     {
-      if (!bfd_alt_mach_code (obfd, use_alt_mach_code))
-       non_fatal (_("unknown alternate machine code, ignored"));
+      if (! bfd_alt_mach_code (obfd, use_alt_mach_code))
+       {
+         non_fatal (_("this target does not support %lu alternative machine codes"),
+                    use_alt_mach_code);
+         if (bfd_get_flavour (obfd) == bfd_target_elf_flavour)
+           {
+             non_fatal (_("treating that number as an absolute e_machine value instead"));
+             elf_elfheader (obfd)->e_machine = use_alt_mach_code;
+           }
+         else
+           non_fatal (_("ignoring the alternative value"));
+       }
     }
-}
 
-#undef MKDIR
-#if defined (_WIN32) && !defined (__CYGWIN32__)
-#define MKDIR(DIR, MODE) mkdir (DIR)
-#else
-#define MKDIR(DIR, MODE) mkdir (DIR, MODE)
-#endif
+  return TRUE;
+}
 
 /* Read each archive element in turn from IBFD, copy the
-   contents to temp file, and keep the temp file handle.  */
+   contents to temp file, and keep the temp file handle.
+   If 'force_output_target' is TRUE then make sure that
+   all elements in the new archive are of the type
+   'output_target'.  */
 
 static void
-copy_archive (bfd *ibfd, bfd *obfd, const char *output_target)
+copy_archive (bfd *ibfd, bfd *obfd, const char *output_target,
+             bfd_boolean force_output_target)
 {
   struct name_list
     {
@@ -1448,12 +1793,13 @@ copy_archive (bfd *ibfd, bfd *obfd, const char *output_target)
     } *list, *l;
   bfd **ptr = &obfd->archive_head;
   bfd *this_element;
-  char *dir = make_tempname (bfd_get_filename (obfd));
+  char * dir;
 
   /* Make a temp directory to hold the contents.  */
-  if (MKDIR (dir, 0700) != 0)
-    fatal (_("cannot mkdir %s for archive copying (error: %s)"),
-          dir, strerror (errno));
+  dir = make_tempdir (bfd_get_filename (obfd));
+  if (dir == NULL)
+      fatal (_("cannot create tempdir for archive copying (error: %s)"),
+          strerror (errno));
 
   obfd->has_armap = ibfd->has_armap;
 
@@ -1471,6 +1817,7 @@ copy_archive (bfd *ibfd, bfd *obfd, const char *output_target)
       bfd *last_element;
       struct stat buf;
       int stat_status = 0;
+      bfd_boolean delete = TRUE;
 
       /* Create an output file for this member.  */
       output_name = concat (dir, "/",
@@ -1479,10 +1826,10 @@ copy_archive (bfd *ibfd, bfd *obfd, const char *output_target)
       /* If the file already exists, make another temp dir.  */
       if (stat (output_name, &buf) >= 0)
        {
-         output_name = make_tempname (output_name);
-         if (MKDIR (output_name, 0700) != 0)
-           fatal (_("cannot mkdir %s for archive copying (error: %s)"),
-                  output_name, strerror (errno));
+         output_name = make_tempdir (output_name);
+         if (output_name == NULL)
+           fatal (_("cannot create tempdir for archive copying (error: %s)"),
+                  strerror (errno));
 
          l = xmalloc (sizeof (struct name_list));
          l->name = output_name;
@@ -1493,7 +1840,6 @@ copy_archive (bfd *ibfd, bfd *obfd, const char *output_target)
                                bfd_get_filename (this_element), (char *) 0);
        }
 
-      output_bfd = bfd_openw (output_name, output_target);
       if (preserve_dates)
        {
          stat_status = bfd_stat_arch_elt (this_element, &buf);
@@ -1506,37 +1852,76 @@ copy_archive (bfd *ibfd, bfd *obfd, const char *output_target)
       l = xmalloc (sizeof (struct name_list));
       l->name = output_name;
       l->next = list;
+      l->obfd = NULL;
       list = l;
 
-      if (output_bfd == NULL)
-       RETURN_NONFATAL (output_name);
-
       if (bfd_check_format (this_element, bfd_object))
-       copy_object (this_element, output_bfd);
+       {
+         /* PR binutils/3110: Cope with archives
+            containing multiple target types.  */
+         if (force_output_target)
+           output_bfd = bfd_openw (output_name, output_target);
+         else
+           output_bfd = bfd_openw (output_name, bfd_get_target (this_element));
+
+         if (output_bfd == NULL)
+           RETURN_NONFATAL (output_name);
 
-      if (!bfd_close (output_bfd))
+         delete = ! copy_object (this_element, output_bfd);
+
+         if (! delete
+             || bfd_get_arch (this_element) != bfd_arch_unknown)
+           {
+             if (!bfd_close (output_bfd))
+               {
+                 bfd_nonfatal (bfd_get_filename (output_bfd));
+                 /* Error in new object file. Don't change archive.  */
+                 status = 1;
+               }
+           }
+         else
+           goto copy_unknown_element;
+       }
+      else
        {
-         bfd_nonfatal (bfd_get_filename (output_bfd));
-         /* Error in new object file. Don't change archive.  */
-         status = 1;
+         non_fatal (_("Unable to recognise the format of the input file `%s'"),
+                    bfd_get_archive_filename (this_element));
+
+         output_bfd = bfd_openw (output_name, output_target);
+copy_unknown_element:
+         delete = !copy_unknown_object (this_element, output_bfd);
+         if (!bfd_close_all_done (output_bfd))
+           {
+             bfd_nonfatal (bfd_get_filename (output_bfd));
+             /* Error in new object file. Don't change archive.  */
+             status = 1;
+           }
        }
 
-      if (preserve_dates && stat_status == 0)
-       set_times (output_name, &buf);
+      if (delete)
+       {
+         unlink (output_name);
+         status = 1;
+       }
+      else
+       {
+         if (preserve_dates && stat_status == 0)
+           set_times (output_name, &buf);
 
-      /* Open the newly output file and attach to our list.  */
-      output_bfd = bfd_openr (output_name, output_target);
+         /* Open the newly output file and attach to our list.  */
+         output_bfd = bfd_openr (output_name, output_target);
 
-      l->obfd = output_bfd;
+         l->obfd = output_bfd;
 
-      *ptr = output_bfd;
-      ptr = &output_bfd->next;
+         *ptr = output_bfd;
+         ptr = &output_bfd->next;
 
-      last_element = this_element;
+         last_element = this_element;
 
-      this_element = bfd_openr_next_archived_file (ibfd, last_element);
+         this_element = bfd_openr_next_archived_file (ibfd, last_element);
 
-      bfd_close (last_element);
+         bfd_close (last_element);
+       }
     }
   *ptr = NULL;
 
@@ -1584,23 +1969,30 @@ copy_file (const char *input_filename, const char *output_filename,
 
   if (bfd_check_format (ibfd, bfd_archive))
     {
+      bfd_boolean force_output_target;
       bfd *obfd;
 
       /* bfd_get_target does not return the correct value until
          bfd_check_format succeeds.  */
       if (output_target == NULL)
-       output_target = bfd_get_target (ibfd);
+       {
+         output_target = bfd_get_target (ibfd);
+         force_output_target = FALSE;
+       }
+      else
+       force_output_target = TRUE;
 
       obfd = bfd_openw (output_filename, output_target);
       if (obfd == NULL)
        RETURN_NONFATAL (output_filename);
 
-      copy_archive (ibfd, obfd, output_target);
+      copy_archive (ibfd, obfd, output_target, force_output_target);
     }
   else if (bfd_check_format_matches (ibfd, bfd_object, &obj_matching))
     {
       bfd *obfd;
     do_copy:
+
       /* bfd_get_target does not return the correct value until
          bfd_check_format succeeds.  */
       if (output_target == NULL)
@@ -1610,13 +2002,15 @@ copy_file (const char *input_filename, const char *output_filename,
       if (obfd == NULL)
        RETURN_NONFATAL (output_filename);
 
-      copy_object (ibfd, obfd);
+      if (! copy_object (ibfd, obfd))
+       status = 1;
 
       if (!bfd_close (obfd))
        RETURN_NONFATAL (output_filename);
 
       if (!bfd_close (ibfd))
        RETURN_NONFATAL (input_filename);
+
     }
   else
     {
@@ -1709,6 +2103,32 @@ find_section_rename (bfd * ibfd ATTRIBUTE_UNUSED, sec_ptr isection,
   return old_name;
 }
 
+/* Once each of the sections is copied, we may still need to do some
+   finalization work for private section headers.  Do that here.  */
+
+static void
+setup_bfd_headers (bfd *ibfd, bfd *obfd)
+{
+  const char *err;
+
+  /* Allow the BFD backend to copy any private data it understands
+     from the input section to the output section.  */
+  if (! bfd_copy_private_header_data (ibfd, obfd))
+    {
+      err = _("private header data");
+      goto loser;
+    }
+
+  /* All went well.  */
+  return;
+
+loser:
+  non_fatal (_("%s: error in %s: %s"),
+            bfd_get_filename (ibfd),
+            err, bfd_errmsg (bfd_get_error ()));
+  status = 1;
+}
+
 /* Create a section in OBFD with the same
    name and attributes as ISECTION in IBFD.  */
 
@@ -1753,7 +2173,12 @@ setup_section (bfd *ibfd, sec_ptr isection, void *obfdarg)
       name = n;
     }
 
-  osection = bfd_make_section_anyway (obfd, name);
+  if (p != NULL && p->set_flags)
+    flags = p->flags | (flags & (SEC_HAS_CONTENTS | SEC_RELOC));
+  else if (strip_symbols == STRIP_NONDEBUG && (flags & SEC_ALLOC) != 0)
+    flags &= ~(SEC_HAS_CONTENTS | SEC_LOAD);
+
+  osection = bfd_make_section_anyway_with_flags (obfd, name, flags);
 
   if (osection == NULL)
     {
@@ -1761,9 +2186,17 @@ setup_section (bfd *ibfd, sec_ptr isection, void *obfdarg)
       goto loser;
     }
 
+  if (strip_symbols == STRIP_NONDEBUG
+      && obfd->xvec->flavour == bfd_target_elf_flavour
+      && (flags & SEC_ALLOC) != 0
+      && (p == NULL || !p->set_flags))
+    elf_section_type (osection) = SHT_NOBITS;
+
   size = bfd_section_size (ibfd, isection);
   if (copy_byte >= 0)
     size = (size + interleave - 1) / interleave;
+  else if (extract_symbol)
+    size = 0;
   if (! bfd_set_section_size (obfd, osection, size))
     {
       err = _("size");
@@ -1778,7 +2211,7 @@ setup_section (bfd *ibfd, sec_ptr isection, void *obfdarg)
   else
     vma += change_section_address;
 
-  if (! bfd_set_section_vma (obfd, osection, vma))
+  if (! bfd_set_section_vma (obfd, osection, extract_symbol ? 0 : vma))
     {
       err = _("vma");
       goto loser;
@@ -1797,7 +2230,7 @@ setup_section (bfd *ibfd, sec_ptr isection, void *obfdarg)
   else
     lma += change_section_address;
 
-  osection->lma = lma;
+  osection->lma = extract_symbol ? 0 : lma;
 
   /* FIXME: This is probably not enough.  If we change the LMA we
      may have to recompute the header for the file as well.  */
@@ -1809,14 +2242,6 @@ setup_section (bfd *ibfd, sec_ptr isection, void *obfdarg)
       goto loser;
     }
 
-  if (p != NULL && p->set_flags)
-    flags = p->flags | (flags & (SEC_HAS_CONTENTS | SEC_RELOC));
-  if (!bfd_set_section_flags (obfd, osection, flags))
-    {
-      err = _("flags");
-      goto loser;
-    }
-
   /* Copy merge entity size.  */
   osection->entsize = isection->entsize;
 
@@ -1824,7 +2249,12 @@ setup_section (bfd *ibfd, sec_ptr isection, void *obfdarg)
      bfd_get_section_by_name since some formats allow multiple
      sections with the same name.  */
   isection->output_section = osection;
-  isection->output_offset = 0;
+  isection->output_offset = extract_symbol ? vma : 0;
+
+  /* Do not copy backend data if --extract-symbol is passed; anything
+     that needs to look at the section contents will fail.  */
+  if (extract_symbol)
+    return;
 
   /* Allow the BFD backend to copy any private data it understands
      from the input section to the output section.  */
@@ -1838,6 +2268,13 @@ setup_section (bfd *ibfd, sec_ptr isection, void *obfdarg)
       err = _("private data");
       goto loser;
     }
+  else if ((isection->flags & SEC_GROUP) != 0)
+    {
+      asymbol *gsym = group_signature (isection);
+
+      if (gsym != NULL)
+       gsym->flags |= BSF_KEEP;
+    }
 
   /* All went well.  */
   return;
@@ -1879,7 +2316,7 @@ copy_section (bfd *ibfd, sec_ptr isection, void *obfdarg)
     return;
 
   osection = isection->output_section;
-  size = bfd_get_section_size_before_reloc (isection);
+  size = bfd_get_section_size (isection);
 
   if (size == 0 || osection == 0)
     return;
@@ -1931,10 +2368,12 @@ copy_section (bfd *ibfd, sec_ptr isection, void *obfdarg)
        }
 
       bfd_set_reloc (obfd, osection, relcount == 0 ? NULL : relpp, relcount);
+      if (relcount == 0)
+       free (relpp);
     }
 
-  isection->_cooked_size = isection->_raw_size;
-  isection->reloc_done = TRUE;
+  if (extract_symbol)
+    return;
 
   if (bfd_get_section_flags (ibfd, isection) & SEC_HAS_CONTENTS
       && bfd_get_section_flags (obfd, osection) & SEC_HAS_CONTENTS)
@@ -1945,7 +2384,18 @@ copy_section (bfd *ibfd, sec_ptr isection, void *obfdarg)
        RETURN_NONFATAL (bfd_get_filename (ibfd));
 
       if (copy_byte >= 0)
-       filter_bytes (memhunk, &size);
+       {
+         /* Keep only every `copy_byte'th byte in MEMHUNK.  */
+         char *from = (char *) memhunk + copy_byte;
+         char *to = memhunk;
+         char *end = (char *) memhunk + size;
+
+         for (; from < end; from += interleave)
+           *to++ = *from;
+
+         size = (size + interleave - 1 - copy_byte) / interleave;
+         osection->lma /= interleave;
+       }
 
       if (!bfd_set_section_contents (obfd, osection, memhunk, 0, size))
        RETURN_NONFATAL (bfd_get_filename (obfd));
@@ -2016,9 +2466,9 @@ compare_section_lma (const void *arg1, const void *arg2)
     return -1;
 
   /* Sort sections with the same LMA by size.  */
-  if ((*sec1)->_raw_size > (*sec2)->_raw_size)
+  if (bfd_get_section_size (*sec1) > bfd_get_section_size (*sec2))
     return 1;
-  else if ((*sec1)->_raw_size < (*sec2)->_raw_size)
+  else if (bfd_get_section_size (*sec1) < bfd_get_section_size (*sec2))
     return -1;
 
   return 0;
@@ -2088,28 +2538,22 @@ write_debugging_info (bfd *obfd, void *dhandle,
       bfd_byte *syms, *strings;
       bfd_size_type symsize, stringsize;
       asection *stabsec, *stabstrsec;
+      flagword flags;
 
       if (! write_stabs_in_sections_debugging_info (obfd, dhandle, &syms,
                                                    &symsize, &strings,
                                                    &stringsize))
        return FALSE;
 
-      stabsec = bfd_make_section (obfd, ".stab");
-      stabstrsec = bfd_make_section (obfd, ".stabstr");
+      flags = SEC_HAS_CONTENTS | SEC_READONLY | SEC_DEBUGGING;
+      stabsec = bfd_make_section_with_flags (obfd, ".stab", flags);
+      stabstrsec = bfd_make_section_with_flags (obfd, ".stabstr", flags);
       if (stabsec == NULL
          || stabstrsec == NULL
          || ! bfd_set_section_size (obfd, stabsec, symsize)
          || ! bfd_set_section_size (obfd, stabstrsec, stringsize)
          || ! bfd_set_section_alignment (obfd, stabsec, 2)
-         || ! bfd_set_section_alignment (obfd, stabstrsec, 0)
-         || ! bfd_set_section_flags (obfd, stabsec,
-                                  (SEC_HAS_CONTENTS
-                                   | SEC_READONLY
-                                   | SEC_DEBUGGING))
-         || ! bfd_set_section_flags (obfd, stabstrsec,
-                                     (SEC_HAS_CONTENTS
-                                      | SEC_READONLY
-                                      | SEC_DEBUGGING)))
+         || ! bfd_set_section_alignment (obfd, stabstrsec, 0))
        {
          non_fatal (_("%s: can't create debugging section: %s"),
                     bfd_get_filename (obfd),
@@ -2151,7 +2595,7 @@ strip_main (int argc, char *argv[])
   struct section_list *p;
   char *output_file = NULL;
 
-  while ((c = getopt_long (argc, argv, "I:O:F:K:N:R:o:sSpdgxXHhVv",
+  while ((c = getopt_long (argc, argv, "I:O:F:K:N:R:o:sSpdgxXHhVvw",
                           strip_options, (int *) 0)) != EOF)
     {
       switch (c)
@@ -2211,9 +2655,15 @@ strip_main (int argc, char *argv[])
        case OPTION_ONLY_KEEP_DEBUG:
          strip_symbols = STRIP_NONDEBUG;
          break;
+       case OPTION_KEEP_FILE_SYMBOLS:
+         keep_file_symbols = 1;
+         break;
        case 0:
          /* We've been given a long option.  */
          break;
+       case 'w':
+         wildcard = TRUE;
+         break;
        case 'H':
        case 'h':
          strip_usage (stdout, 0);
@@ -2252,7 +2702,10 @@ strip_main (int argc, char *argv[])
       char *tmpname;
 
       if (get_file_size (argv[i]) < 1)
-       continue;
+       {
+         status = 1;
+         continue;
+       }
 
       if (preserve_dates)
        /* No need to check the return value of stat().
@@ -2263,8 +2716,16 @@ strip_main (int argc, char *argv[])
        tmpname = output_file;
       else
        tmpname = make_tempname (argv[i]);
-      status = 0;
 
+      if (tmpname == NULL)
+       {
+         non_fatal (_("could not create temporary file to hold stripped copy of '%s'"),
+                    argv[i]);
+         status = 1;
+         continue;
+       }
+
+      status = 0;
       copy_file (argv[i], tmpname, input_target, output_target);
       if (status == 0)
        {
@@ -2275,12 +2736,12 @@ strip_main (int argc, char *argv[])
          status = hold_status;
        }
       else
-       unlink (tmpname);
+       unlink_if_ordinary (tmpname);
       if (output_file == NULL)
        free (tmpname);
     }
 
-  return 0;
+  return status;
 }
 
 static int
@@ -2298,7 +2759,7 @@ copy_main (int argc, char *argv[])
   struct section_list *p;
   struct stat statbuf;
 
-  while ((c = getopt_long (argc, argv, "b:B:i:I:j:K:N:s:O:d:F:L:G:R:SpgxXHhVvW:",
+  while ((c = getopt_long (argc, argv, "b:B:i:I:j:K:N:s:O:d:F:L:G:R:SpgxXHhVvW:w",
                           copy_options, (int *) 0)) != EOF)
     {
       switch (c)
@@ -2365,6 +2826,10 @@ copy_main (int argc, char *argv[])
          strip_symbols = STRIP_NONDEBUG;
          break;
 
+       case OPTION_KEEP_FILE_SYMBOLS:
+         keep_file_symbols = 1;
+         break;
+
        case OPTION_ADD_GNU_DEBUGLINK:
          gnu_debuglink_filename = optarg;
          break;
@@ -2377,10 +2842,18 @@ copy_main (int argc, char *argv[])
          add_specific_symbol (optarg, &strip_specific_list);
          break;
 
+       case OPTION_STRIP_UNNEEDED_SYMBOL:
+         add_specific_symbol (optarg, &strip_unneeded_list);
+         break;
+
        case 'L':
          add_specific_symbol (optarg, &localize_specific_list);
          break;
 
+       case OPTION_GLOBALIZE_SYMBOL:
+         add_specific_symbol (optarg, &globalize_specific_list);
+         break;
+
        case 'G':
          add_specific_symbol (optarg, &keepglobal_specific_list);
          break;
@@ -2393,6 +2866,10 @@ copy_main (int argc, char *argv[])
          preserve_dates = TRUE;
          break;
 
+       case 'w':
+         wildcard = TRUE;
+         break;
+
        case 'x':
          discard_locals = LOCALS_ALL;
          break;
@@ -2433,7 +2910,10 @@ copy_main (int argc, char *argv[])
 
            size = get_file_size (s + 1);
            if (size < 1)
-             break;
+             {
+               status = 1;
+               break;
+             }
 
            pa = xmalloc (sizeof (struct section_add));
 
@@ -2706,14 +3186,26 @@ copy_main (int argc, char *argv[])
          add_specific_symbols (optarg, &strip_specific_list);
          break;
 
+       case OPTION_STRIP_UNNEEDED_SYMBOLS:
+         add_specific_symbols (optarg, &strip_unneeded_list);
+         break;
+
        case OPTION_KEEP_SYMBOLS:
          add_specific_symbols (optarg, &keep_specific_list);
          break;
 
+       case OPTION_LOCALIZE_HIDDEN:
+         localize_hidden = TRUE;
+         break;
+
        case OPTION_LOCALIZE_SYMBOLS:
          add_specific_symbols (optarg, &localize_specific_list);
          break;
 
+       case OPTION_GLOBALIZE_SYMBOLS:
+         add_specific_symbols (optarg, &globalize_specific_list);
+         break;
+
        case OPTION_KEEPGLOBAL_SYMBOLS:
          add_specific_symbols (optarg, &keepglobal_specific_list);
          break;
@@ -2723,9 +3215,9 @@ copy_main (int argc, char *argv[])
          break;
 
        case OPTION_ALT_MACH_CODE:
-         use_alt_mach_code = atoi (optarg);
-         if (use_alt_mach_code <= 0)
-           fatal (_("alternate machine code index must be positive"));
+         use_alt_mach_code = strtoul (optarg, NULL, 0);
+         if (use_alt_mach_code == 0)
+           fatal (_("unable to parse alternative machine code"));
          break;
 
        case OPTION_PREFIX_SYMBOLS:
@@ -2740,6 +3232,30 @@ copy_main (int argc, char *argv[])
          prefix_alloc_sections_string = optarg;
          break;
 
+       case OPTION_READONLY_TEXT:
+         bfd_flags_to_set |= WP_TEXT;
+         bfd_flags_to_clear &= ~WP_TEXT;
+         break;
+
+       case OPTION_WRITABLE_TEXT:
+         bfd_flags_to_clear |= WP_TEXT;
+         bfd_flags_to_set &= ~WP_TEXT;
+         break;
+
+       case OPTION_PURE:
+         bfd_flags_to_set |= D_PAGED;
+         bfd_flags_to_clear &= ~D_PAGED;
+         break;
+
+       case OPTION_IMPURE:
+         bfd_flags_to_clear |= D_PAGED;
+         bfd_flags_to_set &= ~D_PAGED;
+         break;
+
+       case OPTION_EXTRACT_SYMBOL:
+         extract_symbol = TRUE;
+         break;
+
        case 0:
          /* We've been given a long option.  */
          break;
@@ -2813,6 +3329,10 @@ copy_main (int argc, char *argv[])
     {
       char *tmpname = make_tempname (input_filename);
 
+      if (tmpname == NULL)
+       fatal (_("warning: could not create temporary file whilst copying '%s', (error: %s)"),
+              input_filename, strerror (errno));
+
       copy_file (input_filename, tmpname, input_target, output_target);
       if (status == 0)
        {
@@ -2829,6 +3349,8 @@ copy_main (int argc, char *argv[])
 
       if (status == 0 && preserve_dates)
        set_times (output_filename, &statbuf);
+      else if (status != 0)
+       unlink_if_ordinary (output_filename);
     }
 
   if (change_warn)
@@ -2888,6 +3410,8 @@ main (int argc, char *argv[])
 
   START_PROGRESS (program_name, 0);
 
+  expandargv (&argc, &argv);
+
   strip_symbols = STRIP_UNDEF;
   discard_locals = LOCALS_UNDEF;
 
This page took 0.047328 seconds and 4 git commands to generate.