Delete dead function.
[deliverable/binutils-gdb.git] / binutils / objcopy.c
index eff90c51750b7208da569e72d7b2bdf737630b7b..dd16b9b0f4b181cfbc7268d3dd9583c40226d4f9 100644 (file)
@@ -1,11 +1,13 @@
 /* objcopy.c -- copy object file from input to output, optionally massaging it.
-   Copyright (C) 1991, 92, 93, 94 Free Software Foundation, Inc.
+   Copyright 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
+   2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
+   Free Software Foundation, Inc.
 
    This file is part of GNU Binutils.
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
 
    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., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
 \f
-#include "bfd.h"
 #include "sysdep.h"
-#include "bucomm.h"
-#include <getopt.h>
+#include "bfd.h"
+#include "progress.h"
+#include "getopt.h"
 #include "libiberty.h"
+#include "bucomm.h"
+#include "budbg.h"
+#include "filenames.h"
+#include "fnmatch.h"
+#include "elf-bfd.h"
+#include <sys/stat.h>
+#include "libbfd.h"
+#include "coff/internal.h"
+#include "libcoff.h"
+
+/* FIXME: See bfd/peXXigen.c for why we include an architecture specific
+   header in generic PE code.  */
+#include "coff/i386.h"
+#include "coff/pe.h"
+
+static bfd_vma pe_file_alignment = (bfd_vma) -1;
+static bfd_vma pe_heap_commit = (bfd_vma) -1;
+static bfd_vma pe_heap_reserve = (bfd_vma) -1;
+static bfd_vma pe_image_base = (bfd_vma) -1;
+static bfd_vma pe_section_alignment = (bfd_vma) -1;
+static bfd_vma pe_stack_commit = (bfd_vma) -1;
+static bfd_vma pe_stack_reserve = (bfd_vma) -1;
+static short pe_subsystem = -1;
+static short pe_major_subsystem_version = -1;
+static short pe_minor_subsystem_version = -1;
+
+struct is_specified_symbol_predicate_data
+{
+  const char   *name;
+  bfd_boolean  found;
+};
+
+/* A list to support redefine_sym.  */
+struct redefine_node
+{
+  char *source;
+  char *target;
+  struct redefine_node *next;
+};
 
-static bfd_vma parse_vma PARAMS ((const char *, const char *));
-static void setup_section PARAMS ((bfd *, asection *, PTR));
-static void copy_section PARAMS ((bfd *, asection *, PTR));
-static void mark_symbols_used_in_relocations PARAMS ((bfd *, asection *, PTR));
+typedef struct section_rename
+{
+  const char *            old_name;
+  const char *            new_name;
+  flagword                flags;
+  struct section_rename * next;
+}
+section_rename;
 
-#define nonfatal(s) {bfd_nonfatal(s); status = 1; return;}
+/* List of sections to be renamed.  */
+static section_rename *section_rename_list;
 
-static asymbol **isympp = NULL;        /* Input symbols */
-static asymbol **osympp = NULL;        /* Output symbols that survive stripping */
+static asymbol **isympp = NULL;        /* Input symbols */
+static asymbol **osympp = NULL;        /* Output symbols that survive stripping */
 
 /* If `copy_byte' >= 0, copy only that byte of every `interleave' bytes.  */
 static int copy_byte = -1;
 static int interleave = 4;
 
-static boolean verbose;                /* Print file and target names. */
+static bfd_boolean verbose;            /* Print file and target names.  */
+static bfd_boolean preserve_dates;     /* Preserve input file timestamp.  */
 static int status = 0;         /* Exit status.  */
 
 enum strip_action
   {
-    strip_undef,
-    strip_none,                        /* don't strip */
-    strip_debug,               /* strip all debugger symbols */
-    strip_all                  /* strip all symbols */
+    STRIP_UNDEF,
+    STRIP_NONE,                        /* Don't strip.  */
+    STRIP_DEBUG,               /* Strip all debugger symbols.  */
+    STRIP_UNNEEDED,            /* Strip unnecessary symbols.  */
+    STRIP_NONDEBUG,            /* Strip everything but debug info.  */
+    STRIP_ALL                  /* Strip all symbols.  */
   };
 
-/* Which symbols to remove. */
+/* Which symbols to remove.  */
 static enum strip_action strip_symbols;
 
 enum locals_action
   {
-    locals_undef,
-    locals_start_L,            /* discard locals starting with L */
-    locals_all                 /* discard all locals */
+    LOCALS_UNDEF,
+    LOCALS_START_L,            /* Discard locals starting with L.  */
+    LOCALS_ALL                 /* Discard all locals.  */
   };
 
-/* Which local symbols to remove.  Overrides strip_all.  */
+/* Which local symbols to remove.  Overrides STRIP_ALL.  */
 static enum locals_action discard_locals;
 
-/* Structure used to hold lists of sections and actions to take.  */
+/* What kind of change to perform.  */
+enum change_action
+{
+  CHANGE_IGNORE,
+  CHANGE_MODIFY,
+  CHANGE_SET
+};
 
+/* Structure used to hold lists of sections and actions to take.  */
 struct section_list
 {
-  /* Next section to adjust.  */
-  struct section_list *next;
-  /* Section name.  */
-  const char *name;
-  /* Whether this entry was used.  */
-  boolean used;
-  /* Remaining fields only used if not on remove_sections list.  */
-  /* Whether to adjust or set VMA.  */
-  boolean adjust;
-  /* Amount to adjust by or set to.  */
-  bfd_vma val;
+  struct section_list * next;     /* Next section to change.  */
+  const char *         name;      /* Section name.  */
+  bfd_boolean          used;      /* Whether this entry was used.  */
+  bfd_boolean          remove;    /* Whether to remove this section.  */
+  bfd_boolean          copy;      /* Whether to copy this section.  */
+  enum change_action   change_vma;/* Whether to change or set VMA.  */
+  bfd_vma              vma_val;   /* Amount to change by or set to.  */
+  enum change_action   change_lma;/* Whether to change or set LMA.  */
+  bfd_vma              lma_val;   /* Amount to change by or set to.  */
+  bfd_boolean          set_flags; /* Whether to set the section flags.  */
+  flagword             flags;     /* What to set the section flags to.  */
 };
 
-/* List of sections to remove.  */
+static struct section_list *change_sections;
 
-static struct section_list *remove_sections;
+/* TRUE if some sections are to be removed.  */
+static bfd_boolean sections_removed;
 
-/* Adjustments to the start address.  */
-static bfd_vma adjust_start = 0;
-static boolean set_start_set = false;
+/* TRUE if only some sections are to be copied.  */
+static bfd_boolean sections_copied;
+
+/* Changes to the start address.  */
+static bfd_vma change_start = 0;
+static bfd_boolean set_start_set = FALSE;
 static bfd_vma set_start;
 
-/* Adjustments to section VMA's.  */
-static bfd_vma adjust_section_vma = 0;
-static struct section_list *adjust_sections;
+/* Changes to section addresses.  */
+static bfd_vma change_section_address = 0;
+
+/* Filling gaps between sections.  */
+static bfd_boolean gap_fill_set = FALSE;
+static bfd_byte gap_fill = 0;
+
+/* Pad to a given address.  */
+static bfd_boolean pad_to_set = FALSE;
+static bfd_vma pad_to;
+
+/* 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
+{
+  /* Next section to add.  */
+  struct section_add *next;
+  /* Name of section to add.  */
+  const char *name;
+  /* Name of file holding section contents.  */
+  const char *filename;
+  /* Size of file.  */
+  size_t size;
+  /* Contents of file.  */
+  bfd_byte *contents;
+  /* BFD section, after it has been added.  */
+  asection *section;
+};
+
+/* List of sections to add to the output BFD.  */
+static struct section_add *add_sections;
+
+/* If non-NULL the argument to --add-gnu-debuglink.
+   This should be the filename to store in the .gnu_debuglink section.  */
+static const char * gnu_debuglink_filename = NULL;
+
+/* Whether to convert debugging information.  */
+static bfd_boolean convert_debugging = FALSE;
+
+/* Whether to change the leading character in symbol names.  */
+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 htab_t strip_specific_htab = NULL;
+static htab_t strip_unneeded_htab = NULL;
+static htab_t keep_specific_htab = NULL;
+static htab_t localize_specific_htab = NULL;
+static htab_t globalize_specific_htab = NULL;
+static htab_t keepglobal_specific_htab = NULL;
+static htab_t weaken_specific_htab = NULL;
+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;
+
+/* If `reverse_bytes' is nonzero, then reverse the order of every chunk
+   of <reverse_bytes> bytes within each output section.  */
+static int reverse_bytes = 0;
+
+/* For Coff objects, we may want to allow or disallow long section names,
+   or preserve them where found in the inputs.  Debug info relies on them.  */
+enum long_section_name_handling
+  {
+    DISABLE,
+    ENABLE,
+    KEEP
+  };
+
+/* The default long section handling mode is to preserve them.
+   This is also the only behaviour for 'strip'.  */
+static enum long_section_name_handling long_section_names = KEEP;
+
+/* 150 isn't special; it's just an arbitrary non-ASCII char value.  */
+enum command_line_switch
+  {
+    OPTION_ADD_SECTION=150,
+    OPTION_CHANGE_ADDRESSES,
+    OPTION_CHANGE_LEADING_CHAR,
+    OPTION_CHANGE_START,
+    OPTION_CHANGE_SECTION_ADDRESS,
+    OPTION_CHANGE_SECTION_LMA,
+    OPTION_CHANGE_SECTION_VMA,
+    OPTION_CHANGE_WARNINGS,
+    OPTION_DEBUGGING,
+    OPTION_GAP_FILL,
+    OPTION_NO_CHANGE_WARNINGS,
+    OPTION_PAD_TO,
+    OPTION_REMOVE_LEADING_CHAR,
+    OPTION_SET_SECTION_FLAGS,
+    OPTION_SET_START,
+    OPTION_STRIP_UNNEEDED,
+    OPTION_WEAKEN,
+    OPTION_REDEFINE_SYM,
+    OPTION_REDEFINE_SYMS,
+    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_LONG_SECTION_NAMES,
+    OPTION_GLOBALIZE_SYMBOL,
+    OPTION_GLOBALIZE_SYMBOLS,
+    OPTION_KEEPGLOBAL_SYMBOLS,
+    OPTION_WEAKEN_SYMBOLS,
+    OPTION_RENAME_SECTION,
+    OPTION_ALT_MACH_CODE,
+    OPTION_PREFIX_SYMBOLS,
+    OPTION_PREFIX_SECTIONS,
+    OPTION_PREFIX_ALLOC_SECTIONS,
+    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_EXTRACT_SYMBOL,
+    OPTION_REVERSE_BYTES,
+    OPTION_FILE_ALIGNMENT,
+    OPTION_HEAP,
+    OPTION_IMAGE_BASE,
+    OPTION_SECTION_ALIGNMENT,
+    OPTION_STACK,
+    OPTION_SUBSYSTEM
+  };
 
 /* Options to handle if running as "strip".  */
 
@@ -99,594 +313,2225 @@ static struct option strip_options[] =
   {"discard-locals", no_argument, 0, 'X'},
   {"format", required_argument, 0, 'F'}, /* Obsolete */
   {"help", no_argument, 0, 'h'},
