* objcopy.c (strip_main, copy_main): Don't clobber the input file
authorIan Lance Taylor <ian@airs.com>
Fri, 14 Jan 1994 21:44:44 +0000 (21:44 +0000)
committerIan Lance Taylor <ian@airs.com>
Fri, 14 Jan 1994 21:44:44 +0000 (21:44 +0000)
if copy_file fails.

binutils/ChangeLog
binutils/objcopy.c

index 7c51761..b14a7c4 100644 (file)
@@ -1,5 +1,8 @@
 Fri Jan 14 14:42:48 1994  Ian Lance Taylor  (ian@tweedledumb.cygnus.com)
 
+       * objcopy.c (strip_main, copy_main): Don't clobber the input file
+       if copy_file fails.
+
        * nlmconv.c (main): Warn about an attempt to use a shared library
        with uninitialized data.
 
index dea596c..f2dd5a4 100644 (file)
 /* objcopy.c -- copy object file from input to output, optionally massaging it.
    Copyright (C) 1991 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., 675 Mass Ave, Cambridge, MA 02139, USA.  */
-
+   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., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+\f
 #include "bfd.h"
 #include "sysdep.h"
 #include "bucomm.h"
 #include <getopt.h>
 
-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;
+static void setup_sections ();
+static void copy_sections ();
+static void mangle_sections ();
 
+#define nonfatal(s) {bfd_nonfatal(s); status = 1; return;}
 
-static void     setup_sections();
-static void     copy_sections();
-static boolean verbose;
-
-/* 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;
-
-int              show_version = 0;
+static asymbol **isympp = NULL;        /* Input symbols */
+static asymbol **osympp = NULL;        /* Output symbols that survive stripping */
+static boolean verbose;                /* Print file and target names. */
+static int status = 0;
 
 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_all                  /* strip all symbols */
+  };
 
 /* Which symbols to remove. */
-enum strip_action strip_symbols;
+static enum strip_action strip_symbols;
 
 enum locals_action
+  {
+    locals_undef,
+    locals_start_L,            /* discard locals starting with L */
+    locals_all                 /* discard all locals */
+  };
+
+/* Which local symbols to remove.  Overrides strip_all.  */
+static enum locals_action discard_locals;
+
+/* 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 */
+  {"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-target", required_argument, 0, 'I'},
+  {"input-format", required_argument, 0, 'I'}, /* Obsolete */
+  {"output-target", required_argument, 0, 'O'},
+  {"output-format", required_argument, 0, 'O'},        /* Obsolete */
+  {"target", required_argument, 0, 'F'},
+  {"format", required_argument, 0, 'F'}, /* Obsolete */
+
+  {"version", no_argument, 0, 'V'},
+  {"verbose", 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[] =
+{
+  {"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-target", required_argument, 0, 'I'},
+  {"input-format", required_argument, 0, 'I'}, /* Obsolete */
+  {"output-target", required_argument, 0, 'O'},
+  {"output-format", required_argument, 0, 'O'},        /* Obsolete */
+  {"target", required_argument, 0, 'F'},
+  {"format", required_argument, 0, 'F'}, /* Obsolete */
+
+  {"version", no_argument, 0, 'V'},
+  {"verbose", no_argument, 0, 'v'},
+  {0, no_argument, 0, 0}
 };
 
 /* IMPORTS */
-extern char    *program_name;
-extern char    *program_version;
+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. */
+extern int is_strip;
 
 
-static
-void            
-copy_usage(stream, status)
+static void
+copy_usage (stream, status)
      FILE *stream;
      int 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 [-vVSgxX] [-I bfdname] [-O bfdname] [-F bfdname]\n\
+       [--input-target=bfdname] [--output-target=bfdname] [--target=bfdname]\n\
+       [--strip-all] [--strip-debug] [--discard-all] [--discard-locals]\n\
+       [--verbose] [--version] [--help] in-file [out-file]\n", program_name);
+  exit (status);
 }
 
