x
[deliverable/binutils-gdb.git] / binutils / objcopy.c
index dea596c85ef6c1b23c9a927057cccca7b92610a1..f2b1156df038526d1280e6cd6d0f742dbc909b89 100644 (file)
 /* objcopy.c -- copy object file from input to output, optionally massaging it.
-   Copyright (C) 1991 Free Software Foundation, Inc.
+   Copyright (C) 1991, 92, 93, 94, 95, 96, 97, 1998
+   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
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA.  */
+\f
+#include "bfd.h"
+#include "progress.h"
+#include "bucomm.h"
+#include "getopt.h"
+#include "libiberty.h"
+#include "budbg.h"
+#include <sys/stat.h>
+
+#ifdef HAVE_GOOD_UTIME_H
+#include <utime.h>
+#else /* ! HAVE_GOOD_UTIME_H */
+#ifdef HAVE_UTIMES
+#include <sys/time.h>
+#endif /* HAVE_UTIMES */
+#endif /* ! HAVE_GOOD_UTIME_H */
+
+/* A list of symbols to explicitly strip out, or to keep.  A linked
+   list is good enough for a small number from the command line, but
+   this will slow things down a lot if many symbols are being
+   deleted. */
+
+struct symlist
+{
+  const char *name;
+  struct symlist *next;
+};
 
-This file is part of GNU Binutils.
+static void copy_usage PARAMS ((FILE *, int));
+static void strip_usage PARAMS ((FILE *, int));
+static flagword parse_flags PARAMS ((const char *));
+static struct section_list *find_section_list PARAMS ((const char *, boolean));
+static void setup_section PARAMS ((bfd *, asection *, PTR));
+static void copy_section PARAMS ((bfd *, asection *, PTR));
+static void get_sections PARAMS ((bfd *, asection *, PTR));
+static int compare_section_lma PARAMS ((const PTR, const PTR));
+static void add_specific_symbol PARAMS ((const char *, struct symlist **));
+static boolean is_specified_symbol PARAMS ((const char *, struct symlist *));
+static boolean is_strip_section PARAMS ((bfd *, asection *));
+static unsigned int filter_symbols
+  PARAMS ((bfd *, bfd *, asymbol **, asymbol **, long));
+static void mark_symbols_used_in_relocations PARAMS ((bfd *, asection *, PTR));
+static void filter_bytes PARAMS ((char *, bfd_size_type *));
+static boolean write_debugging_info PARAMS ((bfd *, PTR, long *, asymbol ***));
+static void copy_object PARAMS ((bfd *, bfd *));
+static void copy_archive PARAMS ((bfd *, bfd *, const char *));
+static void copy_file
+  PARAMS ((const char *, const char *, const char *, const char *));
+static int simple_copy PARAMS ((const char *, const char *));
+static int smart_rename PARAMS ((const char *, const char *));
+static void set_times PARAMS ((const char *, const struct stat *));
+static int strip_main PARAMS ((int, char **));
+static int copy_main PARAMS ((int, char **));
+
+#define RETURN_NONFATAL(s) {bfd_nonfatal(s); status = 1; return;}
+
+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 boolean preserve_dates; /* Preserve input file timestamp.  */
+static int status = 0;         /* Exit status.  */
 
-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
-(at your option) any later version.
+enum strip_action
+  {
+    STRIP_UNDEF,
+    STRIP_NONE,                        /* don't strip */
+    STRIP_DEBUG,               /* strip all debugger symbols */
+    STRIP_UNNEEDED,            /* strip unnecessary symbols */
+    STRIP_ALL                  /* strip all symbols */
+  };
 
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
+/* Which symbols to remove. */
+static enum strip_action strip_symbols;
 
-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.  */
+enum locals_action
+  {
+    LOCALS_UNDEF,
+    LOCALS_START_L,            /* discard locals starting with L */
+    LOCALS_ALL                 /* discard all locals */
+  };
 
-#include "bfd.h"
-#include "sysdep.h"
-#include "bucomm.h"
-#include <getopt.h>
+/* Which local symbols to remove.  Overrides STRIP_ALL.  */
+static enum locals_action discard_locals;
 
-asymbol       **isympp = NULL; /* Input symbols */
-asymbol       **osympp = NULL; /* Output symbols that survive stripping */
-char           *input_target = NULL;
-char           *output_target = NULL;
-char           *input_filename = NULL;
-char           *output_filename = NULL;
+/* 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
+{
+  struct section_list * next;      /* Next section to change.  */
+  const char *          name;      /* Section name.  */
+  boolean               used;      /* Whether this entry was used.  */
+  boolean               remove;    /* Whether to remove 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.  */
+  boolean              set_flags; /* Whether to set the section flags.  */
+  flagword             flags;     /* What to set the section flags to.  */
+};
 
-static void     setup_sections();
-static void     copy_sections();
-static boolean verbose;
+static struct section_list *change_sections;
+static boolean sections_removed;
 
-/* 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. */
-extern int is_strip;
+/* Changes to the start address.  */
+static bfd_vma change_start = 0;
+static boolean set_start_set = false;
+static bfd_vma set_start;
 
-int              show_version = 0;
+/* Changes to section addresses.  */
+static bfd_vma change_section_address = 0;
 
