diff options
author | Richard Henderson <rth@redhat.com> | 1999-05-03 07:29:11 +0000 |
---|---|---|
committer | Richard Henderson <rth@redhat.com> | 1999-05-03 07:29:11 +0000 |
commit | 252b5132c753830d5fd56823373aed85f2a0db63 (patch) | |
tree | 1af963bfd8d3e55167b81def4207f175eaff3a56 /binutils/objcopy.c | |
download | gdb-252b5132c753830d5fd56823373aed85f2a0db63.zip gdb-252b5132c753830d5fd56823373aed85f2a0db63.tar.gz gdb-252b5132c753830d5fd56823373aed85f2a0db63.tar.bz2 |
19990502 sourceware importbinu_ss_19990502
Diffstat (limited to 'binutils/objcopy.c')
-rw-r--r-- | binutils/objcopy.c | 2029 |
1 files changed, 2029 insertions, 0 deletions
diff --git a/binutils/objcopy.c b/binutils/objcopy.c new file mode 100644 index 0000000..5fd7777 --- /dev/null +++ b/binutils/objcopy.c @@ -0,0 +1,2029 @@ +/* objcopy.c -- copy object file from input to output, optionally massaging it. + Copyright (C) 1991, 92, 93, 94, 95, 96, 97, 98, 1999 + 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. */ + +#include "bfd.h" +#include "progress.h" +#include "bucomm.h" +#include "getopt.h" +#include "libiberty.h" +#include "budbg.h" +#include <sys/stat.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; +}; + +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 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. */ + +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 */ + }; + +/* Which symbols to remove. */ +static enum strip_action strip_symbols; + +enum locals_action + { + LOCALS_UNDEF, + LOCALS_START_L, /* discard locals starting with L */ + LOCALS_ALL /* discard all locals */ + }; + +/* Which local symbols to remove. Overrides STRIP_ALL. */ +static enum locals_action discard_locals; + +/* 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 struct section_list *change_sections; +static boolean sections_removed; + +/* Changes to the start address. */ +static bfd_vma change_start = 0; +static boolean set_start_set = false; +static bfd_vma set_start; + +/* Changes to section addresses. */ +static bfd_vma change_section_address = 0; + +/* 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 +{ + /* 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; +}; + +static struct section_add *add_sections; + +/* 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[] = +{ + {"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} +}; + +/* Options to handle if running as "objcopy". */ + +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; + +/* 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, exit_status) + FILE *stream; + int 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, exit_status) + FILE *stream; + 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; +{ + 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. */ + +static void +add_specific_symbol (name, list) + const char *name; + struct symlist **list; +{ + struct symlist *tmp_list; + + tmp_list = (struct symlist *) xmalloc (sizeof (struct symlist)); + tmp_list->name = name; + tmp_list->next = *list; + *list = tmp_list; +} + +/* See whether a symbol should be stripped or kept based on + strip_specific_list and keep_symbols. */ + +static boolean +is_specified_symbol (name, list) + const char *name; + struct symlist *list; +{ + 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; +} + +/* 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 print. */ + +static unsigned int +filter_symbols (abfd, obfd, osyms, isyms, symcount) + bfd *abfd; + bfd *obfd; + asymbol **osyms, **isyms; + long symcount; +{ + register asymbol **from = isyms, **to = osyms; + long src_count = 0, dst_count = 0; + + for (; src_count < symcount; src_count++) + { + asymbol *sym = from[src_count]; + flagword flags = sym->flags; + 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 (strip_symbols == STRIP_ALL) + keep = 0; + else if ((flags & BSF_KEEP) != 0 /* Used in relocation. */ + || ((flags & BSF_SECTION_SYM) != 0 + && ((*bfd_get_section (sym)->symbol_ptr_ptr)->flags + & BSF_KEEP) != 0)) + 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; + 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++] = 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 +filter_bytes (memhunk, size) + char *memhunk; + bfd_size_type *size; +{ + char *from = memhunk + copy_byte, *to = memhunk, *end = memhunk + *size; + + for (; from < end; from += interleave) + *to++ = *from; + if (*size % interleave > copy_byte) + *size = (*size / interleave) + 1; + else + *size /= interleave; +} + +/* Copy object file IBFD onto OBFD. */ + +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; + long symsize; + PTR dhandle; + + + 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); + + /* 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 (add_sections != NULL) + { + struct section_add *padd; + struct section_list *pset; + + 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 (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) + { + 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 (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); + } + } + } + } + + /* Symbol filtering must happen after the output sections have + been created, but before their contents are set. */ + dhandle = NULL; + symsize = bfd_get_symtab_upper_bound (ibfd); + if (symsize < 0) + 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_ALL + || 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. */ + if (strip_symbols != STRIP_ALL) + 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 (convert_debugging && dhandle != NULL) + { + if (! write_debugging_info (obfd, dhandle, &symcount, &osympp)) + { + status = 1; + return; + } + } + + bfd_set_symtab (obfd, osympp, symcount); + + /* This has to happen after the symbol table has been set. */ + bfd_map_over_sections (ibfd, copy_section, (void *) obfd); + + if (add_sections != NULL) + { + struct section_add *padd; + + for (padd = add_sections; padd != NULL; padd = padd->next) + { + if (! bfd_set_section_contents (obfd, padd->section, + (PTR) padd->contents, + (file_ptr) 0, + (bfd_size_type) padd->size)) + RETURN_NONFATAL (bfd_get_filename (obfd)); + } + } + + if (gap_fill_set || pad_to_set) + { + bfd_byte *buf; + int c, i; + + /* Fill in the gaps. */ + + if (max_gap > 8192) + max_gap = 8192; + buf = (bfd_byte *) xmalloc (max_gap); + memset (buf, gap_fill, (size_t) max_gap); + + c = bfd_count_sections (obfd); + for (i = 0; i < c; i++) + { + if (gaps[i] != 0) + { + bfd_size_type left; + file_ptr off; + + left = gaps[i]; + off = bfd_section_size (obfd, osections[i]) - left; + while (left > 0) + { + bfd_size_type now; + + if (left > 8192) + now = 8192; + else + now = left; + + if (! bfd_set_section_contents (obfd, osections[i], buf, + off, now)) + RETURN_NONFATAL (bfd_get_filename (obfd)); + + left -= now; + off += now; + } + } + } + } + + /* 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; + } +} + +/* 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; +{ + 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); + if (ibfd == NULL) + RETURN_NONFATAL (input_filename); + + if (bfd_check_format (ibfd, bfd_archive)) + { + 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; + + /* 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_object (ibfd, obfd); + + 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. */ + +static void +setup_section (ibfd, isection, obfdarg) + bfd *ibfd; + sec_ptr isection; + PTR obfdarg; +{ + 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; + } + + 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; + } + + 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; + } + + 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; + } + + 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; + } + + /* 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: + 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_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; + long relsize; + + /* 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; + } + + p = find_section_list (bfd_section_name (ibfd, isection), false); + + if (p != NULL && p->remove) + return; + + osection = isection->output_section; + size = bfd_get_section_size_before_reloc (isection); + + if (size == 0 || osection == 0) + return; + + + 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)); + + if (strip_symbols == STRIP_ALL) + { + /* Remove relocations which are not in + keep_strip_specific_list. */ + arelent **temp_relpp; + long temp_relcount = 0; + long i; + + temp_relpp = (arelent **) xmalloc (relsize); + for (i = 0; i < relcount; i++) + if (is_specified_symbol + (bfd_asymbol_name (*relpp [i]->sym_ptr_ptr), + keep_specific_list)) + temp_relpp [temp_relcount++] = relpp [i]; + relcount = temp_relcount; + free (relpp); + relpp = temp_relpp; + } + bfd_set_reloc (obfd, osection, + (relcount == 0 ? (arelent **) NULL : relpp), relcount); + } + + isection->_cooked_size = isection->_raw_size; + isection->reloc_done = true; + + if (bfd_get_section_flags (ibfd, isection) & SEC_HAS_CONTENTS) + { + PTR memhunk = (PTR) xmalloc ((unsigned) size); + + if (!bfd_get_section_contents (ibfd, isection, memhunk, (file_ptr) 0, + size)) + RETURN_NONFATAL (bfd_get_filename (ibfd)); + + 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); +} + +/* 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; +{ + 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; + } + + /* Sort sections by LMA. */ + if ((*sec1)->lma > (*sec2)->lma) + return 1; + else if ((*sec1)->lma < (*sec2)->lma) + return -1; + + /* Sort sections with the same LMA by size. */ + if ((*sec1)->_raw_size > (*sec2)->_raw_size) + return 1; + else if ((*sec1)->_raw_size < (*sec2)->_raw_size) + return -1; + + return 0; +} + +/* Mark all the symbols which will be used in output relocations with + the BSF_KEEP flag so that those symbols will not be stripped. + + Ignore relocations which will not appear in the output file. */ + +static void +mark_symbols_used_in_relocations (ibfd, isection, symbolsarg) + bfd *ibfd; + sec_ptr isection; + PTR symbolsarg; +{ + 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; +} + +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) + strip_symbols = STRIP_ALL; + + if (output_target == (char *) NULL) + output_target = input_target; + + i = optind; + if (i == argc + || (output_file != NULL && (i + 1) < argc)) + strip_usage (stderr, 1); + + for (; i < argc; i++) + { + int hold_status = status; + struct stat statbuf; + char *tmpname; + + if (preserve_dates) + { + if (stat (argv[i], &statbuf) < 0) + { + non_fatal (_("%s: cannot stat: %s"), argv[i], strerror (errno)); + continue; + } + } + + 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], preserve_dates); + 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 */ + input_target = optarg; + break; + case 'O': + case 'd': /* "destination" - 'O' is preferred */ + output_target = optarg; + break; + case 'F': + input_target = output_target = optarg; + break; + case 'R': + p = find_section_list (optarg, true); + p->remove = true; + sections_removed = true; + break; + case 'S': + strip_symbols = STRIP_ALL; + break; + 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 '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; + break; + case 'X': + discard_locals = LOCALS_START_L; + break; + case 'v': + verbose = true; + break; + case 'V': + show_version = true; + break; + 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) + 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]; + + /* Default is to strip no symbols. */ + if (strip_symbols == STRIP_UNDEF && discard_locals == LOCALS_UNDEF) + strip_symbols = STRIP_NONE; + + if (output_target == (char *) NULL) + output_target = input_target; + + 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, preserve_dates); + } + 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); + } + } + } + } + + return 0; +} + +int +main (argc, argv) + int argc; + char *argv[]; +{ +#if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES) + 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; +} |