-static
-void            
-strip_usage(stream, status)
+static void
+strip_usage (stream, status)
      FILE *stream;
      int status;
 {
-    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);
+  fprintf (stream, "\
+Usage: %s [-vVsSgxX] [-I bfdname] [-O bfdname] [-F bfdname]\n\
+       [--input-target=bfdname] [--output-target=bfdname] [--target=bfdname]\n\
+       [--strip-all] [--strip-debug] [--discard-all] [--discard-locals]\n\
+       [--verbose] [--version] [--help] file...\n",
+          program_name);
+  exit (status);
 }
 
 
-/* Create a temp file in the same directory as supplied */
-static
-char *
-make_tempname(filename)
-char *filename;
-{
-    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;
-}
-
-/*
-   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.
+/* Return the name of a temporary file in the same directory as FILENAME.  */
 
-*/
-static void
-mangle_sections(ibfd, obfd)
-    bfd            *ibfd;
-    bfd            *obfd;
+static char *
+make_tempname (filename)
+     char *filename;
 {
-    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;
+  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;
 }
 
-/* Choose which symbol entries to copy; put the result in osyms.
+/* 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)
      bfd *abfd;
@@ -188,459 +174,595 @@ filter_symbols (abfd, osyms, isyms, symcount)
      unsigned 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)))
-       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);
+  char locals_prefix = bfd_get_symbol_leading_char (abfd) == '_' ? 'L' : '.';
+  unsigned int src_count = 0, dst_count = 0;
 
+  for (; src_count < symcount; src_count++)
+    {
+      asymbol *sym = from[src_count];
+      flagword flags = sym->flags;
+      int keep;
 
-    if (keep) {
-      to[dst_count++] = from[src_count];
+      if ((flags & BSF_GLOBAL) /* Keep if external.  */
+         || bfd_get_section (sym) == &bfd_und_section
+         || 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_asymbol_name (sym)[0] != locals_prefix);
+      if (keep)
+       to[dst_count++] = sym;
     }
-  }
 
   return dst_count;
 }
 
+/* Copy object file IBFD onto OBFD.  */
+
 static void
-copy_object(ibfd, obfd)
-bfd *ibfd;
-bfd *obfd;
+copy_object (ibfd, obfd)
+     bfd *ibfd;
+     bfd *obfd;
 {
+  unsigned int symcount;
 
-    unsigned int symcount;
-
+  if (!bfd_set_format (obfd, bfd_get_format (ibfd)))
+    {
+      nonfatal (bfd_get_filename (obfd));
+    }
 
-    if (!bfd_set_format(obfd, bfd_get_format(ibfd)))
-       bfd_fatal(output_filename);
+  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 (!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))))
+    {
+      nonfatal (bfd_get_filename (ibfd));
+    }
 
-    if (verbose)
-       printf("copy from %s(%s) to %s(%s)\n",
-              ibfd->filename, ibfd->xvec->name,
-              obfd->filename, obfd->xvec->name);
+  /* 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 (! 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 (isympp)
+    free (isympp);
+  if (osympp != isympp)
+    free (osympp);
 
-    /* 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 (strip_symbols == strip_all && discard_locals == locals_undef)
+    {
+      osympp = isympp = NULL;
+      symcount = 0;
     }
-    if (!bfd_set_format(obfd, bfd_get_format(ibfd)))
+  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)
        {
-           bfd_fatal(ibfd->filename);
+         osympp = (asymbol **) xmalloc (symcount * sizeof (asymbol *));
+         symcount = filter_symbols (ibfd, osympp, isympp, symcount);
        }
+    }
+
+  bfd_set_symtab (obfd, osympp, symcount);
 
-    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);
+  /* 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_sections, (void *) obfd);
+  bfd_map_over_sections (ibfd, copy_sections, (void *) obfd);
+  bfd_map_over_sections (ibfd, mangle_sections, (void *) obfd);
 }
-static
-char *
-cat(a,b,c)
-char *a;
-char *b;
-char *c;
+
+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;
+  size_t size = strlen (a) + strlen (b) + strlen (c);
+  char *r = xmalloc (size + 1);
+
+  strcpy (r, a);
+  strcat (r, b);
+  strcat (r, c);
+  return r;
 }
 
-static void 
-copy_archive(ibfd, obfd)
-bfd *ibfd;
-bfd *obfd;
+/* 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;
+     char *output_target;
 {
-    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);
+  bfd **ptr = &obfd->archive_head;
+  bfd *this_element;
+  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, "/", bfd_get_filename(this_element));
+      bfd *output_bfd = bfd_openw (output_name, output_target);
+
+      if (output_bfd == (bfd *) NULL)
+       {
+         nonfatal (output_name);
        }
-       if (bfd_check_format(this_element, bfd_object) == true) {
-           copy_object(this_element, output_bfd);
+      if (!bfd_set_format (obfd, bfd_get_format (ibfd)))
+       {
+         nonfatal (bfd_get_filename (obfd));
        }
 
-       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 */
-
-       *ptr = output_bfd;
+      if (bfd_check_format (this_element, bfd_object) == true)
+       {
+         copy_object (this_element, output_bfd);
+       }
 
