2007-03-24 Paul Brook <paul@codesourcery.com>
[binutils-gdb.git] / binutils / objcopy.c
index d46d0936f7fe665d422a7052fdd78cec18e78555..c955e37bbd565368ac533d7fb539a12c48414050 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, 2004, 2005
+   2001, 2002, 2003, 2004, 2005, 2006, 2007
    Free Software Foundation, Inc.
 
    This file is part of GNU Binutils.
@@ -30,6 +30,7 @@
 #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
@@ -146,8 +147,8 @@ 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;
@@ -189,6 +190,9 @@ 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;
@@ -203,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
   {
@@ -236,6 +246,7 @@ enum command_line_switch
     OPTION_STRIP_UNNEEDED_SYMBOL,
     OPTION_STRIP_UNNEEDED_SYMBOLS,
     OPTION_KEEP_SYMBOLS,
+    OPTION_LOCALIZE_HIDDEN,
     OPTION_LOCALIZE_SYMBOLS,
     OPTION_GLOBALIZE_SYMBOL,
     OPTION_GLOBALIZE_SYMBOLS,
@@ -249,10 +260,12 @@ enum command_line_switch
     OPTION_FORMATS_INFO,
     OPTION_ADD_GNU_DEBUGLINK,
     OPTION_ONLY_KEEP_DEBUG,
+    OPTION_KEEP_FILE_SYMBOLS,
     OPTION_READONLY_TEXT,
     OPTION_WRITABLE_TEXT,
     OPTION_PURE,
-    OPTION_IMPURE
+    OPTION_IMPURE,
+    OPTION_EXTRACT_SYMBOL
   };
 
 /* Options to handle if running as "strip".  */
@@ -266,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 */
@@ -307,6 +321,7 @@ 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},
@@ -317,10 +332,12 @@ static struct option copy_options[] =
   {"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},
@@ -419,7 +436,10 @@ copy_usage (FILE *stream, int exit_status)
                                    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\
@@ -465,7 +485,7 @@ copy_usage (FILE *stream, int exit_status)
      --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\
@@ -482,7 +502,7 @@ copy_usage (FILE *stream, int exit_status)
      --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);
 }
@@ -505,6 +525,7 @@ strip_usage (FILE *stream, int exit_status)
      --only-keep-debug             Strip everything but the debug information\n\
   -N --strip-symbol=<name>         Do not 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\
@@ -516,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);
 }
@@ -638,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);
@@ -767,6 +791,30 @@ is_specified_symbol (const char *name, struct symlist *list)
   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
@@ -797,6 +845,49 @@ is_strip_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec)
        return 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;
 }
 
@@ -926,7 +1017,9 @@ filter_symbols (bfd *abfd, bfd *obfd, asymbol **osyms,
          && !(flags & BSF_KEEP)
          && is_specified_symbol (name, strip_unneeded_list))
        keep = 0;
-      if (!keep && is_specified_symbol (name, keep_specific_list))
+      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;
@@ -944,7 +1037,8 @@ filter_symbols (bfd *abfd, bfd *obfd, asymbol **osyms,
              && (flags & (BSF_GLOBAL | BSF_WEAK))
              && (is_specified_symbol (name, localize_specific_list)
                  || (keepglobal_specific_list != NULL
-                     && ! is_specified_symbol (name, keepglobal_specific_list))))
+                     && ! is_specified_symbol (name, keepglobal_specific_list))
+                 || (localize_hidden && is_hidden_symbol (sym))))
            {
              sym->flags &= ~ (BSF_GLOBAL | BSF_WEAK);
              sym->flags |= BSF_LOCAL;
@@ -1222,11 +1316,16 @@ copy_object (bfd *ibfd, bfd *obfd)
            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.  */
@@ -1276,6 +1375,24 @@ 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);
@@ -1299,13 +1416,23 @@ copy_object (bfd *ibfd, bfd *obfd)
          if (pset != NULL && pset->set_flags)
            flags = pset->flags | SEC_HAS_CONTENTS;
 