-enum strip_action
+/* Filling gaps between sections.  */
+static boolean gap_fill_set = false;
+static bfd_byte gap_fill = 0;
+
+/* Pad to a given address.  */
+static boolean pad_to_set = false;
+static bfd_vma pad_to;
+
+/* List of sections to add.  */
+
+struct section_add
 {
-  strip_undef,
-  strip_none,                  /* don't strip */
-  strip_debug,                 /* strip all debugger symbols */
-  strip_all                    /* strip all symbols */
+  /* 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;
 };
 
-/* Which symbols to remove. */
-enum strip_action strip_symbols;
+static struct section_add *add_sections;
 
-enum locals_action
+/* Whether to convert debugging information.  */
+
+static boolean convert_debugging = false;
+
+/* Whether to change the leading character in symbol names.  */
+
+static boolean change_leading_char = false;
+
+/* Whether to remove the leading character from global symbol names.  */
+
+static boolean remove_leading_char = false;
+
+/* List of symbols to strip, keep, localize, and weaken.  */
+
+static struct symlist *strip_specific_list = NULL;
+static struct symlist *keep_specific_list = NULL;
+static struct symlist *localize_specific_list = NULL;
+static struct symlist *weaken_specific_list = NULL;
+
+/* If this is true, we weaken global symbols (set BSF_WEAK).  */
+
+static boolean weaken = false;
+
+/* 150 isn't special; it's just an arbitrary non-ASCII char value.  */
+
+#define OPTION_ADD_SECTION 150
+#define OPTION_CHANGE_ADDRESSES (OPTION_ADD_SECTION + 1)
+#define OPTION_CHANGE_LEADING_CHAR (OPTION_CHANGE_ADDRESSES + 1)
+#define OPTION_CHANGE_START (OPTION_CHANGE_LEADING_CHAR + 1)
+#define OPTION_CHANGE_SECTION_ADDRESS (OPTION_CHANGE_START + 1)
+#define OPTION_CHANGE_SECTION_LMA (OPTION_CHANGE_SECTION_ADDRESS + 1)
+#define OPTION_CHANGE_SECTION_VMA (OPTION_CHANGE_SECTION_LMA + 1)
+#define OPTION_CHANGE_WARNINGS (OPTION_CHANGE_SECTION_VMA + 1)
+#define OPTION_DEBUGGING (OPTION_CHANGE_WARNINGS + 1)
+#define OPTION_GAP_FILL (OPTION_DEBUGGING + 1)
+#define OPTION_NO_CHANGE_WARNINGS (OPTION_GAP_FILL + 1)
+#define OPTION_PAD_TO (OPTION_NO_CHANGE_WARNINGS + 1)
+#define OPTION_REMOVE_LEADING_CHAR (OPTION_PAD_TO + 1)
+#define OPTION_SET_SECTION_FLAGS (OPTION_REMOVE_LEADING_CHAR + 1)
+#define OPTION_SET_START (OPTION_SET_SECTION_FLAGS + 1)
+#define OPTION_STRIP_UNNEEDED (OPTION_SET_START + 1)
+#define OPTION_WEAKEN (OPTION_STRIP_UNNEEDED + 1)
+
+/* Options to handle if running as "strip".  */
+
+static struct option strip_options[] =
 {
-  locals_undef,
-  locals_start_L,              /* discard locals starting with L */
-  locals_all                   /* discard all locals */
+  {"discard-all", no_argument, 0, 'x'},
+  {"discard-locals", no_argument, 0, 'X'},
+  {"format", required_argument, 0, 'F'}, /* Obsolete */
+  {"help", no_argument, 0, 'h'},
+  {"input-format", required_argument, 0, 'I'}, /* Obsolete */
+  {"input-target", required_argument, 0, 'I'},
+  {"keep-symbol", required_argument, 0, 'K'},
+  {"output-format", required_argument, 0, 'O'},        /* Obsolete */
+  {"output-target", 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'},
+  {0, no_argument, 0, 0}
 };
 
-/* Which local symbols to remove. */
-enum locals_action discard_locals;
-
-/* Options to handle if running as "strip". */
-
-struct option strip_options[] = {
-    {"strip-all",      no_argument, 0, 's'},
-    {"strip-debug",    no_argument, 0, 'S'},
-    {"discard-all",    no_argument, 0, 'x'},
-    {"discard-locals", no_argument, 0, 'X'},
-    {"help",           no_argument, 0, 'h'},
-    {"input-format",   required_argument, 0, 'I'},
-    {"output-format",  required_argument, 0, 'O'},
-    {"format",         required_argument, 0, 'F'},
-    {"target",         required_argument, 0, 'F'},
-
-    {"version",         no_argument, 0, 'V'},
-    {"verbose",         no_argument, 0, 'v'},
-    {0, no_argument, 0, 0}
-};
+/* Options to handle if running as "objcopy".  */
 
-/* Options to handle if running as "objcopy". */
-
-struct option copy_options[] = {
-    {"strip-all",      no_argument, 0, 'S'},
-    {"strip-debug",    no_argument, 0, 'g'},
-    {"discard-all",    no_argument, 0, 'x'},
-    {"discard-locals", no_argument, 0, 'X'},
-    {"help",           no_argument, 0, 'h'},
-    {"input-format",   required_argument, 0, 'I'},
-    {"output-format",  required_argument, 0, 'O'},
-    {"format",         required_argument, 0, 'F'},
-    {"target",         required_argument, 0, 'F'},
-    
-    {"version",         no_argument, 0, 'V'},
-    {"verbose",         no_argument, 0, 'v'},
-    {0,                        no_argument, 0, 0}
+static struct option copy_options[] =
+{
+  {"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},
+  {"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'},
+  {"format", required_argument, 0, 'F'}, /* Obsolete */
+  {"gap-fill", required_argument, 0, OPTION_GAP_FILL},
+  {"help", no_argument, 0, 'h'},
+  {"input-format", required_argument, 0, 'I'}, /* Obsolete */
+  {"input-target", required_argument, 0, 'I'},
+  {"interleave", required_argument, 0, 'i'},
+  {"keep-symbol", required_argument, 0, 'K'},
+  {"no-adjust-warnings", no_argument, 0, OPTION_NO_CHANGE_WARNINGS},
+  {"no-change-warnings", no_argument, 0, OPTION_NO_CHANGE_WARNINGS},
+  {"output-format", required_argument, 0, 'O'},        /* Obsolete */
+  {"output-target", required_argument, 0, 'O'},
+  {"pad-to", required_argument, 0, OPTION_PAD_TO},
+  {"preserve-dates", no_argument, 0, 'p'},
+  {"localize-symbol", required_argument, 0, 'L'},
+  {"remove-leading-char", no_argument, 0, OPTION_REMOVE_LEADING_CHAR},
+  {"remove-section", required_argument, 0, 'R'},
+  {"set-section-flags", required_argument, 0, OPTION_SET_SECTION_FLAGS},
+  {"set-start", required_argument, 0, OPTION_SET_START},
+  {"strip-all", no_argument, 0, 'S'},
+  {"strip-debug", no_argument, 0, 'g'},
+  {"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'},
+  {"weaken", no_argument, 0, OPTION_WEAKEN},
+  {"weaken-symbol", required_argument, 0, 'W'},
+  {0, no_argument, 0, 0}
 };
 
 /* IMPORTS */
-extern char    *program_name;
-extern char    *program_version;
+extern char *program_name;
 
+/* 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. */
+extern int is_strip;
 
-static
-void            
-copy_usage(stream, status)
+
+static void
+copy_usage (stream, exit_status)
      FILE *stream;
-     int status;
+     int exit_status;
 {
-    fprintf(stream, "\
-Usage: %s [-vVSgxX] [-I format] [-O format] [-F format]\n\
-       [--format=format] [--target=format] [--input-format=format]\n\
-       [--output-format=format] [--strip-all] [--strip-debug]\n\
-       [--discard-all] [--discard-locals] [--verbose] [--version]\n\
-       [--help] in-file [out-file]\n", program_name);
-    exit(status);
+  fprintf (stream, _("\
+Usage: %s [-vVSpgxX] [-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] [--strip-unneeded] [--discard-all]\n\
+       [--discard-locals] [--debugging] [--remove-section=section]\n"),
+          program_name);
+  fprintf (stream, _("\
+       [--gap-fill=val] [--pad-to=address] [--preserve-dates]\n\
+       [--set-start=val] \n\
+       [--change-start=incr] [--change-addresses=incr] \n\
+       (--adjust-start   and  --adjust-vma are aliases for these two) \n\
+       [--change-section-address=section{=,+,-}val]\n\
+       (--adjust-section-vma is an alias for --change-section-address)\n\
+       [--change-section-lma=section{=,+,-}val]\n\
+       [--change-section-vma=section{=,+,-}val]\n\
+       [--adjust-warnings] [--no-adjust-warnings]\n\
+       [--change-warnings] [--no-change-warnings]\n\
+       [--set-section-flags=section=flags] [--add-section=sectionname=filename]\n\
+       [--keep-symbol symbol] [-K symbol] [--strip-symbol symbol] [-N symbol]\n\
+       [--localize-symbol symbol] [-L symbol] [--weaken-symbol symbol]\n\
+       [-W symbol] [--change-leading-char] [--remove-leading-char] [--weaken]\n\
+       [--verbose] [--version] [--help] in-file [out-file]\n"));
+  list_supported_targets (program_name, stream);
+  if (exit_status == 0)
+    fprintf (stream, _("Report bugs to bug-gnu-utils@gnu.org\n"));
+  exit (exit_status);
 }
 
-static
-void            
-strip_usage(stream, status)
+static void
+strip_usage (stream, exit_status)
      FILE *stream;
-     int status;
+     int exit_status;
+{
+  fprintf (stream, _("\
+Usage: %s [-vVsSpgxX] [-I bfdname] [-O bfdname] [-F bfdname] [-R section]\n\
+       [--input-target=bfdname] [--output-target=bfdname] [--target=bfdname]\n\
+       [--strip-all] [--strip-debug] [--strip-unneeded] [--discard-all]\n\
+       [--discard-locals] [--keep-symbol symbol] [-K symbol]\n\
+       [--strip-symbol symbol] [-N symbol] [--remove-section=section]\n\
+       [-o file] [--preserve-dates] [--verbose] [--version] [--help] file...\n"),
+          program_name);
+  list_supported_targets (program_name, stream);
+  if (exit_status == 0)
+    fprintf (stream, _("Report bugs to bug-gnu-utils@gnu.org\n"));
+  exit (exit_status);
+}
+
+/* Parse section flags into a flagword, with a fatal error if the
+   string can't be parsed.  */
+
+static flagword
+parse_flags (s)
+     const char *s;
+{
+  flagword ret;
+  const char *snext;
+  int len;
+
+  ret = SEC_NO_FLAGS;
+
+  do
+    {
+      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 ("readonly", SEC_READONLY);
+      PARSE_FLAG ("code", SEC_CODE);
+      PARSE_FLAG ("data", SEC_DATA);
+      PARSE_FLAG ("rom", SEC_ROM);
+      PARSE_FLAG ("contents", SEC_HAS_CONTENTS);
+#undef PARSE_FLAG
+      else
+       {
+         char *copy;
+
+         copy = xmalloc (len + 1);
+         strncpy (copy, s, len);
+         copy[len] = '\0';
+         non_fatal (_("unrecognized section flag `%s'"), copy);
+         fatal (_("supported flags: alloc, load, readonly, code, data, rom, contents"));
+       }
+
+      s = snext;
+    }
+  while (s != NULL);
+
+  return ret;
+}
+
+/* Find and optionally add an entry in the change_sections list.  */
+
+static struct section_list *
+find_section_list (name, add)
+     const char *name;
+     boolean add;
 {
-    fprintf(stream, "\
-Usage: %s [-vVsSgxX] [-I format] [-O format] [-F format]\n\
-       [--format=format] [--target=format] [--input-format=format]\n\
-       [--output-format=format] [--strip-all] [--strip-debug] [--discard-all]\n\
-       [--discard-locals] [--verbose] [--version] [--help] file...\n",
-           program_name);
-    exit(status);
+  register struct section_list *p;
+
+  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->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;
 }
 
+/* Add a symbol to strip_specific_list.  */
 
-/* Create a temp file in the same directory as supplied */
-static
-char *
-make_tempname(filename)
-char *filename;
+static void 
+add_specific_symbol (name, list)
+     const char *name;
+     struct symlist **list;
 {
-    static char template[] = "stXXXXXX";
-    char *tmpname;
-    char *      slash = strrchr( filename, '/' );
-    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;
+  struct symlist *tmp_list;
+
+  tmp_list = (struct symlist *) xmalloc (sizeof (struct symlist));
+  tmp_list->name = name;
+  tmp_list->next = *list;
+  *list = tmp_list;
 }
 
-/*
-   All the symbols have been read in and point to their owning input section.
-   They have been relocated to that they are all relative to the base of
-   their owning section. On the way out, all the symbols will be relocated to
-   their new location in the output file, through some complex sums.
+/* See whether a symbol should be stripped or kept based on
+   strip_specific_list and keep_symbols.  */
 
-*/
-static void
-mangle_sections(ibfd, obfd)
-    bfd            *ibfd;
-    bfd            *obfd;
+static boolean
+is_specified_symbol (name, list)
+     const char *name;
+     struct symlist *list;
 {
-    asection       *current = ibfd->sections;
-    for (; current != NULL; current = current->next) {
-       current->output_section = bfd_get_section_by_name(obfd, current->name);
-       current->output_offset = 0;
+  struct symlist *tmp_list;
+
+  for (tmp_list = list; tmp_list; tmp_list = tmp_list->next)
+    {
+      if (strcmp (name, tmp_list->name) == 0)
+       return true;
     }
+  return false;
 }
 
-/* Choose which symbol entries to copy; put the result in osyms.
+/* See if a section is being removed.  */
+
+static boolean
+is_strip_section (abfd, sec)
+     bfd *abfd;
+     asection *sec;
+{
+  struct section_list *p;
+
+  if ((bfd_get_section_flags (abfd, sec) & SEC_DEBUGGING) != 0
+      && (strip_symbols == STRIP_DEBUG
+         || strip_symbols == STRIP_UNNEEDED
+         || strip_symbols == STRIP_ALL
+         || discard_locals == LOCALS_ALL
+         || convert_debugging))
+    return true;
+
+  if (! sections_removed)
+    return false;
+  p = find_section_list (bfd_get_section_name (abfd, sec), false);
+  return p != NULL && p->remove ? true : false;
+}
+
+/* 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 be printed.  */
+   Return the number of symbols to print.  */
+
 static unsigned int
-filter_symbols (abfd, osyms, isyms, symcount)
+filter_symbols (abfd, obfd, osyms, isyms, symcount)
      bfd *abfd;
+     bfd *obfd;
      asymbol **osyms, **isyms;
-     unsigned long symcount;
+     long symcount;
 {
   register asymbol **from = isyms, **to = osyms;
-  unsigned int dst_count = 0;
-  asymbol *sym;
-  char locals_prefix = bfd_get_symbol_leading_char(abfd) == '_' ? 'L' : '.';
-
-  unsigned int src_count = 0;
-  for (; src_count <symcount; src_count++) {
-    int keep = 0;
-
-    flagword flags = (from[src_count])->flags;
-    sym = from[src_count];
-    if ((flags & BSF_GLOBAL) /* Keep if external */
-       || (sym->section == &bfd_und_section)
-       || (bfd_is_com_section (sym->section)))
+  long src_count = 0, dst_count = 0;
+
+  for (; src_count < symcount; src_count++)
+    {
+      asymbol *sym = from[src_count];
+      flagword flags = sym->flags;
+      const char *name = bfd_asymbol_name (sym);
+      int keep;
+
+      if (change_leading_char
+         && (bfd_get_symbol_leading_char (abfd)
+             != bfd_get_symbol_leading_char (obfd))
+         && (bfd_get_symbol_leading_char (abfd) == '\0'
+             || (name[0] == bfd_get_symbol_leading_char (abfd))))
+       {
+         if (bfd_get_symbol_leading_char (obfd) == '\0')
+           name = bfd_asymbol_name (sym) = name + 1;
+         else
+           {
+             char *n;
+
+             n = xmalloc (strlen (name) + 2);
+             n[0] = bfd_get_symbol_leading_char (obfd);
+             if (bfd_get_symbol_leading_char (abfd) == '\0')
+               strcpy (n + 1, name);
+             else
+               strcpy (n + 1, name + 1);
+             name = bfd_asymbol_name (sym) = n;
+           }
+       }
+
+      if (remove_leading_char
+         && ((flags & BSF_GLOBAL) != 0
+             || (flags & BSF_WEAK) != 0
+             || bfd_is_und_section (bfd_get_section (sym))
+             || bfd_is_com_section (bfd_get_section (sym)))
+         && name[0] == bfd_get_symbol_leading_char (abfd))
+       name = bfd_asymbol_name (sym) = name + 1;
+
+      if ((flags & BSF_KEEP) != 0)             /* Used in relocation.  */
+       keep = 1;
+      else if ((flags & BSF_GLOBAL) != 0       /* Global symbol.  */
+              || (flags & BSF_WEAK) != 0
+              || bfd_is_und_section (bfd_get_section (sym))
+              || 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                     /* Local symbol.  */
+       keep = (strip_symbols != STRIP_UNNEEDED
+               && (discard_locals != LOCALS_ALL
+                   && (discard_locals != LOCALS_START_L
+                       || ! bfd_is_local_label (abfd, sym))));
+
+      if (keep && is_specified_symbol (name, strip_specific_list))
+       keep = 0;
+      if (!keep && is_specified_symbol (name, keep_specific_list))
        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 &&
-                     sym->name[0] == locals_prefix);
+      if (keep && is_strip_section (abfd, bfd_get_section (sym)))
+       keep = 0;
 
+      if (keep && (flags & BSF_GLOBAL) != 0
+         && (weaken || is_specified_symbol (name, weaken_specific_list)))
+       {
+         sym->flags &=~ BSF_GLOBAL;
+         sym->flags |= BSF_WEAK;
+       }
+      if (keep && (flags & (BSF_GLOBAL | BSF_WEAK))
+         && is_specified_symbol (name, localize_specific_list))
+       {
+         sym->flags &= ~(BSF_GLOBAL | BSF_WEAK);
+         sym->flags |= BSF_LOCAL;
+       }
 
-    if (keep) {
-      to[dst_count++] = from[src_count];
+      if (keep)
+       to[dst_count++] = sym;
     }
-  }
+
+  to[dst_count] = NULL;
 
   return dst_count;
 }
 
+/* Keep only every `copy_byte'th byte in MEMHUNK, which is *SIZE bytes long.
+   Adjust *SIZE.  */
+
 static void
-copy_object(ibfd, obfd)
-bfd *ibfd;
-bfd *obfd;
+filter_bytes (memhunk, size)
+     char *memhunk;
+     bfd_size_type *size;
 {
+  char *from = memhunk + copy_byte, *to = memhunk, *end = memhunk + *size;
 
-    unsigned int symcount;
+  for (; from < end; from += interleave)
+    *to++ = *from;
+  *size /= interleave;
+}
 
+/* Copy object file IBFD onto OBFD.  */
 
-    if (!bfd_set_format(obfd, bfd_get_format(ibfd)))
-       bfd_fatal(output_filename);
+static void
+copy_object (ibfd, obfd)
+     bfd *ibfd;
+     bfd *obfd;
+{
+  bfd_vma start;
+  long symcount;
+  asection **osections = NULL;
+  bfd_size_type *gaps = NULL;
+  bfd_size_type max_gap = 0;
+
+  if (!bfd_set_format (obfd, bfd_get_format (ibfd)))
+    RETURN_NONFATAL (bfd_get_filename (obfd));
+
+  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));
+
+  if (set_start_set)
+    start = set_start;
+  else
+    start = bfd_get_start_address (ibfd);
+  start += change_start;
+
+  if (!bfd_set_start_address (obfd, start)
+      || !bfd_set_file_flags (obfd,
+                             (bfd_get_file_flags (ibfd)
+                              & bfd_applicable_file_flags (obfd))))
+    RETURN_NONFATAL (bfd_get_filename (ibfd));
+
+  /* Copy architecture of input file to output file */
+  if (!bfd_set_arch_mach (obfd, bfd_get_arch (ibfd),
+                         bfd_get_mach (ibfd)))
+    non_fatal (_("Warning: Output file cannot represent architecture %s"),
+              bfd_printable_arch_mach (bfd_get_arch (ibfd),
+                                       bfd_get_mach (ibfd)));
+  
+  if (!bfd_set_format (obfd, bfd_get_format (ibfd)))
+    RETURN_NONFATAL (bfd_get_filename(ibfd));
 
+  if (isympp)
+    free (isympp);
+  if (osympp != isympp)
+    free (osympp);
 
-    if (verbose)
-       printf("copy from %s(%s) to %s(%s)\n",
-              ibfd->filename, ibfd->xvec->name,
-              obfd->filename, obfd->xvec->name);
+  /* 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);
 
-    if (! bfd_set_start_address (obfd, bfd_get_start_address (ibfd))
-       || ! bfd_set_file_flags (obfd,
-                                (bfd_get_file_flags (ibfd)
-                                 & bfd_applicable_file_flags (obfd))))
-      bfd_fatal (bfd_get_filename (ibfd));
+  if (add_sections != NULL)
+    {
+      struct section_add *padd;
+      struct section_list *pset;
 
-    /* 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)));
+      for (padd = add_sections; padd != NULL; padd = padd->next)
+       {
+         padd->section = bfd_make_section (obfd, padd->name);
+         if (padd->section == NULL)
+           {
+             non_fatal (_("can't create section `%s': %s"),
+                      padd->name, bfd_errmsg (bfd_get_error ()));
+             status = 1;
+             return;
+           }
+         else
+           {
+             flagword flags;
+             
+             if (! bfd_set_section_size (obfd, padd->section, padd->size))
+               RETURN_NONFATAL (bfd_get_filename (obfd));
+
+             pset = find_section_list (padd->name, false);
+             if (pset != NULL)
+               pset->used = true;
+
+             if (pset != NULL && pset->set_flags)
+               flags = pset->flags | SEC_HAS_CONTENTS;
+             else
+               flags = SEC_HAS_CONTENTS | SEC_READONLY | SEC_DATA;
+             
+             if (! bfd_set_section_flags (obfd, padd->section, flags))
+               RETURN_NONFATAL (bfd_get_filename (obfd));
+
+             if (pset != NULL)
+               {
+                 if (pset->change_vma != CHANGE_IGNORE)
+                   if (! bfd_set_section_vma (obfd, padd->section, pset->vma_val))
+                     RETURN_NONFATAL (bfd_get_filename (obfd));
+                 
+                 if (pset->change_lma != CHANGE_IGNORE)
+                   {
+                     padd->section->lma = pset->lma_val;
+                     
+                     if (! bfd_set_section_alignment
+                         (obfd, padd->section,
+                          bfd_section_alignment (obfd, padd->section)))
+                       RETURN_NONFATAL (bfd_get_filename (obfd));
+                   }
+               }
+           }
+       }
     }
-    if (!bfd_set_format(obfd, bfd_get_format(ibfd)))
+
+  if (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, (void *) &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)
        {
-           bfd_fatal(ibfd->filename);
+         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)))
+                   {
+                     non_fatal (_("Can't fill gap after %s: %s"),
+                              bfd_get_section_name (obfd, osections[i]),
+                              bfd_errmsg (bfd_get_error()));
+                     status = 1;
+                     break;
+                   }
+                 gaps[i] = gap_stop - gap_start;
+                 if (max_gap < gap_stop - gap_start)
+                   max_gap = gap_stop - gap_start;
+               }
+           }
        }
 
-    if (isympp)
-      free (isympp);
-    if (osympp != isympp)
-      free (osympp);
-
-    if (strip_symbols == strip_all && discard_locals == locals_undef)
-      {
-       osympp = isympp = NULL;
-       symcount = 0;
-      }
-    else
-      {
-       osympp = isympp = (asymbol **) xmalloc(get_symtab_upper_bound(ibfd));
-       symcount = bfd_canonicalize_symtab(ibfd, isympp);
-
-       if (strip_symbols == strip_debug || discard_locals != locals_undef)
-         {
-           osympp = (asymbol **) xmalloc(symcount * sizeof(asymbol*));
-           symcount = filter_symbols (ibfd, osympp, isympp, symcount);
-         }
-      }
-
-    bfd_set_symtab(obfd, osympp, symcount);
-    
-    /*
-      bfd mandates that all output sections be created and sizes set before
-      any output is done.  Thus, we traverse all sections twice.
-      */
-    bfd_map_over_sections(ibfd, setup_sections, (void *) obfd);
-    bfd_map_over_sections(ibfd, copy_sections, (void *) obfd);
-    mangle_sections(ibfd, obfd);
-}
-static
-char *
-cat(a,b,c)
-char *a;
-char *b;
-char *c;
-{
-    int size = strlen(a) + strlen(b) + strlen(c);
-    char *r = xmalloc(size+1);
-    strcpy(r,a);
-    strcat(r,b);
-    strcat(r,c);
-    return r;
-}
+      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))
+               {
+                 non_fatal (_("Can't add padding to %s: %s"),
+                          bfd_get_section_name (obfd, osections[c - 1]),
+                          bfd_errmsg (bfd_get_error ()));
+                 status = 1;
+               }
+             else
+               {
+                 gaps[c - 1] = pad_to - (lma + size);
+                 if (max_gap < pad_to - (lma + size))
+                   max_gap = pad_to - (lma + size);
+               }
+           }
+       }
+    }
 
-static void 
-copy_archive(ibfd, obfd)
-bfd *ibfd;
-bfd *obfd;
-{
-    bfd **ptr = &obfd->archive_head;
-    bfd *this_element;
-    /* Read each archive element in turn from the input, copy the
-       contents to a temp file, and keep the temp file handle */
-    char *dir = cat("./#",make_tempname(""),"cd");
-
-    /* Make a temp directory to hold the contents */
-    mkdir(dir,0777);
-    obfd->has_armap = ibfd->has_armap;
-    this_element = bfd_openr_next_archived_file(ibfd, NULL);
-    ibfd->archive_head = this_element;
-    while (this_element != (bfd *)NULL) {
-
-       /* Create an output file for this member */
-       char *output_name = cat(dir, "/",this_element->filename);
-       bfd *output_bfd = bfd_openw(output_name, output_target);
-
-       if (!bfd_set_format(obfd, bfd_get_format(ibfd)))
-           bfd_fatal(output_filename);
-
-       if (output_bfd == (bfd *)NULL) {
-           bfd_fatal(output_name);
+  /* Symbol filtering must happen after the output sections have
+     been created, but before their contents are set.  */
+  if (strip_symbols == STRIP_ALL)
+    {
+      osympp = isympp = NULL;
+      symcount = 0;
+    }
+  else
+    {
+      long symsize;
+      PTR dhandle = NULL;
+
+      symsize = bfd_get_symtab_upper_bound (ibfd);
+      if (symsize < 0)
+       RETURN_NONFATAL (bfd_get_filename (ibfd));
+
+      osympp = isympp = (asymbol **) xmalloc (symsize);
+      symcount = bfd_canonicalize_symtab (ibfd, isympp);
+      if (symcount < 0)
+       RETURN_NONFATAL (bfd_get_filename (ibfd));
+
+      if (convert_debugging)
+       dhandle = read_debugging_info (ibfd, isympp, symcount);
+
+      if (strip_symbols == STRIP_DEBUG 
+         || strip_symbols == STRIP_UNNEEDED
+         || discard_locals != LOCALS_UNDEF
+         || strip_specific_list != NULL
+         || keep_specific_list != NULL
+         || localize_specific_list != NULL
+         || weaken_specific_list != NULL
+         || sections_removed
+         || convert_debugging
+         || change_leading_char
+         || remove_leading_char
+         || 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.  */
+         bfd_map_over_sections (ibfd,
+                                mark_symbols_used_in_relocations,
+                                (PTR)isympp);
+         osympp = (asymbol **) xmalloc ((symcount + 1) * sizeof (asymbol *));
+         symcount = filter_symbols (ibfd, obfd, osympp, isympp, symcount);
        }
-       if (bfd_check_format(this_element, bfd_object) == true) {
-           copy_object(this_element, output_bfd);
+
+      if (convert_debugging && dhandle != NULL)
+       {
+         if (! write_debugging_info (obfd, dhandle, &symcount, &osympp))
+           {
+             status = 1;
+             return;
+           }
        }
+    }
 
-       bfd_close(output_bfd);
-       /* Now open the newly output file and attatch to our list */
-       output_bfd = bfd_openr(output_name, output_target);
-       /* Mark it for deletion */
+  bfd_set_symtab (obfd, osympp, symcount);
 
-       *ptr = output_bfd;
+  /* This has to happen after the symbol table has been set.  */
+  bfd_map_over_sections (ibfd, copy_section, (void *) obfd);
 
-       ptr = &output_bfd->next;
-       this_element->next = bfd_openr_next_archived_file(ibfd, this_element);
-       this_element = this_element->next;
+  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,
+                                         (PTR) padd->contents,
+                                         (file_ptr) 0,
+                                         (bfd_size_type) padd->size))
+           RETURN_NONFATAL (bfd_get_filename (obfd));
+       }
     }
-    *ptr = (bfd *)NULL;
 
-    if (!bfd_close(obfd))
-       bfd_fatal(output_filename);
+  if (gap_fill_set || pad_to_set)
+    {
+      bfd_byte *buf;
+      int c, i;
+
+      /* Fill in the gaps.  */
 
-    /* Now delete all the files that we opened.
-       Construct their names again, unfortunately, but so what;
-       we're about to exit anyway. */
-    for (this_element = ibfd->archive_head;
-        this_element != (bfd *)NULL;
-        this_element = this_element->next) 
+      if (max_gap > 8192)
+       max_gap = 8192;
+      buf = (bfd_byte *) xmalloc (max_gap);
+      memset (buf, gap_fill, (size_t) max_gap);
+
+      c = bfd_count_sections (obfd);
+      for (i = 0; i < c; i++)
        {
-       unlink(cat(dir,"/",this_element->filename));
+         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))
+                   RETURN_NONFATAL (bfd_get_filename (obfd));
+
+                 left -= now;
+                 off += now;
+               }
+           }
+       }
     }