-       ptr = &output_bfd->next;
-       this_element->next = bfd_openr_next_archived_file(ibfd, this_element);
-       this_element = this_element->next;
+      bfd_close (output_bfd);
+      /* Open the newly output file and attatch to our list.  */
+      output_bfd = bfd_openr (output_name, output_target);
 
+      /* 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;
     }
-    *ptr = (bfd *)NULL;
-
-    if (!bfd_close(obfd))
-       bfd_fatal(output_filename);
+  *ptr = (bfd *) NULL;
 
-    /* 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) 
-       {
-       unlink(cat(dir,"/",this_element->filename));
+  if (!bfd_close (obfd))
+    {
+      nonfatal (bfd_get_filename (obfd));
     }
-    rmdir(dir);
-    if (!bfd_close(ibfd))
-       bfd_fatal(input_filename);
 
+  /* 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))
+    {
+      nonfatal (bfd_get_filename (ibfd));
+    }
 }
 
-static
-void
-copy_file(input_filename, output_filename)
-    char           *input_filename;
-    char           *output_filename;
+/* The top-level control.  */
+
+static void
+copy_file (input_filename, output_filename, input_target, output_target)
+     char *input_filename;
+     char *output_filename;
+     char *input_target;
+     char *output_target;
 {
-  bfd            *ibfd;
+  bfd *ibfd;
 
   /* 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)
     {
-      bfd_perror(input_filename);
-      return;
+      nonfatal (input_filename);
     }
 
-  if (bfd_check_format(ibfd, bfd_object)) {
-    bfd * obfd = bfd_openw(output_filename, output_target);
-    if (obfd == NULL)
-      {
-       bfd_perror(output_filename);
-       return;
-      }
-
-    copy_object(ibfd, obfd);
-
-    if (!bfd_close(obfd))
-      {
-       bfd_perror(output_filename);
-       return;
-      }
-
-    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_check_format (ibfd, bfd_object))
+    {
+      bfd *obfd = bfd_openw (output_filename, output_target);
+      if (obfd == NULL)
+       {
+         nonfatal (output_filename);
+       }
 
+      copy_object (ibfd, obfd);
 
+      if (!bfd_close (obfd))
+       {
+         nonfatal (output_filename);
+       }
+
+      if (!bfd_close (ibfd))
+       {
+         nonfatal (input_filename);
+       }
+    }
+  else if (bfd_check_format (ibfd, bfd_archive))
+    {
+      bfd *obfd = bfd_openw (output_filename, output_target);
+      if (obfd == NULL)
+       {
+         nonfatal (output_filename);
+       }
+      copy_archive (ibfd, obfd, output_target);
+    }
+  else
+    {
+      /* Get the right error message.  */
+      bfd_check_format (ibfd, bfd_object);
+      nonfatal (input_filename);
+    }
+}
+
+/* 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_sections (ibfd, isection, obfd)
+     bfd *ibfd;
+     sec_ptr isection;
+     bfd *obfd;
 {
-    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;
+  sec_ptr osection;
+  char *err;
+
+  if ((bfd_get_section_flags (ibfd, isection) & SEC_DEBUGGING) != 0
+      && (strip_symbols == strip_debug
+         || strip_symbols == strip_all
+         || discard_locals == locals_all))
+    return;
+
+  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;
        }
     }
 
-    if (!bfd_set_section_size(obfd,
-                             osection,
-                             bfd_section_size(ibfd, isection))) {
-       err = "size";
-       goto loser;
+  if (!bfd_set_section_size (obfd,
+                            osection,
+                            bfd_section_size (ibfd, isection)))
+    {
+      err = "size";
+      goto loser;
     }
 
-    if (bfd_set_section_vma(obfd,
-                           osection,
-                           bfd_section_vma(ibfd, isection))
-       == false) {
-       err = "vma";
-       goto loser;
-    }                          /* on error */
+  if (bfd_set_section_vma (obfd,
+                          osection,
+                          bfd_section_vma (ibfd, isection))
+      == false)
+    {
+      err = "vma";
+      goto loser;
+    }
 