+  {"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 */
   {"output-target", required_argument, 0, 'O'},
+  {"output-file", required_argument, 0, 'o'},
+  {"preserve-dates", no_argument, 0, 'p'},
   {"remove-section", required_argument, 0, 'R'},
   {"strip-all", no_argument, 0, 's'},
   {"strip-debug", no_argument, 0, 'S'},
+  {"strip-unneeded", no_argument, 0, OPTION_STRIP_UNNEEDED},
+  {"strip-symbol", required_argument, 0, 'N'},
   {"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}
 };
 
 /* Options to handle if running as "objcopy".  */
 
-/* 150 isn't special; it's just an arbitrary non-ASCII char value.  */
-
-#define OPTION_ADJUST_START 150
-#define OPTION_ADJUST_VMA (OPTION_ADJUST_START + 1)
-#define OPTION_ADJUST_SECTION_VMA (OPTION_ADJUST_VMA + 1)
-#define OPTION_ADJUST_WARNINGS (OPTION_ADJUST_SECTION_VMA + 1)
-#define OPTION_NO_ADJUST_WARNINGS (OPTION_ADJUST_WARNINGS + 1)
-#define OPTION_SET_START (OPTION_NO_ADJUST_WARNINGS + 1)
-
 static struct option copy_options[] =
 {
-  {"adjust-start", required_argument, 0, OPTION_ADJUST_START},
-  {"adjust-vma", required_argument, 0, OPTION_ADJUST_VMA},
-  {"adjust-section-vma", required_argument, 0, OPTION_ADJUST_SECTION_VMA},
-  {"adjust-warnings", no_argument, 0, OPTION_ADJUST_WARNINGS},
+  {"add-gnu-debuglink", required_argument, 0, OPTION_ADD_GNU_DEBUGLINK},
+  {"add-section", required_argument, 0, OPTION_ADD_SECTION},
+  {"adjust-start", required_argument, 0, OPTION_CHANGE_START},
+  {"adjust-vma", required_argument, 0, OPTION_CHANGE_ADDRESSES},
+  {"adjust-section-vma", required_argument, 0, OPTION_CHANGE_SECTION_ADDRESS},
+  {"adjust-warnings", no_argument, 0, OPTION_CHANGE_WARNINGS},
+  {"alt-machine-code", required_argument, 0, OPTION_ALT_MACH_CODE},
+  {"binary-architecture", required_argument, 0, 'B'},
   {"byte", required_argument, 0, 'b'},
+  {"change-addresses", required_argument, 0, OPTION_CHANGE_ADDRESSES},
+  {"change-leading-char", no_argument, 0, OPTION_CHANGE_LEADING_CHAR},
+  {"change-section-address", required_argument, 0, OPTION_CHANGE_SECTION_ADDRESS},
+  {"change-section-lma", required_argument, 0, OPTION_CHANGE_SECTION_LMA},
+  {"change-section-vma", required_argument, 0, OPTION_CHANGE_SECTION_VMA},
+  {"change-start", required_argument, 0, OPTION_CHANGE_START},
+  {"change-warnings", no_argument, 0, OPTION_CHANGE_WARNINGS},
+  {"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'},
-  {"no-adjust-warnings", no_argument, 0, OPTION_NO_ADJUST_WARNINGS},
+  {"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},
+  {"long-section-names", required_argument, 0, OPTION_LONG_SECTION_NAMES},
+  {"no-adjust-warnings", no_argument, 0, OPTION_NO_CHANGE_WARNINGS},
+  {"no-change-warnings", no_argument, 0, OPTION_NO_CHANGE_WARNINGS},
+  {"only-keep-debug", no_argument, 0, OPTION_ONLY_KEEP_DEBUG},
+  {"only-section", required_argument, 0, 'j'},
   {"output-format", required_argument, 0, 'O'},        /* Obsolete */
   {"output-target", required_argument, 0, 'O'},
+  {"pad-to", required_argument, 0, OPTION_PAD_TO},
+  {"prefix-symbols", required_argument, 0, OPTION_PREFIX_SYMBOLS},
+  {"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},
   {"remove-section", required_argument, 0, 'R'},
+  {"rename-section", required_argument, 0, OPTION_RENAME_SECTION},
+  {"reverse-bytes", required_argument, 0, OPTION_REVERSE_BYTES},
+  {"set-section-flags", required_argument, 0, OPTION_SET_SECTION_FLAGS},
   {"set-start", required_argument, 0, OPTION_SET_START},
+  {"srec-len", required_argument, 0, OPTION_SREC_LEN},
+  {"srec-forceS3", no_argument, 0, OPTION_SREC_FORCES3},
   {"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'},
   {"verbose", no_argument, 0, 'v'},
   {"version", no_argument, 0, 'V'},
+  {"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},
+  {"file-alignment", required_argument, 0, OPTION_FILE_ALIGNMENT},
+  {"heap", required_argument, 0, OPTION_HEAP},
+  {"image-base", required_argument, 0 , OPTION_IMAGE_BASE},
+  {"section-alignment", required_argument, 0, OPTION_SECTION_ALIGNMENT},
+  {"stack", required_argument, 0, OPTION_STACK},
+  {"subsystem", required_argument, 0, OPTION_SUBSYSTEM},
   {0, no_argument, 0, 0}
 };
 
 /* IMPORTS */
 extern char *program_name;
-extern char *program_version;
 
 /* This flag distinguishes between strip and objcopy:
    1 means this is 'strip'; 0 means this is 'objcopy'.
-   -1 means if we should use argv[0] to decide. */
+   -1 means if we should use argv[0] to decide.  */
 extern int is_strip;
 
-
+/* The maximum length of an S record.  This variable is declared in srec.c
+   and can be modified by the --srec-len parameter.  */
+extern unsigned int Chunk;
+
+/* Restrict the generation of Srecords to type S3 only.
+   This variable is declare in bfd/srec.c and can be toggled
+   on by the --srec-forceS3 command line switch.  */
+extern bfd_boolean S3Forced;
+
+/* 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 *);
+static void mark_symbols_used_in_relocations (bfd *, asection *, void *);
+static bfd_boolean write_debugging_info (bfd *, void *, long *, asymbol ***);
+static const char *lookup_sym_redefinition (const char *);
+\f
 static void
-copy_usage (stream, status)
-     FILE *stream;
-     int status;
+copy_usage (FILE *stream, int exit_status)
 {
-  fprintf (stream, "\
-Usage: %s [-vVSgxX] [-I bfdname] [-O bfdname] [-F bfdname] [-b byte]\n\
-       [-R section] [-i interleave] [--interleave=interleave] [--byte=byte]\n\
-       [--input-target=bfdname] [--output-target=bfdname] [--target=bfdname]\n\
-       [--strip-all] [--strip-debug] [--discard-all] [--discard-locals]\n\
-       [--remove-section=section] [--set-start=val] [--adjust-start=incr]\n\
-       [--adjust-vma=incr] [--adjust-section-vma=section{=,+,-}val]\n\
-       [--adjust-warnings] [--no-adjust-warnings] [--verbose] [--version]\n\
-       [--help] in-file [out-file]\n",
-          program_name);
-  exit (status);
+  fprintf (stream, _("Usage: %s [option(s)] in-file [out-file]\n"), program_name);
+  fprintf (stream, _(" Copies a binary file, possibly transforming it in the process\n"));
+  fprintf (stream, _(" The options are:\n"));
+  fprintf (stream, _("\
+  -I --input-target <bfdname>      Assume input file is in format <bfdname>\n\
+  -O --output-target <bfdname>     Create an output file in format <bfdname>\n\
+  -B --binary-architecture <arch>  Set output arch, when input is arch-less\n\
+  -F --target <bfdname>            Set both input and output format to <bfdname>\n\
+     --debugging                   Convert debugging information, if possible\n\
+  -p --preserve-dates              Copy modified/access timestamps to the output\n\
+  -j --only-section <name>         Only copy section <name> into the output\n\
+     --add-gnu-debuglink=<file>    Add section .gnu_debuglink linking to <file>\n\
+  -R --remove-section <name>       Remove section <name> from the output\n\
+  -S --strip-all                   Remove all symbol and relocation information\n\
+  -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\
+     --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\
+  -b --byte <num>                  Select byte <num> in every interleaved block\n\
+     --gap-fill <val>              Fill gaps between sections with <val>\n\
+     --pad-to <addr>               Pad the last section up to address <addr>\n\
+     --set-start <addr>            Set the start address to <addr>\n\
+    {--change-start|--adjust-start} <incr>\n\
+                                   Add <incr> to the start address\n\
+    {--change-addresses|--adjust-vma} <incr>\n\
+                                   Add <incr> to LMA, VMA and start addresses\n\
+    {--change-section-address|--adjust-section-vma} <name>{=|+|-}<val>\n\
+                                   Change LMA and VMA of section <name> by <val>\n\
+     --change-section-lma <name>{=|+|-}<val>\n\
+                                   Change the LMA of section <name> by <val>\n\
+     --change-section-vma <name>{=|+|-}<val>\n\
+                                   Change the VMA of section <name> by <val>\n\
+    {--[no-]change-warnings|--[no-]adjust-warnings}\n\
+                                   Warn if a named section does not exist\n\
+     --set-section-flags <name>=<flags>\n\
+                                   Set section <name>'s properties to <flags>\n\
+     --add-section <name>=<file>   Add section <name> found in <file> to output\n\
+     --rename-section <old>=<new>[,<flags>] Rename section <old> to <new>\n\
+     --long-section-names {enable|disable|keep}\n\
+                                   Handle long section names in Coff objects.\n\
+     --change-leading-char         Force output format's leading character style\n\
+     --remove-leading-char         Remove leading character from global symbols\n\
+     --reverse-bytes=<num>         Reverse <num> bytes at a time, in output sections with content\n\
+     --redefine-sym <old>=<new>    Redefine symbol name <old> to <new>\n\
+     --redefine-syms <file>        --redefine-sym for all symbol pairs \n\
+                                     listed in <file>\n\
+     --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 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\
+     --file-alignment <num>        Set PE file alignment to <num>\n\
+     --heap <reserve>[,<commit>]   Set PE reserve/commit heap to <reserve>/\n\
+                                   <commit>\n\
+     --image-base <address>        Set PE image base to <address>\n\
+     --section-alignment <num>     Set PE section alignment to <num>\n\
+     --stack <reserve>[,<commit>]  Set PE reserve/commit stack to <reserve>/\n\
+                                   <commit>\n\
+     --subsystem <name>[:<version>]\n\
+                                   Set PE subsystem to <name> [& <version>]\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 (REPORT_BUGS_TO[0] && exit_status == 0)
+    fprintf (stream, _("Report bugs to %s\n"), REPORT_BUGS_TO);
+  exit (exit_status);
 }
 
 static void
-strip_usage (stream, status)
-     FILE *stream;
-     int status;
+strip_usage (FILE *stream, int exit_status)
 {
-  fprintf (stream, "\
-Usage: %s [-vVsSgxX] [-I bfdname] [-O bfdname] [-F bfdname] [-R section]\n\
-       [--input-target=bfdname] [--output-target=bfdname] [--target=bfdname]\n\
-       [--strip-all] [--strip-debug] [--discard-all] [--discard-locals]\n\
-       [--remove-section=section] [--verbose] [--version] [--help] file...\n",
-          program_name);
-  exit (status);
+  fprintf (stream, _("Usage: %s <option(s)> in-file(s)\n"), program_name);
+  fprintf (stream, _(" Removes symbols and sections from files\n"));
+  fprintf (stream, _(" The options are:\n"));
+  fprintf (stream, _("\
+  -I --input-target=<bfdname>      Assume input file is in format <bfdname>\n\
+  -O --output-target=<bfdname>     Create an output file in format <bfdname>\n\
+  -F --target=<bfdname>            Set both input and output format to <bfdname>\n\
+  -p --preserve-dates              Copy modified/access timestamps to the output\n\
+  -R --remove-section=<name>       Remove section <name> from the output\n\
+  -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>          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\
+  -V --version                     Display this program's version number\n\
+  -h --help                        Display this output\n\
+     --info                        List object formats & architectures supported\n\
+  -o <file>                        Place stripped output into <file>\n\
+"));
+
+  list_supported_targets (program_name, stream);
+  if (REPORT_BUGS_TO[0] && exit_status == 0)
+    fprintf (stream, _("Report bugs to %s\n"), REPORT_BUGS_TO);
+  exit (exit_status);
 }
 
-/* Parse a string into a VMA, with a fatal error if it can't be
-   parsed.  */
+/* Parse section flags into a flagword, with a fatal error if the
+   string can't be parsed.  */
 
-static bfd_vma
-parse_vma (s, arg)
-     const char *s;
-     const char *arg;
+static flagword
+parse_flags (const char *s)
 {
-  bfd_vma ret;
-  const char *end;
+  flagword ret;
+  const char *snext;
+  int len;
+
+  ret = SEC_NO_FLAGS;
 
-  ret = bfd_scan_vma (s, &end, 0);
-  if (*end != '\0')
+  do
     {
-      fprintf (stderr, "%s: %s: bad number: %s\n", program_name, arg, s);
-      exit (1);
+      snext = strchr (s, ',');
+      if (snext == NULL)
+       len = strlen (s);
+      else
+       {
+         len = snext - s;
+         ++snext;
+       }
+
+      if (0) ;
+#define PARSE_FLAG(fname,fval) \
+  else if (strncasecmp (fname, s, len) == 0) ret |= fval
+      PARSE_FLAG ("alloc", SEC_ALLOC);
+      PARSE_FLAG ("load", SEC_LOAD);
+      PARSE_FLAG ("noload", SEC_NEVER_LOAD);
+      PARSE_FLAG ("readonly", SEC_READONLY);
+      PARSE_FLAG ("debug", SEC_DEBUGGING);
+      PARSE_FLAG ("code", SEC_CODE);
+      PARSE_FLAG ("data", SEC_DATA);
+      PARSE_FLAG ("rom", SEC_ROM);
+      PARSE_FLAG ("share", SEC_COFF_SHARED);
+      PARSE_FLAG ("contents", SEC_HAS_CONTENTS);
+#undef PARSE_FLAG
+      else
+       {
+         char *copy;
+
+         copy = (char *) xmalloc (len + 1);
+         strncpy (copy, s, len);
+         copy[len] = '\0';
+         non_fatal (_("unrecognized section flag `%s'"), copy);
+         fatal (_("supported flags: %s"),
+                "alloc, load, noload, readonly, debug, code, data, rom, share, contents");
+       }
+
+      s = snext;
     }
+  while (s != NULL);
+
   return ret;
 }
 
-/* Return the name of a temporary file in the same directory as FILENAME.  */
+/* Find and optionally add an entry in the change_sections list.  */
 
-static char *
-make_tempname (filename)
-     char *filename;
+static struct section_list *
+find_section_list (const char *name, bfd_boolean add)
 {
-  static char template[] = "stXXXXXX";
-  char *tmpname;
-  char *slash = strrchr (filename, '/');
+  struct section_list *p;
 
-  if (slash != (char *) NULL)
-    {
-      *slash = 0;
-      tmpname = xmalloc (strlen (filename) + sizeof (template) + 1);
-      strcpy (tmpname, filename);
-      strcat (tmpname, "/");
-      strcat (tmpname, template);
-      mktemp (tmpname);
-      *slash = '/';
-    }
-  else
-    {
-      tmpname = xmalloc (sizeof (template));
-      strcpy (tmpname, template);
-      mktemp (tmpname);
-    }
-  return tmpname;
+  for (p = change_sections; p != NULL; p = p->next)
+    if (strcmp (p->name, name) == 0)
+      return p;
+
+  if (! add)
+    return NULL;
+
+  p = (struct section_list *) xmalloc (sizeof (struct section_list));
+  p->name = name;
+  p->used = FALSE;
+  p->remove = FALSE;
+  p->copy = FALSE;
+  p->change_vma = CHANGE_IGNORE;
+  p->change_lma = CHANGE_IGNORE;
+  p->vma_val = 0;
+  p->lma_val = 0;
+  p->set_flags = FALSE;
+  p->flags = 0;
+
+  p->next = change_sections;
+  change_sections = p;
+
+  return p;
 }
 
-/* Choose which symbol entries to copy; put the result in OSYMS.
-   We don't copy in place, because that confuses the relocs.
-   Return the number of symbols to print.  */
+/* There is htab_hash_string but no htab_eq_string. Makes sense.  */
 
-static unsigned int
-filter_symbols (abfd, osyms, isyms, symcount)
-     bfd *abfd;
-     asymbol **osyms, **isyms;
-     long symcount;
+static int
+eq_string (const void *s1, const void *s2)
 {
-  register asymbol **from = isyms, **to = osyms;
-  long src_count = 0, dst_count = 0;
-
-  for (; src_count < symcount; src_count++)
-    {
-      asymbol *sym = from[src_count];
-      flagword flags = sym->flags;
-      int keep;
-
-      if ((flags & BSF_GLOBAL) /* Keep if external.  */
-         || (flags & BSF_KEEP) /* Keep if used in a relocation.  */
-         || bfd_is_und_section (bfd_get_section (sym))
-         || bfd_is_com_section (bfd_get_section (sym)))
-       keep = 1;
-      else if ((flags & BSF_DEBUGGING) != 0)   /* Debugging symbol.  */
-       keep = strip_symbols != strip_debug;
-      else                     /* Local symbol.  */
-       keep = discard_locals != locals_all
-         && (discard_locals != locals_start_L ||
-             ! bfd_is_local_label (abfd, sym));
-      if (keep)
-       to[dst_count++] = sym;
-    }
-
-  return dst_count;
+  return strcmp ((const char *) s1, (const char *) s2) == 0;
 }
 
-/* Keep only every `copy_byte'th byte in MEMHUNK, which is *SIZE bytes long.
-   Adjust *SIZE.  */
-
-void
-filter_bytes (memhunk, size)
-     char *memhunk;
-     bfd_size_type *size;
+static htab_t
+create_symbol_htab (void)
 {
-  char *from = memhunk + copy_byte, *to = memhunk, *end = memhunk + *size;
+  return htab_create_alloc (16, htab_hash_string, eq_string, NULL, xcalloc, free);
+}
 
-  for (; from < end; from += interleave)
-    *to++ = *from;
-  *size /= interleave;
+static void
+create_symbol_htabs (void)
+{
+  strip_specific_htab = create_symbol_htab ();
+  strip_unneeded_htab = create_symbol_htab ();
+  keep_specific_htab = create_symbol_htab ();
+  localize_specific_htab = create_symbol_htab ();
+  globalize_specific_htab = create_symbol_htab ();
+  keepglobal_specific_htab = create_symbol_htab ();
+  weaken_specific_htab = create_symbol_htab ();
 }
 
-/* Copy object file IBFD onto OBFD.  */
+/* Add a symbol to strip_specific_list.  */
 
 static void
-copy_object (ibfd, obfd)
-     bfd *ibfd;
-     bfd *obfd;
+add_specific_symbol (const char *name, htab_t htab)
 {
-  bfd_vma start;
-  long symcount;
+  *htab_find_slot (htab, name, INSERT) = (char *) name;
+}
 
-  if (!bfd_set_format (obfd, bfd_get_format (ibfd)))
-    {
-      nonfatal (bfd_get_filename (obfd));
-    }
+/* Add symbols listed in `filename' to strip_specific_list.  */
 
-  if (verbose)
-    printf ("copy from %s(%s) to %s(%s)\n",
-           bfd_get_filename(ibfd), bfd_get_target(ibfd),
-           bfd_get_filename(obfd), bfd_get_target(obfd));
+#define IS_WHITESPACE(c)      ((c) == ' ' || (c) == '\t')
+#define IS_LINE_TERMINATOR(c) ((c) == '\n' || (c) == '\r' || (c) == '\0')
 
-  if (set_start_set)
-    start = set_start;
-  else
+static void
+add_specific_symbols (const char *filename, htab_t htab)
+{
+  off_t  size;
+  FILE * f;
+  char * line;
+  char * buffer;
+  unsigned int line_count;
+
+  size = get_file_size (filename);
+  if (size == 0)
     {
-      /* As a special hack make it easier to generate a raw binary
-         file, we default the starting address to zero for the binary
-         output format.  */
-      if (strcmp (bfd_get_target (obfd), "binary") == 0)
-       start = 0;
-      else
-       start = bfd_get_start_address (ibfd);
+      status = 1;
+      return;
     }
-  start += adjust_start;
 
-  if (!bfd_set_start_address (obfd, start)
-      || !bfd_set_file_flags (obfd,
-                             (bfd_get_file_flags (ibfd)
-                              & bfd_applicable_file_flags (obfd))))
-    {
-      nonfatal (bfd_get_filename (ibfd));
-    }
+  buffer = (char *) xmalloc (size + 2);
+  f = fopen (filename, FOPEN_RT);
+  if (f == NULL)
+    fatal (_("cannot open '%s': %s"), filename, strerror (errno));
 
-  /* Copy architecture of input file to output file */
-  if (!bfd_set_arch_mach (obfd, bfd_get_arch (ibfd),
-                         bfd_get_mach (ibfd)))
-    {
-      fprintf (stderr, "Output file cannot represent architecture %s\n",
-              bfd_printable_arch_mach (bfd_get_arch (ibfd),
-                                       bfd_get_mach (ibfd)));
-    }
-  if (!bfd_set_format (obfd, bfd_get_format (ibfd)))
-    {
-      nonfatal (bfd_get_filename(ibfd));
-    }
+  if (fread (buffer, 1, size, f) == 0 || ferror (f))
+    fatal (_("%s: fread failed"), filename);
 
-  if (isympp)
-    free (isympp);
-  if (osympp != isympp)
-    free (osympp);
+  fclose (f);
+  buffer [size] = '\n';
+  buffer [size + 1] = '\0';
 
-  /* 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, (void *) obfd);
+  line_count = 1;
 
-  /* Symbol filtering must happen after the output sections have
-     been created, but before their contents are set.  */
-  if (strip_symbols == strip_all && discard_locals == locals_undef)
+  for (line = buffer; * line != '\0'; line ++)
     {
-      osympp = isympp = NULL;
-      symcount = 0;
-    }
-  else
-    {
-      long symsize;
+      char * eol;
+      char * name;
+      char * name_end;
+      int finished = FALSE;
 
-      symsize = bfd_get_symtab_upper_bound (ibfd);
-      if (symsize < 0)
+      for (eol = line;; eol ++)
        {
-         nonfatal (bfd_get_filename (ibfd));
-       }
+         switch (* eol)
+           {
+           case '\n':
+             * eol = '\0';
+             /* Cope with \n\r.  */
+             if (eol[1] == '\r')
+               ++ eol;
+             finished = TRUE;
+             break;
+
+           case '\r':
+             * eol = '\0';
+             /* Cope with \r\n.  */
+             if (eol[1] == '\n')
+               ++ eol;
+             finished = TRUE;
+             break;
+
+           case 0:
+             finished = TRUE;
+             break;
+
+           case '#':
+             /* Line comment, Terminate the line here, in case a
+                name is present and then allow the rest of the
+                loop to find the real end of the line.  */
+             * eol = '\0';
+             break;
+
+           default:
+             break;
+           }
 
-      osympp = isympp = (asymbol **) xmalloc (symsize);
-      symcount = bfd_canonicalize_symtab (ibfd, isympp);
-      if (symcount < 0)
-       {
-         nonfatal (bfd_get_filename (ibfd));
+         if (finished)
+           break;
        }
 
-      if (strip_symbols == strip_debug || discard_locals != locals_undef)
+      /* A name may now exist somewhere between 'line' and 'eol'.
+        Strip off leading whitespace and trailing whitespace,
+        then add it to the list.  */
+      for (name = line; IS_WHITESPACE (* name); name ++)
+       ;
+      for (name_end = name;
+          (! IS_WHITESPACE (* name_end))
+          && (! IS_LINE_TERMINATOR (* name_end));
+          name_end ++)
+       ;
+
+      if (! IS_LINE_TERMINATOR (* name_end))
        {
-         /* Mark symbols used in output relocations so that they
-            are kept, even if they are local labels or static symbols.
+         char * extra;
+
+         for (extra = name_end + 1; IS_WHITESPACE (* extra); extra ++)
+           ;
 
-            Note we iterate over the input sections examining their
-            relocations since the relocations for the output sections
-            haven't been set yet.  mark_symbols_used_in_relocations will
-            ignore input sections which have no corresponding output
-            section.  */
-         bfd_map_over_sections (ibfd,
-                                mark_symbols_used_in_relocations,
-                                (PTR)isympp);
-         osympp = (asymbol **) xmalloc (symcount * sizeof (asymbol *));
-         symcount = filter_symbols (ibfd, osympp, isympp, symcount);
+         if (! IS_LINE_TERMINATOR (* extra))
+           non_fatal (_("%s:%d: Ignoring rubbish found on this line"),
+                      filename, line_count);
        }
+
+      * name_end = '\0';
+
+      if (name_end > name)
+       add_specific_symbol (name, htab);
+
+      /* Advance line pointer to end of line.  The 'eol ++' in the for
+        loop above will then advance us to the start of the next line.  */
+      line = eol;
+      line_count ++;
     }
+}
 
-  bfd_set_symtab (obfd, osympp, symcount);
+/* See whether a symbol should be stripped or kept
+   based on strip_specific_list and keep_symbols.  */
 
-  /* This has to happen after the symbol table has been set.  */
-  bfd_map_over_sections (ibfd, copy_section, (void *) obfd);
+static int
+is_specified_symbol_predicate (void **slot, void *data)
+{
+  struct is_specified_symbol_predicate_data *d =
+      (struct is_specified_symbol_predicate_data *) data;
+  const char *slot_name = (char *) *slot;
 
-  /* 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
-     important for the ECOFF code at least.  */
-  if (!bfd_copy_private_bfd_data (ibfd, obfd))
+  if (*slot_name != '!')
     {
-      fprintf (stderr, "%s: %s: error copying private BFD data: %s\n",
-              program_name, bfd_get_filename (obfd),
-              bfd_errmsg (bfd_get_error ()));
-      status = 1;
-      return;
+      if (! fnmatch (slot_name, d->name, 0))
+       {
+         d->found = TRUE;
+         /* Stop traversal.  */
+         return 0;
+       }
+    }
+  else
+    {
+      if (fnmatch (slot_name + 1, d->name, 0))
+       {
+         d->found = TRUE;
+         /* Stop traversal.  */
+         return 0;
+       }
     }
+
+  /* Continue traversal.  */
+  return 1;
 }
 
-static char *
-cat (a, b, c)
-     char *a;
-     char *b;
-     char *c;
+static bfd_boolean
+is_specified_symbol (const char *name, htab_t htab)
 {
-  size_t size = strlen (a) + strlen (b) + strlen (c);
-  char *r = xmalloc (size + 1);
+  if (wildcard)
+    {
+      struct is_specified_symbol_predicate_data data;
+
+      data.name = name;
+      data.found = FALSE;
 
-  strcpy (r, a);
-  strcat (r, b);
-  strcat (r, c);
-  return r;
+      htab_traverse (htab, is_specified_symbol_predicate, &data);
+
+      return data.found;
+    }
+
+  return htab_find (htab, name) != NULL;
 }
 
-/* Read each archive element in turn from IBFD, copy the
-   contents to temp file, and keep the temp file handle.  */
+/* Return a pointer to the symbol used as a signature for GROUP.  */
 
-static void
-copy_archive (ibfd, obfd, output_target)
-     bfd *ibfd;
-     bfd *obfd;
-     char *output_target;
+static asymbol *
+group_signature (asection *group)
 {
-  bfd **ptr = &obfd->archive_head;
-  bfd *this_element;
-  char *dir = make_tempname (bfd_get_filename (obfd));
+  bfd *abfd = group->owner;
+  Elf_Internal_Shdr *ghdr;
 
-  /* Make a temp directory to hold the contents.  */
-  mkdir (dir, 0700);
-  obfd->has_armap = ibfd->has_armap;
+  if (bfd_get_flavour (abfd) != bfd_target_elf_flavour)
+    return NULL;
 
-  this_element = bfd_openr_next_archived_file (ibfd, NULL);
-  ibfd->archive_head = this_element;
-  while (this_element != (bfd *) NULL)
+  ghdr = &elf_section_data (group)->this_hdr;
+  if (ghdr->sh_link < elf_numsections (abfd))
     {
-      /* Create an output file for this member.  */
-      char *output_name = cat (dir, "/", bfd_get_filename(this_element));
-      bfd *output_bfd = bfd_openw (output_name, output_target);
+      const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+      Elf_Internal_Shdr *symhdr = elf_elfsections (abfd) [ghdr->sh_link];
 
-      if (output_bfd == (bfd *) NULL)
-       {
-         nonfatal (output_name);
-       }
-      if (!bfd_set_format (obfd, bfd_get_format (ibfd)))
-       {
-         nonfatal (bfd_get_filename (obfd));
-       }
+      if (symhdr->sh_type == SHT_SYMTAB
+         && ghdr->sh_info < symhdr->sh_size / bed->s->sizeof_sym)
+       return isympp[ghdr->sh_info - 1];
+    }
+  return NULL;
+}
 
-      if (bfd_check_format (this_element, bfd_object) == true)
-       {
-         copy_object (this_element, output_bfd);
-       }
+/* See if a section is being removed.  */
+
+static bfd_boolean
+is_strip_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec)
+{
+  if (sections_removed || sections_copied)
+    {
+      struct section_list *p;
 
-      bfd_close (output_bfd);
-      /* Open the newly output file and attatch to our list.  */
-      output_bfd = bfd_openr (output_name, output_target);
+      p = find_section_list (bfd_get_section_name (abfd, sec), FALSE);
 
-      /* Mark it for deletion.  */
-      *ptr = output_bfd;
-      ptr = &output_bfd->next;
-      this_element->next = bfd_openr_next_archived_file (ibfd, this_element);
-      this_element = this_element->next;
+      if (sections_removed && p != NULL && p->remove)
+       return TRUE;
+      if (sections_copied && (p == NULL || ! p->copy))
+       return TRUE;
     }
-  *ptr = (bfd *) NULL;
 
-  if (!bfd_close (obfd))
+  if ((bfd_get_section_flags (abfd, sec) & SEC_DEBUGGING) != 0)
     {
-      nonfatal (bfd_get_filename (obfd));
+      if (strip_symbols == STRIP_DEBUG
+         || strip_symbols == STRIP_UNNEEDED
+         || strip_symbols == STRIP_ALL
+         || discard_locals == LOCALS_ALL
+         || convert_debugging)
+       return TRUE;
+
+      if (strip_symbols == STRIP_NONDEBUG)
+       return FALSE;
     }
 
-  /* Delete all the files that we opened.
-     Construct their names again, unfortunately, but
-     we're about to exit anyway.  */
-  for (this_element = ibfd->archive_head;
-       this_element != (bfd *) NULL;
-       this_element = this_element->next)
-    {
-      unlink (cat (dir, "/", bfd_get_filename (this_element)));
-    }
-  rmdir (dir);
-  if (!bfd_close (ibfd))
+  if ((bfd_get_section_flags (abfd, sec) & SEC_GROUP) != 0)
     {
-      nonfatal (bfd_get_filename (ibfd));
+      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_htab))
+         || is_specified_symbol (gname, strip_specific_htab))
+       return TRUE;
     }
+
+  return FALSE;
 }
 
-/* The top-level control.  */
+/* Return true if SYM is a hidden symbol.  */
 
-static void
-copy_file (input_filename, output_filename, input_target, output_target)
-     char *input_filename;
-     char *output_filename;
-     char *input_target;
-     char *output_target;
+static bfd_boolean
+is_hidden_symbol (asymbol *sym)
 {
-  bfd *ibfd;
-  char **matching;
+  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;
+}
 
-  /* To allow us to do "strip *" without dying on the first
-     non-object file, failures are nonfatal.  */
+/* Choose which symbol entries to copy; put the result in OSYMS.
+   We don't copy in place, because that confuses the relocs.
+   Return the number of symbols to print.  */
 
-  ibfd = bfd_openr (input_filename, input_target);
-  if (ibfd == NULL)
-    {
-      nonfatal (input_filename);
-    }
+static unsigned int
+filter_symbols (bfd *abfd, bfd *obfd, asymbol **osyms,
+               asymbol **isyms, long symcount)
+{
+  asymbol **from = isyms, **to = osyms;
+  long src_count = 0, dst_count = 0;
+  int relocatable = (abfd->flags & (EXEC_P | DYNAMIC)) == 0;
 
-  if (bfd_check_format (ibfd, bfd_archive))
+  for (; src_count < symcount; src_count++)
     {
-      bfd *obfd;
+      asymbol *sym = from[src_count];
+      flagword flags = sym->flags;
+      char *name = (char *) bfd_asymbol_name (sym);
+      bfd_boolean keep;
+      bfd_boolean used_in_reloc = FALSE;
+      bfd_boolean undefined;
+      bfd_boolean rem_leading_char;
+      bfd_boolean add_leading_char;
 
-      /* bfd_get_target does not return the correct value until
-         bfd_check_format succeeds.  */
-      if (output_target == NULL)
-       output_target = bfd_get_target (ibfd);
+      undefined = bfd_is_und_section (bfd_get_section (sym));
 
-      obfd = bfd_openw (output_filename, output_target);
-      if (obfd == NULL)
+      if (redefine_sym_list)
        {
-         nonfatal (output_filename);
+         char *old_name, *new_name;
+
+         old_name = (char *) bfd_asymbol_name (sym);
+         new_name = (char *) lookup_sym_redefinition (old_name);
+         bfd_asymbol_name (sym) = new_name;
+         name = new_name;
        }
-      copy_archive (ibfd, obfd, output_target);
-    }
-  else if (bfd_check_format_matches (ibfd, bfd_object, &matching))
-    {
-      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);
+      /* Check if we will remove the current leading character.  */
+      rem_leading_char =
+       (name[0] == bfd_get_symbol_leading_char (abfd))
+       && (change_leading_char
+           || (remove_leading_char
+               && ((flags & (BSF_GLOBAL | BSF_WEAK)) != 0
+                   || undefined
+                   || bfd_is_com_section (bfd_get_section (sym)))));
+
+      /* Check if we will add a new leading character.  */
+      add_leading_char =
+       change_leading_char
+       && (bfd_get_symbol_leading_char (obfd) != '\0')
+       && (bfd_get_symbol_leading_char (abfd) == '\0'
+           || (name[0] == bfd_get_symbol_leading_char (abfd)));
+
+      /* Short circuit for change_leading_char if we can do it in-place.  */
+      if (rem_leading_char && add_leading_char && !prefix_symbols_string)
+        {
+         name[0] = bfd_get_symbol_leading_char (obfd);
+         bfd_asymbol_name (sym) = name;
+         rem_leading_char = FALSE;
+         add_leading_char = FALSE;
+        }
 
-      obfd = bfd_openw (output_filename, output_target);
-      if (obfd == NULL)
-       {
-         nonfatal (output_filename);
-       }
+      /* Remove leading char.  */
+      if (rem_leading_char)
+       bfd_asymbol_name (sym) = ++name;
 
-      copy_object (ibfd, obfd);
+      /* Add new leading char and/or prefix.  */
+      if (add_leading_char || prefix_symbols_string)
+        {
+          char *n, *ptr;
+
+          ptr = n = (char *) xmalloc (1 + strlen (prefix_symbols_string)
+                                      + strlen (name) + 1);
+          if (add_leading_char)
+           *ptr++ = bfd_get_symbol_leading_char (obfd);
+
+          if (prefix_symbols_string)
+            {
+              strcpy (ptr, prefix_symbols_string);
+              ptr += strlen (prefix_symbols_string);
+           }
+
+          strcpy (ptr, name);
+          bfd_asymbol_name (sym) = n;
+          name = n;
+       }
 
-      if (!bfd_close (obfd))
+      if (strip_symbols == STRIP_ALL)
+       keep = FALSE;
+      else if ((flags & BSF_KEEP) != 0         /* Used in relocation.  */
+              || ((flags & BSF_SECTION_SYM) != 0
+                  && ((*bfd_get_section (sym)->symbol_ptr_ptr)->flags
+                      & BSF_KEEP) != 0))
        {
-         nonfatal (output_filename);
+         keep = TRUE;
+         used_in_reloc = TRUE;
        }
+      else if (relocatable                     /* Relocatable file.  */
+              && ((flags & (BSF_GLOBAL | BSF_WEAK)) != 0
+                  || bfd_is_com_section (bfd_get_section (sym))))
+       keep = TRUE;
+      else if (bfd_decode_symclass (sym) == 'I')
+       /* Global symbols in $idata sections need to be retained
+          even if relocatable is FALSE.  External users of the
+          library containing the $idata section may reference these
+          symbols.  */
+       keep = TRUE;
+      else if ((flags & BSF_GLOBAL) != 0       /* Global symbol.  */
+              || (flags & BSF_WEAK) != 0
+              || undefined
+              || bfd_is_com_section (bfd_get_section (sym)))
+       keep = strip_symbols != STRIP_UNNEEDED;
+      else if ((flags & BSF_DEBUGGING) != 0)   /* Debugging symbol.  */
+       keep = (strip_symbols != STRIP_DEBUG
+               && strip_symbols != STRIP_UNNEEDED
+               && ! convert_debugging);
+      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 = TRUE;
+      else                     /* Local symbol.  */
+       keep = (strip_symbols != STRIP_UNNEEDED
+               && (discard_locals != LOCALS_ALL
+                   && (discard_locals != LOCALS_START_L
+                       || ! bfd_is_local_label (abfd, sym))));
 
-      if (!bfd_close (ibfd))
+      if (keep && is_specified_symbol (name, strip_specific_htab))
        {
-         nonfatal (input_filename);
+         /* There are multiple ways to set 'keep' above, but if it
+            was the relocatable symbol case, then that's an error.  */
+         if (used_in_reloc)
+           {
+             non_fatal (_("not stripping symbol `%s' because it is named in a relocation"), name);
+             status = 1;
+           }
+         else
+           keep = FALSE;
        }
-    }
-  else
-    {
-      bfd_nonfatal (input_filename);
-      if (bfd_get_error () == bfd_error_file_ambiguously_recognized)
+
+      if (keep
+         && !(flags & BSF_KEEP)
+         && is_specified_symbol (name, strip_unneeded_htab))
+       keep = FALSE;
+
+      if (!keep
+         && ((keep_file_symbols && (flags & BSF_FILE))
+             || is_specified_symbol (name, keep_specific_htab)))
+       keep = TRUE;
+
+      if (keep && is_strip_section (abfd, bfd_get_section (sym)))
+       keep = FALSE;
+
+      if (keep)
        {
-         list_matching_formats (matching);
-         free (matching);
+         if ((flags & BSF_GLOBAL) != 0
+             && (weaken || is_specified_symbol (name, weaken_specific_htab)))
+           {
+             sym->flags &= ~ BSF_GLOBAL;
+             sym->flags |= BSF_WEAK;
+           }
+
+         if (!undefined
+             && (flags & (BSF_GLOBAL | BSF_WEAK))
+             && (is_specified_symbol (name, localize_specific_htab)
+                 || (htab_elements (keepglobal_specific_htab) != 0
+                     && ! is_specified_symbol (name, keepglobal_specific_htab))
+                 || (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_htab))
+           {
+             sym->flags &= ~ BSF_LOCAL;
+             sym->flags |= BSF_GLOBAL;
+           }
+
+         to[dst_count++] = sym;
        }
-      status = 1;
     }
+
+  to[dst_count] = NULL;
+
+  return dst_count;
 }
 
-/* Create a section in OBFD with the same name and attributes
-   as ISECTION in IBFD.  */
+/* Find the redefined name of symbol SOURCE.  */
 
-static void
-setup_section (ibfd, isection, obfdarg)
-     bfd *ibfd;
-     sec_ptr isection;
-     PTR obfdarg;
+static const char *
+lookup_sym_redefinition (const char *source)
 {
-  bfd *obfd = (bfd *) obfdarg;
-  struct section_list *p;
-  sec_ptr osection;
-  bfd_vma vma;
-  char *err;
+  struct redefine_node *list;
 
-  if ((bfd_get_section_flags (ibfd, isection) & SEC_DEBUGGING) != 0
-      && (strip_symbols == strip_debug
-         || strip_symbols == strip_all
-         || discard_locals == locals_all))
-    return;
+  for (list = redefine_sym_list; list != NULL; list = list->next)
+    if (strcmp (source, list->source) == 0)
+      return list->target;
 
-  for (p = remove_sections; p != NULL; p = p->next)
-    {
-      if (strcmp (p->name, bfd_section_name (ibfd, isection)) == 0)
-       {
-         p->used = true;
+  return source;
+}
+
+/* Add a node to a symbol redefine list.  */
+
+static void
+redefine_list_append (const char *cause, const char *source, const char *target)
+{
+  struct redefine_node **p;
+  struct redefine_node *list;
+  struct redefine_node *new_node;
+
+  for (p = &redefine_sym_list; (list = *p) != NULL; p = &list->next)
+    {
+      if (strcmp (source, list->source) == 0)
+       fatal (_("%s: Multiple redefinition of symbol \"%s\""),
+              cause, source);
+
+      if (strcmp (target, list->target) == 0)
+       fatal (_("%s: Symbol \"%s\" is target of more than one redefinition"),
+              cause, target);
+    }
+
+  new_node = (struct redefine_node *) xmalloc (sizeof (struct redefine_node));
+
+  new_node->source = strdup (source);
+  new_node->target = strdup (target);
+  new_node->next = NULL;
+
+  *p = new_node;
+}
+
+/* Handle the --redefine-syms option.  Read lines containing "old new"
+   from the file, and add them to the symbol redefine list.  */
+
+static void
+add_redefine_syms_file (const char *filename)
+{
+  FILE *file;
+  char *buf;
+  size_t bufsize;
+  size_t len;
+  size_t outsym_off;
+  int c, lineno;
+
+  file = fopen (filename, "r");
+  if (file == NULL)
+    fatal (_("couldn't open symbol redefinition file %s (error: %s)"),
+          filename, strerror (errno));
+
+  bufsize = 100;
+  buf = (char *) xmalloc (bufsize + 1 /* For the terminating NUL.  */);
+
+  lineno = 1;
+  c = getc (file);
+  len = 0;
+  outsym_off = 0;
+  while (c != EOF)
+    {
+      /* Collect the input symbol name.  */
+      while (! IS_WHITESPACE (c) && ! IS_LINE_TERMINATOR (c) && c != EOF)
+       {
+         if (c == '#')
+           goto comment;
+         buf[len++] = c;
+         if (len >= bufsize)
+           {
+             bufsize *= 2;
+             buf = (char *) xrealloc (buf, bufsize + 1);
+           }
+         c = getc (file);
+       }
+      buf[len++] = '\0';
+      if (c == EOF)
+       break;
+
+      /* Eat white space between the symbol names.  */
+      while (IS_WHITESPACE (c))
+       c = getc (file);
+      if (c == '#' || IS_LINE_TERMINATOR (c))
+       goto comment;
+      if (c == EOF)
+       break;
+
+      /* Collect the output symbol name.  */
+      outsym_off = len;
+      while (! IS_WHITESPACE (c) && ! IS_LINE_TERMINATOR (c) && c != EOF)
+       {
+         if (c == '#')
+           goto comment;
+         buf[len++] = c;
+         if (len >= bufsize)
+           {
+             bufsize *= 2;
+             buf = (char *) xrealloc (buf, bufsize + 1);
+           }
+         c = getc (file);
+       }
+      buf[len++] = '\0';
+      if (c == EOF)
+       break;
+
+      /* Eat white space at end of line.  */
+      while (! IS_LINE_TERMINATOR(c) && c != EOF && IS_WHITESPACE (c))
+       c = getc (file);
+      if (c == '#')
+       goto comment;
+      /* Handle \r\n.  */
+      if ((c == '\r' && (c = getc (file)) == '\n')
+         || c == '\n' || c == EOF)
+       {
+ end_of_line:
+         /* Append the redefinition to the list.  */
+         if (buf[0] != '\0')
+           redefine_list_append (filename, &buf[0], &buf[outsym_off]);
+
+         lineno++;
+         len = 0;
+         outsym_off = 0;
+         if (c == EOF)
+           break;
+         c = getc (file);
+         continue;
+       }
+      else
+       fatal (_("%s:%d: garbage found at end of line"), filename, lineno);
+ comment:
+      if (len != 0 && (outsym_off == 0 || outsym_off == len))
+       fatal (_("%s:%d: missing new symbol name"), filename, lineno);
+      buf[len++] = '\0';
+
+      /* Eat the rest of the line and finish it.  */
+      while (c != '\n' && c != EOF)
+       c = getc (file);
+      goto end_of_line;
+    }
+
+  if (len != 0)
+    fatal (_("%s:%d: premature end of file"), filename, lineno);
+
+  free (buf);
+}
+
+/* Copy unkown object file IBFD onto OBFD.
+   Returns TRUE upon success, FALSE otherwise.  */
+
+static bfd_boolean
+copy_unknown_object (bfd *ibfd, bfd *obfd)
+{
+  char *cbuf;
+  int tocopy;
+  long ncopied;
+  long size;
+  struct stat buf;
+
+  if (bfd_stat_arch_elt (ibfd, &buf) != 0)
+    {
+      bfd_nonfatal_message (NULL, ibfd, NULL, NULL);
+      return FALSE;
+    }
+
+  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 = (char *) 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_message (NULL, ibfd, NULL, NULL);
+         free (cbuf);
+         return FALSE;
+       }
+
+      if (bfd_bwrite (cbuf, (bfd_size_type) tocopy, obfd)
+         != (bfd_size_type) tocopy)
+       {
+         bfd_nonfatal_message (NULL, obfd, NULL, NULL);
+         free (cbuf);
+         return FALSE;
+       }
+
+      ncopied += tocopy;
+    }
+
+  chmod (bfd_get_filename (obfd), buf.st_mode);
+  free (cbuf);
+  return TRUE;
+}
+
+/* Copy object file IBFD onto OBFD.
+   Returns TRUE upon success, FALSE otherwise.  */
+
+static bfd_boolean
+copy_object (bfd *ibfd, bfd *obfd, const bfd_arch_info_type *input_arch)
+{
+  bfd_vma start;
+  long symcount;
+  asection **osections = NULL;
+  asection *gnu_debuglink_section = NULL;
+  bfd_size_type *gaps = NULL;
+  bfd_size_type max_gap = 0;
+  long symsize;
+  void *dhandle;
+  enum bfd_architecture iarch;
+  unsigned int imach;
+
+  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)"));
+
+  if (!bfd_set_format (obfd, bfd_get_format (ibfd)))
+    {
+      bfd_nonfatal_message (NULL, obfd, NULL, NULL);
+      return FALSE;
+    }
+
+  if (verbose)
+    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 (extract_symbol)
+    start = 0;
+  else
+    {
+      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 (strip_symbols == STRIP_ALL)
+       flags &= ~HAS_RELOC;
+
+      if (!bfd_set_start_address (obfd, start)
+         || !bfd_set_file_flags (obfd, flags))
+       {
+         bfd_nonfatal_message (NULL, ibfd, NULL, NULL);
+         return FALSE;
+       }
+    }
+
+  /* Copy architecture of input file to output file.  */
+  iarch = bfd_get_arch (ibfd);
+  imach = bfd_get_mach (ibfd);
+  if (input_arch)
+    {
+      if (bfd_get_arch_info (ibfd) == NULL
+         || bfd_get_arch_info (ibfd)->arch == bfd_arch_unknown)
+       {
+         iarch = input_arch->arch;
+         imach = input_arch->mach;
+       }
+      else
+       non_fatal (_("Input file `%s' ignores binary architecture parameter."),
+                  bfd_get_archive_filename (ibfd));
+    }
+  if (!bfd_set_arch_mach (obfd, iarch, imach)
+      && (ibfd->target_defaulted
+         || bfd_get_arch (ibfd) != bfd_get_arch (obfd)))
+    {
+      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 (_("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)))
+    {
+      bfd_nonfatal_message (NULL, ibfd, NULL, NULL);
+      return FALSE;
+    }
+
+  if (bfd_get_flavour (obfd) == bfd_target_coff_flavour
+      && bfd_pei_p (obfd))
+    {
+      /* Set up PE parameters.  */
+      pe_data_type *pe = pe_data (obfd);
+
+      /* Copy PE parameters before changing them.  */
+      if (ibfd->xvec->flavour == bfd_target_coff_flavour
+         && bfd_pei_p (ibfd))
+       pe->pe_opthdr = pe_data (ibfd)->pe_opthdr;
+
+      if (pe_file_alignment != (bfd_vma) -1)
+       pe->pe_opthdr.FileAlignment = pe_file_alignment;
+      else
+       pe_file_alignment = PE_DEF_FILE_ALIGNMENT;
+
+      if (pe_heap_commit != (bfd_vma) -1)
+       pe->pe_opthdr.SizeOfHeapCommit = pe_heap_commit;
+
+      if (pe_heap_reserve != (bfd_vma) -1)
+       pe->pe_opthdr.SizeOfHeapCommit = pe_heap_reserve;
+
+      if (pe_image_base != (bfd_vma) -1)
+       pe->pe_opthdr.ImageBase = pe_image_base;
+
+      if (pe_section_alignment != (bfd_vma) -1)
+       pe->pe_opthdr.SectionAlignment = pe_section_alignment;
+      else
+       pe_section_alignment = PE_DEF_SECTION_ALIGNMENT;
+
+      if (pe_stack_commit != (bfd_vma) -1)
+       pe->pe_opthdr.SizeOfStackCommit = pe_stack_commit;
+
+      if (pe_stack_reserve != (bfd_vma) -1)
+       pe->pe_opthdr.SizeOfStackCommit = pe_stack_reserve;
+
+      if (pe_subsystem != -1)
+       pe->pe_opthdr.Subsystem = pe_subsystem;
+
+      if (pe_major_subsystem_version != -1)
+       pe->pe_opthdr.MajorSubsystemVersion = pe_major_subsystem_version;
+
+      if (pe_minor_subsystem_version != -1)
+       pe->pe_opthdr.MinorSubsystemVersion = pe_minor_subsystem_version;
+
+      if (pe_file_alignment > pe_section_alignment)
+       {
+         char file_alignment[20], section_alignment[20];
+
+         sprintf_vma (file_alignment, pe_file_alignment);
+         sprintf_vma (section_alignment, pe_section_alignment);
+         non_fatal (_("warning: file alignment (0x%s) > section alignment (0x%s)"),
+
+                    file_alignment, section_alignment);
+       }
+    }
+
+  if (isympp)
+    free (isympp);
+
+  if (osympp != isympp)
+    free (osympp);
+
+  isympp = NULL;
+  osympp = NULL;
+
+  symsize = bfd_get_symtab_upper_bound (ibfd);
+  if (symsize < 0)
+    {
+      bfd_nonfatal_message (NULL, ibfd, NULL, NULL);
+      return FALSE;
+    }
+
+  osympp = isympp = (asymbol **) xmalloc (symsize);
+  symcount = bfd_canonicalize_symtab (ibfd, isympp);
+  if (symcount < 0)
+    {
+      bfd_nonfatal_message (NULL, ibfd, NULL, NULL);
+      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);
+
+  if (!extract_symbol)
+    setup_bfd_headers (ibfd, obfd);
+
+  if (add_sections != NULL)
+    {
+      struct section_add *padd;
+      struct section_list *pset;
+
+      for (padd = add_sections; padd != NULL; padd = padd->next)
+       {
+         flagword flags;
+
+         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))
+           {
+             bfd_nonfatal_message (NULL, obfd, NULL,
+                                _("can't add section '%s'"), padd->name);
+             return FALSE;
+           }
+         else
+           {
+             padd->section = bfd_make_section_with_flags (obfd, padd->name, flags);
+             if (padd->section == NULL)
+               {
+                 bfd_nonfatal_message (NULL, obfd, NULL,
+                                       _("can't create section `%s'"),
+                                       padd->name);
+                 return FALSE;
+               }
+           }
+
+         if (! bfd_set_section_size (obfd, padd->section, padd->size))
+           {
+             bfd_nonfatal_message (NULL, obfd, padd->section, NULL);
+             return FALSE;
+           }
+
+         if (pset != NULL)
+           {
+             if (pset->change_vma != CHANGE_IGNORE)
+               if (! bfd_set_section_vma (obfd, padd->section,
+                                          pset->vma_val))
+                 {
+                   bfd_nonfatal_message (NULL, obfd, padd->section, NULL);
+                   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)))
+                   {
+                     bfd_nonfatal_message (NULL, obfd, padd->section, NULL);
+                     return FALSE;
+                   }
+               }
+           }
+       }
+    }
+
+  if (gnu_debuglink_filename != NULL)
+    {
+      gnu_debuglink_section = bfd_create_gnu_debuglink_section
+       (obfd, gnu_debuglink_filename);
+
+      if (gnu_debuglink_section == NULL)
+       {
+         bfd_nonfatal_message (NULL, obfd, NULL,
+                               _("cannot create debug link section `%s'"),
+                               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
+      && (gap_fill_set || pad_to_set))
+    {
+      asection **set;
+      unsigned int c, i;
+
+      /* We must fill in gaps between the sections and/or we must pad
+        the last section to a specified address.  We do this by
+        grabbing a list of the sections, sorting them by VMA, and
+        increasing the section sizes as required to fill the gaps.
+        We write out the gap contents below.  */
+
+      c = bfd_count_sections (obfd);
+      osections = (asection **) xmalloc (c * sizeof (asection *));
+      set = osections;
+      bfd_map_over_sections (obfd, get_sections, &set);
+
+      qsort (osections, c, sizeof (asection *), compare_section_lma);
+
+      gaps = (bfd_size_type *) xmalloc (c * sizeof (bfd_size_type));
+      memset (gaps, 0, c * sizeof (bfd_size_type));
+
+      if (gap_fill_set)
+       {
+         for (i = 0; i < c - 1; i++)
+           {
+             flagword flags;
+             bfd_size_type size;
+             bfd_vma gap_start, gap_stop;
+
+             flags = bfd_get_section_flags (obfd, osections[i]);
+             if ((flags & SEC_HAS_CONTENTS) == 0
+                 || (flags & SEC_LOAD) == 0)
+               continue;
+
+             size = bfd_section_size (obfd, osections[i]);
+             gap_start = bfd_section_lma (obfd, osections[i]) + size;
+             gap_stop = bfd_section_lma (obfd, osections[i + 1]);
+             if (gap_start < gap_stop)
+               {
+                 if (! bfd_set_section_size (obfd, osections[i],
+                                             size + (gap_stop - gap_start)))
+                   {
+                     bfd_nonfatal_message (NULL, obfd, osections[i],
+                                           _("Can't fill gap after section"));
+                     status = 1;
+                     break;
+                   }
+                 gaps[i] = gap_stop - gap_start;
+                 if (max_gap < gap_stop - gap_start)
+                   max_gap = gap_stop - gap_start;
+               }
+           }
+       }
+
+      if (pad_to_set)
+       {
+         bfd_vma lma;
+         bfd_size_type size;
+
+         lma = bfd_section_lma (obfd, osections[c - 1]);
+         size = bfd_section_size (obfd, osections[c - 1]);
+         if (lma + size < pad_to)
+           {
+             if (! bfd_set_section_size (obfd, osections[c - 1],
+                                         pad_to - lma))
+               {
+                 bfd_nonfatal_message (NULL, obfd, osections[c - 1],
+                                       _("can't add padding"));
+                 status = 1;
+               }
+             else
+               {
+                 gaps[c - 1] = pad_to - (lma + size);
+                 if (max_gap < pad_to - (lma + size))
+                   max_gap = pad_to - (lma + size);
+               }
+           }
+       }
+    }
+
+  /* Symbol filtering must happen after the output sections
+     have been created, but before their contents are set.  */
+  dhandle = NULL;
+  if (convert_debugging)
+    dhandle = read_debugging_info (ibfd, isympp, symcount, FALSE);
+
+  if (strip_symbols == STRIP_DEBUG
+      || strip_symbols == STRIP_ALL
+      || strip_symbols == STRIP_UNNEEDED
+      || strip_symbols == STRIP_NONDEBUG
+      || discard_locals != LOCALS_UNDEF
+      || localize_hidden
+      || htab_elements (strip_specific_htab) != 0
+      || htab_elements (keep_specific_htab) != 0
+      || htab_elements (localize_specific_htab) != 0
+      || htab_elements (globalize_specific_htab) != 0
+      || htab_elements (keepglobal_specific_htab) != 0
+      || htab_elements (weaken_specific_htab) != 0
+      || prefix_symbols_string
+      || sections_removed
+      || sections_copied
+      || convert_debugging
+      || change_leading_char
+      || remove_leading_char
+      || redefine_sym_list
+      || weaken)
+    {
+      /* Mark symbols used in output relocations so that they
+        are kept, even if they are local labels or static symbols.
+
+        Note we iterate over the input sections examining their
+        relocations since the relocations for the output sections
+        haven't been set yet.  mark_symbols_used_in_relocations will
+        ignore input sections which have no corresponding output
+        section.  */
+      if (strip_symbols != STRIP_ALL)
+       bfd_map_over_sections (ibfd,
+                              mark_symbols_used_in_relocations,
+                              isympp);
+      osympp = (asymbol **) xmalloc ((symcount + 1) * sizeof (asymbol *));
+      symcount = filter_symbols (ibfd, obfd, osympp, isympp, symcount);
+    }
+
+  if (convert_debugging && dhandle != NULL)
+    {
+      if (! write_debugging_info (obfd, dhandle, &symcount, &osympp))
+       {
+         status = 1;
+         return FALSE;
+       }
+    }
+
+  bfd_set_symtab (obfd, osympp, symcount);
+
+  /* This has to happen after the symbol table has been set.  */
+  bfd_map_over_sections (ibfd, copy_section, obfd);
+
+  if (add_sections != NULL)
+    {
+      struct section_add *padd;
+
+      for (padd = add_sections; padd != NULL; padd = padd->next)
+       {
+         if (! bfd_set_section_contents (obfd, padd->section, padd->contents,
+                                         0, padd->size))
+           {
+             bfd_nonfatal_message (NULL, obfd, padd->section, NULL);
+             return FALSE;
+           }
+       }
+    }
+
+  if (gnu_debuglink_filename != NULL)
+    {
+      if (! bfd_fill_in_gnu_debuglink_section
+         (obfd, gnu_debuglink_section, gnu_debuglink_filename))
+       {
+         bfd_nonfatal_message (NULL, obfd, NULL,
+                               _("cannot fill debug link section `%s'"),
+                               gnu_debuglink_filename);
+         return FALSE;
+       }
+    }
+
+  if (gap_fill_set || pad_to_set)
+    {
+      bfd_byte *buf;
+      int c, i;
+
+      /* Fill in the gaps.  */
+      if (max_gap > 8192)
+       max_gap = 8192;
+      buf = (bfd_byte *) xmalloc (max_gap);
+      memset (buf, gap_fill, max_gap);
+
+      c = bfd_count_sections (obfd);
+      for (i = 0; i < c; i++)
+       {
+         if (gaps[i] != 0)
+           {
+             bfd_size_type left;
+             file_ptr off;
+
+             left = gaps[i];
+             off = bfd_section_size (obfd, osections[i]) - left;
+
+             while (left > 0)
+               {
+                 bfd_size_type now;
+
+                 if (left > 8192)
+                   now = 8192;
+                 else
+                   now = left;
+
+                 if (! bfd_set_section_contents (obfd, osections[i], buf,
+                                                 off, now))
+                   {
+                     bfd_nonfatal_message (NULL, obfd, osections[i], NULL);
+                     return FALSE;
+                   }
+
+                 left -= now;
+                 off += now;
+               }
+           }
+       }
+    }
+
+  /* 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
+     important for the ECOFF code at least.  */
+  if (! bfd_copy_private_bfd_data (ibfd, obfd))
+    {
+      bfd_nonfatal_message (NULL, obfd, NULL,
+                           _("error copying private BFD data"));
+      return FALSE;
+    }
+
+  /* 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)
+    {
+      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;
+}
+
+/* Read each archive element in turn from IBFD, copy the
+   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,
+             bfd_boolean force_output_target,
+             const bfd_arch_info_type *input_arch)
+{
+  struct name_list
+    {
+      struct name_list *next;
+      const char *name;
+      bfd *obfd;
+    } *list, *l;
+  bfd **ptr = &obfd->archive_head;
+  bfd *this_element;
+  char *dir;
+  const char *filename;
+
+  /* Make a temp directory to hold the contents.  */
+  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;
+  obfd->is_thin_archive = ibfd->is_thin_archive;
+
+  list = NULL;
+
+  this_element = bfd_openr_next_archived_file (ibfd, NULL);
+
+  if (!bfd_set_format (obfd, bfd_get_format (ibfd)))
+    {
+      status = 1;
+      bfd_nonfatal_message (NULL, obfd, NULL, NULL);
+      return;
+    }
+
+  while (!status && this_element != NULL)
+    {
+      char *output_name;
+      bfd *output_bfd;
+      bfd *last_element;
+      struct stat buf;
+      int stat_status = 0;
+      bfd_boolean del = TRUE;
+
+      /* Create an output file for this member.  */
+      output_name = concat (dir, "/",
+                           bfd_get_filename (this_element), (char *) 0);
+
+      /* If the file already exists, make another temp dir.  */
+      if (stat (output_name, &buf) >= 0)
+       {
+         output_name = make_tempdir (output_name);
+         if (output_name == NULL)
+           fatal (_("cannot create tempdir for archive copying (error: %s)"),
+                  strerror (errno));
+
+         l = (struct name_list *) xmalloc (sizeof (struct name_list));
+         l->name = output_name;
+         l->next = list;
+         l->obfd = NULL;
+         list = l;
+         output_name = concat (output_name, "/",
+                               bfd_get_filename (this_element), (char *) 0);
+       }
+
+      if (preserve_dates)
+       {
+         stat_status = bfd_stat_arch_elt (this_element, &buf);
+
+         if (stat_status != 0)
+           non_fatal (_("internal stat error on %s"),
+                      bfd_get_filename (this_element));
+       }
+
+      l = (struct name_list *) xmalloc (sizeof (struct name_list));
+      l->name = output_name;
+      l->next = list;
+      l->obfd = NULL;
+      list = l;
+
+      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)
+           {
+             bfd_nonfatal_message (output_name, NULL, NULL, NULL);
+             status = 1;
+             return;
+           }
+
+         del = ! copy_object (this_element, output_bfd, input_arch);
+
+         if (! del
+             || bfd_get_arch (this_element) != bfd_arch_unknown)
+           {
+             if (!bfd_close (output_bfd))
+               {
+                 bfd_nonfatal_message (output_name, NULL, NULL, NULL);
+                 /* Error in new object file. Don't change archive.  */
+                 status = 1;
+               }
+           }
+         else
+           goto copy_unknown_element;
+       }
+      else
+       {
+         bfd_nonfatal_message (NULL, this_element, NULL,
+                               _("Unable to recognise the format of file"));
+
+         output_bfd = bfd_openw (output_name, output_target);
+copy_unknown_element:
+         del = !copy_unknown_object (this_element, output_bfd);
+         if (!bfd_close_all_done (output_bfd))
+           {
+             bfd_nonfatal_message (output_name, NULL, NULL, NULL);
+             /* Error in new object file. Don't change archive.  */
+             status = 1;
+           }
+       }
+
+      if (del)
+       {
+         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);
+
+         l->obfd = output_bfd;
+
+         *ptr = output_bfd;
+         ptr = &output_bfd->archive_next;
+
+         last_element = this_element;
+
+         this_element = bfd_openr_next_archived_file (ibfd, last_element);
+
+         bfd_close (last_element);
+       }
+    }
+  *ptr = NULL;
+
+  filename = bfd_get_filename (obfd);
+  if (!bfd_close (obfd))
+    {
+      status = 1;
+      bfd_nonfatal_message (filename, NULL, NULL, NULL);
+      return;
+    }
+
+  filename = bfd_get_filename (ibfd);
+  if (!bfd_close (ibfd))
+    {
+      status = 1;
+      bfd_nonfatal_message (filename, NULL, NULL, NULL);
+      return;
+    }
+
+  /* Delete all the files that we opened.  */
+  for (l = list; l != NULL; l = l->next)
+    {
+      if (l->obfd == NULL)
+       rmdir (l->name);
+      else
+       {
+         bfd_close (l->obfd);
+         unlink (l->name);
+       }
+    }
+  rmdir (dir);
+}
+
+static void
+set_long_section_mode (bfd *output_bfd, bfd *input_bfd, enum long_section_name_handling style)
+{
+  /* This is only relevant to Coff targets.  */
+  if (bfd_get_flavour (output_bfd) == bfd_target_coff_flavour)
+    {
+      if (style == KEEP
+         && bfd_get_flavour (input_bfd) == bfd_target_coff_flavour)
+       style = bfd_coff_long_section_names (input_bfd) ? ENABLE : DISABLE;
+      bfd_coff_set_long_section_names (output_bfd, style != DISABLE);
+    }
+}
+
+/* The top-level control.  */
+
+static void
+copy_file (const char *input_filename, const char *output_filename,
+          const char *input_target,   const char *output_target,
+          const bfd_arch_info_type *input_arch)
+{
+  bfd *ibfd;
+  char **obj_matching;
+  char **core_matching;
+  off_t size = get_file_size (input_filename);
+
+  if (size < 1)
+    {
+      if (size == 0)
+       non_fatal (_("error: the input file '%s' is empty"),
+                  input_filename);
+      status = 1;
+      return;
+    }
+
+  /* To allow us to do "strip *" without dying on the first
+     non-object file, failures are nonfatal.  */
+  ibfd = bfd_openr (input_filename, input_target);
+  if (ibfd == NULL)
+    {
+      bfd_nonfatal_message (input_filename, NULL, NULL, NULL);
+      status = 1;
+      return;
+    }
+
+  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);
+         force_output_target = FALSE;
+       }
+      else
+       force_output_target = TRUE;
+
+      obfd = bfd_openw (output_filename, output_target);
+      if (obfd == NULL)
+       {
+         bfd_nonfatal_message (output_filename, NULL, NULL, NULL);
+         status = 1;
+         return;
+       }
+      /* This is a no-op on non-Coff targets.  */
+      set_long_section_mode (obfd, ibfd, long_section_names);
+
+      copy_archive (ibfd, obfd, output_target, force_output_target, input_arch);
+    }
+  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)
+       output_target = bfd_get_target (ibfd);
+
+      obfd = bfd_openw (output_filename, output_target);
+      if (obfd == NULL)
+       {
+         bfd_nonfatal_message (output_filename, NULL, NULL, NULL);
+         status = 1;
+         return;
+       }
+      /* This is a no-op on non-Coff targets.  */
+      set_long_section_mode (obfd, ibfd, long_section_names);
+
+      if (! copy_object (ibfd, obfd, input_arch))
+       status = 1;
+
+      if (!bfd_close (obfd))
+       {
+         status = 1;
+         bfd_nonfatal_message (output_filename, NULL, NULL, NULL);
+         return;
+       }
+
+      if (!bfd_close (ibfd))
+       {
+         status = 1;
+         bfd_nonfatal_message (input_filename, NULL, NULL, NULL);
+         return;
+       }
+    }
+  else
+    {
+      bfd_error_type obj_error = bfd_get_error ();
+      bfd_error_type core_error;
+
+      if (bfd_check_format_matches (ibfd, bfd_core, &core_matching))
+       {
+         /* This probably can't happen..  */
+         if (obj_error == bfd_error_file_ambiguously_recognized)
+           free (obj_matching);
+         goto do_copy;
+       }
+
+      core_error = bfd_get_error ();
+      /* Report the object error in preference to the core error.  */
+      if (obj_error != core_error)
+       bfd_set_error (obj_error);
+
+      bfd_nonfatal_message (input_filename, NULL, NULL, NULL);
+
+      if (obj_error == bfd_error_file_ambiguously_recognized)
+       {
+         list_matching_formats (obj_matching);
+         free (obj_matching);
+       }
+      if (core_error == bfd_error_file_ambiguously_recognized)
+       {
+         list_matching_formats (core_matching);
+         free (core_matching);
+       }
+
+      status = 1;
+    }
+}
+
+/* Add a name to the section renaming list.  */
+
+static void
+add_section_rename (const char * old_name, const char * new_name,
+                   flagword flags)
+{
+  section_rename * srename;
+
+  /* Check for conflicts first.  */
+  for (srename = section_rename_list; srename != NULL; srename = srename->next)
+    if (strcmp (srename->old_name, old_name) == 0)
+      {
+       /* Silently ignore duplicate definitions.  */
+       if (strcmp (srename->new_name, new_name) == 0
+           && srename->flags == flags)
          return;
+
+       fatal (_("Multiple renames of section %s"), old_name);
+      }
+
+  srename = (section_rename *) xmalloc (sizeof (* srename));
+
+  srename->old_name = old_name;
+  srename->new_name = new_name;
+  srename->flags    = flags;
+  srename->next     = section_rename_list;
+
+  section_rename_list = srename;
+}
+
+/* Check the section rename list for a new name of the input section
+   ISECTION.  Return the new name if one is found.
+   Also set RETURNED_FLAGS to the flags to be used for this section.  */
+
+static const char *
+find_section_rename (bfd * ibfd ATTRIBUTE_UNUSED, sec_ptr isection,
+                    flagword * returned_flags)
+{
+  const char * old_name = bfd_section_name (ibfd, isection);
+  section_rename * srename;
+
+  /* Default to using the flags of the input section.  */
+  * returned_flags = bfd_get_section_flags (ibfd, isection);
+
+  for (srename = section_rename_list; srename != NULL; srename = srename->next)
+    if (strcmp (srename->old_name, old_name) == 0)
+      {
+       if (srename->flags != (flagword) -1)
+         * returned_flags = srename->flags;
+
+       return srename->new_name;
+      }
+
+  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)
+{
+  /* 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))
+    {
+      status = 1;
+      bfd_nonfatal_message (NULL, ibfd, NULL,
+                           _("error in private header data"));
+      return;
+    }
+
+  /* All went well.  */
+  return;
+}
+
+/* Create a section in OBFD with the same
+   name and attributes as ISECTION in IBFD.  */
+
+static void
+setup_section (bfd *ibfd, sec_ptr isection, void *obfdarg)
+{
+  bfd *obfd = (bfd *) obfdarg;
+  struct section_list *p;
+  sec_ptr osection;
+  bfd_size_type size;
+  bfd_vma vma;
+  bfd_vma lma;
+  flagword flags;
+  const char *err;
+  const char * name;
+  char *prefix = NULL;
+  bfd_boolean make_nobits;
+
+  if (is_strip_section (ibfd, isection))
+    return;
+
+  p = find_section_list (bfd_section_name (ibfd, isection), FALSE);
+  if (p != NULL)
+    p->used = TRUE;
+
+  /* Get the, possibly new, name of the output section.  */
+  name = find_section_rename (ibfd, isection, & flags);
+
+  /* Prefix sections.  */
+  if ((prefix_alloc_sections_string)
+      && (bfd_get_section_flags (ibfd, isection) & SEC_ALLOC))
+    prefix = prefix_alloc_sections_string;
+  else if (prefix_sections_string)
+    prefix = prefix_sections_string;
+
+  if (prefix)
+    {
+      char *n;
+
+      n = (char *) xmalloc (strlen (prefix) + strlen (name) + 1);
+      strcpy (n, prefix);
+      strcat (n, name);
+      name = n;
+    }
+
+  make_nobits = FALSE;
+  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
+          && (ibfd->xvec->flavour != bfd_target_elf_flavour
+              || elf_section_type (isection) != SHT_NOTE))
+    {
+      flags &= ~(SEC_HAS_CONTENTS | SEC_LOAD);
+      if (obfd->xvec->flavour == bfd_target_elf_flavour)
+       {
+         make_nobits = TRUE;
+
+         /* Twiddle the input section flags so that it seems to
+            elf.c:copy_private_bfd_data that section flags have not
+            changed between input and output sections.  This hack
+            prevents wholesale rewriting of the program headers.  */
+         isection->flags &= ~(SEC_HAS_CONTENTS | SEC_LOAD);
        }
     }
 
