Add --only-keep-debug switch
[deliverable/binutils-gdb.git] / binutils / objcopy.c
index 27c844fbb75fdd15e42ccc055a1637406c4a02f3..de777678962dd8e071fb66ca06dc5ba171dcc4fe 100644 (file)
@@ -105,11 +105,13 @@ static int copy_main
 static const char *lookup_sym_redefinition
   PARAMS((const char *));
 static void redefine_list_append
-  PARAMS ((const char *, const char *));
+  PARAMS ((const char *, const char *, const char *));
 static const char * find_section_rename
   PARAMS ((bfd *, sec_ptr, flagword *));
 static void add_section_rename
   PARAMS ((const char *, const char *, flagword));
+static void add_redefine_syms_file 
+  PARAMS ((const char *));
 
 #define RETURN_NONFATAL(s) {bfd_nonfatal (s); status = 1; return;}
 
@@ -130,6 +132,7 @@ enum strip_action
     STRIP_NONE,                        /* don't strip */
     STRIP_DEBUG,               /* strip all debugger symbols */
     STRIP_UNNEEDED,            /* strip unnecessary symbols */
+    STRIP_NONDEBUG,            /* Strip everything but debug info.  */
     STRIP_ALL                  /* strip all symbols */
   };
 
@@ -217,6 +220,10 @@ struct section_add
 /* List of sections to add to the output BFD.  */
 static struct section_add *add_sections;
 
+/* If non-NULL the argument to --add-gnu-debuglink.
+   This should be the filename to store in the .gnu_debuglink section.  */
+static const char * gnu_debuglink_filename = NULL;
+
 /* Whether to convert debugging information.  */
 static bfd_boolean convert_debugging = FALSE;
 
@@ -263,7 +270,8 @@ static char *prefix_alloc_sections_string = 0;
 #define OPTION_STRIP_UNNEEDED (OPTION_SET_START + 1)
 #define OPTION_WEAKEN (OPTION_STRIP_UNNEEDED + 1)
 #define OPTION_REDEFINE_SYM (OPTION_WEAKEN + 1)
-#define OPTION_SREC_LEN (OPTION_REDEFINE_SYM + 1)
+#define OPTION_REDEFINE_SYMS (OPTION_REDEFINE_SYM + 1)
+#define OPTION_SREC_LEN (OPTION_REDEFINE_SYMS + 1)
 #define OPTION_SREC_FORCES3 (OPTION_SREC_LEN + 1)
 #define OPTION_STRIP_SYMBOLS (OPTION_SREC_FORCES3 + 1)
 #define OPTION_KEEP_SYMBOLS (OPTION_STRIP_SYMBOLS + 1)
@@ -276,6 +284,8 @@ static char *prefix_alloc_sections_string = 0;
 #define OPTION_PREFIX_SECTIONS (OPTION_PREFIX_SYMBOLS + 1)
 #define OPTION_PREFIX_ALLOC_SECTIONS (OPTION_PREFIX_SECTIONS + 1)
 #define OPTION_FORMATS_INFO (OPTION_PREFIX_ALLOC_SECTIONS + 1)
+#define OPTION_ADD_GNU_DEBUGLINK (OPTION_FORMATS_INFO + 1)
+#define OPTION_ONLY_KEEP_DEBUG (OPTION_ADD_GNU_DEBUGLINK + 1)
 
 /* Options to handle if running as "strip".  */
 
@@ -289,6 +299,7 @@ static struct option strip_options[] =
   {"input-format", required_argument, 0, 'I'}, /* Obsolete */
   {"input-target", required_argument, 0, 'I'},
   {"keep-symbol", required_argument, 0, 'K'},
+  {"only-keep-debug", no_argument, 0, OPTION_ONLY_KEEP_DEBUG},
   {"output-format", required_argument, 0, 'O'},        /* Obsolete */
   {"output-target", required_argument, 0, 'O'},
   {"output-file", required_argument, 0, 'o'},