-    rmdir(dir);
-    if (!bfd_close(ibfd))
-       bfd_fatal(input_filename);
 
+  /* 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))
+    {
+      non_fatal (_("%s: error copying private BFD data: %s"),
+                bfd_get_filename (obfd),
+                bfd_errmsg (bfd_get_error ()));
+      status = 1;
+      return;
+    }
 }
 
-static
-void
-copy_file(input_filename, output_filename)
-    char           *input_filename;
-    char           *output_filename;
+/* Read each archive element in turn from IBFD, copy the
+   contents to temp file, and keep the temp file handle.  */
+
+static void
+copy_archive (ibfd, obfd, output_target)
+     bfd *ibfd;
+     bfd *obfd;
+     const char *output_target;
 {
-  bfd            *ibfd;
+  struct name_list
+    {
+      struct name_list *next;
+      char *name;
+      bfd *obfd;
+    } *list, *l;
+  bfd **ptr = &obfd->archive_head;
+  bfd *this_element;
+  char *dir = make_tempname (bfd_get_filename (obfd));
+
+  /* Make a temp directory to hold the contents.  */
+#if defined (_WIN32) && !defined (__CYGWIN32__)
+  if (mkdir (dir) != 0)
+#else
+  if (mkdir (dir, 0700) != 0)
+#endif
+    {
+      fatal (_("cannot mkdir %s for archive copying (error: %s)"),
+            dir, strerror (errno));
+    }
+  obfd->has_armap = ibfd->has_armap;
+
+  list = NULL;
+
+  this_element = bfd_openr_next_archived_file (ibfd, NULL);
+  while (!status && this_element != (bfd *) NULL)
+    {
+      /* Create an output file for this member.  */
+      char *output_name = concat (dir, "/", bfd_get_filename(this_element),
+                                 (char *) NULL);
+      bfd *output_bfd = bfd_openw (output_name, output_target);
+      bfd *last_element;
+
+      l = (struct name_list *) xmalloc (sizeof (struct name_list));
+      l->name = output_name;
+      l->next = list;
+      list = l;
+
+      if (output_bfd == (bfd *) NULL)
+       RETURN_NONFATAL (output_name);
+
+      if (!bfd_set_format (obfd, bfd_get_format (ibfd)))
+       RETURN_NONFATAL (bfd_get_filename (obfd));
+
+      if (bfd_check_format (this_element, bfd_object) == true)
+       copy_object (this_element, output_bfd);
+
+      if (!bfd_close (output_bfd))
+       {
+         bfd_nonfatal (bfd_get_filename (output_bfd));
+         /* Error in new object file. Don't change archive. */
+         status = 1;
+       }
+
+      /* 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->next;
+
+      last_element = this_element;
+
+      this_element = bfd_openr_next_archived_file (ibfd, last_element);
+
+      bfd_close (last_element);
+    }
+  *ptr = (bfd *) NULL;
+
+  if (!bfd_close (obfd))
+    RETURN_NONFATAL (bfd_get_filename (obfd));
+
+  if (!bfd_close (ibfd))
+    RETURN_NONFATAL (bfd_get_filename (ibfd));
+
+  /* Delete all the files that we opened.  */
+  for (l = list; l != NULL; l = l->next)
+    {
+      bfd_close (l->obfd);
+      unlink (l->name);
+    }
+  rmdir (dir);
+}
+
+/* The top-level control.  */
+
+static void
+copy_file (input_filename, output_filename, input_target, output_target)
+     const char *input_filename;
+     const char *output_filename;
+     const char *input_target;
+     const char *output_target;
+{
+  bfd *ibfd;
+  char **matching;
 
   /* To allow us to do "strip *" without dying on the first
      non-object file, failures are nonfatal.  */
 
-  ibfd = bfd_openr(input_filename, input_target);
+  ibfd = bfd_openr (input_filename, input_target);
   if (ibfd == NULL)
+    RETURN_NONFATAL (input_filename);
+
+  if (bfd_check_format (ibfd, bfd_archive))
     {
-      bfd_perror(input_filename);
-      return;
+      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);
+
+      obfd = bfd_openw (output_filename, output_target);
+      if (obfd == NULL)
+       RETURN_NONFATAL (output_filename);
+
+      copy_archive (ibfd, obfd, output_target);
     }
+  else if (bfd_check_format_matches (ibfd, bfd_object, &matching))
+    {
+      bfd *obfd;
 
-  if (bfd_check_format(ibfd, bfd_object)) {
-    bfd * obfd = bfd_openw(output_filename, output_target);
-    if (obfd == NULL)
-      {
-       bfd_perror(output_filename);
-       return;
-      }
+      /* bfd_get_target does not return the correct value until
+         bfd_check_format succeeds.  */
+      if (output_target == NULL)
+       output_target = bfd_get_target (ibfd);
 
-    copy_object(ibfd, obfd);
+      obfd = bfd_openw (output_filename, output_target);
+      if (obfd == NULL)
+       RETURN_NONFATAL (output_filename);
 
-    if (!bfd_close(obfd))
-      {
-       bfd_perror(output_filename);
-       return;
-      }
+      copy_object (ibfd, obfd);
 
-    if (!bfd_close(ibfd))
-      {
-       bfd_perror(input_filename);
-       return;
-      }
-  }
-  else if (bfd_check_format(ibfd, bfd_archive)) {
-    bfd * obfd = bfd_openw(output_filename, output_target);
-    if (obfd == NULL)
-      {
-       bfd_perror(output_filename);
-       return;
-      }
-    copy_archive(ibfd, obfd);
-  }
-  else {
-    /* Get the right error message.  */
-    (void) bfd_check_format (ibfd, bfd_object);
-    bfd_perror (input_filename);
-  }
-}
+      if (!bfd_close (obfd))
+       RETURN_NONFATAL (output_filename);
 
+      if (!bfd_close (ibfd))
+       RETURN_NONFATAL (input_filename);
+    }
+  else
+    {
+      bfd_nonfatal (input_filename);
+      
+      if (bfd_get_error () == bfd_error_file_ambiguously_recognized)
+       {
+         list_matching_formats (matching);
+         free (matching);
+       }
+      
+      status = 1;
+    }
+}
 