-  osection = bfd_make_section_anyway (obfd, bfd_section_name (ibfd, isection));
+  osection = bfd_make_section_anyway_with_flags (obfd, name, flags);
+
   if (osection == NULL)
     {
-      err = "making";
+      err = _("failed to create output section");
       goto loser;
     }
 
-  if (!bfd_set_section_size (obfd,
-                            osection,
-                            bfd_section_size (ibfd, isection)))
+  if (make_nobits)
+    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";
+      err = _("failed to set size");
       goto loser;
     }
 
   vma = bfd_section_vma (ibfd, isection);
-  for (p = adjust_sections; p != NULL; p = p->next)
-    {
-      if (strcmp (p->name, bfd_section_name (ibfd, isection)) == 0)
-       {
-         if (p->adjust)
-           vma += p->val;
-         else
-           vma = p->val;
-         p->used = true;
-         break;
-       }
-    }
-  if (p == NULL)
-    vma += adjust_section_vma;
+  if (p != NULL && p->change_vma == CHANGE_MODIFY)
+    vma += p->vma_val;
+  else if (p != NULL && p->change_vma == CHANGE_SET)
+    vma = p->vma_val;
+  else
+    vma += change_section_address;
 
   if (! bfd_set_section_vma (obfd, osection, vma))
     {
-      err = "vma";
+      err = _("failed to set vma");
       goto loser;
     }
 