-         padd->section = bfd_make_section_with_flags (obfd, padd->name, flags);
-         if (padd->section == NULL)
+         /* 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 create section `%s': %s"),
-                      padd->name, bfd_errmsg (bfd_get_error ()));
+             non_fatal (_("can't add section '%s' - it already exists!"), padd->name);
              return FALSE;
            }
+         else
+           {
+             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_size (obfd, padd->section, padd->size))
            {
@@ -1349,6 +1476,49 @@ copy_object (bfd *ibfd, bfd *obfd)
          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 (bfd_count_sections (obfd) == 0)
@@ -1442,21 +1612,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)
-    {
-      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;
-    }
-
   if (convert_debugging)
     dhandle = read_debugging_info (ibfd, isympp, symcount);
 
@@ -1465,6 +1620,7 @@ 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
@@ -1580,6 +1736,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
@@ -1600,25 +1761,34 @@ copy_object (bfd *ibfd, bfd *obfd)
   /* Switch to the alternate machine code.  We have to do this at the
      very end, because we only initialize the header when we create
      the first section.  */
-  if (use_alt_mach_code != 0
-      && ! bfd_alt_mach_code (obfd, use_alt_mach_code))
-    non_fatal (_("unknown alternate machine code, ignored"));
+  if (use_alt_mach_code != 0)
+    {
+      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"));
+       }
+    }
 
   return TRUE;
 }
 
-#undef MKDIR
-#if defined (_WIN32) && !defined (__CYGWIN32__)
-#define MKDIR(DIR, MODE) mkdir (DIR)
-#else
-#define MKDIR(DIR, MODE) mkdir (DIR, MODE)
-#endif
-
 /* 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
     {
@@ -1628,12 +1798,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;
 
@@ -1660,10 +1831,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;
@@ -1674,7 +1845,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);
@@ -1690,11 +1860,18 @@ copy_archive (bfd *ibfd, bfd *obfd, const char *output_target)
       l->obfd = NULL;
       list = l;
 
-      if (output_bfd == NULL)
-       RETURN_NONFATAL (output_name);
-
       if (bfd_check_format (this_element, bfd_object))
        {
+         /* 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);
+
          delete = ! copy_object (this_element, output_bfd);
 
          if (! delete
@@ -1715,6 +1892,7 @@ copy_archive (bfd *ibfd, bfd *obfd, const char *output_target)
          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))
@@ -1784,7 +1962,6 @@ copy_file (const char *input_filename, const char *output_filename,
 
   if (get_file_size (input_filename) < 1)
     {
-      non_fatal (_("error: the input file '%s' is empty"), input_filename);
       status = 1;
       return;
     }
@@ -1797,18 +1974,24 @@ 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))
     {
@@ -2017,6 +2200,8 @@ setup_section (bfd *ibfd, sec_ptr isection, void *obfdarg)
   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");
@@ -2031,7 +2216,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;
@@ -2050,7 +2235,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.  */
@@ -2069,7 +2254,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.  */
@@ -2083,6 +2273,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;
@@ -2180,6 +2377,9 @@ copy_section (bfd *ibfd, sec_ptr isection, void *obfdarg)
        free (relpp);
     }
 
+  if (extract_symbol)
+    return;
+
   if (bfd_get_section_flags (ibfd, isection) & SEC_HAS_CONTENTS
       && bfd_get_section_flags (obfd, osection) & SEC_HAS_CONTENTS)
     {
@@ -2460,6 +2660,9 @@ 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;
@@ -2504,7 +2707,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().
@@ -2515,8 +2721,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)
        {
@@ -2532,7 +2746,7 @@ strip_main (int argc, char *argv[])
        free (tmpname);
     }
 
-  return 0;
+  return status;
 }
 
 static int
@@ -2617,6 +2831,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;
@@ -2697,7 +2915,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));
 
@@ -2978,6 +3199,10 @@ copy_main (int argc, char *argv[])
          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;
@@ -2995,9 +3220,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:
@@ -3032,6 +3257,10 @@ copy_main (int argc, char *argv[])
          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;
@@ -3105,6 +3334,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)
        {