+/* Create a section in OBFD with the same name and attributes
+   as ISECTION in IBFD.  */
 
-/** Actually do the work */
 static void
-setup_sections(ibfd, isection, obfd)
-    bfd            *ibfd;
-    sec_ptr         isection;
-    bfd            *obfd;
+setup_section (ibfd, isection, obfdarg)
+     bfd *ibfd;
+     sec_ptr isection;
+     PTR obfdarg;
 {
-    sec_ptr         osection;
-    char           *err;
-
-    osection = bfd_get_section_by_name(obfd, bfd_section_name(ibfd, isection));
-    if (osection == NULL) {
-       osection = bfd_make_section(obfd, bfd_section_name(ibfd, isection));
-       if (osection == NULL) {
-           err = "making";
-           goto loser;
-       }
+  bfd *obfd = (bfd *) obfdarg;
+  struct section_list *p;
+  sec_ptr osection;
+  bfd_size_type size;
+  bfd_vma vma;
+  bfd_vma lma;
+  flagword flags;
+  char *err;
+
+  if ((bfd_get_section_flags (ibfd, isection) & SEC_DEBUGGING) != 0
+      && (strip_symbols == STRIP_DEBUG
+         || strip_symbols == STRIP_UNNEEDED
+         || strip_symbols == STRIP_ALL
+         || discard_locals == LOCALS_ALL
+         || convert_debugging))
+    return;
+
+  p = find_section_list (bfd_section_name (ibfd, isection), false);
+  if (p != NULL)
+    p->used = true;
+
+  if (p != NULL && p->remove)
+    return;
+
+  osection = bfd_make_section_anyway (obfd, bfd_section_name (ibfd, isection));
+  if (osection == NULL)
+    {
+      err = "making";
+      goto loser;
     }
 
-    if (!bfd_set_section_size(obfd,
-                             osection,
-                             bfd_section_size(ibfd, isection))) {
-       err = "size";
-       goto loser;
+  size = bfd_section_size (ibfd, isection);
+  if (copy_byte >= 0)
+    size = (size + interleave - 1) / interleave;
+  if (! bfd_set_section_size (obfd, osection, size))
+    {
+      err = "size";
+      goto loser;
     }
 
-    if (bfd_set_section_vma(obfd,
-                           osection,
-                           bfd_section_vma(ibfd, isection))
-       == false) {
-       err = "vma";
-       goto loser;
-    }                          /* on error */
+  vma = bfd_section_vma (ibfd, isection);
+  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";
+      goto loser;
+    }
 
-    if (bfd_set_section_alignment(obfd,
-                                 osection,
-                                 bfd_section_alignment(ibfd, isection))
-       == false) {
-       err = "alignment";
-       goto loser;
-    }                          /* on error */
+  lma = isection->lma;
+  if ((p != NULL) && p->change_lma != CHANGE_IGNORE)
+    {
+      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;
+
+  /* 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))
+      == false)
+    {
+      err = "alignment";
+      goto loser;
+    }
 
-    if (!bfd_set_section_flags(obfd, osection,
-                              bfd_get_section_flags(ibfd, isection))) {
-       err = "flags";
-       goto loser;
+  flags = bfd_get_section_flags (ibfd, isection);
+  if (p != NULL && p->set_flags)
+    flags = p->flags | (flags & SEC_HAS_CONTENTS);
+  if (!bfd_set_section_flags (obfd, osection, flags))
+    {
+      err = "flags";
+      goto loser;
     }
 
-    /* All went well */
-    return;
+  /* 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;
+
+  /* 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";
+      goto loser;
+    }
+
+  /* All went well */
+  return;
 
 loser:
-    fprintf(stderr, "%s: file \"%s\", section \"%s\": error in %s: %s\n",
-           program_name,
-           bfd_get_filename(ibfd), bfd_section_name(ibfd, isection),
-           err, bfd_errmsg(bfd_error));
-    exit(1);
-}                              /* setup_sections() */
-
-/*
-Copy all the section related data from an input section
-to an output section
-
-If stripping then don't copy any relocation info
-*/
+  non_fatal (_("%s: section `%s': error in %s: %s"),
+            bfd_get_filename (ibfd),
+            bfd_section_name (ibfd, isection),
+            err, bfd_errmsg (bfd_get_error ()));
+  status = 1;
+}
+
+/* Copy the data of input section ISECTION of IBFD
+   to an output section with the same name in OBFD.
+   If stripping then don't copy any relocation info.  */
+
 static void
-copy_sections(ibfd, isection, obfd)
-    bfd            *ibfd;
-    sec_ptr         isection;
-    bfd            *obfd;
+copy_section (ibfd, isection, obfdarg)
+     bfd *ibfd;
+     sec_ptr isection;
+     PTR obfdarg;
 {
+  bfd *obfd = (bfd *) obfdarg;
+  struct section_list *p;
+  arelent **relpp;
+  long relcount;
+  sec_ptr osection;
+  bfd_size_type size;
+
+  /* If we have already failed earlier on, do not keep on generating
+     complaints now.  */
+  if (status != 0)
+    return;
+  
+  if ((bfd_get_section_flags (ibfd, isection) & SEC_DEBUGGING) != 0
+      && (strip_symbols == STRIP_DEBUG
+         || strip_symbols == STRIP_UNNEEDED
+         || strip_symbols == STRIP_ALL
+         || discard_locals == LOCALS_ALL
+         || convert_debugging))
+    {
+      return;
+    }
 
-  arelent       **relpp;
-  int             relcount;
-  sec_ptr         osection;
-  bfd_size_type   size;
-  osection = bfd_get_section_by_name(obfd,
-                                    bfd_section_name(ibfd, isection));
+  p = find_section_list (bfd_section_name (ibfd, isection), false);
 
-  size = bfd_get_section_size_before_reloc(isection);
+  if (p != NULL && p->remove)
+    return;
+
+  osection = isection->output_section;
+  size = bfd_get_section_size_before_reloc (isection);
 
-  if (size == 0)
+  if (size == 0 || osection == 0)
     return;
 
-  if (strip_symbols == strip_all
-      || bfd_get_reloc_upper_bound(ibfd, isection) == 0) 
-    {
-      bfd_set_reloc(obfd, osection, (arelent **)NULL, 0);
-    } 
-  else 
+  if (strip_symbols == STRIP_ALL)
+    bfd_set_reloc (obfd, osection, (arelent **) NULL, 0);
+  else
     {
-      relpp = (arelent **) xmalloc(bfd_get_reloc_upper_bound(ibfd, isection));
-      relcount = bfd_canonicalize_reloc(ibfd, isection, relpp, isympp);
-      bfd_set_reloc(obfd, osection, relpp, relcount);
+      long relsize;
+
+      relsize = bfd_get_reloc_upper_bound (ibfd, isection);
+      if (relsize < 0)
+       RETURN_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)
+           RETURN_NONFATAL (bfd_get_filename (ibfd));
+
+         bfd_set_reloc (obfd, osection, relpp, relcount);
+       }
     }
 
   isection->_cooked_size = isection->_raw_size;