-  if (bfd_set_section_alignment (obfd,
-                                osection,
-                                bfd_section_alignment (ibfd, isection))
-      == false)
+  lma = isection->lma;
+  if ((p != NULL) && p->change_lma != CHANGE_IGNORE)
     {
-      err = "alignment";
-      goto loser;
+      if (p->change_lma == CHANGE_MODIFY)
+       lma += p->lma_val;
+      else if (p->change_lma == CHANGE_SET)
+       lma = p->lma_val;
+      else
+       abort ();
     }
+  else
+    lma += change_section_address;
+
+  osection->lma = lma;
 
-  if (!bfd_set_section_flags (obfd, osection,
-                             bfd_get_section_flags (ibfd, isection)))
+  /* FIXME: This is probably not enough.  If we change the LMA we
+     may have to recompute the header for the file as well.  */
+  if (!bfd_set_section_alignment (obfd,
+                                 osection,
+                                 bfd_section_alignment (ibfd, isection)))
     {
-      err = "flags";
+      err = _("failed to set alignment");
       goto loser;
     }
 
+  /* Copy merge entity size.  */
+  osection->entsize = isection->entsize;
+
   /* This used to be mangle_section; we do here to avoid using
      bfd_get_section_by_name since some formats allow multiple
      sections with the same name.  */
   isection->output_section = osection;
   isection->output_offset = 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;