-    if (bfd_set_section_alignment(obfd,
-                                 osection,
-                                 bfd_section_alignment(ibfd, isection))
-       == false) {
-       err = "alignment";
-       goto loser;
-    }                          /* on error */
+  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;
+  if (!bfd_set_section_flags (obfd, osection,
+                             bfd_get_section_flags (ibfd, isection)))
+    {
+      err = "flags";
+      goto loser;
     }
 
-    /* All went well */
-    return;
+  /* 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
-*/
+  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_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_sections (ibfd, isection, obfd)
+     bfd *ibfd;
+     sec_ptr isection;
+     bfd *obfd;
 {
+  arelent **relpp;
+  int relcount;
+  sec_ptr osection;
+  bfd_size_type size;
+
+  if ((bfd_get_section_flags (ibfd, isection) & SEC_DEBUGGING) != 0
+      && (strip_symbols == strip_debug
+         || strip_symbols == strip_all
+         || discard_locals == locals_all))
+    {
+      return;
+    }
 
-  arelent       **relpp;
-  int             relcount;
-  sec_ptr         osection;
-  bfd_size_type   size;
-  osection = bfd_get_section_by_name(obfd,
-                                    bfd_section_name(ibfd, isection));
+  osection = bfd_get_section_by_name (obfd,
+                                     bfd_section_name (ibfd, isection));
 
-  size = bfd_get_section_size_before_reloc(isection);
+  size = bfd_get_section_size_before_reloc (isection);
 
   if (size == 0)
     return;
 
   if (strip_symbols == strip_all
-      || bfd_get_reloc_upper_bound(ibfd, isection) == 0) 
+      || bfd_get_reloc_upper_bound (ibfd, isection) == 0)
     {
-      bfd_set_reloc(obfd, osection, (arelent **)NULL, 0);
-    } 
-  else 
+      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);
+      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);
     }
 
   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))
+       {
+         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 (!bfd_set_section_contents (obfd, osection, memhunk, (file_ptr) 0,
+                                    size))
+       {
+         nonfatal (bfd_get_filename (obfd));
+       }
+      free (memhunk);
     }
+}
 
+/* All the symbols have been read in and point to their owning input section.
+   They have been relocated so 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.  */
 
+static void
+mangle_sections (ibfd, p, obfd)
+     bfd *ibfd;
+     asection *p;
+     bfd *obfd;
+{
+  p->output_section = bfd_get_section_by_name (obfd, p->name);
+  p->output_offset = 0;
 }