@@ -308,6 +319,7 @@ static struct option strip_options[] =
 
 static struct option copy_options[] =
 {
+  {"add-gnu-debuglink", required_argument, 0, OPTION_ADD_GNU_DEBUGLINK},
   {"add-section", required_argument, 0, OPTION_ADD_SECTION},
   {"adjust-start", required_argument, 0, OPTION_CHANGE_START},
   {"adjust-vma", required_argument, 0, OPTION_CHANGE_ADDRESSES},
@@ -341,6 +353,7 @@ static struct option copy_options[] =
   {"localize-symbols", required_argument, 0, OPTION_LOCALIZE_SYMBOLS},
   {"no-adjust-warnings", no_argument, 0, OPTION_NO_CHANGE_WARNINGS},
   {"no-change-warnings", no_argument, 0, OPTION_NO_CHANGE_WARNINGS},
+  {"only-keep-debug", no_argument, 0, OPTION_ONLY_KEEP_DEBUG},
   {"only-section", required_argument, 0, 'j'},
   {"output-format", required_argument, 0, 'O'},        /* Obsolete */
   {"output-target", required_argument, 0, 'O'},
@@ -350,6 +363,7 @@ static struct option copy_options[] =
   {"prefix-alloc-sections", required_argument, 0, OPTION_PREFIX_ALLOC_SECTIONS},
   {"preserve-dates", no_argument, 0, 'p'},
   {"redefine-sym", required_argument, 0, OPTION_REDEFINE_SYM},
+  {"redefine-syms", required_argument, 0, OPTION_REDEFINE_SYMS},
   {"remove-leading-char", no_argument, 0, OPTION_REMOVE_LEADING_CHAR},
   {"remove-section", required_argument, 0, 'R'},
   {"rename-section", required_argument, 0, OPTION_RENAME_SECTION},
@@ -408,9 +422,10 @@ copy_usage (stream, exit_status)
      --debugging                   Convert debugging information, if possible\n\
   -p --preserve-dates              Copy modified/access timestamps to the output\n\
   -j --only-section <name>         Only copy section <name> into the output\n\
+     --add-gnu-debuglink=<file>    Add section .gnu_debuglink linking to <file>\n\
   -R --remove-section <name>       Remove section <name> from the output\n\
   -S --strip-all                   Remove all symbol and relocation information\n\
-  -g --strip-debug                 Remove all debugging symbols\n\
+  -g --strip-debug                 Remove all debugging symbols & sections\n\
      --strip-unneeded              Remove all symbols not needed by relocations\n\
   -N --strip-symbol <name>         Do not copy symbol <name>\n\
   -K --keep-symbol <name>          Only copy symbol <name>\n\
@@ -444,6 +459,8 @@ copy_usage (stream, exit_status)
      --change-leading-char         Force output format's leading character style\n\
      --remove-leading-char         Remove leading character from global symbols\n\
      --redefine-sym <old>=<new>    Redefine symbol name <old> to <new>\n\
+     --redefine-syms <file>        --redefine-sym for all symbol pairs \n\
+                                     listed in <file>\n\
      --srec-len <number>           Restrict the length of generated Srecords\n\
      --srec-forceS3                Restrict the type of generated Srecords to S3\n\
      --strip-symbols <file>        -N for all symbols listed in <file>\n\
@@ -483,7 +500,7 @@ strip_usage (stream, exit_status)
   -p --preserve-dates              Copy modified/access timestamps to the output\n\
   -R --remove-section=<name>       Remove section <name> from the output\n\
   -s --strip-all                   Remove all symbol and relocation information\n\
-  -g -S -d --strip-debug           Remove all debugging symbols\n\
+  -g -S -d --strip-debug           Remove all debugging symbols & sections\n\
      --strip-unneeded              Remove all symbols not needed by relocations\n\
   -N --strip-symbol=<name>         Do not copy symbol <name>\n\
   -K --keep-symbol=<name>          Only copy symbol <name>\n\
@@ -748,25 +765,32 @@ is_strip_section (abfd, sec)
      bfd *abfd ATTRIBUTE_UNUSED;
      asection *sec;
 {
-  struct section_list *p;
+  if (sections_removed || sections_copied)
+    {
+      struct section_list *p;
+
+      p = find_section_list (bfd_get_section_name (abfd, sec), FALSE);
 
-  if ((bfd_get_section_flags (abfd, sec) & SEC_DEBUGGING) != 0
-      && (strip_symbols == STRIP_DEBUG
+      if (sections_removed && p != NULL && p->remove)
+       return TRUE;
+      if (sections_copied && (p == NULL || ! p->copy))
+       return TRUE;
+    }
+
+  if ((bfd_get_section_flags (abfd, sec) & SEC_DEBUGGING) != 0)
+    {
+      if (strip_symbols == STRIP_DEBUG
          || strip_symbols == STRIP_UNNEEDED
          || strip_symbols == STRIP_ALL
          || discard_locals == LOCALS_ALL
-         || convert_debugging))
-    return TRUE;
+         || convert_debugging)
+       return TRUE;
 
-  if (! sections_removed && ! sections_copied)
-    return FALSE;
+      if (strip_symbols == STRIP_NONDEBUG)
+       return FALSE;
+    }
 
-  p = find_section_list (bfd_get_section_name (abfd, sec), FALSE);
-  if (sections_removed && p != NULL && p->remove)
-    return TRUE;
-  if (sections_copied && (p == NULL || ! p->copy))
-    return TRUE;
-  return FALSE;
+  return strip_symbols == STRIP_NONDEBUG ? TRUE : FALSE;
 }
 
 /* Choose which symbol entries to copy; put the result in OSYMS.
@@ -940,7 +964,8 @@ lookup_sym_redefinition (source)
 /* Add a node to a symbol redefine list.  */
 
 static void
-redefine_list_append (source, target)
+redefine_list_append (cause, source, target)
+     const char *cause;
      const char *source;
      const char *target;
 {
@@ -952,13 +977,11 @@ redefine_list_append (source, target)
     {
       if (strcmp (source, list->source) == 0)
        fatal (_("%s: Multiple redefinition of symbol \"%s\""),
-              "--redefine-sym",
-              source);
+              cause, source);
 
       if (strcmp (target, list->target) == 0)
        fatal (_("%s: Symbol \"%s\" is target of more than one redefinition"),
-              "--redefine-sym",
-              target);
+              cause, target);
     }
 
   new_node = (struct redefine_node *) xmalloc (sizeof (struct redefine_node));