+
+  if ((isection->flags & SEC_GROUP) != 0)
+    {
+      asymbol *gsym = group_signature (isection);
+
+      if (gsym != NULL)
+       {
+         gsym->flags |= BSF_KEEP;
+         if (ibfd->xvec->flavour == bfd_target_elf_flavour)
+           elf_group_id (isection) = gsym;
+       }
+    }
+
   /* Allow the BFD backend to copy any private data it understands
      from the input section to the output section.  */
   if (!bfd_copy_private_section_data (ibfd, isection, obfd, osection))
     {
-      err = "private data";
+      err = _("failed to copy private data");
       goto loser;
     }
 
-  /* All went well */
+  /* All went well */
   return;
 
 loser:
-  fprintf (stderr, "%s: %s: section `%s': error in %s: %s\n",
-          program_name,
-          bfd_get_filename (ibfd), bfd_section_name (ibfd, isection),
-          err, bfd_errmsg (bfd_get_error ()));
   status = 1;
+  bfd_nonfatal_message (NULL, obfd, osection, err);
 }
 
 /* Copy the data of input section ISECTION of IBFD
@@ -694,10 +2539,7 @@ loser:
    If stripping then don't copy any relocation info.  */
 
 static void
-copy_section (ibfd, isection, obfdarg)
-     bfd *ibfd;
-     sec_ptr isection;
-     PTR obfdarg;
+copy_section (bfd *ibfd, sec_ptr isection, void *obfdarg)
 {
   bfd *obfd = (bfd *) obfdarg;
   struct section_list *p;
@@ -705,90 +2547,233 @@ copy_section (ibfd, isection, obfdarg)
   long relcount;
   sec_ptr osection;
   bfd_size_type size;
+  long relsize;
+  flagword flags;
 
-  if ((bfd_get_section_flags (ibfd, isection) & SEC_DEBUGGING) != 0
-      && (strip_symbols == strip_debug
-         || strip_symbols == strip_all
-         || discard_locals == locals_all))
-    {
-      return;
-    }
+  /* If we have already failed earlier on,
+     do not keep on generating complaints now.  */
+  if (status != 0)
+    return;
 
-  for (p = remove_sections; p != NULL; p = p->next)
-    if (strcmp (p->name, bfd_section_name (ibfd, isection)) == 0)
-      return;
+  if (is_strip_section (ibfd, isection))
+    return;
+
+  flags = bfd_get_section_flags (ibfd, isection);
+  if ((flags & SEC_GROUP) != 0)
+    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;
 
-  if (strip_symbols == strip_all)
-    bfd_set_reloc (obfd, osection, (arelent **) NULL, 0);
+  if (extract_symbol)
+    return;
+
+  p = find_section_list (bfd_get_section_name (ibfd, isection), FALSE);
+
+  /* Core files do not need to be relocated.  */
+  if (bfd_get_format (obfd) == bfd_core)
+    relsize = 0;
   else
     {
-      long relsize;
-
       relsize = bfd_get_reloc_upper_bound (ibfd, isection);
+
       if (relsize < 0)
        {
-         nonfatal (bfd_get_filename (ibfd));
-       }
-      if (relsize == 0)
-       bfd_set_reloc (obfd, osection, (arelent **) NULL, 0);
-      else
-       {
-         relpp = (arelent **) xmalloc (relsize);
-         relcount = bfd_canonicalize_reloc (ibfd, isection, relpp, isympp);
-         if (relcount < 0)
+         /* Do not complain if the target does not support relocations.  */
+         if (relsize == -1 && bfd_get_error () == bfd_error_invalid_operation)
+           relsize = 0;
+         else
            {
-             nonfatal (bfd_get_filename (ibfd));
+             status = 1;
+             bfd_nonfatal_message (NULL, ibfd, isection, NULL);
+             return;
            }
-         bfd_set_reloc (obfd, osection, relpp, relcount);
        }
     }
 
-  isection->_cooked_size = isection->_raw_size;
-  isection->reloc_done = true;
+  if (relsize == 0)
+    bfd_set_reloc (obfd, osection, NULL, 0);
+  else
+    {
+      relpp = (arelent **) xmalloc (relsize);
+      relcount = bfd_canonicalize_reloc (ibfd, isection, relpp, isympp);
+      if (relcount < 0)
+       {
+         status = 1;
+         bfd_nonfatal_message (NULL, ibfd, isection,
+                               _("relocation count is negative"));
+         return;
+       }
+
+      if (strip_symbols == STRIP_ALL)
+       {
+         /* Remove relocations which are not in
+            keep_strip_specific_list.  */
+         arelent **temp_relpp;
+         long temp_relcount = 0;
+         long i;
+
+         temp_relpp = (arelent **) xmalloc (relsize);
+         for (i = 0; i < relcount; i++)
+           if (is_specified_symbol (bfd_asymbol_name (*relpp[i]->sym_ptr_ptr),
+                                    keep_specific_htab))
+             temp_relpp [temp_relcount++] = relpp [i];
+         relcount = temp_relcount;
+         free (relpp);
+         relpp = temp_relpp;
+       }
+
+      bfd_set_reloc (obfd, osection, relcount == 0 ? NULL : relpp, relcount);
+      if (relcount == 0)
+       free (relpp);
+    }
 
-  if (bfd_get_section_flags (ibfd, isection) & SEC_HAS_CONTENTS)
+  if (bfd_get_section_flags (ibfd, isection) & SEC_HAS_CONTENTS
+      && bfd_get_section_flags (obfd, osection) & SEC_HAS_CONTENTS)
     {
-      PTR memhunk = (PTR) xmalloc ((unsigned) size);
+      void *memhunk = xmalloc (size);
 
-      if (!bfd_get_section_contents (ibfd, isection, memhunk, (file_ptr) 0,
-                                    size))
+      if (!bfd_get_section_contents (ibfd, isection, memhunk, 0, size))
        {
-         nonfatal (bfd_get_filename (ibfd));
+         status = 1;
+         bfd_nonfatal_message (NULL, ibfd, isection, NULL);
+         return;
        }
 
-      if (copy_byte >= 0) 
-        {
-         filter_bytes (memhunk, &size);
-              /* The section has gotten smaller. */
-          if (!bfd_set_section_size (obfd, osection, size))
-            nonfatal (bfd_get_filename (obfd));
-        }
+      if (reverse_bytes)
+       {
+         /* We don't handle leftover bytes (too many possible behaviors,
+            and we don't know what the user wants).  The section length
+            must be a multiple of the number of bytes to swap.  */
+         if ((size % reverse_bytes) == 0)
+           {
+             unsigned long i, j;
+             bfd_byte b;
+
+             for (i = 0; i < size; i += reverse_bytes)
+               for (j = 0; j < (unsigned long)(reverse_bytes / 2); j++)
+                 {
+                   bfd_byte *m = (bfd_byte *) memhunk;
+
+                   b = m[i + j];
+                   m[i + j] = m[(i + reverse_bytes) - (j + 1)];
+                   m[(i + reverse_bytes) - (j + 1)] = b;
+                 }
+           }
+         else
+           /* User must pad the section up in order to do this.  */
+           fatal (_("cannot reverse bytes: length of section %s must be evenly divisible by %d"),
+                  bfd_section_name (ibfd, isection), reverse_bytes);
+       }
+
+      if (copy_byte >= 0)
+       {
+         /* Keep only every `copy_byte'th byte in MEMHUNK.  */
+         char *from = (char *) memhunk + copy_byte;
+         char *to = (char *) 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))
+       {
+         status = 1;
+         bfd_nonfatal_message (NULL, obfd, osection, NULL);
+         return;
+       }
+      free (memhunk);
+    }
+  else if (p != NULL && p->set_flags && (p->flags & SEC_HAS_CONTENTS) != 0)
+    {
+      void *memhunk = xmalloc (size);
+
+      /* We don't permit the user to turn off the SEC_HAS_CONTENTS
+        flag--they can just remove the section entirely and add it
+        back again.  However, we do permit them to turn on the
+        SEC_HAS_CONTENTS flag, and take it to mean that the section
+        contents should be zeroed out.  */
 
-      if (!bfd_set_section_contents (obfd, osection, memhunk, (file_ptr) 0,
-                                    size))
+      memset (memhunk, 0, size);
+      if (! bfd_set_section_contents (obfd, osection, memhunk, 0, size))
        {
-         nonfatal (bfd_get_filename (obfd));
+         status = 1;
+         bfd_nonfatal_message (NULL, obfd, osection, NULL);
+         return;
        }
       free (memhunk);
     }
 }
 