-  isection->reloc_done =true;
-  
+  isection->reloc_done = true;
 
-  if (bfd_get_section_flags(ibfd, isection) & SEC_HAS_CONTENTS) 
+  if (bfd_get_section_flags (ibfd, isection) & SEC_HAS_CONTENTS)
     {
-      PTR memhunk = (PTR) xmalloc((unsigned)size);
+      PTR memhunk = (PTR) xmalloc ((unsigned) size);
 
-      if (!bfd_get_section_contents(ibfd, isection, memhunk, (file_ptr) 0, size))
-       bfd_fatal(bfd_get_filename(ibfd));
+      if (!bfd_get_section_contents (ibfd, isection, memhunk, (file_ptr) 0,
+                                    size))
+       RETURN_NONFATAL (bfd_get_filename (ibfd));
 
-      if (!bfd_set_section_contents(obfd, osection, memhunk, (file_ptr)0, size))
-       bfd_fatal(bfd_get_filename(obfd));
-      free(memhunk);
+      if (copy_byte >= 0) 
+       filter_bytes (memhunk, &size);
+
+      if (!bfd_set_section_contents (obfd, osection, memhunk, (file_ptr) 0,
+                                    size))
+       RETURN_NONFATAL (bfd_get_filename (obfd));
+
+      free (memhunk);
+    }
+  else if (p != NULL && p->set_flags && (p->flags & SEC_HAS_CONTENTS) != 0)
+    {
+      PTR memhunk = (PTR) xmalloc ((unsigned) 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.  */
+
+      memset (memhunk, 0, size);
+      if (! bfd_set_section_contents (obfd, osection, memhunk, (file_ptr) 0,
+                                     size))
+       RETURN_NONFATAL (bfd_get_filename (obfd));
+      free (memhunk);
     }
+}
+
+/* Get all the sections.  This is used when --gap-fill or --pad-to is
+   used.  */
 