@@ -970,6 +993,116 @@ redefine_list_append (source, target)
   *p = new_node;
 }
 
+/* Handle the --redefine-syms option.  Read lines containing "old new"
+   from the file, and add them to the symbol redefine list.  */
+
+static void
+add_redefine_syms_file (filename)
+     const char *filename;
+{
+  FILE *file;
+  char *buf;
+  size_t bufsize, len, outsym_off;
+  int c, lineno;
+
+  file = fopen (filename, "r");
+  if (file == (FILE *) NULL)
+    fatal (_("couldn't open symbol redefinition file %s (error: %s)"),
+          filename, strerror (errno));
+
+  bufsize = 100;
+  buf = (char *) xmalloc (bufsize);
+
+  lineno = 1;
+  c = getc (file);
+  len = 0;
+  outsym_off = 0;
+  while (c != EOF)
+    {
+      /* Collect the input symbol name.  */
+      while (! IS_WHITESPACE (c) && ! IS_LINE_TERMINATOR (c) && c != EOF)
+       {
+         if (c == '#')
+           goto comment;
+         buf[len++] = c;
+         if (len >= bufsize)
+           {
+             bufsize *= 2;
+             buf = xrealloc (buf, bufsize);
+           }
+         c = getc (file);
+       }
+      buf[len++] = '\0';
+      if (c == EOF)
+       break;
+
+      /* Eat white space between the symbol names.  */
+      while (IS_WHITESPACE (c))
+       c = getc (file);
+      if (c == '#' || IS_LINE_TERMINATOR (c))
+       goto comment;
+      if (c == EOF)
+       break;
+
+      /* Collect the output symbol name.  */
+      outsym_off = len;
+      while (! IS_WHITESPACE (c) && ! IS_LINE_TERMINATOR (c) && c != EOF)
+       {
+         if (c == '#')
+           goto comment;
+         buf[len++] = c;
+         if (len >= bufsize)
+           {
+             bufsize *= 2;
+             buf = xrealloc (buf, bufsize);
+           }
+         c = getc (file);
+       }
+      buf[len++] = '\0';
+      if (c == EOF)
+       break;
+
+      /* Eat white space at end of line.  */
+      while (! IS_LINE_TERMINATOR(c) && c != EOF && IS_WHITESPACE (c))
+       c = getc (file);
+      if (c == '#')
+       goto comment;
+      /* Handle \r\n.  */
+      if ((c == '\r' && (c = getc (file)) == '\n')
+         || c == '\n' || c == EOF)
+       {
+ end_of_line:
+         /* Append the redefinition to the list.  */
+         if (buf[0] != '\0')
+           redefine_list_append (filename, &buf[0], &buf[outsym_off]);
+
+         lineno++;     
+         len = 0;
+         outsym_off = 0;
+         if (c == EOF)
+           break;
+         c = getc (file);
+         continue;
+       }
+      else
+       fatal (_("%s: garbage at end of line %d"), filename, lineno);
+ comment:
+      if (len != 0 && (outsym_off == 0 || outsym_off == len))
+       fatal (_("%s: missing new symbol name at line %d"), filename, lineno);
+      buf[len++] = '\0';
+
+      /* Eat the rest of the line and finish it.  */
+      while (c != '\n' && c != EOF)
+       c = getc (file);
+      goto end_of_line;
+    }
+
+  if (len != 0)
+    fatal (_("%s: premature end of file at line %d"), filename, lineno);
+
+  free (buf);
+}
+
 /* Keep only every `copy_byte'th byte in MEMHUNK, which is *SIZE bytes long.
    Adjust *SIZE.  */
 