+/* Get all the sections.  This is used when --gap-fill or --pad-to is
+   used.  */
+
+static void
+get_sections (bfd *obfd ATTRIBUTE_UNUSED, asection *osection, void *secppparg)
+{
+  asection ***secppp = (asection ***) secppparg;
+
+  **secppp = osection;
+  ++(*secppp);
+}
+
+/* Sort sections by VMA.  This is called via qsort, and is used when
+   --gap-fill or --pad-to is used.  We force non loadable or empty
+   sections to the front, where they are easier to ignore.  */
+
+static int
+compare_section_lma (const void *arg1, const void *arg2)
+{
+  const asection *const *sec1 = (const asection * const *) arg1;
+  const asection *const *sec2 = (const asection * const *) arg2;
+  flagword flags1, flags2;
+
+  /* Sort non loadable sections to the front.  */
+  flags1 = (*sec1)->flags;
+  flags2 = (*sec2)->flags;
+  if ((flags1 & SEC_HAS_CONTENTS) == 0
+      || (flags1 & SEC_LOAD) == 0)
+    {
+      if ((flags2 & SEC_HAS_CONTENTS) != 0
+         && (flags2 & SEC_LOAD) != 0)
+       return -1;
+    }
+  else
+    {
+      if ((flags2 & SEC_HAS_CONTENTS) == 0
+         || (flags2 & SEC_LOAD) == 0)
+       return 1;
+    }
+
+  /* Sort sections by LMA.  */
+  if ((*sec1)->lma > (*sec2)->lma)
+    return 1;
+  else if ((*sec1)->lma < (*sec2)->lma)
+    return -1;
+
+  /* Sort sections with the same LMA by size.  */
+  if (bfd_get_section_size (*sec1) > bfd_get_section_size (*sec2))
+    return 1;
+  else if (bfd_get_section_size (*sec1) < bfd_get_section_size (*sec2))
+    return -1;
+
+  return 0;
+}
+
 /* Mark all the symbols which will be used in output relocations with
    the BSF_KEEP flag so that those symbols will not be stripped.
 
    Ignore relocations which will not appear in the output file.  */
 
 static void
-mark_symbols_used_in_relocations (ibfd, isection, symbolsarg)
-     bfd *ibfd;
-     sec_ptr isection;
-     PTR symbolsarg;
+mark_symbols_used_in_relocations (bfd *ibfd, sec_ptr isection, void *symbolsarg)
 {
   asymbol **symbols = (asymbol **) symbolsarg;
   long relsize;
@@ -801,7 +2786,15 @@ mark_symbols_used_in_relocations (ibfd, isection, symbolsarg)
 
   relsize = bfd_get_reloc_upper_bound (ibfd, isection);
   if (relsize < 0)
-    bfd_fatal (bfd_get_filename (ibfd));
+    {
+      /* Do not complain if the target does not support relocations.  */
+      if (relsize == -1 && bfd_get_error () == bfd_error_invalid_operation)
+       return;
+      bfd_fatal (bfd_get_filename (ibfd));
+    }
+
+  if (relsize == 0)
+    return;
 
   relpp = (arelent **) xmalloc (relsize);
   relcount = bfd_canonicalize_reloc (ibfd, isection, relpp, symbols);
@@ -822,98 +2815,79 @@ mark_symbols_used_in_relocations (ibfd, isection, symbolsarg)
     free (relpp);
 }
 
-/* The number of bytes to copy at once.  */
-#define COPY_BUF 8192
+/* Write out debugging information.  */
 
-/* Copy file FROM to file TO, performing no translations.
-   Return 0 if ok, -1 if error.  */
-
-static int
-simple_copy (from, to)
-     char *from, *to;
+static bfd_boolean
+write_debugging_info (bfd *obfd, void *dhandle,
+                     long *symcountp ATTRIBUTE_UNUSED,
+                     asymbol ***symppp ATTRIBUTE_UNUSED)
 {
-  int fromfd, tofd, nread;
-  char buf[COPY_BUF];
+  if (bfd_get_flavour (obfd) == bfd_target_ieee_flavour)
+    return write_ieee_debugging_info (obfd, dhandle);
 
-  fromfd = open (from, O_RDONLY);
-  if (fromfd < 0)
-    return -1;
-  tofd = open (to, O_WRONLY | O_CREAT | O_TRUNC);
-  if (tofd < 0)
-    {
-      close (fromfd);
-      return -1;
-    }
-  while ((nread = read (fromfd, buf, sizeof buf)) > 0)
+  if (bfd_get_flavour (obfd) == bfd_target_coff_flavour
+      || bfd_get_flavour (obfd) == bfd_target_elf_flavour)
     {
-      if (write (tofd, buf, nread) != nread)
+      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;
+
+      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))
        {
-         close (fromfd);
-         close (tofd);
-         return -1;
+         bfd_nonfatal_message (NULL, obfd, NULL,
+                               _("can't create debugging section"));
+         return FALSE;
        }
-    }
-  close (fromfd);
-  close (tofd);
-  if (nread < 0)
-    return -1;
-  return 0;
-}
-
-#ifndef S_ISLNK
-#ifdef S_IFLNK
-#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
-#else
-#define S_ISLNK(m) 0
-#define lstat stat
-#endif
-#endif
-
-/* Rename FROM to TO, copying if TO is a link.
-   Assumes that TO already exists, because FROM is a temp file.
-   Return 0 if ok, -1 if error.  */
-
-static int
-smart_rename (from, to)
-     char *from, *to;
-{
-  struct stat s;
-  int ret = 0;
-
-  if (lstat (to, &s))
-    return -1;
 
-  /* Use rename only if TO is not a symbolic link and has
-     only one hard link.  */
-  if (!S_ISLNK (s.st_mode) && s.st_nlink == 1)
-    {
-      ret = rename (from, to);
-      if (ret == 0)
+      /* We can get away with setting the section contents now because
+         the next thing the caller is going to do is copy over the
+         real sections.  We may someday have to split the contents
+         setting out of this function.  */
+      if (! bfd_set_section_contents (obfd, stabsec, syms, 0, symsize)
+         || ! bfd_set_section_contents (obfd, stabstrsec, strings, 0,
+                                        stringsize))
        {
-         /* Try to preserve the permission bits and ownership of TO.  */
-         chmod (to, s.st_mode & 07777);
-         chown (to, s.st_uid, s.st_gid);
+         bfd_nonfatal_message (NULL, obfd, NULL,
+                               _("can't set debugging section contents"));
+         return FALSE;
        }
+
+      return TRUE;
     }
-  else
-    {
-      ret = simple_copy (from, to);
-      if (ret == 0)
-       unlink (from);
-    }
-  return ret;
+
+  bfd_nonfatal_message (NULL, obfd, NULL,
+                       _("don't know how to write debugging information for %s"),
+            bfd_get_target (obfd));
+  return FALSE;
 }
 
 static int
