X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=binutils%2Fobjcopy.c;h=21f579366dd6b390c5820ec0108064c1a7b42d6b;hb=23ccc829e251d20c40a4337d2ae6057a0bb951aa;hp=690bf8d0987fab96ae500df93321d1aee1c00896;hpb=5d2f7e30c5a6f59137cb330da4de65b58a02bdf5;p=deliverable%2Fbinutils-gdb.git diff --git a/binutils/objcopy.c b/binutils/objcopy.c index 690bf8d098..21f579366d 100644 --- a/binutils/objcopy.c +++ b/binutils/objcopy.c @@ -1,5 +1,6 @@ /* objcopy.c -- copy object file from input to output, optionally massaging it. - Copyright (C) 1991, 92, 93, 94 Free Software Foundation, Inc. + Copyright (C) 1991, 92, 93, 94, 95, 96, 97, 98, 99, 2000 + Free Software Foundation, Inc. This file is part of GNU Binutils. @@ -15,18 +16,63 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ #include "bfd.h" -#include "sysdep.h" +#include "progress.h" #include "bucomm.h" -#include +#include "getopt.h" +#include "libiberty.h" +#include "budbg.h" +#include "filenames.h" +#include + +/* 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 setup_section (); -static void copy_section (); -static void mangle_section (); +/* A list to support redefine_sym. */ +struct redefine_node +{ + char *source; + char *target; + struct redefine_node *next; +}; -#define nonfatal(s) {bfd_nonfatal(s); status = 1; return;} +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 **)); +static const char *lookup_sym_redefinition PARAMS((const char *)); +static void redefine_list_append PARAMS ((const char *, const 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 */ @@ -36,14 +82,16 @@ 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_all /* strip all symbols */ + 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. */ @@ -51,14 +99,125 @@ static enum strip_action strip_symbols; enum locals_action { - locals_undef, - locals_start_L, /* discard locals starting with L */ - locals_all /* discard all locals */ + LOCALS_UNDEF, + LOCALS_START_L, /* discard locals starting with L */ + LOCALS_ALL /* discard all locals */ }; -/* Which local symbols to remove. Overrides strip_all. */ +/* Which local symbols to remove. Overrides STRIP_ALL. */ static enum locals_action discard_locals; +/* 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. */ + boolean copy; /* Whether to copy this section. */ + enum change_action change_vma;/* Whether to change or set VMA. */ + bfd_vma vma_val; /* Amount to change by or set to. */ + enum change_action change_lma;/* Whether to change or set LMA. */ + bfd_vma lma_val; /* Amount to change by or set to. */ + 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; +static boolean sections_copied; + +/* 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, weaken, or redefine. */ + +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; +static struct redefine_node *redefine_sym_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) +#define OPTION_REDEFINE_SYM (OPTION_WEAKEN + 1) +#define OPTION_SREC_LEN (OPTION_REDEFINE_SYM + 1) +#define OPTION_SREC_FORCES3 (OPTION_SREC_LEN + 1) + /* Options to handle if running as "strip". */ static struct option strip_options[] = @@ -69,10 +228,15 @@ static struct option strip_options[] = {"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'}, @@ -83,91 +247,315 @@ static struct option strip_options[] = 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'}, + {"only-section", required_argument, 0, 'j'}, {"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'}, + {"redefine-sym", required_argument, 0, OPTION_REDEFINE_SYM}, + {"srec-len", required_argument, 0, OPTION_SREC_LEN}, + {"srec-forceS3", no_argument, 0, OPTION_SREC_FORCES3}, {0, no_argument, 0, 0} }; /* IMPORTS */ extern char *program_name; -extern char *program_version; /* This flag distinguishes between strip and objcopy: 1 means this is 'strip'; 0 means this is 'objcopy'. -1 means if we should use argv[0] to decide. */ extern int is_strip; +/* The maximum length of an S record. This variable is declared in srec.c + and can be modified by the --srec-len parameter. */ +extern unsigned int Chunk; + +/* Restrict the generation of Srecords to type S3 only. + This variable is declare in bfd/srec.c and can be toggled + on by the --srec-forceS3 command line switch. */ +extern boolean S3Forced; static void -copy_usage (stream, status) +copy_usage (stream, exit_status) FILE *stream; - int status; + int exit_status; { - fprintf (stream, "\ -Usage: %s [-vVSgxX] [-I bfdname] [-O bfdname] [-F bfdname] [-b byte]\n\ - [-i interleave] [--interleave=interleave] [--byte=byte]\n\ - [--input-target=bfdname] [--output-target=bfdname] [--target=bfdname]\n\ - [--strip-all] [--strip-debug] [--discard-all] [--discard-locals]\n\ - [--verbose] [--version] [--help] in-file [out-file]\n", - program_name); - exit (status); + fprintf (stream, _("Usage: %s in-file [out-file]\n"), program_name); + fprintf (stream, _(" The switches are:\n")); + fprintf (stream, _("\ + -I --input-target Assume input file is in format \n\ + -O --output-target Create an output file in format \n\ + -F --target Set both input and output format to \n\ + --debugging Convert debugging information, if possible\n\ + -p --preserve-dates Copy modified/access timestamps to the output\n\ + -j --only-section Only copy section into the output\n\ + -R --remove-section Remove section from the output\n\ + -S --strip-all Remove all symbol and relocation information\n\ + -g --strip-debug Remove all debugging symbols\n\ + --strip-unneeded Remove all symbols not needed by relocations\n\ + -N --strip-symbol Do not copy symbol \n\ + -K --keep-symbol Only copy symbol \n\ + -L --localize-symbol Force symbol to be marked as a local\n\ + -W --weaken-symbol Force symbol to be marked as a weak\n\ + --weaken Force all global symbols to be marked as weak\n\ + -x --discard-all Remove all non-global symbols\n\ + -X --discard-locals Remove any compiler-generated symbols\n\ + -i --interleave Only copy one out of every bytes\n\ + -b --byte Select byte in every interleaved block\n\ + --gap-fill Fill gaps between sections with \n\ + --pad-to Pad the last section up to address \n\ + --set-start Set the start address to \n\ + {--change-start|--adjust-start} \n\ + Add to the start address\n\ + {--change-addresses|--adjust-vma} \n\ + Add to LMA, VMA and start addresses\n\ + {--change-section-address|--adjust-section-vma} {=|+|-}\n\ + Change LMA and VMA of section by \n\ + --change-section-lma {=|+|-}\n\ + Change the LMA of section by \n\ + --change-section-vma {=|+|-}\n\ + Change the VMA of section by \n\ + {--[no-]change-warnings|--[no-]adjust-warnings}\n\ + Warn if a named section does not exist\n\ + --set-section-flags =\n\ + Set section 's properties to \n\ + --add-section = Add section found in to output\n\ + --change-leading-char Force output format's leading character style\n\ + --remove-leading-char Remove leading character from global symbols\n\ + --redefine-sym = Redefine symbol name to \n\ + --srec-len Restrict the length of generated Srecords\n\ + --srec-forceS3 Restrict the type of generated Srecords to S3\n\ + -v --verbose List all object files modified\n\ + -V --version Display this program's version number\n\ + -h --help Display this output\n\ +")); + list_supported_targets (program_name, stream); + if (exit_status == 0) + fprintf (stream, _("Report bugs to %s\n"), REPORT_BUGS_TO); + exit (exit_status); } static void -strip_usage (stream, status) +strip_usage (stream, exit_status) FILE *stream; - int status; + int 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); + fprintf (stream, _("Usage: %s in-file(s)\n"), program_name); + fprintf (stream, _(" The switches are:\n")); + fprintf (stream, _("\ + -I --input-target Assume input file is in format \n\ + -O --output-target Create an output file in format \n\ + -F --target Set both input and output format to \n\ + -p --preserve-dates Copy modified/access timestamps to the output\n\ + -R --remove-section Remove section from the output\n\ + -s --strip-all Remove all symbol and relocation information\n\ + -g -S --strip-debug Remove all debugging symbols\n\ + --strip-unneeded Remove all symbols not needed by relocations\n\ + -N --strip-symbol Do not copy symbol \n\ + -K --keep-symbol Only copy symbol \n\ + -x --discard-all Remove all non-global symbols\n\ + -X --discard-locals Remove any compiler-generated symbols\n\ + -v --verbose List all object files modified\n\ + -V --version Display this program's version number\n\ + -h --help Display this output\n\ + -o Place stripped output into \n\ +")); + + list_supported_targets (program_name, stream); + if (exit_status == 0) + fprintf (stream, _("Report bugs to %s\n"), REPORT_BUGS_TO); + exit (exit_status); } +/* Parse section flags into a flagword, with a fatal error if the + string can't be parsed. */ -/* Return the name of a temporary file in the same directory as FILENAME. */ - -static char * -make_tempname (filename) - char *filename; +static flagword +parse_flags (s) + const char *s; { - static char template[] = "stXXXXXX"; - char *tmpname; - char *slash = strrchr (filename, '/'); + flagword ret; + const char *snext; + int len; + + ret = SEC_NO_FLAGS; - if (slash != (char *) NULL) + do { - *slash = 0; - tmpname = xmalloc (strlen (filename) + sizeof (template) + 1); - strcpy (tmpname, filename); - strcat (tmpname, "/"); - strcat (tmpname, template); - mktemp (tmpname); - *slash = '/'; + snext = strchr (s, ','); + if (snext == NULL) + len = strlen (s); + else + { + len = snext - s; + ++snext; + } + + if (0) ; +#define PARSE_FLAG(fname,fval) \ + else if (strncasecmp (fname, s, len) == 0) ret |= fval + PARSE_FLAG ("alloc", SEC_ALLOC); + PARSE_FLAG ("load", SEC_LOAD); + PARSE_FLAG ("noload", SEC_NEVER_LOAD); + PARSE_FLAG ("readonly", SEC_READONLY); + PARSE_FLAG ("debug", SEC_DEBUGGING); + PARSE_FLAG ("code", SEC_CODE); + PARSE_FLAG ("data", SEC_DATA); + PARSE_FLAG ("rom", SEC_ROM); + PARSE_FLAG ("share", SEC_SHARED); + 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: %s"), + "alloc, load, noload, readonly, debug, code, data, rom, share, contents"); + } + + s = snext; } - else + 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->copy = false; + p->change_vma = CHANGE_IGNORE; + p->change_lma = CHANGE_IGNORE; + p->vma_val = 0; + p->lma_val = 0; + p->set_flags = false; + p->flags = 0; + + p->next = change_sections; + change_sections = p; + + return p; +} + +/* 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) { - tmpname = xmalloc (sizeof (template)); - strcpy (tmpname, template); - mktemp (tmpname); + if (strcmp (name, tmp_list->name) == 0) + return true; } - return tmpname; + return false; +} + +/* See if a section is being removed. */ + +static boolean +is_strip_section (abfd, sec) + bfd *abfd ATTRIBUTE_UNUSED; + 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 && ! sections_copied) + 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; } /* Choose which symbol entries to copy; put the result in OSYMS. @@ -175,42 +563,179 @@ make_tempname (filename) Return the number of symbols to print. */ static unsigned int -filter_symbols (abfd, osyms, isyms, symcount) +filter_symbols (abfd, obfd, osyms, isyms, symcount) bfd *abfd; + bfd *obfd; asymbol **osyms, **isyms; - unsigned long symcount; + long symcount; { register asymbol **from = isyms, **to = osyms; - char locals_prefix = bfd_get_symbol_leading_char (abfd) == '_' ? 'L' : '.'; - unsigned int src_count = 0, dst_count = 0; + long src_count = 0, dst_count = 0; + int relocatable = (abfd->flags & (HAS_RELOC | EXEC_P | DYNAMIC)) + == HAS_RELOC; 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 ((flags & BSF_GLOBAL) /* Keep if external. */ - || bfd_get_section (sym) == &bfd_und_section - || bfd_is_com_section (bfd_get_section (sym))) + if (redefine_sym_list) + { + const char *old_name, *new_name; + + old_name = bfd_asymbol_name (sym); + new_name = lookup_sym_redefinition (old_name); + name = bfd_asymbol_name (sym) = new_name; + } + + 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 (relocatable /* Relocatable file. */ + && (flags & (BSF_GLOBAL | BSF_WEAK)) != 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; + keep = (strip_symbols != STRIP_DEBUG + && strip_symbols != STRIP_UNNEEDED + && ! convert_debugging); else /* Local symbol. */ - keep = discard_locals != locals_all - && (discard_locals != locals_start_L || - bfd_asymbol_name (sym)[0] != locals_prefix); + 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; } +static const char * +lookup_sym_redefinition (source) + const char *source; +{ + const char *result; + struct redefine_node *list; + + result = source; + + for (list = redefine_sym_list; list != NULL; list = list->next) + { + if (strcmp (source, list->source) == 0) + { + result = list->target; + break; + } + } + return result; +} + +/* Add a node to a symbol redefine list */ + +static void +redefine_list_append (source, target) + const char *source; + const char *target; +{ + struct redefine_node **p; + struct redefine_node *list; + struct redefine_node *new_node; + + for (p = &redefine_sym_list; (list = *p) != NULL; p = &list->next) + { + if (strcmp (source, list->source) == 0) + { + fatal (_("%s: Multiple redefinition of symbol \"%s\""), + "--redefine-sym", + source); + } + + if (strcmp (target, list->target) == 0) + { + fatal (_("%s: Symbol \"%s\" is target of more than one redefinition"), + "--redefine-sym", + target); + } + } + + new_node = (struct redefine_node *) xmalloc (sizeof (struct redefine_node)); + + new_node->source = strdup (source); + new_node->target = strdup (target); + new_node->next = NULL; + + *p = new_node; +} + + /* Keep only every `copy_byte'th byte in MEMHUNK, which is *SIZE bytes long. Adjust *SIZE. */ -void +static void filter_bytes (memhunk, size) char *memhunk; bfd_size_type *size; @@ -219,7 +744,10 @@ filter_bytes (memhunk, size) for (; from < end; from += interleave) *to++ = *from; - *size /= interleave; + if (*size % interleave > (bfd_size_type) copy_byte) + *size = (*size / interleave) + 1; + else + *size /= interleave; } /* Copy object file IBFD onto OBFD. */ @@ -229,83 +757,320 @@ copy_object (ibfd, obfd) bfd *ibfd; bfd *obfd; { - unsigned int symcount; + 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))) - { - nonfatal (bfd_get_filename (obfd)); - } + 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)); + 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)) + 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)))) - { - nonfatal (bfd_get_filename (ibfd)); - } + 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))) - { - fprintf (stderr, "Output file cannot represent architecture %s\n", + 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))) - { - nonfatal (bfd_get_filename(ibfd)); - } + RETURN_NONFATAL (bfd_get_filename (ibfd)); if (isympp) free (isympp); + if (osympp != isympp) free (osympp); - if (strip_symbols == strip_all && discard_locals == locals_undef) + /* 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) { - osympp = isympp = NULL; - symcount = 0; + 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)); + } + } + } + } } - else + + 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 + || sections_copied + || convert_debugging + || change_leading_char + || remove_leading_char + || redefine_sym_list + || weaken) { - osympp = isympp = (asymbol **) xmalloc (get_symtab_upper_bound (ibfd)); - symcount = bfd_canonicalize_symtab (ibfd, isympp); + /* 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 (strip_symbols == strip_debug || discard_locals != locals_undef) + if (convert_debugging && dhandle != NULL) + { + if (! write_debugging_info (obfd, dhandle, &symcount, &osympp)) { - osympp = (asymbol **) xmalloc (symcount * sizeof (asymbol *)); - symcount = filter_symbols (ibfd, osympp, isympp, symcount); + status = 1; + return; } } 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 multiple times. */ - bfd_map_over_sections (ibfd, setup_section, (void *) obfd); + /* This has to happen after the symbol table has been set. */ bfd_map_over_sections (ibfd, copy_section, (void *) obfd); - bfd_map_over_sections (ibfd, mangle_section, (void *) obfd); -} -static char * -cat (a, b, c) - char *a; - char *b; - char *c; -{ - size_t size = strlen (a) + strlen (b) + strlen (c); - char *r = xmalloc (size + 1); + 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; - strcpy (r, a); - strcat (r, b); - strcat (r, c); - return r; + /* 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 @@ -315,79 +1080,114 @@ static void copy_archive (ibfd, obfd, output_target) bfd *ibfd; bfd *obfd; - char *output_target; + 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 = cat ("./#", make_tempname (""), "cd"); + char *dir = make_tempname (bfd_get_filename (obfd)); /* Make a temp directory to hold the contents. */ - mkdir (dir, 0777); +#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); - ibfd->archive_head = this_element; - while (this_element != (bfd *) NULL) + while (!status && this_element != (bfd *) NULL) { /* Create an output file for this member. */ - char *output_name = cat (dir, "/", bfd_get_filename(this_element)); + char *output_name = concat (dir, "/", bfd_get_filename (this_element), + (char *) NULL); bfd *output_bfd = bfd_openw (output_name, output_target); + bfd *last_element; + struct stat buf; + int stat_status = 0; - if (output_bfd == (bfd *) NULL) + if (preserve_dates) { - nonfatal (output_name); + stat_status = bfd_stat_arch_elt (this_element, &buf); + if (stat_status != 0) + non_fatal (_("internal stat error on %s"), + bfd_get_filename (this_element)); } + + l = (struct name_list *) xmalloc (sizeof (struct name_list)); + l->name = output_name; + l->next = list; + list = l; + + if (output_bfd == (bfd *) NULL) + RETURN_NONFATAL (output_name); + if (!bfd_set_format (obfd, bfd_get_format (ibfd))) - { - nonfatal (bfd_get_filename (obfd)); - } + 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)) { - copy_object (this_element, output_bfd); + bfd_nonfatal (bfd_get_filename (output_bfd)); + /* Error in new object file. Don't change archive. */ + status = 1; } - bfd_close (output_bfd); - /* Open the newly output file and attatch to our list. */ + if (preserve_dates && stat_status == 0) + set_times (output_name, &buf); + + /* Open the newly output file and attach to our list. */ output_bfd = bfd_openr (output_name, output_target); - /* Mark it for deletion. */ + l->obfd = output_bfd; + *ptr = output_bfd; ptr = &output_bfd->next; - this_element->next = bfd_openr_next_archived_file (ibfd, this_element); - this_element = this_element->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)) - { - nonfatal (bfd_get_filename (obfd)); - } + RETURN_NONFATAL (bfd_get_filename (obfd)); - /* 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)) + RETURN_NONFATAL (bfd_get_filename (ibfd)); + + /* Delete all the files that we opened. */ + for (l = list; l != NULL; l = l->next) { - nonfatal (bfd_get_filename (ibfd)); + 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) - char *input_filename; - char *output_filename; - char *input_target; - char *output_target; + const char *input_filename; + const char *output_filename; + const char *input_target; + const char *output_target; { bfd *ibfd; char **matching; @@ -397,47 +1197,54 @@ copy_file (input_filename, output_filename, input_target, output_target) ibfd = bfd_openr (input_filename, input_target); if (ibfd == NULL) - { - nonfatal (input_filename); - } + RETURN_NONFATAL (input_filename); if (bfd_check_format (ibfd, bfd_archive)) { - bfd *obfd = bfd_openw (output_filename, output_target); + bfd *obfd; + + /* bfd_get_target does not return the correct value until + bfd_check_format succeeds. */ + if (output_target == NULL) + output_target = bfd_get_target (ibfd); + + obfd = bfd_openw (output_filename, output_target); if (obfd == NULL) - { - nonfatal (output_filename); - } + RETURN_NONFATAL (output_filename); + copy_archive (ibfd, obfd, output_target); } else if (bfd_check_format_matches (ibfd, bfd_object, &matching)) { - bfd *obfd = bfd_openw (output_filename, output_target); + bfd *obfd; + + /* bfd_get_target does not return the correct value until + bfd_check_format succeeds. */ + if (output_target == NULL) + output_target = bfd_get_target (ibfd); + + obfd = bfd_openw (output_filename, output_target); if (obfd == NULL) - { - nonfatal (output_filename); - } + RETURN_NONFATAL (output_filename); copy_object (ibfd, obfd); if (!bfd_close (obfd)) - { - nonfatal (output_filename); - } + RETURN_NONFATAL (output_filename); if (!bfd_close (ibfd)) - { - nonfatal (input_filename); - } + RETURN_NONFATAL (input_filename); } else { bfd_nonfatal (input_filename); - if (bfd_error == file_ambiguously_recognized) + + if (bfd_get_error () == bfd_error_file_ambiguously_recognized) { list_matching_formats (matching); free (matching); } + status = 1; } } @@ -446,61 +1253,114 @@ copy_file (input_filename, output_filename, input_target, output_target) as ISECTION in IBFD. */ static void -setup_section (ibfd, isection, obfd) +setup_section (ibfd, isection, obfdarg) bfd *ibfd; sec_ptr isection; - bfd *obfd; + PTR obfdarg; { + bfd *obfd = (bfd *) obfdarg; + struct section_list *p; sec_ptr osection; - char *err; + bfd_size_type size; + bfd_vma vma; + bfd_vma lma; + flagword flags; + const char *err; if ((bfd_get_section_flags (ibfd, isection) & SEC_DEBUGGING) != 0 - && (strip_symbols == strip_debug - || strip_symbols == strip_all - || discard_locals == locals_all)) + && (strip_symbols == STRIP_DEBUG + || strip_symbols == STRIP_UNNEEDED + || strip_symbols == STRIP_ALL + || discard_locals == LOCALS_ALL + || convert_debugging)) return; - osection = bfd_get_section_by_name (obfd, bfd_section_name (ibfd, isection)); + 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; + + osection = bfd_make_section_anyway (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; - } + err = _("making"); + goto loser; } - if (!bfd_set_section_size (obfd, - osection, - bfd_section_size (ibfd, isection))) + 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"; + err = _("size"); goto loser; } - if (bfd_set_section_vma (obfd, - osection, - bfd_section_vma (ibfd, isection)) - == false) + 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"; + 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"; + 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; } - if (!bfd_set_section_flags (obfd, osection, - bfd_get_section_flags (ibfd, isection))) + /* 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 = "flags"; + err = _("private data"); goto loser; } @@ -508,10 +1368,10 @@ setup_section (ibfd, isection, obfd) return; loser: - fprintf (stderr, "%s: %s: section `%s': error in %s: %s\n", - program_name, - bfd_get_filename (ibfd), bfd_section_name (ibfd, isection), - err, bfd_errmsg (bfd_error)); + 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; } @@ -520,42 +1380,81 @@ loser: If stripping then don't copy any relocation info. */ static void -copy_section (ibfd, isection, obfd) +copy_section (ibfd, isection, obfdarg) bfd *ibfd; sec_ptr isection; - bfd *obfd; + PTR obfdarg; { + bfd *obfd = (bfd *) obfdarg; + struct section_list *p; arelent **relpp; - int relcount; + 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_all - || discard_locals == locals_all)) + && (strip_symbols == STRIP_DEBUG + || strip_symbols == STRIP_UNNEEDED + || strip_symbols == STRIP_ALL + || discard_locals == LOCALS_ALL + || convert_debugging)) { return; } - osection = bfd_get_section_by_name (obfd, - bfd_section_name (ibfd, isection)); + 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) + if (size == 0 || osection == 0) return; - if (strip_symbols == strip_all - || bfd_get_reloc_upper_bound (ibfd, isection) == 0) - { - bfd_set_reloc (obfd, osection, (arelent **) NULL, 0); - } + + 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 (bfd_get_reloc_upper_bound (ibfd, isection)); + relpp = (arelent **) xmalloc (relsize); relcount = bfd_canonicalize_reloc (ibfd, isection, relpp, isympp); - bfd_set_reloc (obfd, osection, relpp, relcount); + 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; @@ -567,117 +1466,209 @@ copy_section (ibfd, isection, obfd) if (!bfd_get_section_contents (ibfd, isection, memhunk, (file_ptr) 0, size)) - { - nonfatal (bfd_get_filename (ibfd)); - } + 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)) - { - nonfatal (bfd_get_filename (obfd)); - } + 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); } } -/* 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. */ +/* Get all the sections. This is used when --gap-fill or --pad-to is + used. */ static void -mangle_section (ibfd, p, obfd) - bfd *ibfd; - asection *p; - bfd *obfd; +get_sections (obfd, osection, secppparg) + bfd *obfd ATTRIBUTE_UNUSED; + asection *osection; + PTR secppparg; { - p->output_section = bfd_get_section_by_name (obfd, p->name); - p->output_offset = 0; -} + asection ***secppp = (asection ***) secppparg; -/* The number of bytes to copy at once. */ -#define COPY_BUF 8192 + **secppp = osection; + ++(*secppp); +} -/* Copy file FROM to file TO, performing no translations. - Return 0 if ok, -1 if error. */ +/* 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 -simple_copy (from, to) - char *from, *to; +compare_section_lma (arg1, arg2) + const PTR arg1; + const PTR arg2; { - 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) + 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) { - close (fromfd); - return -1; + if ((flags2 & SEC_HAS_CONTENTS) != 0 + && (flags2 & SEC_LOAD) != 0) + return -1; } - while ((nread = read (fromfd, buf, sizeof buf)) > 0) + else { - if (write (tofd, buf, nread) != nread) - { - close (fromfd); - close (tofd); - return -1; - } + if ((flags2 & SEC_HAS_CONTENTS) == 0 + || (flags2 & SEC_LOAD) == 0) + return 1; } - close (fromfd); - close (tofd); - if (nread < 0) + + /* 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; } -#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 +/* Mark all the symbols which will be used in output relocations with + the BSF_KEEP flag so that those symbols will not be stripped. -/* 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. */ + Ignore relocations which will not appear in the output file. */ -static int -smart_rename (from, to) - char *from, *to; +static void +mark_symbols_used_in_relocations (ibfd, isection, symbolsarg) + bfd *ibfd; + sec_ptr isection; + PTR symbolsarg; { - struct stat s; - int ret = 0; + asymbol **symbols = (asymbol **) symbolsarg; + long relsize; + arelent **relpp; + long relcount, i; - if (lstat (to, &s)) - return -1; + /* Ignore an input section with no corresponding output section. */ + if (isection->output_section == NULL) + return; - /* 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) + 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++) { - 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); - } + 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; } - else + + if (relpp != NULL) + free (relpp); +} + +/* Write out debugging information. */ + +static boolean +write_debugging_info (obfd, dhandle, symcountp, symppp) + bfd *obfd; + PTR dhandle; + long *symcountp ATTRIBUTE_UNUSED; + asymbol ***symppp ATTRIBUTE_UNUSED; +{ + 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) { - ret = simple_copy (from, to); - if (ret == 0) - unlink (from); + 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; } - return ret; + + 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 @@ -688,32 +1679,56 @@ strip_main (argc, 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:sSgxXVv", + while ((c = getopt_long (argc, argv, "I:O:F:K:N:R:o:sSpdgxXVv", 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; + strip_symbols = STRIP_ALL; break; case 'S': case 'g': - strip_symbols = strip_debug; + case 'd': /* Historic BSD alias for -g. Used by early NetBSD. */ + strip_symbols = STRIP_DEBUG; + break; + case OPTION_STRIP_UNNEEDED: + strip_symbols = STRIP_UNNEEDED; + break; + case 'K': + add_specific_symbol (optarg, &keep_specific_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; + discard_locals = LOCALS_ALL; break; case 'X': - discard_locals = locals_start_L; + discard_locals = LOCALS_START_L; break; case 'v': verbose = true; @@ -731,37 +1746,56 @@ strip_main (argc, argv) } if (show_version) - { - printf ("GNU %s version %s\n", program_name, program_version); - exit (0); - } + print_version ("strip"); /* Default is to strip all symbols. */ - if (strip_symbols == strip_undef && discard_locals == locals_undef) - strip_symbols = strip_all; + if (strip_symbols == STRIP_UNDEF + && discard_locals == LOCALS_UNDEF + && strip_specific_list == NULL) + strip_symbols = STRIP_ALL; if (output_target == (char *) NULL) output_target = input_target; i = optind; - if (i == argc) + if (i == argc + || (output_file != NULL && (i + 1) < argc)) strip_usage (stderr, 1); for (; i < argc; i++) { 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; + } + } - char *tmpname = make_tempname (argv[i]); + 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) { - smart_rename (tmpname, argv[i]); + if (preserve_dates) + set_times (tmpname, &statbuf); + if (output_file == NULL) + smart_rename (tmpname, argv[i], preserve_dates); status = hold_status; } else unlink (tmpname); - free (tmpname); + if (output_file == NULL) + free (tmpname); } return 0; @@ -772,83 +1806,367 @@ copy_main (argc, argv) int argc; char *argv[]; { - char *input_filename, *output_filename; + 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:s:O:d:F:SgxXVv", + while ((c = getopt_long (argc, argv, "b:i:I:j:K:N:s:O:d:F:L:R:SpgxXVvW:", copy_options, (int *) 0)) != EOF) { switch (c) { case 'b': - copy_byte = atoi(optarg); + copy_byte = atoi (optarg); if (copy_byte < 0) - { - fprintf (stderr, "%s: byte number must be non-negative\n", - program_name); - exit (1); - } + fatal (_("byte number must be non-negative")); break; + case 'i': - interleave = atoi(optarg); + interleave = atoi (optarg); if (interleave < 1) - { - fprintf(stderr, "%s: interleave must be positive\n", - program_name); - exit (1); - } + fatal (_("interleave must be positive")); break; + case 'I': case 's': /* "source" - 'I' is preferred */ input_target = optarg; + break; + case 'O': case 'd': /* "destination" - 'O' is preferred */ output_target = optarg; break; + case 'F': input_target = output_target = optarg; break; + + case 'j': + p = find_section_list (optarg, true); + if (p->remove) + fatal (_("%s both copied and removed"), optarg); + p->copy = true; + sections_copied = true; + break; + + case 'R': + p = find_section_list (optarg, true); + if (p->copy) + fatal (_("%s both copied and removed"), optarg); + p->remove = true; + sections_removed = true; + break; + case 'S': - strip_symbols = strip_all; + strip_symbols = STRIP_ALL; break; + case 'g': - strip_symbols = strip_debug; + strip_symbols = STRIP_DEBUG; + break; + + case OPTION_STRIP_UNNEEDED: + strip_symbols = STRIP_UNNEEDED; + break; + + case 'K': + add_specific_symbol (optarg, &keep_specific_list); + break; + + case 'N': + add_specific_symbol (optarg, &strip_specific_list); + break; + + case 'L': + add_specific_symbol (optarg, &localize_specific_list); + break; + + case 'W': + add_specific_symbol (optarg, &weaken_specific_list); break; + + case 'p': + preserve_dates = true; + break; + case 'x': - discard_locals = locals_all; + discard_locals = LOCALS_ALL; break; + case 'X': - discard_locals = locals_start_L; + discard_locals = LOCALS_START_L; break; + case 'v': verbose = true; 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 %s"), "--add-section"); + + 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 = NULL; + bfd_vma val; + enum change_action what = CHANGE_IGNORE; + + switch (c) + { + case OPTION_CHANGE_SECTION_ADDRESS: + option = "--change-section-address"; + break; + case OPTION_CHANGE_SECTION_LMA: + option = "--change-section-lma"; + break; + case OPTION_CHANGE_SECTION_VMA: + option = "--change-section-vma"; + break; + } + + s = strchr (optarg, '='); + if (s == NULL) + { + s = strchr (optarg, '+'); + if (s == NULL) + { + s = strchr (optarg, '-'); + if (s == NULL) + 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_REDEFINE_SYM: + { + /* Push this redefinition onto redefine_symbol_list. */ + + int len; + const char *s; + const char *nextarg; + char *source, *target; + + s = strchr (optarg, '='); + if (s == NULL) + { + fatal (_("bad format for %s"), "--redefine-sym"); + } + + len = s - optarg; + source = (char *) xmalloc (len + 1); + strncpy (source, optarg, len); + source[len] = '\0'; + + nextarg = s + 1; + len = strlen (nextarg); + target = (char *) xmalloc (len + 1); + strcpy (target, nextarg); + + redefine_list_append (source, target); + + free (source); + free (target); + } + break; + + case OPTION_SET_SECTION_FLAGS: + { + const char *s; + int len; + char *name; + + s = strchr (optarg, '='); + if (s == NULL) + fatal (_("bad format for %s"), "--set-section-flags"); + + len = s - optarg; + name = (char *) xmalloc (len + 1); + strncpy (name, optarg, len); + name[len] = '\0'; + + p = find_section_list (name, true); + + p->set_flags = true; + p->flags = parse_flags (s + 1); + } + break; + + case OPTION_SET_START: + set_start = parse_vma (optarg, "--set-start"); + set_start_set = true; + break; + + case OPTION_SREC_LEN: + Chunk = parse_vma (optarg, "--srec-len"); + break; + + case OPTION_SREC_FORCES3: + S3Forced = true; + break; + case 0: break; /* we've been given a long option */ + case 'h': copy_usage (stdout, 0); + default: copy_usage (stderr, 1); } } if (show_version) - { - printf ("GNU %s version %s\n", program_name, program_version); - exit (0); - } + print_version ("objcopy"); if (copy_byte >= interleave) - { - fprintf (stderr, "%s: byte number must be less than interleave\n", - program_name); - exit (1); - } + fatal (_("byte number must be less than interleave")); if (optind == argc || optind + 2 < argc) copy_usage (stderr, 1); @@ -858,27 +2176,77 @@ copy_main (argc, argv) output_filename = argv[optind + 1]; /* Default is to strip no symbols. */ - if (strip_symbols == strip_undef && discard_locals == locals_undef) - strip_symbols = strip_none; + if (strip_symbols == STRIP_UNDEF && discard_locals == LOCALS_UNDEF) + strip_symbols = STRIP_NONE; if (output_target == (char *) NULL) 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) - smart_rename (tmpname, input_filename); + { + 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 (_("%s %s%c0x%s never used"), + "--change-section-vma", + p->name, + p->change_vma == CHANGE_SET ? '=' : '+', + buff); + } + + if (p->change_lma != CHANGE_IGNORE) + { + char buff [20]; + + sprintf_vma (buff, p->lma_val); + + /* xgettext:c-format */ + non_fatal (_("%s %s%c0x%s never used"), + "--change-section-lma", + p->name, + p->change_lma == CHANGE_SET ? '=' : '+', + buff); + } + } + } } return 0; @@ -889,16 +2257,35 @@ 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]; - strip_symbols = strip_undef; - discard_locals = locals_undef; + 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")); +#ifdef HAVE_DOS_BASED_FILE_SYSTEM + /* Drop the .exe suffix, if any. */ + if (i > 4 && FILENAME_CMP (program_name + i - 4, ".exe") == 0) + { + i -= 4; + program_name[i] = '\0'; + } +#endif + is_strip = (i >= 5 && FILENAME_CMP (program_name + i - 5, "strip") == 0); } if (is_strip) @@ -906,5 +2293,7 @@ main (argc, argv) else copy_main (argc, argv); + END_PROGRESS (program_name); + return status; }