+static void
+get_sections (obfd, osection, secppparg)
+     bfd *obfd;
+     asection *osection;
+     PTR secppparg;
+{
+  asection ***secppp = (asection ***) secppparg;
 
+  **secppp = osection;
+  ++(*secppp);
 }
-int
-main(argc, argv)
-    int             argc;
-    char           *argv[];
+
+/* 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 (arg1, arg2)
+     const PTR arg1;
+     const PTR arg2;
 {
-  int             i;
-  int c;                       /* sez which option char */
+  const asection **sec1 = (const asection **) arg1;
+  const asection **sec2 = (const asection **) 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;
+    }
 
-  program_name = argv[0];
+  /* Sort sections by LMA.  */
+  if ((*sec1)->lma > (*sec2)->lma)
+    return 1;
+  else if ((*sec1)->lma < (*sec2)->lma)
+    return -1;
 
-  strip_symbols = strip_undef; /* default is to strip everything.  */
-  discard_locals = locals_undef;
+  /* Sort sections with the same LMA by size.  */
+  if ((*sec1)->_raw_size > (*sec2)->_raw_size)
+    return 1;
+  else if ((*sec1)->_raw_size < (*sec2)->_raw_size)
+    return -1;
 
-  bfd_init();
+  return 0;
+}
 