-int
-main(argc, argv)
-    int             argc;
-    char           *argv[];
+
+/* 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)
+     char *from, *to;
 {
-  int             i;
-  int c;                       /* sez which option char */
+  int fromfd, tofd, nread;
+  char buf[COPY_BUF];
+
+  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 (write (tofd, buf, nread) != nread)
+       {
+         close (fromfd);
+         close (tofd);
+         return -1;
+       }
+    }
+  close (fromfd);
+  close (tofd);
+  if (nread < 0)
+    return -1;
+  return 0;
+}
 
-  program_name = argv[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;
 
-  strip_symbols = strip_undef; /* default is to strip everything.  */
-  discard_locals = locals_undef;
+  if (lstat (to, &s))
+    return -1;
 
-  bfd_init();
-
-  if (is_strip < 0) {
-      i = strlen (program_name);
-      is_strip = (i >= 5 && strcmp(program_name+i-5,"strip"));
-  }
-
-  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);
-         }
-      }
-      
-      i = optind;
-
-      /* Default is to strip all symbols.  */
-      if (strip_symbols == strip_undef && discard_locals == locals_undef)
+  /* 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.  */
+         chmod (to, s.st_mode & 07777);
+         chown (to, s.st_uid, s.st_gid);
+       }
+    }
+  else
+    {
+      ret = simple_copy (from, to);
+      if (ret == 0)
+       unlink (from);
+    }
+  return ret;
+}
+
+static int
+strip_main (argc, argv)
+     int argc;
+     char *argv[];
+{
+  char *input_target = NULL, *output_target = NULL;
+  boolean show_version = false;
+  int c, i;
+
+  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);
+       }
+    }
+
+  if (show_version)
+    {
+      printf ("GNU %s version %s\n", program_name, program_version);
+      exit (0);
+    }
 
-      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;
+
+  if (output_target == (char *) NULL)
+    output_target = input_target;
+
+  i = optind;
+  if (i == argc)
+    strip_usage (stderr, 1);
+
+  for (; i < argc; i++)
+    {
+      int hold_status = status;
 
-      if (show_version) {
-         printf ("GNU %s version %s\n", program_name, program_version);
-         exit (0);
+      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 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) {
+      else
+       unlink (tmpname);
+      free (tmpname);
+    }
+
+  return 0;
+}
+
+static int
+copy_main (argc, argv)
+     int argc;
+     char *argv[];
+{
+  char *input_filename, *output_filename;
+  char *input_target = NULL, *output_target = NULL;
+  boolean show_version = false;
+  int c;
+
+  while ((c = getopt_long (argc, argv, "I:s:O:d:F:b:SgxXVv",
+                          copy_options, (int *) 0)) != EOF)
+    {
+      switch (c)
+       {
        case 'I':
-       case 's': /* "source" - 'I' is preferred */
+       case 's':               /* "source" - 'I' is preferred */
          input_target = optarg;
        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 */
+       case 'b':               /* "both" - 'F' is preferred */
          input_target = output_target = optarg;
          break;
-         
        case 'S':
          strip_symbols = strip_all;
          break;
@@ -659,48 +781,76 @@ main(argc, argv)
        case 'V':
          show_version = true;
          break;
-       case  0:
+       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 (show_version)
+    {
+      printf ("GNU %s version %s\n", program_name, program_version);
+      exit (0);
+    }
 
-  if (optind == argc)
-    copy_usage(stderr, 1);
+  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);
+    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 (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);
-  }
+  /* 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)
+       smart_rename (tmpname, input_filename);
+      else
+       unlink (tmpname);
+    }
+  else
+    {
+      copy_file (input_filename, output_filename, input_target, output_target);
+    }
+
   return 0;
 }
+
+int
+main (argc, argv)
+     int argc;
+     char *argv[];
+{
+  program_name = argv[0];
+  strip_symbols = strip_undef;
+  discard_locals = locals_undef;
+
+  bfd_init ();
+
+  if (is_strip < 0)
+    {
+      int i = strlen (program_name);
+      is_strip = (i >= 5 && strcmp (program_name + i - 5, "strip"));
+    }
+
+  if (is_strip)
+    strip_main (argc, argv);
+  else
+    copy_main (argc, argv);
+
+  return status;
+}