@@ -1069,6 +1202,8 @@ copy_object (ibfd, obfd)
 
       for (padd = add_sections; padd != NULL; padd = padd->next)
        {
+         flagword flags;
+
          padd->section = bfd_make_section (obfd, padd->name);
          if (padd->section == NULL)
            {
@@ -1077,45 +1212,47 @@ copy_object (ibfd, obfd)
              status = 1;
              return;
            }
-         else
-           {
-             flagword flags;
 
-             if (! bfd_set_section_size (obfd, padd->section, padd->size))
-               RETURN_NONFATAL (bfd_get_filename (obfd));
+         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;
+         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 && pset->set_flags)
+           flags = pset->flags | SEC_HAS_CONTENTS;
+         else
+           flags = SEC_HAS_CONTENTS | SEC_READONLY | SEC_DATA;
 
-             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 (! bfd_set_section_flags (obfd, padd->section, flags))
+           RETURN_NONFATAL (bfd_get_filename (obfd));
 
-                 if (pset->change_lma != CHANGE_IGNORE)
-                   {
-                     padd->section->lma = pset->lma_val;
+         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 (! bfd_set_section_alignment
-                         (obfd, padd->section,
-                          bfd_section_alignment (obfd, padd->section)))
-                       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 (gnu_debuglink_filename != NULL)
+    {
+      if (! bfd_add_gnu_debuglink (obfd, gnu_debuglink_filename))
+       RETURN_NONFATAL (gnu_debuglink_filename);
+    }
+
   if (gap_fill_set || pad_to_set)
     {
       asection **set;
@@ -1216,6 +1353,7 @@ copy_object (ibfd, obfd)
   if (strip_symbols == STRIP_DEBUG
       || strip_symbols == STRIP_ALL
       || strip_symbols == STRIP_UNNEEDED
+      || strip_symbols == STRIP_NONDEBUG
       || discard_locals != LOCALS_UNDEF
       || strip_specific_list != NULL
       || keep_specific_list != NULL
@@ -1321,7 +1459,12 @@ copy_object (ibfd, obfd)
      from the input BFD to the output BFD.  This is done last to
      permit the routine to look at the filtered symbol table, which is
      important for the ECOFF code at least.  */
-  if (! bfd_copy_private_bfd_data (ibfd, obfd))
+  if (bfd_get_flavour (ibfd) == bfd_target_elf_flavour
+      && strip_symbols == STRIP_NONDEBUG)
+    /* Do not copy the private data when creating an ELF format
+       debug info file.  We do not want the program headers.  */
+    ;
+  else if (! bfd_copy_private_bfd_data (ibfd, obfd))
     {
       non_fatal (_("%s: error copying private BFD data: %s"),
                 bfd_get_filename (obfd),
@@ -1648,23 +1791,13 @@ setup_section (ibfd, isection, obfdarg)
   const char * name;
   char *prefix = NULL;
 
-  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))
+  if (is_strip_section (ibfd, isection))
     return;
 
   p = find_section_list (bfd_section_name (ibfd, isection), FALSE);
   if (p != NULL)
     p->used = TRUE;
 
-  if (sections_removed && p != NULL && p->remove)
-    return;
-  if (sections_copied && (p == NULL || ! p->copy))
-    return;
-
   /* Get the, possibly new, name of the output section.  */
   name = find_section_rename (ibfd, isection, & flags);
 
@@ -1759,7 +1892,12 @@ setup_section (ibfd, isection, obfdarg)
 
   /* 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))
+  if (bfd_get_flavour (ibfd) == bfd_target_elf_flavour
+      && strip_symbols == STRIP_NONDEBUG)
+    /* Do not copy the private data when creating an ELF format
+       debug info file.  We do not want the program headers.  */
+    ;
+  else if (!bfd_copy_private_section_data (ibfd, isection, obfd, osection))
     {
       err = _("private data");
       goto loser;
@@ -1800,31 +1938,21 @@ copy_section (ibfd, isection, obfdarg)
   if (status != 0)
     return;
 
-  flags = bfd_get_section_flags (ibfd, isection);
-  if ((flags & SEC_DEBUGGING) != 0
-      && (strip_symbols == STRIP_DEBUG
-         || strip_symbols == STRIP_UNNEEDED
-         || strip_symbols == STRIP_ALL
-         || discard_locals == LOCALS_ALL
-         || convert_debugging))
+  if (is_strip_section (ibfd, isection))
     return;
 
+  flags = bfd_get_section_flags (ibfd, isection);
   if ((flags & SEC_GROUP) != 0)
     return;
 
-  p = find_section_list (bfd_section_name (ibfd, isection), FALSE);
-
-  if (sections_removed && p != NULL && p->remove)
-    return;
-  if (sections_copied && (p == NULL || ! p->copy))
-    return;
-
   osection = isection->output_section;
   size = bfd_get_section_size_before_reloc (isection);
 
   if (size == 0 || osection == 0)
     return;
 
+  p = find_section_list (bfd_get_section_name (ibfd, isection), FALSE);
+
   /* Core files do not need to be relocated.  */
   if (bfd_get_format (obfd) == bfd_core)
     relsize = 0;
@@ -2152,6 +2280,9 @@ strip_main (argc, argv)
        case OPTION_FORMATS_INFO:
          formats_info = TRUE;
          break;
+       case OPTION_ONLY_KEEP_DEBUG:
+         strip_symbols = STRIP_NONDEBUG;
+         break;
        case 0:
          /* We've been given a long option.  */
          break;
@@ -2305,6 +2436,14 @@ copy_main (argc, argv)
          strip_symbols = STRIP_UNNEEDED;
          break;
 
+       case OPTION_ONLY_KEEP_DEBUG:
+         strip_symbols = STRIP_NONDEBUG;
+         break;
+
+       case OPTION_ADD_GNU_DEBUGLINK:
+         gnu_debuglink_filename = optarg;
+         break;
+
        case 'K':
          add_specific_symbol (optarg, &keep_specific_list);
          break;
@@ -2547,13 +2686,17 @@ copy_main (argc, argv)
            target = (char *) xmalloc (len + 1);
            strcpy (target, nextarg);
 
-           redefine_list_append (source, target);
+           redefine_list_append ("--redefine-sym", source, target);
 
            free (source);
            free (target);
          }
          break;
 
+       case OPTION_REDEFINE_SYMS:
+         add_redefine_syms_file (optarg);
+         break;
+
        case OPTION_SET_SECTION_FLAGS:
          {
            const char *s;
@@ -2672,7 +2815,8 @@ copy_main (argc, argv)
          break;
 
        case 0:
-         break;                /* we've been given a long option */
+         /* We've been given a long option.  */
+         break;
 
        case 'H':
        case 'h':
This page took 0.030954 seconds and 4 git commands to generate.