-  if (is_strip < 0) {
-      i = strlen (program_name);
-      is_strip = (i >= 5 && strcmp(program_name+i-5,"strip"));
-  }
+/* Mark all the symbols which will be used in output relocations with
+   the BSF_KEEP flag so that those symbols will not be stripped.
 
-  if (is_strip) {
-    
-      while ((c = getopt_long(argc, argv, "I:O:F:sSgxXVv",
-                             strip_options, (int *) 0))
-            != EOF) {
-         switch (c) {
-           case 'I':
-             input_target = optarg;
-           case 'O':
-             output_target = optarg;
-             break;
-           case 'F':
-             input_target = output_target = optarg;
-             break;
-
-           case 's':
-             strip_symbols = strip_all;
-             break;
-           case 'S':
-           case 'g':
-             strip_symbols = strip_debug;
-             break;
-           case 'x':
-             discard_locals = locals_all;
-             break;
-           case 'X':
-             discard_locals = locals_start_L;
-             break;
-           case 'v':
-             verbose = true;
-             break;
-           case 'V':
-             show_version = true;
-             break;
-           case  0:
-             break;            /* we've been given a long option */
-           case 'h':
-             strip_usage (stdout, 0);
-           default:
-             strip_usage (stderr, 1);
-         }
-      }
+   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;
+{
+  asymbol **symbols = (asymbol **) symbolsarg;
+  long relsize;
+  arelent **relpp;
+  long relcount, i;
+
+  /* Ignore an input section with no corresponding output section.  */
+  if (isection->output_section == NULL)
+    return;
+
+  relsize = bfd_get_reloc_upper_bound (ibfd, isection);
+  if (relsize < 0)
+    bfd_fatal (bfd_get_filename (ibfd));
+
+  if (relsize == 0)
+    return;
+
+  relpp = (arelent **) xmalloc (relsize);
+  relcount = bfd_canonicalize_reloc (ibfd, isection, relpp, symbols);
+  if (relcount < 0)
+    bfd_fatal (bfd_get_filename (ibfd));
+
+  /* Examine each symbol used in a relocation.  If it's not one of the
+     special bfd section symbols, then mark it with BSF_KEEP.  */
+  for (i = 0; i < relcount; i++)
+    {
+      if (*relpp[i]->sym_ptr_ptr != bfd_com_section_ptr->symbol
+         && *relpp[i]->sym_ptr_ptr != bfd_abs_section_ptr->symbol
+         && *relpp[i]->sym_ptr_ptr != bfd_und_section_ptr->symbol)
+       (*relpp[i]->sym_ptr_ptr)->flags |= BSF_KEEP;
+    }
+
+  if (relpp != NULL)
+    free (relpp);
+}
+
+/* Write out debugging information.  */
+
+static boolean
+write_debugging_info (obfd, dhandle, symcountp, symppp)
+     bfd *obfd;
+     PTR dhandle;
+     long *symcountp;
+     asymbol ***symppp;
+{
+  if (bfd_get_flavour (obfd) == bfd_target_ieee_flavour)
+    return write_ieee_debugging_info (obfd, dhandle);
+
+  if (bfd_get_flavour (obfd) == bfd_target_coff_flavour
+      || bfd_get_flavour (obfd) == bfd_target_elf_flavour)
+    {
+      bfd_byte *syms, *strings;
+      bfd_size_type symsize, stringsize;
+      asection *stabsec, *stabstrsec;
+
+      if (! write_stabs_in_sections_debugging_info (obfd, dhandle, &syms,
+                                                   &symsize, &strings,
+                                                   &stringsize))
+       return false;
+
+      stabsec = bfd_make_section (obfd, ".stab");
+      stabstrsec = bfd_make_section (obfd, ".stabstr");
+      if (stabsec == NULL
+         || stabstrsec == NULL
+         || ! bfd_set_section_size (obfd, stabsec, symsize)
+         || ! bfd_set_section_size (obfd, stabstrsec, stringsize)
+         || ! bfd_set_section_alignment (obfd, stabsec, 2)
+         || ! bfd_set_section_alignment (obfd, stabstrsec, 0)
+         || ! bfd_set_section_flags (obfd, stabsec,
+                                  (SEC_HAS_CONTENTS
+                                   | SEC_READONLY
+                                   | SEC_DEBUGGING))
+         || ! bfd_set_section_flags (obfd, stabstrsec,
+                                     (SEC_HAS_CONTENTS
+                                      | SEC_READONLY
+                                      | SEC_DEBUGGING)))
+       {
+         non_fatal (_("%s: can't create debugging section: %s"),
+                    bfd_get_filename (obfd),
+                    bfd_errmsg (bfd_get_error ()));
+         return false;
+       }
+
+      /* 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, (file_ptr) 0,
+                                     symsize)
+         || ! bfd_set_section_contents (obfd, stabstrsec, strings,
+                                        (file_ptr) 0, stringsize))
+       {
+         non_fatal (_("%s: can't set debugging section contents: %s"),
+                    bfd_get_filename (obfd),
+                    bfd_errmsg (bfd_get_error ()));
+         return false;
+       }
+
+      return true;
+    }
+
+  non_fatal (_("%s: don't know how to write debugging information for %s"),
+            bfd_get_filename (obfd), bfd_get_target (obfd));
+  return false;
+}
+
+/* The number of bytes to copy at once.  */
+#define COPY_BUF 8192
+
+/* Copy file FROM to file TO, performing no translations.
+   Return 0 if ok, -1 if error.  */
+
+static int
+simple_copy (from, to)
+     const char *from;
+     const char *to;
+{
+  int fromfd, tofd, nread;
+  int saved;
+  char buf[COPY_BUF];
+
+  fromfd = open (from, O_RDONLY);
+  if (fromfd < 0)
+    return -1;
+  tofd = creat (to, 0777);
+  if (tofd < 0)
+    {
+      saved = errno;
+      close (fromfd);
+      errno = saved;
+      return -1;
+    }
+  while ((nread = read (fromfd, buf, sizeof buf)) > 0)
+    {
+      if (write (tofd, buf, nread) != nread)
+       {
+         saved = errno;
+         close (fromfd);
+         close (tofd);
+         errno = saved;
+         return -1;
+       }
+    }
+  saved = errno;
+  close (fromfd);
+  close (tofd);
+  if (nread < 0)
+    {
+      errno = saved;
+      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)
+     const char *from;
+     const char *to;
+{
+  struct stat s;
+  int ret = 0;
+
+  if (lstat (to, &s))
+    return -1;
+
+#if defined (_WIN32) && !defined (__CYGWIN32__)
+  /* Win32, unlike unix, will not erase `to' in `rename(from, to)' but
+     fail instead.  Also, chown is not present.  */
+
+  if (stat (to, &s) == 0)
+    remove (to);
+
+  ret = rename (from, to);
+  if (ret != 0)
+    {
+      /* We have to clean up here. */
       
-      i = optind;
+      non_fatal (_("%s: rename: %s"), to, strerror (errno));
+      unlink (from);
+    }
+#else
+  /* 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)
+       {
+         /* Try to preserve the permission bits and ownership of TO.
+             First get the mode right except for the setuid bit.  Then
+             change the ownership.  Then fix the setuid bit.  We do
+             the chmod before the chown because if the chown succeeds,
+             and we are a normal user, we won't be able to do the
+             chmod afterward.  We don't bother to fix the setuid bit
+             first because that might introduce a fleeting security
+             problem, and because the chown will clear the setuid bit
+             anyhow.  We only fix the setuid bit if the chown
+             succeeds, because we don't want to introduce an
+             unexpected setuid file owned by the user running objcopy.  */
+         chmod (to, s.st_mode & 0777);
+         if (chown (to, s.st_uid, s.st_gid) >= 0)
+           chmod (to, s.st_mode & 07777);
+       }
+      else
+       {
+         /* We have to clean up here. */
+         non_fatal (_("%s: rename: %s"), to, strerror (errno));
+         unlink (from);
+       }
+    }
+  else
+    {
+      ret = simple_copy (from, to);
+      if (ret != 0)
+       non_fatal (_("%s: simple_copy: %s"), to, strerror (errno));
+
+      if (preserve_dates)
+       set_times (to, &s);
+      unlink (from);
+    }
+#endif /* _WIN32 && !__CYGWIN32__ */
+
+  return ret;
+}
+
+/* Set the times of the file DESTINATION to be the same as those in
+   STATBUF.  */
+
+static void
+set_times (destination, statbuf)
+     const char *destination;
+     const struct stat *statbuf;
+{
+  int result;
+
+  {
+#ifdef HAVE_GOOD_UTIME_H
+    struct utimbuf tb;
+
+    tb.actime = statbuf->st_atime;
+    tb.modtime = statbuf->st_mtime;
+    result = utime (destination, &tb);
+#else /* ! HAVE_GOOD_UTIME_H */
+#ifndef HAVE_UTIMES
+    long tb[2];
+
+    tb[0] = statbuf->st_atime;
+    tb[1] = statbuf->st_mtime;
+    result = utime (destination, tb);
+#else /* HAVE_UTIMES */
+    struct timeval tv[2];
+
+    tv[0].tv_sec = statbuf->st_atime;
+    tv[0].tv_usec = 0;
+    tv[1].tv_sec = statbuf->st_mtime;
+    tv[1].tv_usec = 0;
+    result = utimes (destination, tv);
+#endif /* HAVE_UTIMES */
+#endif /* ! HAVE_GOOD_UTIME_H */
+  }
+
+  if (result != 0)
+    non_fatal (_("%s: cannot set time: %s"), destination, strerror (errno));
+}
+
+static int
+strip_main (argc, argv)
+     int argc;
+     char *argv[];
+{
+  char *input_target = NULL, *output_target = NULL;
+  boolean show_version = false;
+  int c, i;
+  struct section_list *p;
+  char *output_file = NULL;
+
+  while ((c = getopt_long (argc, argv, "I:O:F:K:N:R:o:sSpgxXVv",
+                          strip_options, (int *) 0)) != EOF)
+    {
+      switch (c)
+       {
+       case 'I':
+         input_target = optarg;
+         break;
+       case 'O':
+         output_target = optarg;
+         break;
+       case 'F':
+         input_target = output_target = optarg;
+         break;
+       case 'R':
+         p = find_section_list (optarg, true);
+         p->remove = true;
+         sections_removed = true;
+         break;
+       case 's':
+         strip_symbols = STRIP_ALL;
+         break;
+       case 'S':
+       case 'g':
+         strip_symbols = STRIP_DEBUG;
+         break;
+       case OPTION_STRIP_UNNEEDED:
+         strip_symbols = STRIP_UNNEEDED;
+         break;
+       case 'K':
+         add_specific_symbol (optarg, &keep_specific_list);
+         break;
+       case 'N':
+         add_specific_symbol (optarg, &strip_specific_list);
+         break;
+       case 'o':
+         output_file = optarg;
+         break;
+       case 'p':
+         preserve_dates = true;
+         break;
+       case 'x':
+         discard_locals = LOCALS_ALL;
+         break;
+       case 'X':
+         discard_locals = LOCALS_START_L;
+         break;
+       case 'v':
+         verbose = true;
+         break;
+       case 'V':
+         show_version = true;
+         break;
+       case 0:
+         break;                /* we've been given a long option */
+       case 'h':
+         strip_usage (stdout, 0);
+       default:
+         strip_usage (stderr, 1);
+       }
+    }
+
+  if (show_version)
+    print_version ("strip");
+
+  /* Default is to strip all symbols.  */
+  if (strip_symbols == STRIP_UNDEF
+      && discard_locals == LOCALS_UNDEF
+      && strip_specific_list == NULL
+      && keep_specific_list == NULL)
+    strip_symbols = STRIP_ALL;
+
+  if (output_target == (char *) NULL)
+    output_target = input_target;
 
-      /* Default is to strip all symbols.  */
-      if (strip_symbols == strip_undef && discard_locals == locals_undef)
-         strip_symbols = strip_all;
+  i = optind;
+  if (i == argc
+      || (output_file != NULL && (i + 1) < argc))
+    strip_usage (stderr, 1);
 
-      if (output_target == (char *) NULL)
-         output_target = input_target;
+  for (; i < argc; i++)
+    {
+      int hold_status = status;
+      struct stat statbuf;
+      char *tmpname;
 
-      if (show_version) {
-         printf ("GNU %s version %s\n", program_name, program_version);
-         exit (0);
+      if (preserve_dates)
+       {
+         if (stat (argv[i], &statbuf) < 0)
+           {
+             non_fatal (_("%s: cannot stat: %s"), argv[i], strerror (errno));
+             continue;
+           }
        }
-      else if (i == argc)
-         strip_usage(stderr, 1);
-      for ( ; i < argc; i++) {
-           char *tmpname = make_tempname(argv[i]);
-           copy_file(argv[i], tmpname);
-           rename(tmpname, argv[i]);
-      }
-      return 0;
-    }
-
-  /* Invoked as "objcopy", not "strip" */
-
-  while ((c = getopt_long(argc, argv, "I:s:O:d:F:b:SgxXVv",
-                         strip_options, (int *) 0))
-        != EOF) {
-      switch (c) {
+
+      if (output_file != NULL)
+       tmpname = output_file;
+      else
+       tmpname = make_tempname (argv[i]);
+      status = 0;
+
+      copy_file (argv[i], tmpname, input_target, output_target);
+      if (status == 0)
+       {
+         if (preserve_dates)
+           set_times (tmpname, &statbuf);
+         if (output_file == NULL)
+           smart_rename (tmpname, argv[i]);
+         status = hold_status;
+       }
+      else
+       unlink (tmpname);
+      if (output_file == NULL)
+       free (tmpname);
+    }
+
+  return 0;
+}
+
+static int
+copy_main (argc, argv)
+     int argc;
+     char *argv[];
+{
+  char *input_filename = NULL, *output_filename = NULL;
+  char *input_target = NULL, *output_target = NULL;
+  boolean show_version = false;
+  boolean change_warn = true;
+  int c;
+  struct section_list *p;
+  struct stat statbuf;
+
+  while ((c = getopt_long (argc, argv, "b:i:I:K:N:s:O:d:F:L:R:SpgxXVvW:",
+                          copy_options, (int *) 0)) != EOF)
+    {
+      switch (c)
+       {
+       case 'b':
+         copy_byte = atoi(optarg);
+         if (copy_byte < 0)
+           fatal (_("byte number must be non-negative"));
+         break;
+       case 'i':
+         interleave = atoi(optarg);
+         if (interleave < 1)
+           fatal (_("interleave must be positive"));
+         break;
        case 'I':
-       case 's': /* "source" - 'I' is preferred */
+       case 's':               /* "source" - 'I' is preferred */
          input_target = optarg;
+         break;
        case 'O':
-       case 'd': /* "destination" - 'O' is preferred */
+       case 'd':               /* "destination" - 'O' is preferred */
          output_target = optarg;
          break;
        case 'F':
-       case 'b': /* "both" - 'F' is preferred */
          input_target = output_target = optarg;
          break;
-         
+       case 'R':
+         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 'g':
-         strip_symbols = strip_debug;
+         strip_symbols = STRIP_DEBUG;
+         break;
+       case OPTION_STRIP_UNNEEDED:
+         strip_symbols = STRIP_UNNEEDED;
+         break;
+       case 'K':
+         add_specific_symbol (optarg, &keep_specific_list);
+         break;
+       case 'N':
+         add_specific_symbol (optarg, &strip_specific_list);
+         break;
+       case 'L':
+         add_specific_symbol (optarg, &localize_specific_list);
+         break;
+       case 'W':
+         add_specific_symbol (optarg, &weaken_specific_list);
+         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;
@@ -659,48 +1875,319 @@ main(argc, argv)
        case 'V':
          show_version = true;
          break;
-       case  0:
+       case OPTION_WEAKEN:
+         weaken = true;
+         break;
+       case OPTION_ADD_SECTION:
+         {
+           const char *s;
+           struct stat st;
+           struct section_add *pa;
+           int len;
+           char *name;
+           FILE *f;
+
+           s = strchr (optarg, '=');
+           
+           if (s == NULL)
+             fatal (_("bad format for --add-section NAME=FILENAME"));
+
+           if (stat (s + 1, & st) < 0)
+             fatal (_("cannot stat: %s: %s"), s + 1, strerror (errno));
+
+           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 = st.st_size;
+
+           pa->contents = (bfd_byte *) xmalloc (pa->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;
+           bfd_vma val;
+           enum change_action what;
+           
+           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)
+                     fatal (_("bad format for %s"), option);
+                 }
+             }
+
+           len = s - optarg;
+           name = (char *) xmalloc (len + 1);
+           strncpy (name, optarg, len);
+           name[len] = '\0';
+
+           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_SET_SECTION_FLAGS:
+         {
+           const char *s;
+           int len;
+           char *name;
+
+           s = strchr (optarg, '=');
+           if (s == NULL)
+             fatal (_("bad format for --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_SET_START:
+         set_start = parse_vma (optarg, "--set-start");
+         set_start_set = true;
+         break;
+       case 0:
          break;                /* we've been given a long option */
        case 'h':
          copy_usage (stdout, 0);
        default:
          copy_usage (stderr, 1);
-      }
-  }
-      
-  if (show_version) {
-    printf ("GNU %s version %s\n", program_name, program_version);
-    exit (0);
-  }
+       }
+    }
 
-  if (optind == argc)
-    copy_usage(stderr, 1);
+  if (show_version)
+    print_version ("objcopy");
+
+  if (copy_byte >= interleave)
+    fatal (_("byte number must be less than interleave"));
+
+  if (optind == argc || optind + 2 < argc)
+    copy_usage (stderr, 1);
 
   input_filename = argv[optind];
   if (optind + 1 < argc)
-      output_filename = argv[optind+1];
+    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 (input_filename == (char *) NULL)
-    copy_usage(stderr, 1);
+  if (strip_symbols == STRIP_UNDEF && discard_locals == LOCALS_UNDEF)
+    strip_symbols = STRIP_NONE;
 
   if (output_target == (char *) NULL)
     output_target = input_target;
 
-  /* If there is no  destination file then create a temp and rename
-     the result into the input */
+  if (preserve_dates)
+    {
+      if (stat (input_filename, &statbuf) < 0)
+       fatal (_("Cannot stat: %s: %s"), input_filename, strerror (errno));
+    }
+
+  /* If there is no destination file then create a temp and rename
+     the result into the input.  */
+
+  if (output_filename == (char *) NULL)
+    {
+      char *tmpname = make_tempname (input_filename);
+
+      copy_file (input_filename, tmpname, input_target, output_target);
+      if (status == 0)
+       {       
+         if (preserve_dates)
+           set_times (tmpname, &statbuf);
+         smart_rename (tmpname, input_filename);
+       }
+      else
+       unlink (tmpname);
+    }
+  else
+    {
+      copy_file (input_filename, output_filename, input_target, output_target);
+      if (status == 0 && preserve_dates)
+       set_times (output_filename, &statbuf);
+    }
+
+  if (change_warn)
+    {
+      for (p = change_sections; p != NULL; p = p->next)
+       {
+         if (! p->used)
+           {
+             if (p->change_vma != CHANGE_IGNORE)
+               {
+                 char buff [20];
+
+                 sprintf_vma (buff, p->vma_val);
+                 
+                 /* xgettext:c-format */
+                 non_fatal (_("Warning: --change-section-vma %s%c0x%s never used"),
+                            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 (_("Warning: --change-section-lma %s%c0x%s never used"),
+                            p->name,
+                            p->change_lma == CHANGE_SET ? '=' : '+',
+                            buff);
+               }
+           }
+       }
+    }
 
-  if (output_filename == (char *)NULL) {
-    char *     tmpname = make_tempname(input_filename);
-    copy_file(input_filename, tmpname);
-    output_filename = input_filename;
-    rename(tmpname, input_filename);
-  }
-  else {
-    copy_file(input_filename, output_filename);
-  }
   return 0;
 }
+
+int
+main (argc, argv)
+     int argc;
+     char *argv[];
+{
+#ifdef HAVE_SETLOCALE
+  setlocale (LC_MESSAGES, "");
+#endif
+  bindtextdomain (PACKAGE, LOCALEDIR);
+  textdomain (PACKAGE);
+
+  program_name = argv[0];
+  xmalloc_set_program_name (program_name);
+
+  START_PROGRESS (program_name, 0);
+
+  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") == 0);
+    }
+
+  if (is_strip)
+    strip_main (argc, argv);
+  else
+    copy_main (argc, argv);
+
+  END_PROGRESS (program_name);
+
+  return status;
+}
This page took 0.04879 seconds and 4 git commands to generate.