-strip_main (argc, argv)
-     int argc;
-     char *argv[];
+strip_main (int argc, char *argv[])
 {
-  char *input_target = NULL, *output_target = NULL;
-  boolean show_version = false;
-  int c, i;
+  char *input_target = NULL;
+  char *output_target = NULL;
+  bfd_boolean show_version = FALSE;
+  bfd_boolean formats_info = FALSE;
+  int c;
+  int i;
+  struct section_list *p;
+  char *output_file = NULL;
 
-  while ((c = getopt_long (argc, argv, "I:O:F:R:sSgxXVv",
+  while ((c = getopt_long (argc, argv, "I:O:F:K:N:R:o:sSpdgxXHhVvw",
                           strip_options, (int *) 0)) != EOF)
     {
       switch (c)
@@ -928,37 +2902,61 @@ strip_main (argc, argv)
          input_target = output_target = optarg;
          break;
        case 'R':
-         {
-           struct section_list *n;
-
-           n = (struct section_list *) xmalloc (sizeof (struct section_list));
-           n->name = optarg;
-           n->used = false;
-           n->next = remove_sections;
-           remove_sections = n;
-         }
+         p = find_section_list (optarg, TRUE);
+         p->remove = TRUE;
+         sections_removed = TRUE;
          break;
        case 's':
-         strip_symbols = strip_all;
+         strip_symbols = STRIP_ALL;
          break;
        case 'S':
        case 'g':
-         strip_symbols = strip_debug;
+       case 'd':       /* Historic BSD alias for -g.  Used by early NetBSD.  */
+         strip_symbols = STRIP_DEBUG;
+         break;
+       case OPTION_STRIP_UNNEEDED:
+         strip_symbols = STRIP_UNNEEDED;
+         break;
+       case 'K':
+         add_specific_symbol (optarg, keep_specific_htab);
+         break;
+       case 'N':
+         add_specific_symbol (optarg, strip_specific_htab);
+         break;
+       case 'o':
+         output_file = optarg;
+         break;
+       case 'p':
+         preserve_dates = TRUE;
          break;
        case 'x':
-         discard_locals = locals_all;
+         discard_locals = LOCALS_ALL;
          break;
        case 'X':
-         discard_locals = locals_start_L;
+         discard_locals = LOCALS_START_L;
          break;
        case 'v':
-         verbose = true;
+         verbose = TRUE;
          break;
        case 'V':
-         show_version = true;
+         show_version = TRUE;
+         break;
+       case OPTION_FORMATS_INFO:
+         formats_info = TRUE;
+         break;
+       case OPTION_ONLY_KEEP_DEBUG:
+         strip_symbols = STRIP_NONDEBUG;
+         break;
+       case OPTION_KEEP_FILE_SYMBOLS:
+         keep_file_symbols = 1;
          break;
        case 0:
-         break;                /* we've been given a long option */
+         /* We've been given a long option.  */
+         break;
+       case 'w':
+         wildcard = TRUE;
+         break;
+       case 'H':
        case 'h':
          strip_usage (stdout, 0);
        default:
@@ -966,197 +2964,809 @@ strip_main (argc, argv)
        }
     }
 
-  if (show_version)
+  if (formats_info)
     {
-      printf ("GNU %s version %s\n", program_name, program_version);
-      exit (0);
+      display_info ();
+      return 0;
     }
 
+  if (show_version)
+    print_version ("strip");
+
   /* Default is to strip all symbols.  */
-  if (strip_symbols == strip_undef && discard_locals == locals_undef)
-    strip_symbols = strip_all;
+  if (strip_symbols == STRIP_UNDEF
+      && discard_locals == LOCALS_UNDEF
+      && htab_elements (strip_specific_htab) == 0)
+    strip_symbols = STRIP_ALL;
 
-  if (output_target == (char *) NULL)
+  if (output_target == NULL)
     output_target = input_target;
 
   i = optind;
-  if (i == argc)
+  if (i == argc
+      || (output_file != NULL && (i + 1) < argc))
     strip_usage (stderr, 1);
 
-  for (; i < argc; i++)
+  for (; i < argc; i++)
+    {
+      int hold_status = status;
+      struct stat statbuf;
+      char *tmpname;
+
+      if (get_file_size (argv[i]) < 1)
+       {
+         status = 1;
+         continue;
+       }
+
+      if (preserve_dates)
+       /* No need to check the return value of stat().
+          It has already been checked in get_file_size().  */
+       stat (argv[i], &statbuf);
+
+      if (output_file == NULL || strcmp (argv[i], output_file) == 0)
+       tmpname = make_tempname (argv[i]);
+      else
+       tmpname = output_file;
+
+      if (tmpname == NULL)
+       {
+         bfd_nonfatal_message (argv[i], NULL, NULL,
+                               _("could not create temporary file to hold stripped copy"));
+         status = 1;
+         continue;
+       }
+
+      status = 0;
+      copy_file (argv[i], tmpname, input_target, output_target, NULL);
+      if (status == 0)
+       {
+         if (preserve_dates)
+           set_times (tmpname, &statbuf);
+         if (output_file != tmpname)
+           status = (smart_rename (tmpname,
+                                   output_file ? output_file : argv[i],
+                                   preserve_dates) != 0);
+         if (status == 0)
+           status = hold_status;
+       }
+      else
+       unlink_if_ordinary (tmpname);
+      if (output_file != tmpname)
+       free (tmpname);
+    }
+
+  return status;
+}
+
+/* Set up PE subsystem.  */
+
+static void
+set_pe_subsystem (const char *s)
+{
+  const char *version, *subsystem;
+  size_t i;
+  static const struct
+    {
+      const char *name;
+      const char set_def;
+      const short value;
+    }
+  v[] =
+    {
+      { "native", 0, IMAGE_SUBSYSTEM_NATIVE },  
+      { "windows", 0, IMAGE_SUBSYSTEM_WINDOWS_GUI },
+      { "console", 0, IMAGE_SUBSYSTEM_WINDOWS_CUI },
+      { "posix", 0, IMAGE_SUBSYSTEM_POSIX_CUI },
+      { "wince", 0, IMAGE_SUBSYSTEM_WINDOWS_CE_GUI },
+      { "efi-app", 1, IMAGE_SUBSYSTEM_EFI_APPLICATION },
+      { "efi-bsd", 1, IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER },
+      { "efi-rtd", 1, IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER },
+      { "sal-rtd", 1, IMAGE_SUBSYSTEM_SAL_RUNTIME_DRIVER },
+      { "xbox", 0, IMAGE_SUBSYSTEM_XBOX }
+    };
+  short value;
+  char *copy;
+  int set_def = -1;
+
+  /* Check for the presence of a version number.  */
+  version = strchr (s, ':');
+  if (version == NULL)
+    subsystem = s;
+  else
+    {
+      int len = version - s;
+      copy = xstrdup (s);
+      subsystem = copy;
+      copy[len] = '\0';
+      version = copy + 1 + len;
+      pe_major_subsystem_version = strtoul (version, &copy, 0);
+      if (*copy == '.')
+       pe_minor_subsystem_version = strtoul (copy + 1, &copy, 0);
+      if (*copy != '\0')
+       non_fatal (_("%s: bad version in PE subsystem"), s);
+    }
+
+  /* Check for numeric subsystem.  */
+  value = (short) strtol (subsystem, &copy, 0);
+  if (*copy == '\0')
+    {
+      for (i = 0; i < ARRAY_SIZE (v); i++)
+       if (v[i].value == value)
+         {
+           pe_subsystem = value;
+           set_def = v[i].set_def;
+           break;
+         }
+    }
+  else
+    {
+      /* Search for subsystem by name.  */
+      for (i = 0; i < ARRAY_SIZE (v); i++)
+       if (strcmp (subsystem, v[i].name) == 0)
+         {
+           pe_subsystem = v[i].value;
+           set_def = v[i].set_def;
+           break;
+         }
+    }
+
+  switch (set_def)
+    {
+    case -1:
+      fatal (_("unknown PE subsystem: %s"), s);
+      break;
+    case 0:
+      break;
+    default:
+      if (pe_file_alignment == (bfd_vma) -1)
+       pe_file_alignment = PE_DEF_FILE_ALIGNMENT;
+      if (pe_section_alignment == (bfd_vma) -1)
+       pe_section_alignment = PE_DEF_SECTION_ALIGNMENT;
+      break;
+    }
+}
+
+/* Convert EFI target to PEI target.  */
+
+static void
+convert_efi_target (char *efi)
+{
+  efi[0] = 'p';
+  efi[1] = 'e';
+  efi[2] = 'i';
+
+  if (strcmp (efi + 4, "ia32") == 0)
     {
-      int hold_status = status;
-
-      char *tmpname = make_tempname (argv[i]);
-      status = 0;
-      copy_file (argv[i], tmpname, input_target, output_target);
-      if (status == 0)
-       {
-         smart_rename (tmpname, argv[i]);
-         status = hold_status;
-       }
-      else
-       unlink (tmpname);
-      free (tmpname);
+      /* Change ia32 to i386.  */
+      efi[5]= '3';
+      efi[6]= '8';
+      efi[7]= '6';
+    }
+  else if (strcmp (efi + 4, "x86_64") == 0)
+    {
+      /* Change x86_64 to x86-64.  */
+      efi[7] = '-';
     }
-
-  return 0;
 }
 
 static int
-copy_main (argc, argv)
-     int argc;
-     char *argv[];
+copy_main (int argc, char *argv[])
 {
-  char *input_filename = NULL, *output_filename = NULL;
-  char *input_target = NULL, *output_target = NULL;
-  boolean show_version = false;
-  boolean adjust_warn = true;
+  char *input_filename = NULL;
+  char *output_filename = NULL;
+  char *tmpname;
+  char *input_target = NULL;
+  char *output_target = NULL;
+  bfd_boolean show_version = FALSE;
+  bfd_boolean change_warn = TRUE;
+  bfd_boolean formats_info = FALSE;
   int c;
   struct section_list *p;
+  struct stat statbuf;
+  const bfd_arch_info_type *input_arch = NULL;
 
-  while ((c = getopt_long (argc, argv, "b:i:I:s:O:d:F:R:SgxXVv",
+  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)
        {
        case 'b':
-         copy_byte = atoi(optarg);
+         copy_byte = atoi (optarg);
          if (copy_byte < 0)
-           {
-             fprintf (stderr, "%s: byte number must be non-negative\n",
-                      program_name);
-             exit (1);
-           }
+           fatal (_("byte number must be non-negative"));
+         break;
+
+       case 'B':
+         input_arch = bfd_scan_arch (optarg);
+         if (input_arch == NULL)
+           fatal (_("architecture %s unknown"), optarg);
          break;
+
        case 'i':
-         interleave = atoi(optarg);
+         interleave = atoi (optarg);
          if (interleave < 1)
-           {
-             fprintf(stderr, "%s: interleave must be positive\n",
-                     program_name);
-             exit (1);
-           }
+           fatal (_("interleave must be positive"));
          break;
+
        case 'I':
        case 's':               /* "source" - 'I' is preferred */
          input_target = optarg;
          break;
+
        case 'O':
        case 'd':               /* "destination" - 'O' is preferred */
          output_target = optarg;
          break;
+
        case 'F':
          input_target = output_target = optarg;
          break;
+
+       case 'j':
+         p = find_section_list (optarg, TRUE);
+         if (p->remove)
+           fatal (_("%s both copied and removed"), optarg);
+         p->copy = TRUE;
+         sections_copied = TRUE;
+         break;
+
        case 'R':
-         p = (struct section_list *) xmalloc (sizeof (struct section_list));
-         p->name = optarg;
-         p->used = false;
-         p->next = remove_sections;
-         remove_sections = p;
+         p = find_section_list (optarg, TRUE);
+         if (p->copy)
+           fatal (_("%s both copied and removed"), optarg);
+         p->remove = TRUE;
+         sections_removed = TRUE;
          break;
+
        case 'S':
-         strip_symbols = strip_all;
+         strip_symbols = STRIP_ALL;
          break;
+
        case 'g':
-         strip_symbols = strip_debug;
+         strip_symbols = STRIP_DEBUG;
+         break;
+
+       case OPTION_STRIP_UNNEEDED:
+         strip_symbols = STRIP_UNNEEDED;
+         break;
+
+       case OPTION_ONLY_KEEP_DEBUG:
+         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;
+
+       case 'K':
+         add_specific_symbol (optarg, keep_specific_htab);
+         break;
+
+       case 'N':
+         add_specific_symbol (optarg, strip_specific_htab);
+         break;
+
+       case OPTION_STRIP_UNNEEDED_SYMBOL:
+         add_specific_symbol (optarg, strip_unneeded_htab);
+         break;
+
+       case 'L':
+         add_specific_symbol (optarg, localize_specific_htab);
+         break;
+
+       case OPTION_GLOBALIZE_SYMBOL:
+         add_specific_symbol (optarg, globalize_specific_htab);
+         break;
+
+       case 'G':
+         add_specific_symbol (optarg, keepglobal_specific_htab);
+         break;
+
+       case 'W':
+         add_specific_symbol (optarg, weaken_specific_htab);
+         break;
+
+       case 'p':
+         preserve_dates = TRUE;
+         break;
+
+       case 'w':
+         wildcard = TRUE;
+         break;
+
        case 'x':
-         discard_locals = locals_all;
+         discard_locals = LOCALS_ALL;
          break;
+
        case 'X':
-         discard_locals = locals_start_L;
+         discard_locals = LOCALS_START_L;
          break;
+
        case 'v':
-         verbose = true;
+         verbose = TRUE;
          break;
+
        case 'V':
-         show_version = true;
+         show_version = TRUE;
+         break;
+
+       case OPTION_FORMATS_INFO:
+         formats_info = TRUE;
          break;
-       case OPTION_ADJUST_START:
-         adjust_start = parse_vma (optarg, "--adjust-start");
+
+       case OPTION_WEAKEN:
+         weaken = TRUE;
          break;
-       case OPTION_ADJUST_SECTION_VMA:
+
+       case OPTION_ADD_SECTION:
          {
            const char *s;
+           off_t size;
+           struct section_add *pa;
            int len;
            char *name;
+           FILE *f;
 
-           p = (struct section_list *) xmalloc (sizeof (struct section_list));
            s = strchr (optarg, '=');
-           if (s != NULL)
+
+           if (s == NULL)
+             fatal (_("bad format for %s"), "--add-section");
+
+           size = get_file_size (s + 1);
+           if (size < 1)
              {
-               p->adjust = false;
-               p->val = parse_vma (s + 1, "--adjust-section-vma");
+               status = 1;
+               break;
              }
-           else
+
+           pa = (struct section_add *) xmalloc (sizeof (struct section_add));
+
+           len = s - optarg;
+           name = (char *) xmalloc (len + 1);
+           strncpy (name, optarg, len);
+           name[len] = '\0';
+           pa->name = name;
+
+           pa->filename = s + 1;
+           pa->size = size;
+           pa->contents = (bfd_byte *) xmalloc (size);
+
+           f = fopen (pa->filename, FOPEN_RB);
+
+           if (f == NULL)
+             fatal (_("cannot open: %s: %s"),
+                    pa->filename, strerror (errno));
+
+           if (fread (pa->contents, 1, pa->size, f) == 0
+               || ferror (f))
+             fatal (_("%s: fread failed"), pa->filename);
+
+           fclose (f);
+
+           pa->next = add_sections;
+           add_sections = pa;
+         }
+         break;
+
+       case OPTION_CHANGE_START:
+         change_start = parse_vma (optarg, "--change-start");
+         break;
+
+       case OPTION_CHANGE_SECTION_ADDRESS:
+       case OPTION_CHANGE_SECTION_LMA:
+       case OPTION_CHANGE_SECTION_VMA:
+         {
+           const char *s;
+           int len;
+           char *name;
+           char *option = NULL;
+           bfd_vma val;
+           enum change_action what = CHANGE_IGNORE;
+
+           switch (c)
+             {
+             case OPTION_CHANGE_SECTION_ADDRESS:
+               option = "--change-section-address";
+               break;
+             case OPTION_CHANGE_SECTION_LMA:
+               option = "--change-section-lma";
+               break;
+             case OPTION_CHANGE_SECTION_VMA:
+               option = "--change-section-vma";
+               break;
+             }
+
+           s = strchr (optarg, '=');
+           if (s == NULL)
              {
                s = strchr (optarg, '+');
                if (s == NULL)
                  {
                    s = strchr (optarg, '-');
                    if (s == NULL)
-                     {
-                       fprintf (stderr,
-                                "%s: bad format for --adjust-section-vma\n",
-                                program_name);
-                       exit (1);
-                     }
+                     fatal (_("bad format for %s"), option);
                  }
-               p->adjust = true;
-               p->val = parse_vma (s + 1, "--adjust-section-vma");
-               if (*s == '-')
-                 p->val = - p->val;
              }
 
            len = s - optarg;
            name = (char *) xmalloc (len + 1);
            strncpy (name, optarg, len);
            name[len] = '\0';
-           p->name = name;
 
-           p->used = false;
+           p = find_section_list (name, TRUE);
+
+           val = parse_vma (s + 1, option);
+
+           switch (*s)
+             {
+             case '=': what = CHANGE_SET; break;
+             case '-': val  = - val; /* Drop through.  */
+             case '+': what = CHANGE_MODIFY; break;
+             }
+
+           switch (c)
+             {
+             case OPTION_CHANGE_SECTION_ADDRESS:
+               p->change_vma = what;
+               p->vma_val    = val;
+               /* Drop through.  */
+
+             case OPTION_CHANGE_SECTION_LMA:
+               p->change_lma = what;
+               p->lma_val    = val;
+               break;
+
+             case OPTION_CHANGE_SECTION_VMA:
+               p->change_vma = what;
+               p->vma_val    = val;
+               break;
+             }
+         }
+         break;
+
+       case OPTION_CHANGE_ADDRESSES:
+         change_section_address = parse_vma (optarg, "--change-addresses");
+         change_start = change_section_address;
+         break;
+
+       case OPTION_CHANGE_WARNINGS:
+         change_warn = TRUE;
+         break;
+
+       case OPTION_CHANGE_LEADING_CHAR:
+         change_leading_char = TRUE;
+         break;
+
+       case OPTION_DEBUGGING:
+         convert_debugging = TRUE;
+         break;
+
+       case OPTION_GAP_FILL:
+         {
+           bfd_vma gap_fill_vma;
+
+           gap_fill_vma = parse_vma (optarg, "--gap-fill");
+           gap_fill = (bfd_byte) gap_fill_vma;
+           if ((bfd_vma) gap_fill != gap_fill_vma)
+             {
+               char buff[20];
+
+               sprintf_vma (buff, gap_fill_vma);
+
+               non_fatal (_("Warning: truncating gap-fill from 0x%s to 0x%x"),
+                          buff, gap_fill);
+             }
+           gap_fill_set = TRUE;
+         }
+         break;
+
+       case OPTION_NO_CHANGE_WARNINGS:
+         change_warn = FALSE;
+         break;
+
+       case OPTION_PAD_TO:
+         pad_to = parse_vma (optarg, "--pad-to");
+         pad_to_set = TRUE;
+         break;
+
+       case OPTION_REMOVE_LEADING_CHAR:
+         remove_leading_char = TRUE;
+         break;
+
+       case OPTION_REDEFINE_SYM:
+         {
+           /* Push this redefinition onto redefine_symbol_list.  */
+
+           int len;
+           const char *s;
+           const char *nextarg;
+           char *source, *target;
+
+           s = strchr (optarg, '=');
+           if (s == NULL)
+             fatal (_("bad format for %s"), "--redefine-sym");
+
+           len = s - optarg;
+           source = (char *) xmalloc (len + 1);
+           strncpy (source, optarg, len);
+           source[len] = '\0';
+
+           nextarg = s + 1;
+           len = strlen (nextarg);
+           target = (char *) xmalloc (len + 1);
+           strcpy (target, nextarg);
+
+           redefine_list_append ("--redefine-sym", source, target);
 
-           p->next = adjust_sections;
-           adjust_sections = p;
+           free (source);
+           free (target);
          }
          break;
-       case OPTION_ADJUST_VMA:
-         adjust_section_vma = parse_vma (optarg, "--adjust-vma");
-         adjust_start = adjust_section_vma;
+
+       case OPTION_REDEFINE_SYMS:
+         add_redefine_syms_file (optarg);
          break;
-       case OPTION_ADJUST_WARNINGS:
-         adjust_warn = true;
+
+       case OPTION_SET_SECTION_FLAGS:
+         {
+           const char *s;
+           int len;
+           char *name;
+
+           s = strchr (optarg, '=');
+           if (s == NULL)
+             fatal (_("bad format for %s"), "--set-section-flags");
+
+           len = s - optarg;
+           name = (char *) xmalloc (len + 1);
+           strncpy (name, optarg, len);
+           name[len] = '\0';
+
+           p = find_section_list (name, TRUE);
+
+           p->set_flags = TRUE;
+           p->flags = parse_flags (s + 1);
+         }
          break;
-       case OPTION_NO_ADJUST_WARNINGS:
-         adjust_warn = false;
+
+       case OPTION_RENAME_SECTION:
+         {
+           flagword flags;
+           const char *eq, *fl;
+           char *old_name;
+           char *new_name;
+           unsigned int len;
+
+           eq = strchr (optarg, '=');
+           if (eq == NULL)
+             fatal (_("bad format for %s"), "--rename-section");
+
+           len = eq - optarg;
+           if (len == 0)
+             fatal (_("bad format for %s"), "--rename-section");
+
+           old_name = (char *) xmalloc (len + 1);
+           strncpy (old_name, optarg, len);
+           old_name[len] = 0;
+
+           eq++;
+           fl = strchr (eq, ',');
+           if (fl)
+             {
+               flags = parse_flags (fl + 1);
+               len = fl - eq;
+             }
+           else
+             {
+               flags = -1;
+               len = strlen (eq);
+             }
+
+           if (len == 0)
+             fatal (_("bad format for %s"), "--rename-section");
+
+           new_name = (char *) xmalloc (len + 1);
+           strncpy (new_name, eq, len);
+           new_name[len] = 0;
+
+           add_section_rename (old_name, new_name, flags);
+         }
          break;
+
        case OPTION_SET_START:
          set_start = parse_vma (optarg, "--set-start");
-         set_start_set = true;
+         set_start_set = TRUE;
+         break;
+
+       case OPTION_SREC_LEN:
+         Chunk = parse_vma (optarg, "--srec-len");
+         break;
+
+       case OPTION_SREC_FORCES3:
+         S3Forced = TRUE;
+         break;
+
+       case OPTION_STRIP_SYMBOLS:
+         add_specific_symbols (optarg, strip_specific_htab);
+         break;
+
+       case OPTION_STRIP_UNNEEDED_SYMBOLS:
+         add_specific_symbols (optarg, strip_unneeded_htab);
+         break;
+
+       case OPTION_KEEP_SYMBOLS:
+         add_specific_symbols (optarg, keep_specific_htab);
+         break;
+
+       case OPTION_LOCALIZE_HIDDEN:
+         localize_hidden = TRUE;
+         break;
+
+       case OPTION_LOCALIZE_SYMBOLS:
+         add_specific_symbols (optarg, localize_specific_htab);
+         break;
+
+       case OPTION_LONG_SECTION_NAMES:
+         if (!strcmp ("enable", optarg))
+           long_section_names = ENABLE;
+         else if (!strcmp ("disable", optarg))
+           long_section_names = DISABLE;
+         else if (!strcmp ("keep", optarg))
+           long_section_names = KEEP;
+         else
+           fatal (_("unknown long section names option '%s'"), optarg);
+         break;
+
+       case OPTION_GLOBALIZE_SYMBOLS:
+         add_specific_symbols (optarg, globalize_specific_htab);
+         break;
+
+       case OPTION_KEEPGLOBAL_SYMBOLS:
+         add_specific_symbols (optarg, keepglobal_specific_htab);
+         break;
+
+       case OPTION_WEAKEN_SYMBOLS:
+         add_specific_symbols (optarg, weaken_specific_htab);
+         break;
+
+       case OPTION_ALT_MACH_CODE:
+         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:
+         prefix_symbols_string = optarg;
+         break;
+
+       case OPTION_PREFIX_SECTIONS:
+         prefix_sections_string = optarg;
+         break;
+
+       case OPTION_PREFIX_ALLOC_SECTIONS:
+         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 OPTION_REVERSE_BYTES:
+          {
+            int prev = reverse_bytes;
+
+            reverse_bytes = atoi (optarg);
+            if ((reverse_bytes <= 0) || ((reverse_bytes % 2) != 0))
+              fatal (_("number of bytes to reverse must be positive and even"));
+
+            if (prev && prev != reverse_bytes)
+              non_fatal (_("Warning: ignoring previous --reverse-bytes value of %d"),
+                         prev);
+            break;
+          }
+
+       case OPTION_FILE_ALIGNMENT:
+         pe_file_alignment = parse_vma (optarg, "--file-alignment");
          break;
+       
+       case OPTION_HEAP:
+           {
+             char *end;
+             pe_heap_reserve = strtoul (optarg, &end, 0);
+             if (end == optarg
+                 || (*end != '.' && *end != '\0'))
+               non_fatal (_("%s: invalid reserve value for --heap"),
+                          optarg);
+             else if (*end != '\0')
+               {
+                 pe_heap_commit = strtoul (end + 1, &end, 0);
+                 if (*end != '\0')
+                   non_fatal (_("%s: invalid commit value for --heap"),
+                              optarg);
+               }
+           }
+         break;
+       
+       case OPTION_IMAGE_BASE:
+         pe_image_base = parse_vma (optarg, "--image-base");
+         break;
+       
+       case OPTION_SECTION_ALIGNMENT:
+         pe_section_alignment = parse_vma (optarg,
+                                           "--section-alignment");
+         break;
+       
+       case OPTION_SUBSYSTEM:
+         set_pe_subsystem (optarg);
+         break;
+       
+       case OPTION_STACK:
+           {
+             char *end;
+             pe_stack_reserve = strtoul (optarg, &end, 0);
+             if (end == optarg
+                 || (*end != '.' && *end != '\0'))
+               non_fatal (_("%s: invalid reserve value for --stack"),
+                          optarg);
+             else if (*end != '\0')
+               {
+                 pe_stack_commit = strtoul (end + 1, &end, 0);
+                 if (*end != '\0')
+                   non_fatal (_("%s: invalid commit value for --stack"),
+                              optarg);
+               }
+           }
+         break;
+       
        case 0:
-         break;                /* we've been given a long option */
+         /* We've been given a long option.  */
+         break;
+
+       case 'H':
        case 'h':
          copy_usage (stdout, 0);
+
        default:
          copy_usage (stderr, 1);
        }
     }
 
-  if (show_version)
+  if (formats_info)
     {
-      printf ("GNU %s version %s\n", program_name, program_version);
-      exit (0);
+      display_info ();
+      return 0;
     }
 
+  if (show_version)
+    print_version ("objcopy");
+
   if (copy_byte >= interleave)
-    {
-      fprintf (stderr, "%s: byte number must be less than interleave\n",
-              program_name);
-      exit (1);
-    }
+    fatal (_("byte number must be less than interleave"));
 
   if (optind == argc || optind + 2 < argc)
     copy_usage (stderr, 1);
@@ -1166,72 +3776,180 @@ copy_main (argc, argv)
     output_filename = argv[optind + 1];
 
   /* Default is to strip no symbols.  */
-  if (strip_symbols == strip_undef && discard_locals == locals_undef)
-    strip_symbols = strip_none;
+  if (strip_symbols == STRIP_UNDEF && discard_locals == LOCALS_UNDEF)
+    strip_symbols = STRIP_NONE;
 
-  if (output_target == (char *) NULL)
+  if (output_target == NULL)
     output_target = input_target;
 
-  /* If there is no destination file then create a temp and rename
-     the result into the input.  */
+  /* Convert input EFI target to PEI target.  */
+  if (input_target != NULL
+      && strncmp (input_target, "efi-", 4) == 0)
+    {
+      char *efi;
+
+      efi = xstrdup (output_target + 4);
+      if (strncmp (efi, "bsdrv-", 6) == 0
+         || strncmp (efi, "rtdrv-", 6) == 0)
+       efi += 2;
+      else if (strncmp (efi, "app-", 4) != 0)
+       fatal (_("unknown input EFI target: %s"), input_target);
+
+      input_target = efi;
+      convert_efi_target (efi);
+    }
 
-  if (output_filename == (char *) NULL)
+  /* Convert output EFI target to PEI target.  */
+  if (output_target != NULL
+      && strncmp (output_target, "efi-", 4) == 0)
     {
-      char *tmpname = make_tempname (input_filename);
-      copy_file (input_filename, tmpname, input_target, output_target);
-      if (status == 0)
-       smart_rename (tmpname, input_filename);
+      char *efi;
+
+      efi = xstrdup (output_target + 4);
+      if (strncmp (efi, "app-", 4) == 0)
+       {
+         if (pe_subsystem == -1)
+           pe_subsystem = IMAGE_SUBSYSTEM_EFI_APPLICATION;
+       }
+      else if (strncmp (efi, "bsdrv-", 6) == 0)
+       {
+         if (pe_subsystem == -1)
+           pe_subsystem = IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER;
+         efi += 2;
+       }
+      else if (strncmp (efi, "rtdrv-", 6) == 0)
+       {
+         if (pe_subsystem == -1)
+           pe_subsystem = IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER;
+         efi += 2;
+       }
       else
-       unlink (tmpname);
+       fatal (_("unknown output EFI target: %s"), output_target);
+
+      if (pe_file_alignment == (bfd_vma) -1)
+       pe_file_alignment = PE_DEF_FILE_ALIGNMENT;
+      if (pe_section_alignment == (bfd_vma) -1)
+       pe_section_alignment = PE_DEF_SECTION_ALIGNMENT;
+
+      output_target = efi;
+      convert_efi_target (efi);
     }
+
+  if (preserve_dates)
+    if (stat (input_filename, & statbuf) < 0)
+      fatal (_("warning: could not locate '%s'.  System error message: %s"),
+            input_filename, strerror (errno));
+
+  /* If there is no destination file, or the source and destination files
+     are the same, then create a temp and rename the result into the input.  */
+  if (output_filename == NULL || strcmp (input_filename, output_filename) == 0)
+    tmpname = make_tempname (input_filename);
   else
+    tmpname = output_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, input_arch);
+  if (status == 0)
     {
-      copy_file (input_filename, output_filename, input_target, output_target);
+      if (preserve_dates)
+       set_times (tmpname, &statbuf);
+      if (tmpname != output_filename)
+       status = (smart_rename (tmpname, input_filename,
+                               preserve_dates) != 0);
     }
+  else
+    unlink_if_ordinary (tmpname);
 
-  if (adjust_warn)
+  if (change_warn)
     {
-      for (p = adjust_sections; p != NULL; p = p->next)
+      for (p = change_sections; p != NULL; p = p->next)
        {
          if (! p->used)
            {
-             fprintf (stderr, "%s: warning: --adjust-section-vma %s%c0x",
-                      program_name, p->name,
-                      p->adjust ? '=' : '+');
-             fprintf_vma (stderr, p->val);
-             fprintf (stderr, " never used\n");
+             if (p->change_vma != CHANGE_IGNORE)
+               {
+                 char buff [20];
+
+                 sprintf_vma (buff, p->vma_val);
+
+                 /* xgettext:c-format */
+                 non_fatal (_("%s %s%c0x%s never used"),
+                            "--change-section-vma",
+                            p->name,
+                            p->change_vma == CHANGE_SET ? '=' : '+',
+                            buff);
+               }
+
+             if (p->change_lma != CHANGE_IGNORE)
+               {
+                 char buff [20];
+
+                 sprintf_vma (buff, p->lma_val);
+
+                 /* xgettext:c-format */
+                 non_fatal (_("%s %s%c0x%s never used"),
+                            "--change-section-lma",
+                            p->name,
+                            p->change_lma == CHANGE_SET ? '=' : '+',
+                            buff);
+               }
            }
        }
     }
 
-  /* We could issue similar warnings for remove_sections, but I don't
-     think that would be as useful.  */
-
   return 0;
 }
 
 int
-main (argc, argv)
-     int argc;
-     char *argv[];
+main (int argc, char *argv[])
 {
+#if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES)
+  setlocale (LC_MESSAGES, "");
+#endif
+#if defined (HAVE_SETLOCALE)
+  setlocale (LC_CTYPE, "");
+#endif
+  bindtextdomain (PACKAGE, LOCALEDIR);
+  textdomain (PACKAGE);
+
   program_name = argv[0];
   xmalloc_set_program_name (program_name);
-  strip_symbols = strip_undef;
-  discard_locals = locals_undef;
+
+  START_PROGRESS (program_name, 0);
+
+  expandargv (&argc, &argv);
+
+  strip_symbols = STRIP_UNDEF;
+  discard_locals = LOCALS_UNDEF;
 
   bfd_init ();
+  set_default_bfd_target ();
 
   if (is_strip < 0)
     {
       int i = strlen (program_name);
-      is_strip = (i >= 5 && strcmp (program_name + i - 5, "strip"));
+#ifdef HAVE_DOS_BASED_FILE_SYSTEM
+      /* Drop the .exe suffix, if any.  */
+      if (i > 4 && FILENAME_CMP (program_name + i - 4, ".exe") == 0)
+       {
+         i -= 4;
+         program_name[i] = '\0';
+       }
+#endif
+      is_strip = (i >= 5 && FILENAME_CMP (program_name + i - 5, "strip") == 0);
     }
 
+  create_symbol_htabs ();
+
   if (is_strip)
     strip_main (argc, argv);
   else
     copy_main (argc, argv);
 
+  END_PROGRESS (program_name);
+
   return status;
 }
This page took 0.066915 seconds and 4 git commands to generate.