X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=binutils%2Fobjcopy.c;h=61b33c7e40517d937b24bf8353453a1b7d76df17;hb=06a6207a1ab458521656f293bb1ca8fd013d6f7c;hp=b9932765c348039cfbfd858ef43978eab44f970e;hpb=32866df75ece22ec1fd88e99e3c5effe9238e072;p=deliverable%2Fbinutils-gdb.git diff --git a/binutils/objcopy.c b/binutils/objcopy.c index b9932765c3..61b33c7e40 100644 --- a/binutils/objcopy.c +++ b/binutils/objcopy.c @@ -1,7 +1,5 @@ /* objcopy.c -- copy object file from input to output, optionally massaging it. - Copyright 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, - 2001, 2002, 2003, 2004, 2005, 2006, 2007 - Free Software Foundation, Inc. + Copyright (C) 1991-2020 Free Software Foundation, Inc. This file is part of GNU Binutils. @@ -30,26 +28,47 @@ #include "filenames.h" #include "fnmatch.h" #include "elf-bfd.h" -#include -#include "libbfd.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 +#include "coff/internal.h" +#include "libcoff.h" +#include "safe-ctype.h" + +/* FIXME: See bfd/peXXigen.c for why we include an architecture specific + header in generic PE code. */ +#include "coff/i386.h" +#include "coff/pe.h" + +static bfd_vma pe_file_alignment = (bfd_vma) -1; +static bfd_vma pe_heap_commit = (bfd_vma) -1; +static bfd_vma pe_heap_reserve = (bfd_vma) -1; +static bfd_vma pe_image_base = (bfd_vma) -1; +static bfd_vma pe_section_alignment = (bfd_vma) -1; +static bfd_vma pe_stack_commit = (bfd_vma) -1; +static bfd_vma pe_stack_reserve = (bfd_vma) -1; +static short pe_subsystem = -1; +static short pe_major_subsystem_version = -1; +static short pe_minor_subsystem_version = -1; + +struct is_specified_symbol_predicate_data { - const char *name; - struct symlist *next; + const char * name; + bfd_boolean found; }; -/* A list to support redefine_sym. */ +/* A node includes symbol name mapping to support redefine_sym. */ struct redefine_node { char *source; char *target; - struct redefine_node *next; +}; + +struct addsym_node +{ + struct addsym_node *next; + char * symdef; + long symval; + flagword flags; + char * section; + char * othersym; }; typedef struct section_rename @@ -64,64 +83,79 @@ section_rename; /* List of sections to be renamed. */ static section_rename *section_rename_list; -#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. */ +/* If `copy_byte' >= 0, copy 'copy_width' byte(s) of every `interleave' bytes. */ static int copy_byte = -1; -static int interleave = 4; +static int interleave = 0; /* Initialised to 4 in copy_main(). */ +static int copy_width = 1; static bfd_boolean verbose; /* Print file and target names. */ static bfd_boolean preserve_dates; /* Preserve input file timestamp. */ -static int status = 0; /* Exit status. */ +static int deterministic = -1; /* Enable deterministic archives. */ +static int status = 0; /* Exit status. */ + +static bfd_boolean merge_notes = FALSE; /* Merge note sections. */ + +typedef struct merged_note_section +{ + asection * sec; /* The section that is being merged. */ + bfd_byte * contents;/* New contents of the section. */ + bfd_size_type size; /* New size of the section. */ + struct merged_note_section * next; /* Link to next merged note section. */ +} merged_note_section; enum strip_action - { - STRIP_UNDEF, - STRIP_NONE, /* Don't strip. */ - STRIP_DEBUG, /* Strip all debugger symbols. */ - STRIP_UNNEEDED, /* Strip unnecessary symbols. */ - STRIP_NONDEBUG, /* Strip everything but debug info. */ - STRIP_ALL /* Strip all symbols. */ - }; +{ + STRIP_UNDEF, + STRIP_NONE, /* Don't strip. */ + STRIP_DEBUG, /* Strip all debugger symbols. */ + STRIP_UNNEEDED, /* Strip unnecessary symbols. */ + STRIP_NONDEBUG, /* Strip everything but debug info. */ + STRIP_DWO, /* Strip all DWO info. */ + STRIP_NONDWO, /* Strip everything but DWO info. */ + STRIP_ALL /* Strip all symbols. */ +}; /* Which symbols to remove. */ -static enum strip_action strip_symbols; +static enum strip_action strip_symbols = STRIP_UNDEF; 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. */ 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. */ + const char * pattern; /* Section name pattern. */ bfd_boolean used; /* Whether this entry was used. */ - bfd_boolean remove; /* Whether to remove this section. */ - bfd_boolean copy; /* Whether to copy this section. */ - enum change_action change_vma;/* Whether to change or set VMA. */ + + unsigned int context; /* What to do with matching sections. */ + /* Flag bits used in the context field. + COPY and REMOVE are mutually exlusive. SET and ALTER are mutually exclusive. */ +#define SECTION_CONTEXT_REMOVE (1 << 0) /* Remove this section. */ +#define SECTION_CONTEXT_COPY (1 << 1) /* Copy this section, delete all non-copied section. */ +#define SECTION_CONTEXT_KEEP (1 << 2) /* Keep this section. */ +#define SECTION_CONTEXT_SET_VMA (1 << 3) /* Set the sections' VMA address. */ +#define SECTION_CONTEXT_ALTER_VMA (1 << 4) /* Increment or decrement the section's VMA address. */ +#define SECTION_CONTEXT_SET_LMA (1 << 5) /* Set the sections' LMA address. */ +#define SECTION_CONTEXT_ALTER_LMA (1 << 6) /* Increment or decrement the section's LMA address. */ +#define SECTION_CONTEXT_SET_FLAGS (1 << 7) /* Set the section's flags. */ +#define SECTION_CONTEXT_REMOVE_RELOCS (1 << 8) /* Remove relocations for this section. */ +#define SECTION_CONTEXT_SET_ALIGNMENT (1 << 9) /* Set alignment for section. */ + 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. */ - bfd_boolean set_flags; /* Whether to set the section flags. */ flagword flags; /* What to set the section flags to. */ + unsigned int alignment; /* Alignment of output section. */ }; static struct section_list *change_sections; @@ -175,6 +209,12 @@ struct section_add /* List of sections to add to the output BFD. */ static struct section_add *add_sections; +/* List of sections to update in the output BFD. */ +static struct section_add *update_sections; + +/* List of sections to dump from the output BFD. */ +static struct section_add *dump_sections; + /* If non-NULL the argument to --add-gnu-debuglink. This should be the filename to store in the .gnu_debuglink section. */ static const char * gnu_debuglink_filename = NULL; @@ -182,6 +222,20 @@ static const char * gnu_debuglink_filename = NULL; /* Whether to convert debugging information. */ static bfd_boolean convert_debugging = FALSE; +/* Whether to compress/decompress DWARF debug sections. */ +static enum +{ + nothing = 0, + compress = 1 << 0, + compress_zlib = compress | 1 << 1, + compress_gnu_zlib = compress | 1 << 2, + compress_gabi_zlib = compress | 1 << 3, + decompress = 1 << 4 +} do_debug_sections = nothing; + +/* Whether to generate ELF common symbols with the STT_COMMON type. */ +static enum bfd_link_elf_stt_common do_elf_stt_common = unchanged; + /* Whether to change the leading character in symbol names. */ static bfd_boolean change_leading_char = FALSE; @@ -196,14 +250,25 @@ static bfd_boolean localize_hidden = FALSE; /* List of symbols to strip, keep, localize, keep-global, weaken, or redefine. */ -static struct symlist *strip_specific_list = NULL; -static struct symlist *strip_unneeded_list = NULL; -static struct symlist *keep_specific_list = NULL; -static struct symlist *localize_specific_list = NULL; -static struct symlist *globalize_specific_list = NULL; -static struct symlist *keepglobal_specific_list = NULL; -static struct symlist *weaken_specific_list = NULL; -static struct redefine_node *redefine_sym_list = NULL; +static htab_t strip_specific_htab = NULL; +static htab_t strip_unneeded_htab = NULL; +static htab_t keep_specific_htab = NULL; +static htab_t localize_specific_htab = NULL; +static htab_t globalize_specific_htab = NULL; +static htab_t keepglobal_specific_htab = NULL; +static htab_t weaken_specific_htab = NULL; +static htab_t redefine_specific_htab = NULL; +static htab_t redefine_specific_reverse_htab = NULL; +static struct addsym_node *add_sym_list = NULL, **add_sym_tail = &add_sym_list; +static int add_symbols = 0; + +static char *strip_specific_buffer = NULL; +static char *strip_unneeded_buffer = NULL; +static char *keep_specific_buffer = NULL; +static char *localize_specific_buffer = NULL; +static char *globalize_specific_buffer = NULL; +static char *keepglobal_specific_buffer = NULL; +static char *weaken_specific_buffer = NULL; /* If this is TRUE, we weaken global symbols (set BSF_WEAK). */ static bfd_boolean weaken = FALSE; @@ -223,81 +288,122 @@ static bfd_boolean extract_symbol = FALSE; of bytes within each output section. */ static int reverse_bytes = 0; +/* For Coff objects, we may want to allow or disallow long section names, + or preserve them where found in the inputs. Debug info relies on them. */ +enum long_section_name_handling +{ + DISABLE, + ENABLE, + KEEP +}; + +/* The default long section handling mode is to preserve them. + This is also the only behaviour for 'strip'. */ +static enum long_section_name_handling long_section_names = KEEP; /* 150 isn't special; it's just an arbitrary non-ASCII char value. */ enum command_line_switch - { - OPTION_ADD_SECTION=150, - OPTION_CHANGE_ADDRESSES, - OPTION_CHANGE_LEADING_CHAR, - OPTION_CHANGE_START, - OPTION_CHANGE_SECTION_ADDRESS, - OPTION_CHANGE_SECTION_LMA, - OPTION_CHANGE_SECTION_VMA, - OPTION_CHANGE_WARNINGS, - OPTION_DEBUGGING, - OPTION_GAP_FILL, - OPTION_NO_CHANGE_WARNINGS, - OPTION_PAD_TO, - OPTION_REMOVE_LEADING_CHAR, - OPTION_SET_SECTION_FLAGS, - OPTION_SET_START, - OPTION_STRIP_UNNEEDED, - OPTION_WEAKEN, - OPTION_REDEFINE_SYM, - OPTION_REDEFINE_SYMS, - OPTION_SREC_LEN, - OPTION_SREC_FORCES3, - OPTION_STRIP_SYMBOLS, - OPTION_STRIP_UNNEEDED_SYMBOL, - OPTION_STRIP_UNNEEDED_SYMBOLS, - OPTION_KEEP_SYMBOLS, - OPTION_LOCALIZE_HIDDEN, - OPTION_LOCALIZE_SYMBOLS, - OPTION_GLOBALIZE_SYMBOL, - OPTION_GLOBALIZE_SYMBOLS, - OPTION_KEEPGLOBAL_SYMBOLS, - OPTION_WEAKEN_SYMBOLS, - OPTION_RENAME_SECTION, - OPTION_ALT_MACH_CODE, - OPTION_PREFIX_SYMBOLS, - OPTION_PREFIX_SECTIONS, - OPTION_PREFIX_ALLOC_SECTIONS, - OPTION_FORMATS_INFO, - OPTION_ADD_GNU_DEBUGLINK, - OPTION_ONLY_KEEP_DEBUG, - OPTION_KEEP_FILE_SYMBOLS, - OPTION_READONLY_TEXT, - OPTION_WRITABLE_TEXT, - OPTION_PURE, - OPTION_IMPURE, - OPTION_EXTRACT_SYMBOL, - OPTION_REVERSE_BYTES - }; +{ + OPTION_ADD_SECTION=150, + OPTION_ADD_GNU_DEBUGLINK, + OPTION_ADD_SYMBOL, + OPTION_ALT_MACH_CODE, + OPTION_CHANGE_ADDRESSES, + OPTION_CHANGE_LEADING_CHAR, + OPTION_CHANGE_SECTION_ADDRESS, + OPTION_CHANGE_SECTION_LMA, + OPTION_CHANGE_SECTION_VMA, + OPTION_CHANGE_START, + OPTION_CHANGE_WARNINGS, + OPTION_COMPRESS_DEBUG_SECTIONS, + OPTION_DEBUGGING, + OPTION_DECOMPRESS_DEBUG_SECTIONS, + OPTION_DUMP_SECTION, + OPTION_ELF_STT_COMMON, + OPTION_EXTRACT_DWO, + OPTION_EXTRACT_SYMBOL, + OPTION_FILE_ALIGNMENT, + OPTION_FORMATS_INFO, + OPTION_GAP_FILL, + OPTION_GLOBALIZE_SYMBOL, + OPTION_GLOBALIZE_SYMBOLS, + OPTION_HEAP, + OPTION_IMAGE_BASE, + OPTION_IMPURE, + OPTION_INTERLEAVE_WIDTH, + OPTION_KEEPGLOBAL_SYMBOLS, + OPTION_KEEP_FILE_SYMBOLS, + OPTION_KEEP_SECTION, + OPTION_KEEP_SYMBOLS, + OPTION_LOCALIZE_HIDDEN, + OPTION_LOCALIZE_SYMBOLS, + OPTION_LONG_SECTION_NAMES, + OPTION_MERGE_NOTES, + OPTION_NO_MERGE_NOTES, + OPTION_NO_CHANGE_WARNINGS, + OPTION_ONLY_KEEP_DEBUG, + OPTION_PAD_TO, + OPTION_PREFIX_ALLOC_SECTIONS, + OPTION_PREFIX_SECTIONS, + OPTION_PREFIX_SYMBOLS, + OPTION_PURE, + OPTION_READONLY_TEXT, + OPTION_REDEFINE_SYM, + OPTION_REDEFINE_SYMS, + OPTION_REMOVE_LEADING_CHAR, + OPTION_REMOVE_RELOCS, + OPTION_RENAME_SECTION, + OPTION_REVERSE_BYTES, + OPTION_PE_SECTION_ALIGNMENT, + OPTION_SET_SECTION_FLAGS, + OPTION_SET_SECTION_ALIGNMENT, + OPTION_SET_START, + OPTION_SREC_FORCES3, + OPTION_SREC_LEN, + OPTION_STACK, + OPTION_STRIP_DWO, + OPTION_STRIP_SYMBOLS, + OPTION_STRIP_UNNEEDED, + OPTION_STRIP_UNNEEDED_SYMBOL, + OPTION_STRIP_UNNEEDED_SYMBOLS, + OPTION_SUBSYSTEM, + OPTION_UPDATE_SECTION, + OPTION_VERILOG_DATA_WIDTH, + OPTION_WEAKEN, + OPTION_WEAKEN_SYMBOLS, + OPTION_WRITABLE_TEXT +}; /* Options to handle if running as "strip". */ static struct option strip_options[] = { + {"disable-deterministic-archives", no_argument, 0, 'U'}, {"discard-all", no_argument, 0, 'x'}, {"discard-locals", no_argument, 0, 'X'}, + {"enable-deterministic-archives", no_argument, 0, 'D'}, {"format", required_argument, 0, 'F'}, /* Obsolete */ {"help", no_argument, 0, 'h'}, {"info", no_argument, 0, OPTION_FORMATS_INFO}, {"input-format", required_argument, 0, 'I'}, /* Obsolete */ {"input-target", required_argument, 0, 'I'}, {"keep-file-symbols", no_argument, 0, OPTION_KEEP_FILE_SYMBOLS}, + {"keep-section", required_argument, 0, OPTION_KEEP_SECTION}, {"keep-symbol", required_argument, 0, 'K'}, + {"merge-notes", no_argument, 0, 'M'}, + {"no-merge-notes", no_argument, 0, OPTION_NO_MERGE_NOTES}, {"only-keep-debug", no_argument, 0, OPTION_ONLY_KEEP_DEBUG}, + {"output-file", required_argument, 0, 'o'}, {"output-format", required_argument, 0, 'O'}, /* Obsolete */ {"output-target", required_argument, 0, 'O'}, - {"output-file", required_argument, 0, 'o'}, {"preserve-dates", no_argument, 0, 'p'}, {"remove-section", required_argument, 0, 'R'}, + {"remove-relocations", required_argument, 0, OPTION_REMOVE_RELOCS}, {"strip-all", no_argument, 0, 's'}, {"strip-debug", no_argument, 0, 'S'}, - {"strip-unneeded", no_argument, 0, OPTION_STRIP_UNNEEDED}, + {"strip-dwo", no_argument, 0, OPTION_STRIP_DWO}, {"strip-symbol", required_argument, 0, 'N'}, + {"strip-unneeded", no_argument, 0, OPTION_STRIP_UNNEEDED}, {"target", required_argument, 0, 'F'}, {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, @@ -311,9 +417,10 @@ static struct option copy_options[] = { {"add-gnu-debuglink", required_argument, 0, OPTION_ADD_GNU_DEBUGLINK}, {"add-section", required_argument, 0, OPTION_ADD_SECTION}, + {"add-symbol", required_argument, 0, OPTION_ADD_SYMBOL}, + {"adjust-section-vma", required_argument, 0, OPTION_CHANGE_SECTION_ADDRESS}, {"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}, {"alt-machine-code", required_argument, 0, OPTION_ALT_MACH_CODE}, {"binary-architecture", required_argument, 0, 'B'}, @@ -325,28 +432,43 @@ static struct option copy_options[] = {"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}, + {"compress-debug-sections", optional_argument, 0, OPTION_COMPRESS_DEBUG_SECTIONS}, {"debugging", no_argument, 0, OPTION_DEBUGGING}, + {"decompress-debug-sections", no_argument, 0, OPTION_DECOMPRESS_DEBUG_SECTIONS}, + {"disable-deterministic-archives", no_argument, 0, 'U'}, {"discard-all", no_argument, 0, 'x'}, {"discard-locals", no_argument, 0, 'X'}, + {"dump-section", required_argument, 0, OPTION_DUMP_SECTION}, + {"elf-stt-common", required_argument, 0, OPTION_ELF_STT_COMMON}, + {"enable-deterministic-archives", no_argument, 0, 'D'}, + {"extract-dwo", no_argument, 0, OPTION_EXTRACT_DWO}, {"extract-symbol", no_argument, 0, OPTION_EXTRACT_SYMBOL}, + {"file-alignment", required_argument, 0, OPTION_FILE_ALIGNMENT}, {"format", required_argument, 0, 'F'}, /* Obsolete */ {"gap-fill", required_argument, 0, OPTION_GAP_FILL}, {"globalize-symbol", required_argument, 0, OPTION_GLOBALIZE_SYMBOL}, {"globalize-symbols", required_argument, 0, OPTION_GLOBALIZE_SYMBOLS}, + {"heap", required_argument, 0, OPTION_HEAP}, {"help", no_argument, 0, 'h'}, + {"image-base", required_argument, 0 , OPTION_IMAGE_BASE}, {"impure", no_argument, 0, OPTION_IMPURE}, {"info", no_argument, 0, OPTION_FORMATS_INFO}, {"input-format", required_argument, 0, 'I'}, /* Obsolete */ {"input-target", required_argument, 0, 'I'}, - {"interleave", required_argument, 0, 'i'}, + {"interleave", optional_argument, 0, 'i'}, + {"interleave-width", required_argument, 0, OPTION_INTERLEAVE_WIDTH}, {"keep-file-symbols", no_argument, 0, OPTION_KEEP_FILE_SYMBOLS}, {"keep-global-symbol", required_argument, 0, 'G'}, {"keep-global-symbols", required_argument, 0, OPTION_KEEPGLOBAL_SYMBOLS}, + {"keep-section", required_argument, 0, OPTION_KEEP_SECTION}, {"keep-symbol", required_argument, 0, 'K'}, {"keep-symbols", required_argument, 0, OPTION_KEEP_SYMBOLS}, {"localize-hidden", no_argument, 0, OPTION_LOCALIZE_HIDDEN}, {"localize-symbol", required_argument, 0, 'L'}, {"localize-symbols", required_argument, 0, OPTION_LOCALIZE_SYMBOLS}, + {"long-section-names", required_argument, 0, OPTION_LONG_SECTION_NAMES}, + {"merge-notes", no_argument, 0, 'M'}, + {"no-merge-notes", no_argument, 0, OPTION_NO_MERGE_NOTES}, {"no-adjust-warnings", no_argument, 0, OPTION_NO_CHANGE_WARNINGS}, {"no-change-warnings", no_argument, 0, OPTION_NO_CHANGE_WARNINGS}, {"only-keep-debug", no_argument, 0, OPTION_ONLY_KEEP_DEBUG}, @@ -354,9 +476,9 @@ static struct option copy_options[] = {"output-format", required_argument, 0, 'O'}, /* Obsolete */ {"output-target", required_argument, 0, 'O'}, {"pad-to", required_argument, 0, OPTION_PAD_TO}, - {"prefix-symbols", required_argument, 0, OPTION_PREFIX_SYMBOLS}, - {"prefix-sections", required_argument, 0, OPTION_PREFIX_SECTIONS}, {"prefix-alloc-sections", required_argument, 0, OPTION_PREFIX_ALLOC_SECTIONS}, + {"prefix-sections", required_argument, 0, OPTION_PREFIX_SECTIONS}, + {"prefix-symbols", required_argument, 0, OPTION_PREFIX_SYMBOLS}, {"preserve-dates", no_argument, 0, 'p'}, {"pure", no_argument, 0, OPTION_PURE}, {"readonly-text", no_argument, 0, OPTION_READONLY_TEXT}, @@ -364,21 +486,29 @@ static struct option copy_options[] = {"redefine-syms", required_argument, 0, OPTION_REDEFINE_SYMS}, {"remove-leading-char", no_argument, 0, OPTION_REMOVE_LEADING_CHAR}, {"remove-section", required_argument, 0, 'R'}, + {"remove-relocations", required_argument, 0, OPTION_REMOVE_RELOCS}, {"rename-section", required_argument, 0, OPTION_RENAME_SECTION}, {"reverse-bytes", required_argument, 0, OPTION_REVERSE_BYTES}, + {"section-alignment", required_argument, 0, OPTION_PE_SECTION_ALIGNMENT}, {"set-section-flags", required_argument, 0, OPTION_SET_SECTION_FLAGS}, + {"set-section-alignment", required_argument, 0, OPTION_SET_SECTION_ALIGNMENT}, {"set-start", required_argument, 0, OPTION_SET_START}, - {"srec-len", required_argument, 0, OPTION_SREC_LEN}, {"srec-forceS3", no_argument, 0, OPTION_SREC_FORCES3}, + {"srec-len", required_argument, 0, OPTION_SREC_LEN}, + {"stack", required_argument, 0, OPTION_STACK}, {"strip-all", no_argument, 0, 'S'}, {"strip-debug", no_argument, 0, 'g'}, + {"strip-dwo", no_argument, 0, OPTION_STRIP_DWO}, + {"strip-symbol", required_argument, 0, 'N'}, + {"strip-symbols", required_argument, 0, OPTION_STRIP_SYMBOLS}, {"strip-unneeded", no_argument, 0, OPTION_STRIP_UNNEEDED}, {"strip-unneeded-symbol", required_argument, 0, OPTION_STRIP_UNNEEDED_SYMBOL}, {"strip-unneeded-symbols", required_argument, 0, OPTION_STRIP_UNNEEDED_SYMBOLS}, - {"strip-symbol", required_argument, 0, 'N'}, - {"strip-symbols", required_argument, 0, OPTION_STRIP_SYMBOLS}, + {"subsystem", required_argument, 0, OPTION_SUBSYSTEM}, {"target", required_argument, 0, 'F'}, + {"update-section", required_argument, 0, OPTION_UPDATE_SECTION}, {"verbose", no_argument, 0, 'v'}, + {"verilog-data-width", required_argument, 0, OPTION_VERILOG_DATA_WIDTH}, {"version", no_argument, 0, 'V'}, {"weaken", no_argument, 0, OPTION_WEAKEN}, {"weaken-symbol", required_argument, 0, 'W'}, @@ -396,31 +526,33 @@ extern char *program_name; -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 +/* The maximum length of an S record. This variable is defined in srec.c and can be modified by the --srec-len parameter. */ -extern unsigned int Chunk; +extern unsigned int _bfd_srec_len; /* Restrict the generation of Srecords to type S3 only. - This variable is declare in bfd/srec.c and can be toggled + This variable is defined in bfd/srec.c and can be toggled on by the --srec-forceS3 command line switch. */ -extern bfd_boolean S3Forced; +extern bfd_boolean _bfd_srec_forceS3; -/* Defined in bfd/binary.c. Used to set architecture and machine of input - binary files. */ -extern enum bfd_architecture bfd_external_binary_architecture; -extern unsigned long bfd_external_machine; +/* Width of data in bytes for verilog output. + This variable is declared in bfd/verilog.c and can be modified by + the --verilog-data-width parameter. */ +extern unsigned int VerilogDataWidth; /* Forward declarations. */ static void setup_section (bfd *, asection *, void *); static void setup_bfd_headers (bfd *, bfd *); +static void copy_relocations_in_section (bfd *, asection *, void *); static void copy_section (bfd *, asection *, void *); static void get_sections (bfd *, asection *, void *); static int compare_section_lma (const void *, const void *); static void mark_symbols_used_in_relocations (bfd *, asection *, void *); static bfd_boolean write_debugging_info (bfd *, void *, long *, asymbol ***); static const char *lookup_sym_redefinition (const char *); +static const char *find_section_rename (const char *, flagword *); -static void +ATTRIBUTE_NORETURN static void copy_usage (FILE *stream, int exit_status) { fprintf (stream, _("Usage: %s [option(s)] in-file [out-file]\n"), program_name); @@ -429,22 +561,39 @@ copy_usage (FILE *stream, int exit_status) fprintf (stream, _("\ -I --input-target Assume input file is in format \n\ -O --output-target Create an output file in format \n\ - -B --binary-architecture Set arch of output file, when input is binary\n\ + -B --binary-architecture Set output arch, when input is arch-less\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\ + -p --preserve-dates Copy modified/access timestamps to the output\n")); + if (DEFAULT_AR_DETERMINISTIC) + fprintf (stream, _("\ + -D --enable-deterministic-archives\n\ + Produce deterministic output when stripping archives (default)\n\ + -U --disable-deterministic-archives\n\ + Disable -D behavior\n")); + else + fprintf (stream, _("\ + -D --enable-deterministic-archives\n\ + Produce deterministic output when stripping archives\n\ + -U --disable-deterministic-archives\n\ + Disable -D behavior (default)\n")); + fprintf (stream, _("\ -j --only-section Only copy section into the output\n\ --add-gnu-debuglink= Add section .gnu_debuglink linking to \n\ -R --remove-section Remove section from the output\n\ + --remove-relocations Remove relocations from section \n\ -S --strip-all Remove all symbol and relocation information\n\ -g --strip-debug Remove all debugging symbols & sections\n\ + --strip-dwo Remove all DWO sections\n\ --strip-unneeded Remove all symbols not needed by relocations\n\ -N --strip-symbol Do not copy symbol \n\ --strip-unneeded-symbol \n\ Do not copy symbol unless needed by\n\ relocations\n\ --only-keep-debug Strip everything but the debug information\n\ + --extract-dwo Copy only DWO sections\n\ --extract-symbol Remove section contents but keep symbols\n\ + --keep-section Do not strip section \n\ -K --keep-symbol Do not strip symbol \n\ --keep-file-symbols Do not strip file symbol(s)\n\ --localize-hidden Turn all ELF hidden symbols into locals\n\ @@ -456,7 +605,8 @@ copy_usage (FILE *stream, int exit_status) -w --wildcard Permit wildcard in symbol comparison\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\ + -i --interleave[=] Only copy N out of every bytes\n\ + --interleave-width Set N for --interleave\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\ @@ -475,8 +625,16 @@ copy_usage (FILE *stream, int exit_status) Warn if a named section does not exist\n\ --set-section-flags =\n\ Set section 's properties to \n\ + --set-section-alignment =\n\ + Set section 's alignment to bytes\n\ --add-section = Add section found in to output\n\ + --update-section =\n\ + Update contents of section with\n\ + contents found in \n\ + --dump-section = Dump the contents of section into \n\ --rename-section =[,] Rename section to \n\ + --long-section-names {enable|disable|keep}\n\ + Handle long section names in Coff objects.\n\ --change-leading-char Force output format's leading character style\n\ --remove-leading-char Remove leading character from global symbols\n\ --reverse-bytes= Reverse bytes at a time, in output sections with content\n\ @@ -494,6 +652,7 @@ copy_usage (FILE *stream, int exit_status) --globalize-symbols --globalize-symbol for all in \n\ --keep-global-symbols -G for all symbols listed in \n\ --weaken-symbols -W for all symbols listed in \n\ + --add-symbol =[
:][,] Add a symbol\n\ --alt-machine-code Use the target's 'th alternative machine\n\ --writable-text Mark the output text as writable\n\ --readonly-text Make the output text write protected\n\ @@ -504,6 +663,23 @@ copy_usage (FILE *stream, int exit_status) --prefix-alloc-sections \n\ Add to start of every allocatable\n\ section name\n\ + --file-alignment Set PE file alignment to \n\ + --heap [,] Set PE reserve/commit heap to /\n\ + \n\ + --image-base
Set PE image base to
\n\ + --section-alignment Set PE section alignment to \n\ + --stack [,] Set PE reserve/commit stack to /\n\ + \n\ + --subsystem [:]\n\ + Set PE subsystem to [& ]\n\ + --compress-debug-sections[={none|zlib|zlib-gnu|zlib-gabi}]\n\ + Compress DWARF debug sections using zlib\n\ + --decompress-debug-sections Decompress DWARF debug sections using zlib\n\ + --elf-stt-common=[yes|no] Generate ELF common symbols with STT_COMMON\n\ + type\n\ + --verilog-data-width Specifies data width, in bytes, for verilog output\n\ + -M --merge-notes Remove redundant entries in note sections\n\ + --no-merge-notes Do not attempt to remove redundant notes (default)\n\ -v --verbose List all object files modified\n\ @ Read options from \n\ -V --version Display this program's version number\n\ @@ -516,7 +692,7 @@ copy_usage (FILE *stream, int exit_status) exit (exit_status); } -static void +ATTRIBUTE_NORETURN static void strip_usage (FILE *stream, int exit_status) { fprintf (stream, _("Usage: %s in-file(s)\n"), program_name); @@ -527,12 +703,31 @@ strip_usage (FILE *stream, int exit_status) -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\ +")); + if (DEFAULT_AR_DETERMINISTIC) + fprintf (stream, _("\ + -D --enable-deterministic-archives\n\ + Produce deterministic output when stripping archives (default)\n\ + -U --disable-deterministic-archives\n\ + Disable -D behavior\n")); + else + fprintf (stream, _("\ + -D --enable-deterministic-archives\n\ + Produce deterministic output when stripping archives\n\ + -U --disable-deterministic-archives\n\ + Disable -D behavior (default)\n")); + fprintf (stream, _("\ + -R --remove-section= Also remove section from the output\n\ + --remove-relocations Remove relocations from section \n\ -s --strip-all Remove all symbol and relocation information\n\ -g -S -d --strip-debug Remove all debugging symbols & sections\n\ + --strip-dwo Remove all DWO sections\n\ --strip-unneeded Remove all symbols not needed by relocations\n\ --only-keep-debug Strip everything but the debug information\n\ + -M --merge-notes Remove redundant entries in note sections (default)\n\ + --no-merge-notes Do not attempt to remove redundant notes\n\ -N --strip-symbol= Do not copy symbol \n\ + --keep-section= Do not strip section \n\ -K --keep-symbol= Do not strip symbol \n\ --keep-file-symbols Do not strip file symbol(s)\n\ -w --wildcard Permit wildcard in symbol comparison\n\ @@ -575,8 +770,8 @@ parse_flags (const char *s) } if (0) ; -#define PARSE_FLAG(fname,fval) \ - else if (strncasecmp (fname, s, len) == 0) ret |= fval +#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); @@ -587,17 +782,93 @@ parse_flags (const char *s) PARSE_FLAG ("rom", SEC_ROM); PARSE_FLAG ("share", SEC_COFF_SHARED); PARSE_FLAG ("contents", SEC_HAS_CONTENTS); + PARSE_FLAG ("merge", SEC_MERGE); + PARSE_FLAG ("strings", SEC_STRINGS); #undef PARSE_FLAG else { char *copy; - copy = xmalloc (len + 1); + copy = (char *) 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"); + "alloc, load, noload, readonly, debug, code, data, rom, share, contents, merge, strings"); + } + + s = snext; + } + while (s != NULL); + + return ret; +} + +/* Parse symbol flags into a flagword, with a fatal error if the + string can't be parsed. */ + +static flagword +parse_symflags (const char *s, char **other) +{ + flagword ret; + const char *snext; + size_t len; + + ret = BSF_NO_FLAGS; + + do + { + snext = strchr (s, ','); + if (snext == NULL) + len = strlen (s); + else + { + len = snext - s; + ++snext; + } + +#define PARSE_FLAG(fname, fval) \ + else if (len == sizeof fname - 1 \ + && strncasecmp (fname, s, len) == 0) \ + ret |= fval + +#define PARSE_OTHER(fname, fval) \ + else if (len >= sizeof fname \ + && strncasecmp (fname, s, sizeof fname - 1) == 0) \ + fval = xstrndup (s + sizeof fname - 1, len - sizeof fname + 1) + + if (0) ; + PARSE_FLAG ("local", BSF_LOCAL); + PARSE_FLAG ("global", BSF_GLOBAL); + PARSE_FLAG ("export", BSF_EXPORT); + PARSE_FLAG ("debug", BSF_DEBUGGING); + PARSE_FLAG ("function", BSF_FUNCTION); + PARSE_FLAG ("weak", BSF_WEAK); + PARSE_FLAG ("section", BSF_SECTION_SYM); + PARSE_FLAG ("constructor", BSF_CONSTRUCTOR); + PARSE_FLAG ("warning", BSF_WARNING); + PARSE_FLAG ("indirect", BSF_INDIRECT); + PARSE_FLAG ("file", BSF_FILE); + PARSE_FLAG ("object", BSF_OBJECT); + PARSE_FLAG ("synthetic", BSF_SYNTHETIC); + PARSE_FLAG ("indirect-function", BSF_GNU_INDIRECT_FUNCTION | BSF_FUNCTION); + PARSE_FLAG ("unique-object", BSF_GNU_UNIQUE | BSF_OBJECT); + PARSE_OTHER ("before=", *other); + +#undef PARSE_FLAG +#undef PARSE_OTHER + else + { + char *copy; + + copy = (char *) xmalloc (len + 1); + strncpy (copy, s, len); + copy[len] = '\0'; + non_fatal (_("unrecognized symbol flag `%s'"), copy); + fatal (_("supported flags: %s"), + "local, global, export, debug, function, weak, section, " + "constructor, warning, indirect, file, object, synthetic, " + "indirect-function, unique-object, before="); } s = snext; @@ -607,49 +878,189 @@ parse_flags (const char *s) return ret; } -/* Find and optionally add an entry in the change_sections list. */ +/* Find and optionally add an entry in the change_sections list. + + We need to be careful in how we match section names because of the support + for wildcard characters. For example suppose that the user has invoked + objcopy like this: + + --set-section-flags .debug_*=debug + --set-section-flags .debug_str=readonly,debug + --change-section-address .debug_*ranges=0x1000 + + With the idea that all debug sections will receive the DEBUG flag, the + .debug_str section will also receive the READONLY flag and the + .debug_ranges and .debug_aranges sections will have their address set to + 0x1000. (This may not make much sense, but it is just an example). + + When adding the section name patterns to the section list we need to make + sure that previous entries do not match with the new entry, unless the + match is exact. (In which case we assume that the user is overriding + the previous entry with the new context). + + When matching real section names to the section list we make use of the + wildcard characters, but we must do so in context. Eg if we are setting + section addresses then we match for .debug_ranges but not for .debug_info. + + Finally, if ADD is false and we do find a match, we mark the section list + entry as used. */ static struct section_list * -find_section_list (const char *name, bfd_boolean add) +find_section_list (const char *name, bfd_boolean add, unsigned int context) { - struct section_list *p; + struct section_list *p, *match = NULL; + + /* assert ((context & ((1 << 7) - 1)) != 0); */ for (p = change_sections; p != NULL; p = p->next) - if (strcmp (p->name, name) == 0) - return p; + { + if (add) + { + if (strcmp (p->pattern, name) == 0) + { + /* Check for context conflicts. */ + if (((p->context & SECTION_CONTEXT_REMOVE) + && (context & SECTION_CONTEXT_COPY)) + || ((context & SECTION_CONTEXT_REMOVE) + && (p->context & SECTION_CONTEXT_COPY))) + fatal (_("error: %s both copied and removed"), name); + + if (((p->context & SECTION_CONTEXT_SET_VMA) + && (context & SECTION_CONTEXT_ALTER_VMA)) + || ((context & SECTION_CONTEXT_SET_VMA) + && (context & SECTION_CONTEXT_ALTER_VMA))) + fatal (_("error: %s both sets and alters VMA"), name); + + if (((p->context & SECTION_CONTEXT_SET_LMA) + && (context & SECTION_CONTEXT_ALTER_LMA)) + || ((context & SECTION_CONTEXT_SET_LMA) + && (context & SECTION_CONTEXT_ALTER_LMA))) + fatal (_("error: %s both sets and alters LMA"), name); + + /* Extend the context. */ + p->context |= context; + return p; + } + } + /* If we are not adding a new name/pattern then + only check for a match if the context applies. */ + else if (p->context & context) + { + /* We could check for the presence of wildchar characters + first and choose between calling strcmp and fnmatch, + but is that really worth it ? */ + if (p->pattern [0] == '!') + { + if (fnmatch (p->pattern + 1, name, 0) == 0) + { + p->used = TRUE; + return NULL; + } + } + else + { + if (fnmatch (p->pattern, name, 0) == 0) + { + if (match == NULL) + match = p; + } + } + } + } if (! add) - return NULL; + { + if (match != NULL) + match->used = TRUE; + return match; + } - p = xmalloc (sizeof (struct section_list)); - p->name = name; + p = (struct section_list *) xmalloc (sizeof (struct section_list)); + p->pattern = name; p->used = FALSE; - p->remove = FALSE; - p->copy = FALSE; - p->change_vma = CHANGE_IGNORE; - p->change_lma = CHANGE_IGNORE; + p->context = context; p->vma_val = 0; p->lma_val = 0; - p->set_flags = FALSE; p->flags = 0; - + p->alignment = 0; p->next = change_sections; change_sections = p; return p; } +/* S1 is the entry node already in the table, S2 is the key node. */ + +static int +eq_string_redefnode (const void *s1, const void *s2) +{ + struct redefine_node *node1 = (struct redefine_node *) s1; + struct redefine_node *node2 = (struct redefine_node *) s2; + return !strcmp ((const char *) node1->source, (const char *) node2->source); +} + +/* P is redefine node. Hash value is generated from its "source" filed. */ + +static hashval_t +htab_hash_redefnode (const void *p) +{ + struct redefine_node *redefnode = (struct redefine_node *) p; + return htab_hash_string (redefnode->source); +} + +/* Create hashtab used for redefine node. */ + +static htab_t +create_symbol2redef_htab (void) +{ + return htab_create_alloc (16, htab_hash_redefnode, eq_string_redefnode, NULL, + xcalloc, free); +} + +/* There is htab_hash_string but no htab_eq_string. Makes sense. */ + +static int +eq_string (const void *s1, const void *s2) +{ + return strcmp ((const char *) s1, (const char *) s2) == 0; +} + +static htab_t +create_symbol_htab (void) +{ + return htab_create_alloc (16, htab_hash_string, eq_string, NULL, xcalloc, free); +} + +static void +create_symbol_htabs (void) +{ + strip_specific_htab = create_symbol_htab (); + strip_unneeded_htab = create_symbol_htab (); + keep_specific_htab = create_symbol_htab (); + localize_specific_htab = create_symbol_htab (); + globalize_specific_htab = create_symbol_htab (); + keepglobal_specific_htab = create_symbol_htab (); + weaken_specific_htab = create_symbol_htab (); + redefine_specific_htab = create_symbol2redef_htab (); + /* As there is no bidirectional hash table in libiberty, need a reverse table + to check duplicated target string. */ + redefine_specific_reverse_htab = create_symbol_htab (); +} + /* Add a symbol to strip_specific_list. */ static void -add_specific_symbol (const char *name, struct symlist **list) +add_specific_symbol (const char *name, htab_t htab) { - struct symlist *tmp_list; + *htab_find_slot (htab, name, INSERT) = (char *) name; +} + +/* Like add_specific_symbol, but the element type is void *. */ - tmp_list = xmalloc (sizeof (struct symlist)); - tmp_list->name = name; - tmp_list->next = *list; - *list = tmp_list; +static void +add_specific_symbol_node (const void *node, htab_t htab) +{ + *htab_find_slot (htab, node, INSERT) = (void *) node; } /* Add symbols listed in `filename' to strip_specific_list. */ @@ -658,7 +1069,7 @@ add_specific_symbol (const char *name, struct symlist **list) #define IS_LINE_TERMINATOR(c) ((c) == '\n' || (c) == '\r' || (c) == '\0') static void -add_specific_symbols (const char *filename, struct symlist **list) +add_specific_symbols (const char *filename, htab_t htab, char **buffer_p) { off_t size; FILE * f; @@ -673,7 +1084,7 @@ add_specific_symbols (const char *filename, struct symlist **list) return; } - buffer = xmalloc (size + 2); + buffer = (char *) xmalloc (size + 2); f = fopen (filename, FOPEN_RT); if (f == NULL) fatal (_("cannot open '%s': %s"), filename, strerror (errno)); @@ -759,45 +1170,68 @@ add_specific_symbols (const char *filename, struct symlist **list) * name_end = '\0'; if (name_end > name) - add_specific_symbol (name, list); + add_specific_symbol (name, htab); /* Advance line pointer to end of line. The 'eol ++' in the for loop above will then advance us to the start of the next line. */ line = eol; line_count ++; } + + /* Do not free the buffer. Parts of it will have been referenced + in the calls to add_specific_symbol. */ + *buffer_p = buffer; } -/* See whether a symbol should be stripped or kept based on - strip_specific_list and keep_symbols. */ +/* See whether a symbol should be stripped or kept + based on strip_specific_list and keep_symbols. */ -static bfd_boolean -is_specified_symbol (const char *name, struct symlist *list) +static int +is_specified_symbol_predicate (void **slot, void *data) { - struct symlist *tmp_list; + struct is_specified_symbol_predicate_data *d = + (struct is_specified_symbol_predicate_data *) data; + const char *slot_name = (char *) *slot; - if (wildcard) + if (*slot_name != '!') { - for (tmp_list = list; tmp_list; tmp_list = tmp_list->next) - if (*(tmp_list->name) != '!') - { - if (!fnmatch (tmp_list->name, name, 0)) - return TRUE; - } - else - { - if (fnmatch (tmp_list->name + 1, name, 0)) - return TRUE; - } + if (! fnmatch (slot_name, d->name, 0)) + { + d->found = TRUE; + /* Continue traversal, there might be a non-match rule. */ + return 1; + } } else { - for (tmp_list = list; tmp_list; tmp_list = tmp_list->next) - if (strcmp (name, tmp_list->name) == 0) - return TRUE; + if (! fnmatch (slot_name + 1, d->name, 0)) + { + d->found = FALSE; + /* Stop traversal. */ + return 0; + } } - return FALSE; + /* Continue traversal. */ + return 1; +} + +static bfd_boolean +is_specified_symbol (const char *name, htab_t htab) +{ + if (wildcard) + { + struct is_specified_symbol_predicate_data data; + + data.name = name; + data.found = FALSE; + + htab_traverse (htab, is_specified_symbol_predicate, &data); + + return data.found; + } + + return htab_find (htab, name) != NULL; } /* Return a pointer to the symbol used as a signature for GROUP. */ @@ -808,80 +1242,199 @@ group_signature (asection *group) bfd *abfd = group->owner; Elf_Internal_Shdr *ghdr; + /* PR 20089: An earlier error may have prevented us from loading the symbol table. */ + if (isympp == NULL) + return NULL; + if (bfd_get_flavour (abfd) != bfd_target_elf_flavour) return NULL; ghdr = &elf_section_data (group)->this_hdr; - if (ghdr->sh_link < elf_numsections (abfd)) + if (ghdr->sh_link == elf_onesymtab (abfd)) { const struct elf_backend_data *bed = get_elf_backend_data (abfd); - Elf_Internal_Shdr *symhdr = elf_elfsections (abfd) [ghdr->sh_link]; + Elf_Internal_Shdr *symhdr = &elf_symtab_hdr (abfd); - if (symhdr->sh_type == SHT_SYMTAB + if (ghdr->sh_info > 0 && ghdr->sh_info < symhdr->sh_size / bed->s->sizeof_sym) return isympp[ghdr->sh_info - 1]; } return NULL; } -/* See if a section is being removed. */ +/* Return TRUE if the section is a DWO section. */ static bfd_boolean -is_strip_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec) +is_dwo_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec) +{ + const char *name = bfd_section_name (sec); + int len = strlen (name); + + return strncmp (name + len - 4, ".dwo", 4) == 0; +} + +/* Return TRUE if section SEC is in the update list. */ + +static bfd_boolean +is_update_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec) +{ + if (update_sections != NULL) + { + struct section_add *pupdate; + + for (pupdate = update_sections; + pupdate != NULL; + pupdate = pupdate->next) + { + if (strcmp (sec->name, pupdate->name) == 0) + return TRUE; + } + } + + return FALSE; +} + +static bfd_boolean +is_mergeable_note_section (bfd * abfd, asection * sec) +{ + if (merge_notes + && bfd_get_flavour (abfd) == bfd_target_elf_flavour + && elf_section_data (sec)->this_hdr.sh_type == SHT_NOTE + /* FIXME: We currently only support merging GNU_BUILD_NOTEs. + We should add support for more note types. */ + && ((elf_section_data (sec)->this_hdr.sh_flags & SHF_GNU_BUILD_NOTE) != 0 + /* Old versions of GAS (prior to 2.27) could not set the section + flags to OS-specific values, so we also accept sections that + start with the expected name. */ + || (CONST_STRNEQ (sec->name, GNU_BUILD_ATTRS_SECTION_NAME)))) + return TRUE; + + return FALSE; +} + +/* See if a non-group section is being removed. */ + +static bfd_boolean +is_strip_section_1 (bfd *abfd ATTRIBUTE_UNUSED, asection *sec) { + if (find_section_list (bfd_section_name (sec), FALSE, SECTION_CONTEXT_KEEP) + != NULL) + return FALSE; + if (sections_removed || sections_copied) { struct section_list *p; + struct section_list *q; - p = find_section_list (bfd_get_section_name (abfd, sec), FALSE); + p = find_section_list (bfd_section_name (sec), FALSE, + SECTION_CONTEXT_REMOVE); + q = find_section_list (bfd_section_name (sec), FALSE, + SECTION_CONTEXT_COPY); - if (sections_removed && p != NULL && p->remove) + if (p && q) + fatal (_("error: section %s matches both remove and copy options"), + bfd_section_name (sec)); + if (p && is_update_section (abfd, sec)) + fatal (_("error: section %s matches both update and remove options"), + bfd_section_name (sec)); + + if (p != NULL) return TRUE; - if (sections_copied && (p == NULL || ! p->copy)) + if (sections_copied && q == NULL) return TRUE; } - if ((bfd_get_section_flags (abfd, sec) & SEC_DEBUGGING) != 0) + if ((bfd_section_flags (sec) & SEC_DEBUGGING) != 0) { if (strip_symbols == STRIP_DEBUG || strip_symbols == STRIP_UNNEEDED || strip_symbols == STRIP_ALL || discard_locals == LOCALS_ALL || convert_debugging) - return TRUE; + { + /* By default we don't want to strip .reloc section. + This section has for pe-coff special meaning. See + pe-dll.c file in ld, and peXXigen.c in bfd for details. */ + if (strcmp (bfd_section_name (sec), ".reloc") != 0) + return TRUE; + } + + if (strip_symbols == STRIP_DWO) + return is_dwo_section (abfd, sec); if (strip_symbols == STRIP_NONDEBUG) return FALSE; } - if ((bfd_get_section_flags (abfd, sec) & SEC_GROUP) != 0) + if (strip_symbols == STRIP_NONDWO) + return !is_dwo_section (abfd, sec); + + return FALSE; +} + +/* See if a section is being removed. */ + +static bfd_boolean +is_strip_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec) +{ + if (is_strip_section_1 (abfd, sec)) + return TRUE; + + if ((bfd_section_flags (sec) & SEC_GROUP) != 0) { asymbol *gsym; const char *gname; + asection *elt, *first; - /* PR binutils/3166 - Group sections look like debugging sections but they are not. - (They have a non-zero size but they are not ALLOCated). */ - if (strip_symbols == STRIP_NONDEBUG) + gsym = group_signature (sec); + /* Strip groups without a valid signature. */ + if (gsym == NULL) return TRUE; /* PR binutils/3181 If we are going to strip the group signature symbol, then strip the group section too. */ - gsym = group_signature (sec); - if (gsym != NULL) - gname = gsym->name; - else - gname = sec->name; + gname = gsym->name; if ((strip_symbols == STRIP_ALL - && !is_specified_symbol (gname, keep_specific_list)) - || is_specified_symbol (gname, strip_specific_list)) + && !is_specified_symbol (gname, keep_specific_htab)) + || is_specified_symbol (gname, strip_specific_htab)) return TRUE; + + /* Remove the group section if all members are removed. */ + first = elt = elf_next_in_group (sec); + while (elt != NULL) + { + if (!is_strip_section_1 (abfd, elt)) + return FALSE; + elt = elf_next_in_group (elt); + if (elt == first) + break; + } + + return TRUE; } return FALSE; } +static bfd_boolean +is_nondebug_keep_contents_section (bfd *ibfd, asection *isection) +{ + /* Always keep ELF note sections. */ + if (bfd_get_flavour (ibfd) == bfd_target_elf_flavour) + return elf_section_type (isection) == SHT_NOTE; + + /* Always keep the .buildid section for PE/COFF. + + Strictly, this should be written "always keep the section storing the debug + directory", but that may be the .text section for objects produced by some + tools, which it is not sensible to keep. */ + if (bfd_get_flavour (ibfd) == bfd_target_coff_flavour) + return strcmp (bfd_section_name (isection), ".buildid") == 0; + + return FALSE; +} + /* Return true if SYM is a hidden symbol. */ static bfd_boolean @@ -900,38 +1453,91 @@ is_hidden_symbol (asymbol *sym) return 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 (bfd *abfd, bfd *obfd, asymbol **osyms, - asymbol **isyms, long symcount) +static bfd_boolean +need_sym_before (struct addsym_node **node, const char *sym) { - asymbol **from = isyms, **to = osyms; - long src_count = 0, dst_count = 0; - int relocatable = (abfd->flags & (EXEC_P | DYNAMIC)) == 0; + int count; + struct addsym_node *ptr = add_sym_list; - for (; src_count < symcount; src_count++) + /* 'othersym' symbols are at the front of the list. */ + for (count = 0; count < add_symbols; count++) { - asymbol *sym = from[src_count]; - flagword flags = sym->flags; - char *name = (char *) bfd_asymbol_name (sym); - bfd_boolean keep; - bfd_boolean used_in_reloc = FALSE; + if (!ptr->othersym) + break; + else if (strcmp (ptr->othersym, sym) == 0) + { + free (ptr->othersym); + ptr->othersym = ""; /* Empty name is hopefully never a valid symbol name. */ + *node = ptr; + return TRUE; + } + ptr = ptr->next; + } + return FALSE; +} + +static asymbol * +create_new_symbol (struct addsym_node *ptr, bfd *obfd) +{ + asymbol *sym = bfd_make_empty_symbol (obfd); + + bfd_set_asymbol_name (sym, ptr->symdef); + sym->value = ptr->symval; + sym->flags = ptr->flags; + if (ptr->section) + { + asection *sec = bfd_get_section_by_name (obfd, ptr->section); + if (!sec) + fatal (_("Section %s not found"), ptr->section); + sym->section = sec; + } + else + sym->section = bfd_abs_section_ptr; + return sym; +} + +/* 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 (bfd *abfd, bfd *obfd, asymbol **osyms, + asymbol **isyms, long symcount) +{ + asymbol **from = isyms, **to = osyms; + long src_count = 0, dst_count = 0; + int relocatable = (abfd->flags & (EXEC_P | DYNAMIC)) == 0; + + for (; src_count < symcount; src_count++) + { + asymbol *sym = from[src_count]; + flagword flags = sym->flags; + char *name = (char *) bfd_asymbol_name (sym); + bfd_boolean keep; + bfd_boolean used_in_reloc = FALSE; bfd_boolean undefined; bfd_boolean rem_leading_char; bfd_boolean add_leading_char; - undefined = bfd_is_und_section (bfd_get_section (sym)); + undefined = bfd_is_und_section (bfd_asymbol_section (sym)); + + if (add_sym_list) + { + struct addsym_node *ptr; + + if (need_sym_before (&ptr, name)) + to[dst_count++] = create_new_symbol (ptr, obfd); + } - if (redefine_sym_list) + if (htab_elements (redefine_specific_htab) || section_rename_list) { - char *old_name, *new_name; + char *new_name; - old_name = (char *) bfd_asymbol_name (sym); - new_name = (char *) lookup_sym_redefinition (old_name); - bfd_asymbol_name (sym) = new_name; + new_name = (char *) lookup_sym_redefinition (name); + if (new_name == name + && (flags & BSF_SECTION_SYM) != 0) + new_name = (char *) find_section_rename (name, NULL); + bfd_set_asymbol_name (sym, new_name); name = new_name; } @@ -942,7 +1548,7 @@ filter_symbols (bfd *abfd, bfd *obfd, asymbol **osyms, || (remove_leading_char && ((flags & (BSF_GLOBAL | BSF_WEAK)) != 0 || undefined - || bfd_is_com_section (bfd_get_section (sym))))); + || bfd_is_com_section (bfd_asymbol_section (sym))))); /* Check if we will add a new leading character. */ add_leading_char = @@ -953,50 +1559,51 @@ filter_symbols (bfd *abfd, bfd *obfd, asymbol **osyms, /* Short circuit for change_leading_char if we can do it in-place. */ if (rem_leading_char && add_leading_char && !prefix_symbols_string) - { + { name[0] = bfd_get_symbol_leading_char (obfd); - bfd_asymbol_name (sym) = name; + bfd_set_asymbol_name (sym, name); rem_leading_char = FALSE; add_leading_char = FALSE; - } + } /* Remove leading char. */ if (rem_leading_char) - bfd_asymbol_name (sym) = ++name; + bfd_set_asymbol_name (sym, ++name); /* Add new leading char and/or prefix. */ if (add_leading_char || prefix_symbols_string) - { - char *n, *ptr; + { + char *n, *ptr; - ptr = n = xmalloc (1 + strlen (prefix_symbols_string) - + strlen (name) + 1); - if (add_leading_char) + ptr = n = (char *) xmalloc (1 + strlen (prefix_symbols_string) + + strlen (name) + 1); + if (add_leading_char) *ptr++ = bfd_get_symbol_leading_char (obfd); - if (prefix_symbols_string) - { - strcpy (ptr, prefix_symbols_string); - ptr += strlen (prefix_symbols_string); - } + if (prefix_symbols_string) + { + strcpy (ptr, prefix_symbols_string); + ptr += strlen (prefix_symbols_string); + } - strcpy (ptr, name); - bfd_asymbol_name (sym) = n; - name = n; + strcpy (ptr, name); + bfd_set_asymbol_name (sym, n); + name = n; } if (strip_symbols == STRIP_ALL) keep = FALSE; else if ((flags & BSF_KEEP) != 0 /* Used in relocation. */ || ((flags & BSF_SECTION_SYM) != 0 - && ((*bfd_get_section (sym)->symbol_ptr_ptr)->flags + && ((*bfd_asymbol_section (sym)->symbol_ptr_ptr)->flags & BSF_KEEP) != 0)) { keep = TRUE; used_in_reloc = TRUE; } else if (relocatable /* Relocatable file. */ - && (flags & (BSF_GLOBAL | BSF_WEAK)) != 0) + && ((flags & (BSF_GLOBAL | BSF_WEAK)) != 0 + || bfd_is_com_section (bfd_asymbol_section (sym)))) keep = TRUE; else if (bfd_decode_symclass (sym) == 'I') /* Global symbols in $idata sections need to be retained @@ -1007,13 +1614,13 @@ filter_symbols (bfd *abfd, bfd *obfd, asymbol **osyms, else if ((flags & BSF_GLOBAL) != 0 /* Global symbol. */ || (flags & BSF_WEAK) != 0 || undefined - || bfd_is_com_section (bfd_get_section (sym))) + || bfd_is_com_section (bfd_asymbol_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 if (bfd_coff_get_comdat_section (abfd, bfd_get_section (sym))) + else if (bfd_coff_get_comdat_section (abfd, bfd_asymbol_section (sym))) /* COMDAT sections store special information in local symbols, so we cannot risk stripping any of them. */ keep = TRUE; @@ -1023,7 +1630,7 @@ filter_symbols (bfd *abfd, bfd *obfd, asymbol **osyms, && (discard_locals != LOCALS_START_L || ! bfd_is_local_label (abfd, sym)))); - if (keep && is_specified_symbol (name, strip_specific_list)) + if (keep && is_specified_symbol (name, strip_specific_htab)) { /* There are multiple ways to set 'keep' above, but if it was the relocatable symbol case, then that's an error. */ @@ -1038,21 +1645,21 @@ filter_symbols (bfd *abfd, bfd *obfd, asymbol **osyms, if (keep && !(flags & BSF_KEEP) - && is_specified_symbol (name, strip_unneeded_list)) + && is_specified_symbol (name, strip_unneeded_htab)) keep = FALSE; if (!keep && ((keep_file_symbols && (flags & BSF_FILE)) - || is_specified_symbol (name, keep_specific_list))) + || is_specified_symbol (name, keep_specific_htab))) keep = TRUE; - if (keep && is_strip_section (abfd, bfd_get_section (sym))) + if (keep && is_strip_section (abfd, bfd_asymbol_section (sym))) keep = FALSE; if (keep) { if ((flags & BSF_GLOBAL) != 0 - && (weaken || is_specified_symbol (name, weaken_specific_list))) + && (weaken || is_specified_symbol (name, weaken_specific_htab))) { sym->flags &= ~ BSF_GLOBAL; sym->flags |= BSF_WEAK; @@ -1060,9 +1667,9 @@ filter_symbols (bfd *abfd, bfd *obfd, asymbol **osyms, if (!undefined && (flags & (BSF_GLOBAL | BSF_WEAK)) - && (is_specified_symbol (name, localize_specific_list) - || (keepglobal_specific_list != NULL - && ! is_specified_symbol (name, keepglobal_specific_list)) + && (is_specified_symbol (name, localize_specific_htab) + || (htab_elements (keepglobal_specific_htab) != 0 + && ! is_specified_symbol (name, keepglobal_specific_htab)) || (localize_hidden && is_hidden_symbol (sym)))) { sym->flags &= ~ (BSF_GLOBAL | BSF_WEAK); @@ -1071,7 +1678,7 @@ filter_symbols (bfd *abfd, bfd *obfd, asymbol **osyms, if (!undefined && (flags & BSF_LOCAL) - && is_specified_symbol (name, globalize_specific_list)) + && is_specified_symbol (name, globalize_specific_htab)) { sym->flags &= ~ BSF_LOCAL; sym->flags |= BSF_GLOBAL; @@ -1080,6 +1687,23 @@ filter_symbols (bfd *abfd, bfd *obfd, asymbol **osyms, to[dst_count++] = sym; } } + if (add_sym_list) + { + struct addsym_node *ptr = add_sym_list; + + for (src_count = 0; src_count < add_symbols; src_count++) + { + if (ptr->othersym) + { + if (strcmp (ptr->othersym, "")) + fatal (_("'before=%s' not found"), ptr->othersym); + } + else + to[dst_count++] = create_new_symbol (ptr, obfd); + + ptr = ptr->next; + } + } to[dst_count] = NULL; @@ -1091,42 +1715,40 @@ filter_symbols (bfd *abfd, bfd *obfd, asymbol **osyms, static const char * lookup_sym_redefinition (const char *source) { - struct redefine_node *list; - - for (list = redefine_sym_list; list != NULL; list = list->next) - if (strcmp (source, list->source) == 0) - return list->target; + struct redefine_node key_node = {(char *) source, NULL}; + struct redefine_node *redef_node + = (struct redefine_node *) htab_find (redefine_specific_htab, &key_node); - return source; + return redef_node == NULL ? source : redef_node->target; } -/* Add a node to a symbol redefine list. */ +/* Insert a node into symbol redefine hash tabel. */ static void -redefine_list_append (const char *cause, const char *source, const char *target) +add_redefine_and_check (const char *cause, const char *source, + const char *target) { - struct redefine_node **p; - struct redefine_node *list; - struct redefine_node *new_node; + struct redefine_node *new_node + = (struct redefine_node *) xmalloc (sizeof (struct redefine_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\""), - cause, source); + new_node->source = strdup (source); + new_node->target = strdup (target); - if (strcmp (target, list->target) == 0) - fatal (_("%s: Symbol \"%s\" is target of more than one redefinition"), - cause, target); - } + if (htab_find (redefine_specific_htab, new_node) != HTAB_EMPTY_ENTRY) + fatal (_("%s: Multiple redefinition of symbol \"%s\""), + cause, source); - new_node = xmalloc (sizeof (struct redefine_node)); + if (htab_find (redefine_specific_reverse_htab, target) != HTAB_EMPTY_ENTRY) + fatal (_("%s: Symbol \"%s\" is target of more than one redefinition"), + cause, target); - new_node->source = strdup (source); - new_node->target = strdup (target); - new_node->next = NULL; + /* Insert the NEW_NODE into hash table for quick search. */ + add_specific_symbol_node (new_node, redefine_specific_htab); + + /* Insert the target string into the reverse hash table, this is needed for + duplicated target string check. */ + add_specific_symbol (new_node->target, redefine_specific_reverse_htab); - *p = new_node; } /* Handle the --redefine-syms option. Read lines containing "old new" @@ -1148,7 +1770,7 @@ add_redefine_syms_file (const char *filename) filename, strerror (errno)); bufsize = 100; - buf = xmalloc (bufsize); + buf = (char *) xmalloc (bufsize + 1 /* For the terminating NUL. */); lineno = 1; c = getc (file); @@ -1165,7 +1787,7 @@ add_redefine_syms_file (const char *filename) if (len >= bufsize) { bufsize *= 2; - buf = xrealloc (buf, bufsize); + buf = (char *) xrealloc (buf, bufsize + 1); } c = getc (file); } @@ -1191,7 +1813,7 @@ add_redefine_syms_file (const char *filename) if (len >= bufsize) { bufsize *= 2; - buf = xrealloc (buf, bufsize); + buf = (char *) xrealloc (buf, bufsize + 1); } c = getc (file); } @@ -1208,10 +1830,10 @@ add_redefine_syms_file (const char *filename) if ((c == '\r' && (c = getc (file)) == '\n') || c == '\n' || c == EOF) { - end_of_line: + end_of_line: /* Append the redefinition to the list. */ if (buf[0] != '\0') - redefine_list_append (filename, &buf[0], &buf[outsym_off]); + add_redefine_and_check (filename, &buf[0], &buf[outsym_off]); lineno++; len = 0; @@ -1223,7 +1845,7 @@ add_redefine_syms_file (const char *filename) } else fatal (_("%s:%d: garbage found at end of line"), filename, lineno); - comment: + comment: if (len != 0 && (outsym_off == 0 || outsym_off == len)) fatal (_("%s:%d: missing new symbol name"), filename, lineno); buf[len++] = '\0'; @@ -1238,9 +1860,10 @@ add_redefine_syms_file (const char *filename) fatal (_("%s:%d: premature end of file"), filename, lineno); free (buf); + fclose (file); } -/* Copy unkown object file IBFD onto OBFD. +/* Copy unknown object file IBFD onto OBFD. Returns TRUE upon success, FALSE otherwise. */ static bfd_boolean @@ -1254,7 +1877,7 @@ copy_unknown_object (bfd *ibfd, bfd *obfd) if (bfd_stat_arch_elt (ibfd, &buf) != 0) { - bfd_nonfatal (bfd_get_archive_filename (ibfd)); + bfd_nonfatal_message (NULL, ibfd, NULL, NULL); return FALSE; } @@ -1276,7 +1899,7 @@ copy_unknown_object (bfd *ibfd, bfd *obfd) printf (_("copy from `%s' [unknown] to `%s' [unknown]\n"), bfd_get_archive_filename (ibfd), bfd_get_filename (obfd)); - cbuf = xmalloc (BUFSIZE); + cbuf = (char *) xmalloc (BUFSIZE); ncopied = 0; while (ncopied < size) { @@ -1287,7 +1910,7 @@ copy_unknown_object (bfd *ibfd, bfd *obfd) if (bfd_bread (cbuf, (bfd_size_type) tocopy, ibfd) != (bfd_size_type) tocopy) { - bfd_nonfatal (bfd_get_archive_filename (ibfd)); + bfd_nonfatal_message (NULL, ibfd, NULL, NULL); free (cbuf); return FALSE; } @@ -1295,7 +1918,7 @@ copy_unknown_object (bfd *ibfd, bfd *obfd) if (bfd_bwrite (cbuf, (bfd_size_type) tocopy, obfd) != (bfd_size_type) tocopy) { - bfd_nonfatal (bfd_get_filename (obfd)); + bfd_nonfatal_message (NULL, obfd, NULL, NULL); free (cbuf); return FALSE; } @@ -1303,20 +1926,638 @@ copy_unknown_object (bfd *ibfd, bfd *obfd) ncopied += tocopy; } - chmod (bfd_get_filename (obfd), buf.st_mode); + /* We should at least to be able to read it back when copying an + unknown object in an archive. */ + chmod (bfd_get_filename (obfd), buf.st_mode | S_IRUSR); free (cbuf); return TRUE; } +typedef struct objcopy_internal_note +{ + Elf_Internal_Note note; + unsigned long padded_namesz; + bfd_vma start; + bfd_vma end; +} objcopy_internal_note; + +#define DEBUG_MERGE 0 + +#if DEBUG_MERGE +#define merge_debug(format, ...) fprintf (stderr, format, ## __VA_ARGS__) +#else +#define merge_debug(format, ...) +#endif + +/* Returns TRUE iff PNOTE1 overlaps or adjoins PNOTE2. */ + +static bfd_boolean +overlaps_or_adjoins (objcopy_internal_note * pnote1, + objcopy_internal_note * pnote2) +{ + if (pnote1->end < pnote2->start) + /* FIXME: Alignment of 16 bytes taken from x86_64 binaries. + Really we should extract the alignment of the section + covered by the notes. */ + return BFD_ALIGN (pnote1->end, 16) < pnote2->start; + + if (pnote2->end < pnote2->start) + return BFD_ALIGN (pnote2->end, 16) < pnote1->start; + + if (pnote1->end < pnote2->end) + return TRUE; + + if (pnote2->end < pnote1->end) + return TRUE; + + return FALSE; +} + +/* Returns TRUE iff NEEDLE is fully contained by HAYSTACK. */ + +static bfd_boolean +contained_by (objcopy_internal_note * needle, + objcopy_internal_note * haystack) +{ + return needle->start >= haystack->start && needle->end <= haystack->end; +} + +static bfd_boolean +is_open_note (objcopy_internal_note * pnote) +{ + return pnote->note.type == NT_GNU_BUILD_ATTRIBUTE_OPEN; +} + +static bfd_boolean +is_func_note (objcopy_internal_note * pnote) +{ + return pnote->note.type == NT_GNU_BUILD_ATTRIBUTE_FUNC; +} + +static bfd_boolean +is_deleted_note (objcopy_internal_note * pnote) +{ + return pnote->note.type == 0; +} + +static bfd_boolean +is_version_note (objcopy_internal_note * pnote) +{ + return (pnote->note.namesz > 4 + && pnote->note.namedata[0] == 'G' + && pnote->note.namedata[1] == 'A' + && pnote->note.namedata[2] == '$' + && pnote->note.namedata[3] == GNU_BUILD_ATTRIBUTE_VERSION); +} + +static bfd_boolean +is_64bit (bfd * abfd) +{ + /* Should never happen, but let's be paranoid. */ + if (bfd_get_flavour (abfd) != bfd_target_elf_flavour) + return FALSE; + + return elf_elfheader (abfd)->e_ident[EI_CLASS] == ELFCLASS64; +} + +/* This sorting function is used to get the notes into an order + that makes merging easy. */ + +static int +compare_gnu_build_notes (const void * data1, const void * data2) +{ + objcopy_internal_note * pnote1 = (objcopy_internal_note *) data1; + objcopy_internal_note * pnote2 = (objcopy_internal_note *) data2; + + /* Sort notes based upon the attribute they record. */ + int cmp = memcmp (pnote1->note.namedata + 3, + pnote2->note.namedata + 3, + pnote1->note.namesz < pnote2->note.namesz ? + pnote1->note.namesz - 3 : pnote2->note.namesz - 3); + if (cmp) + return cmp; + + if (pnote1->end < pnote2->start) + return -1; + if (pnote1->start > pnote2->end) + return 1; + + /* Overlaps - we should merge the two ranges. */ + if (pnote1->start < pnote2->start) + return -1; + if (pnote1->end > pnote2->end) + return 1; + + /* Put OPEN notes before function notes. */ + if (is_open_note (pnote1) && ! is_open_note (pnote2)) + return -1; + if (! is_open_note (pnote1) && is_open_note (pnote2)) + return 1; + + return 0; +} + +/* This sorting function is used to get the notes into an order + that makes eliminating address ranges easier. */ + +static int +sort_gnu_build_notes (const void * data1, const void * data2) +{ + objcopy_internal_note * pnote1 = (objcopy_internal_note *) data1; + objcopy_internal_note * pnote2 = (objcopy_internal_note *) data2; + + if (pnote1->note.type != pnote2->note.type) + { + /* Move deleted notes to the end. */ + if (is_deleted_note (pnote1)) /* 1: OFD 2: OFD */ + return 1; + + /* Move OPEN notes to the start. */ + if (is_open_note (pnote1)) /* 1: OF 2: OFD */ + return -1; + + if (is_deleted_note (pnote2)) /* 1: F 2: O D */ + return -1; + + return 1; /* 1: F 2: O */ + } + + /* Sort by starting address. */ + if (pnote1->start < pnote2->start) + return -1; + if (pnote1->start > pnote2->start) + return 1; + + /* Then by end address (bigger range first). */ + if (pnote1->end > pnote2->end) + return -1; + if (pnote1->end < pnote2->end) + return 1; + + /* Then by attribute type. */ + if (pnote1->note.namesz > 4 + && pnote2->note.namesz > 4 + && pnote1->note.namedata[3] != pnote2->note.namedata[3]) + return pnote1->note.namedata[3] - pnote2->note.namedata[3]; + + return 0; +} + +/* Merge the notes on SEC, removing redundant entries. + Returns the new, smaller size of the section upon success. */ + +static bfd_size_type +merge_gnu_build_notes (bfd * abfd, + asection * sec, + bfd_size_type size, + bfd_byte * contents) +{ + objcopy_internal_note * pnotes_end; + objcopy_internal_note * pnotes = NULL; + objcopy_internal_note * pnote; + bfd_size_type remain = size; + unsigned version_1_seen = 0; + unsigned version_2_seen = 0; + unsigned version_3_seen = 0; + const char * err = NULL; + bfd_byte * in = contents; + unsigned long previous_func_start = 0; + unsigned long previous_open_start = 0; + unsigned long previous_func_end = 0; + unsigned long previous_open_end = 0; + long relsize; + + relsize = bfd_get_reloc_upper_bound (abfd, sec); + if (relsize > 0) + { + arelent ** relpp; + long relcount; + + /* If there are relocs associated with this section then we + cannot safely merge it. */ + relpp = (arelent **) xmalloc (relsize); + relcount = bfd_canonicalize_reloc (abfd, sec, relpp, isympp); + free (relpp); + if (relcount != 0) + { + if (! is_strip) + non_fatal (_("%s[%s]: Cannot merge - there are relocations against this section"), + bfd_get_filename (abfd), bfd_section_name (sec)); + goto done; + } + } + + /* Make a copy of the notes and convert to our internal format. + Minimum size of a note is 12 bytes. Also locate the version + notes and check them. */ + pnote = pnotes = (objcopy_internal_note *) + xcalloc ((size / 12), sizeof (* pnote)); + while (remain >= 12) + { + bfd_vma start, end; + + pnote->note.namesz = bfd_get_32 (abfd, in); + pnote->note.descsz = bfd_get_32 (abfd, in + 4); + pnote->note.type = bfd_get_32 (abfd, in + 8); + pnote->padded_namesz = (pnote->note.namesz + 3) & ~3; + + if (((pnote->note.descsz + 3) & ~3) != pnote->note.descsz) + { + err = _("corrupt GNU build attribute note: description size not a factor of 4"); + goto done; + } + + if (pnote->note.type != NT_GNU_BUILD_ATTRIBUTE_OPEN + && pnote->note.type != NT_GNU_BUILD_ATTRIBUTE_FUNC) + { + err = _("corrupt GNU build attribute note: wrong note type"); + goto done; + } + + if (pnote->padded_namesz + pnote->note.descsz + 12 > remain) + { + err = _("corrupt GNU build attribute note: note too big"); + goto done; + } + + if (pnote->note.namesz < 2) + { + err = _("corrupt GNU build attribute note: name too small"); + goto done; + } + + pnote->note.namedata = (char *)(in + 12); + pnote->note.descdata = (char *)(in + 12 + pnote->padded_namesz); + + remain -= 12 + pnote->padded_namesz + pnote->note.descsz; + in += 12 + pnote->padded_namesz + pnote->note.descsz; + + if (pnote->note.namesz > 2 + && pnote->note.namedata[0] == '$' + && pnote->note.namedata[1] == GNU_BUILD_ATTRIBUTE_VERSION + && pnote->note.namedata[2] == '1') + ++ version_1_seen; + else if (is_version_note (pnote)) + { + if (pnote->note.namedata[4] == '2') + ++ version_2_seen; + else if (pnote->note.namedata[4] == '3') + ++ version_3_seen; + else + { + err = _("corrupt GNU build attribute note: unsupported version"); + goto done; + } + } + + switch (pnote->note.descsz) + { + case 0: + start = end = 0; + break; + + case 4: + start = bfd_get_32 (abfd, pnote->note.descdata); + /* FIXME: For version 1 and 2 notes we should try to + calculate the end address by finding a symbol whose + value is START, and then adding in its size. + + For now though, since v1 and v2 was not intended to + handle gaps, we chose an artificially large end + address. */ + end = (bfd_vma) -1; + break; + + case 8: + if (! is_64bit (abfd)) + { + start = bfd_get_32 (abfd, pnote->note.descdata); + end = bfd_get_32 (abfd, pnote->note.descdata + 4); + } + else + { + start = bfd_get_64 (abfd, pnote->note.descdata); + /* FIXME: For version 1 and 2 notes we should try to + calculate the end address by finding a symbol whose + value is START, and then adding in its size. + + For now though, since v1 and v2 was not intended to + handle gaps, we chose an artificially large end + address. */ + end = (bfd_vma) -1; + } + break; + + case 16: + start = bfd_get_64 (abfd, pnote->note.descdata); + end = bfd_get_64 (abfd, pnote->note.descdata + 8); + break; + + default: + err = _("corrupt GNU build attribute note: bad description size"); + goto done; + } + + if (is_open_note (pnote)) + { + if (start) + previous_open_start = start; + + pnote->start = previous_open_start; + + if (end) + previous_open_end = end; + + pnote->end = previous_open_end; + } + else + { + if (start) + previous_func_start = start; + + pnote->start = previous_func_start; + + if (end) + previous_func_end = end; + + pnote->end = previous_func_end; + } + + if (pnote->note.namedata[pnote->note.namesz - 1] != 0) + { + err = _("corrupt GNU build attribute note: name not NUL terminated"); + goto done; + } + + pnote ++; + } + + pnotes_end = pnote; + + /* Check that the notes are valid. */ + if (remain != 0) + { + err = _("corrupt GNU build attribute notes: excess data at end"); + goto done; + } + + if (version_1_seen == 0 && version_2_seen == 0 && version_3_seen == 0) + { +#if 0 + err = _("bad GNU build attribute notes: no known versions detected"); + goto done; +#else + /* This happens with glibc. No idea why. */ + non_fatal (_("%s[%s]: Warning: version note missing - assuming version 3"), + bfd_get_filename (abfd), bfd_section_name (sec)); + version_3_seen = 2; +#endif + } + + if ( (version_1_seen > 0 && version_2_seen > 0) + || (version_1_seen > 0 && version_3_seen > 0) + || (version_2_seen > 0 && version_3_seen > 0)) + { + err = _("bad GNU build attribute notes: multiple different versions"); + goto done; + } + + /* We are now only supporting the merging v3+ notes + - it makes things much simpler. */ + if (version_3_seen == 0) + { + merge_debug ("%s: skipping merge - not using v3 notes", bfd_section_name (sec)); + goto done; + } + + merge_debug ("Merging section %s which contains %ld notes\n", + sec->name, pnotes_end - pnotes); + + /* Sort the notes. */ + qsort (pnotes, pnotes_end - pnotes, sizeof (* pnotes), + compare_gnu_build_notes); + +#if DEBUG_MERGE + merge_debug ("Results of initial sort:\n"); + for (pnote = pnotes; pnote < pnotes_end; pnote ++) + merge_debug ("offset %#08lx range %#08lx..%#08lx type %ld attribute %d namesz %ld\n", + (pnote->note.namedata - (char *) contents) - 12, + pnote->start, pnote->end, + pnote->note.type, + pnote->note.namedata[3], + pnote->note.namesz + ); +#endif + + /* Now merge the notes. The rules are: + 1. If a note has a zero range, it can be eliminated. + 2. If two notes have the same namedata then: + 2a. If one note's range is fully covered by the other note + then it can be deleted. + 2b. If one note's range partially overlaps or adjoins the + other note then if they are both of the same type (open + or func) then they can be merged and one deleted. If + they are of different types then they cannot be merged. */ + for (pnote = pnotes; pnote < pnotes_end; pnote ++) + { + /* Skip already deleted notes. + FIXME: Can this happen ? We are scanning forwards and + deleting backwards after all. */ + if (is_deleted_note (pnote)) + continue; + + /* Rule 1 - delete 0-range notes. */ + if (pnote->start == pnote->end) + { + merge_debug ("Delete note at offset %#08lx - empty range\n", + (pnote->note.namedata - (char *) contents) - 12); + pnote->note.type = 0; + continue; + } + + int iter; + objcopy_internal_note * back; + + /* Rule 2: Check to see if there is an identical previous note. */ + for (iter = 0, back = pnote - 1; back >= pnotes; back --) + { + if (is_deleted_note (back)) + continue; + + /* Our sorting function should have placed all identically + attributed notes together, so if we see a note of a different + attribute type stop searching. */ + if (back->note.namesz != pnote->note.namesz + || memcmp (back->note.namedata, + pnote->note.namedata, pnote->note.namesz) != 0) + break; + + if (back->start == pnote->start + && back->end == pnote->end) + { + merge_debug ("Delete note at offset %#08lx - duplicate of note at offset %#08lx\n", + (pnote->note.namedata - (char *) contents) - 12, + (back->note.namedata - (char *) contents) - 12); + pnote->note.type = 0; + break; + } + + /* Rule 2a. */ + if (contained_by (pnote, back)) + { + merge_debug ("Delete note at offset %#08lx - fully contained by note at %#08lx\n", + (pnote->note.namedata - (char *) contents) - 12, + (back->note.namedata - (char *) contents) - 12); + pnote->note.type = 0; + break; + } + +#if DEBUG_MERGE + /* This should not happen as we have sorted the + notes with earlier starting addresses first. */ + if (contained_by (back, pnote)) + merge_debug ("ERROR: UNEXPECTED CONTAINMENT\n"); +#endif + + /* Rule 2b. */ + if (overlaps_or_adjoins (back, pnote) + && is_func_note (back) == is_func_note (pnote)) + { + merge_debug ("Delete note at offset %#08lx - merge into note at %#08lx\n", + (pnote->note.namedata - (char *) contents) - 12, + (back->note.namedata - (char *) contents) - 12); + + back->end = back->end > pnote->end ? back->end : pnote->end; + back->start = back->start < pnote->start ? back->start : pnote->start; + pnote->note.type = 0; + break; + } + + /* Don't scan too far back however. */ + if (iter ++ > 16) + { + /* FIXME: Not sure if this can ever be triggered. */ + merge_debug ("ITERATION LIMIT REACHED\n"); + break; + } + } +#if DEBUG_MERGE + if (! is_deleted_note (pnote)) + merge_debug ("Unable to do anything with note at %#08lx\n", + (pnote->note.namedata - (char *) contents) - 12); +#endif + } + + /* Resort the notes. */ + merge_debug ("Final sorting of notes\n"); + qsort (pnotes, pnotes_end - pnotes, sizeof (* pnotes), sort_gnu_build_notes); + + /* Reconstruct the ELF notes. */ + bfd_byte * new_contents; + bfd_byte * old; + bfd_byte * new; + bfd_size_type new_size; + bfd_vma prev_start = 0; + bfd_vma prev_end = 0; + + /* Not sure how, but the notes might grow in size. + (eg see PR 1774507). Allow for this here. */ + new = new_contents = xmalloc (size * 2); + for (pnote = pnotes, old = contents; + pnote < pnotes_end; + pnote ++) + { + bfd_size_type note_size = 12 + pnote->padded_namesz + pnote->note.descsz; + + if (! is_deleted_note (pnote)) + { + /* Create the note, potentially using the + address range of the previous note. */ + if (pnote->start == prev_start && pnote->end == prev_end) + { + bfd_put_32 (abfd, pnote->note.namesz, new); + bfd_put_32 (abfd, 0, new + 4); + bfd_put_32 (abfd, pnote->note.type, new + 8); + new += 12; + memcpy (new, pnote->note.namedata, pnote->note.namesz); + if (pnote->note.namesz < pnote->padded_namesz) + memset (new + pnote->note.namesz, 0, pnote->padded_namesz - pnote->note.namesz); + new += pnote->padded_namesz; + } + else + { + bfd_put_32 (abfd, pnote->note.namesz, new); + bfd_put_32 (abfd, is_64bit (abfd) ? 16 : 8, new + 4); + bfd_put_32 (abfd, pnote->note.type, new + 8); + new += 12; + memcpy (new, pnote->note.namedata, pnote->note.namesz); + if (pnote->note.namesz < pnote->padded_namesz) + memset (new + pnote->note.namesz, 0, pnote->padded_namesz - pnote->note.namesz); + new += pnote->padded_namesz; + if (is_64bit (abfd)) + { + bfd_put_64 (abfd, pnote->start, new); + bfd_put_64 (abfd, pnote->end, new + 8); + new += 16; + } + else + { + bfd_put_32 (abfd, pnote->start, new); + bfd_put_32 (abfd, pnote->end, new + 4); + new += 8; + } + + prev_start = pnote->start; + prev_end = pnote->end; + } + } + + old += note_size; + } + +#if DEBUG_MERGE + merge_debug ("Results of merge:\n"); + for (pnote = pnotes; pnote < pnotes_end; pnote ++) + if (! is_deleted_note (pnote)) + merge_debug ("offset %#08lx range %#08lx..%#08lx type %ld attribute %d namesz %ld\n", + (pnote->note.namedata - (char *) contents) - 12, + pnote->start, pnote->end, + pnote->note.type, + pnote->note.namedata[3], + pnote->note.namesz + ); +#endif + + new_size = new - new_contents; + if (new_size < size) + { + memcpy (contents, new_contents, new_size); + size = new_size; + } + free (new_contents); + + done: + if (err) + { + bfd_set_error (bfd_error_bad_value); + bfd_nonfatal_message (NULL, abfd, sec, err); + status = 1; + } + + free (pnotes); + return size; +} + /* Copy object file IBFD onto OBFD. Returns TRUE upon success, FALSE otherwise. */ static bfd_boolean -copy_object (bfd *ibfd, bfd *obfd) +copy_object (bfd *ibfd, bfd *obfd, const bfd_arch_info_type *input_arch) { bfd_vma start; long symcount; asection **osections = NULL; + asection *osec; asection *gnu_debuglink_section = NULL; bfd_size_type *gaps = NULL; bfd_size_type max_gap = 0; @@ -1324,18 +2565,49 @@ copy_object (bfd *ibfd, bfd *obfd) void *dhandle; enum bfd_architecture iarch; unsigned int imach; + unsigned int c, i; if (ibfd->xvec->byteorder != obfd->xvec->byteorder && ibfd->xvec->byteorder != BFD_ENDIAN_UNKNOWN && obfd->xvec->byteorder != BFD_ENDIAN_UNKNOWN) - fatal (_("Unable to change endianness of input file(s)")); + { + /* PR 17636: Call non-fatal so that we return to our parent who + may need to tidy temporary files. */ + non_fatal (_("Unable to change endianness of input file(s)")); + return FALSE; + } if (!bfd_set_format (obfd, bfd_get_format (ibfd))) { - bfd_nonfatal (bfd_get_filename (obfd)); + bfd_nonfatal_message (NULL, obfd, NULL, NULL); + return FALSE; + } + + if (ibfd->sections == NULL) + { + non_fatal (_("error: the input file '%s' has no sections"), + bfd_get_archive_filename (ibfd)); return FALSE; } + if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour) + { + if ((do_debug_sections & compress) != 0 + && do_debug_sections != compress) + { + non_fatal (_("--compress-debug-sections=[zlib|zlib-gnu|zlib-gabi] is unsupported on `%s'"), + bfd_get_archive_filename (ibfd)); + return FALSE; + } + + if (do_elf_stt_common) + { + non_fatal (_("--elf-stt-common=[yes|no] is unsupported on `%s'"), + bfd_get_archive_filename (ibfd)); + return FALSE; + } + } + if (verbose) printf (_("copy from `%s' [%s] to `%s' [%s]\n"), bfd_get_archive_filename (ibfd), bfd_get_target (ibfd), @@ -1363,10 +2635,13 @@ copy_object (bfd *ibfd, bfd *obfd) flags &= ~bfd_flags_to_clear; flags &= bfd_applicable_file_flags (obfd); + if (strip_symbols == STRIP_ALL) + flags &= ~HAS_RELOC; + if (!bfd_set_start_address (obfd, start) || !bfd_set_file_flags (obfd, flags)) { - bfd_nonfatal (bfd_get_archive_filename (ibfd)); + bfd_nonfatal_message (NULL, ibfd, NULL, NULL); return FALSE; } } @@ -1374,6 +2649,25 @@ copy_object (bfd *ibfd, bfd *obfd) /* Copy architecture of input file to output file. */ iarch = bfd_get_arch (ibfd); imach = bfd_get_mach (ibfd); + if (input_arch) + { + if (iarch == bfd_arch_unknown) + { + iarch = input_arch->arch; + imach = input_arch->mach; + } + else + non_fatal (_("Input file `%s' ignores binary architecture parameter."), + bfd_get_archive_filename (ibfd)); + } + if (iarch == bfd_arch_unknown + && bfd_get_flavour (ibfd) != bfd_target_elf_flavour + && bfd_get_flavour (obfd) == bfd_target_elf_flavour) + { + const struct elf_backend_data *bed = get_elf_backend_data (obfd); + iarch = bed->arch; + imach = 0; + } if (!bfd_set_arch_mach (obfd, iarch, imach) && (ibfd->target_defaulted || bfd_get_arch (ibfd) != bfd_get_arch (obfd))) @@ -1382,7 +2676,7 @@ copy_object (bfd *ibfd, bfd *obfd) non_fatal (_("Unable to recognise the format of the input file `%s'"), bfd_get_archive_filename (ibfd)); else - non_fatal (_("Warning: Output file cannot represent architecture `%s'"), + non_fatal (_("Output file cannot represent architecture `%s'"), bfd_printable_arch_mach (bfd_get_arch (ibfd), bfd_get_mach (ibfd))); return FALSE; @@ -1390,10 +2684,67 @@ copy_object (bfd *ibfd, bfd *obfd) if (!bfd_set_format (obfd, bfd_get_format (ibfd))) { - bfd_nonfatal (bfd_get_archive_filename (ibfd)); + bfd_nonfatal_message (NULL, ibfd, NULL, NULL); return FALSE; } + if (bfd_get_flavour (obfd) == bfd_target_coff_flavour + && bfd_pei_p (obfd)) + { + /* Set up PE parameters. */ + pe_data_type *pe = pe_data (obfd); + + /* Copy PE parameters before changing them. */ + if (bfd_get_flavour (ibfd) == bfd_target_coff_flavour + && bfd_pei_p (ibfd)) + pe->pe_opthdr = pe_data (ibfd)->pe_opthdr; + + if (pe_file_alignment != (bfd_vma) -1) + pe->pe_opthdr.FileAlignment = pe_file_alignment; + else + pe_file_alignment = PE_DEF_FILE_ALIGNMENT; + + if (pe_heap_commit != (bfd_vma) -1) + pe->pe_opthdr.SizeOfHeapCommit = pe_heap_commit; + + if (pe_heap_reserve != (bfd_vma) -1) + pe->pe_opthdr.SizeOfHeapCommit = pe_heap_reserve; + + if (pe_image_base != (bfd_vma) -1) + pe->pe_opthdr.ImageBase = pe_image_base; + + if (pe_section_alignment != (bfd_vma) -1) + pe->pe_opthdr.SectionAlignment = pe_section_alignment; + else + pe_section_alignment = PE_DEF_SECTION_ALIGNMENT; + + if (pe_stack_commit != (bfd_vma) -1) + pe->pe_opthdr.SizeOfStackCommit = pe_stack_commit; + + if (pe_stack_reserve != (bfd_vma) -1) + pe->pe_opthdr.SizeOfStackCommit = pe_stack_reserve; + + if (pe_subsystem != -1) + pe->pe_opthdr.Subsystem = pe_subsystem; + + if (pe_major_subsystem_version != -1) + pe->pe_opthdr.MajorSubsystemVersion = pe_major_subsystem_version; + + if (pe_minor_subsystem_version != -1) + pe->pe_opthdr.MinorSubsystemVersion = pe_minor_subsystem_version; + + if (pe_file_alignment > pe_section_alignment) + { + char file_alignment[20], section_alignment[20]; + + sprintf_vma (file_alignment, pe_file_alignment); + sprintf_vma (section_alignment, pe_section_alignment); + non_fatal (_("warning: file alignment (0x%s) > section alignment (0x%s)"), + + file_alignment, section_alignment); + } + } + if (isympp) free (isympp); @@ -1406,23 +2757,32 @@ copy_object (bfd *ibfd, bfd *obfd) symsize = bfd_get_symtab_upper_bound (ibfd); if (symsize < 0) { - bfd_nonfatal (bfd_get_archive_filename (ibfd)); + bfd_nonfatal_message (NULL, ibfd, NULL, NULL); return FALSE; } - osympp = isympp = xmalloc (symsize); + osympp = isympp = (asymbol **) xmalloc (symsize); symcount = bfd_canonicalize_symtab (ibfd, isympp); if (symcount < 0) { - bfd_nonfatal (bfd_get_filename (ibfd)); + bfd_nonfatal_message (NULL, ibfd, NULL, NULL); return FALSE; } + /* PR 17512: file: d6323821 + If the symbol table could not be loaded do not pretend that we have + any symbols. This trips us up later on when we load the relocs. */ + if (symcount == 0) + { + free (isympp); + osympp = isympp = NULL; + } /* 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, obfd); - setup_bfd_headers (ibfd, obfd); + if (!extract_symbol) + setup_bfd_headers (ibfd, obfd); if (add_sections != NULL) { @@ -1433,124 +2793,285 @@ copy_object (bfd *ibfd, bfd *obfd) { flagword flags; - pset = find_section_list (padd->name, FALSE); + pset = find_section_list (padd->name, FALSE, + SECTION_CONTEXT_SET_FLAGS); if (pset != NULL) - pset->used = TRUE; - - flags = SEC_HAS_CONTENTS | SEC_READONLY | SEC_DATA; - if (pset != NULL && pset->set_flags) flags = pset->flags | SEC_HAS_CONTENTS; + else + flags = SEC_HAS_CONTENTS | SEC_READONLY | SEC_DATA; /* bfd_make_section_with_flags() does not return very helpful error codes, so check for the most likely user error first. */ if (bfd_get_section_by_name (obfd, padd->name)) { - non_fatal (_("can't add section '%s' - it already exists!"), padd->name); + bfd_nonfatal_message (NULL, obfd, NULL, + _("can't add section '%s'"), padd->name); return FALSE; } else { - padd->section = bfd_make_section_with_flags (obfd, padd->name, flags); + /* We use LINKER_CREATED here so that the backend hooks + will create any special section type information, + instead of presuming we know what we're doing merely + because we set the flags. */ + padd->section = bfd_make_section_with_flags + (obfd, padd->name, flags | SEC_LINKER_CREATED); if (padd->section == NULL) { - non_fatal (_("can't create section `%s': %s"), - padd->name, bfd_errmsg (bfd_get_error ())); + bfd_nonfatal_message (NULL, obfd, NULL, + _("can't create section `%s'"), + padd->name); return FALSE; } } - if (! bfd_set_section_size (obfd, padd->section, padd->size)) + if (!bfd_set_section_size (padd->section, padd->size)) { - bfd_nonfatal (bfd_get_filename (obfd)); + bfd_nonfatal_message (NULL, obfd, padd->section, NULL); return FALSE; } + pset = find_section_list (padd->name, FALSE, + SECTION_CONTEXT_SET_VMA | SECTION_CONTEXT_ALTER_VMA); + if (pset != NULL + && !bfd_set_section_vma (padd->section, pset->vma_val)) + { + bfd_nonfatal_message (NULL, obfd, padd->section, NULL); + return FALSE; + } + + pset = find_section_list (padd->name, FALSE, + SECTION_CONTEXT_SET_LMA | SECTION_CONTEXT_ALTER_LMA); if (pset != NULL) { - if (pset->change_vma != CHANGE_IGNORE) - if (! bfd_set_section_vma (obfd, padd->section, - pset->vma_val)) - { - bfd_nonfatal (bfd_get_filename (obfd)); - return FALSE; - } + padd->section->lma = pset->lma_val; - if (pset->change_lma != CHANGE_IGNORE) + if (!bfd_set_section_alignment + (padd->section, bfd_section_alignment (padd->section))) { - padd->section->lma = pset->lma_val; + bfd_nonfatal_message (NULL, obfd, padd->section, NULL); + return FALSE; + } + } + } + } - if (! bfd_set_section_alignment - (obfd, padd->section, - bfd_section_alignment (obfd, padd->section))) - { - bfd_nonfatal (bfd_get_filename (obfd)); - return FALSE; - } + if (update_sections != NULL) + { + struct section_add *pupdate; + + for (pupdate = update_sections; + pupdate != NULL; + pupdate = pupdate->next) + { + pupdate->section = bfd_get_section_by_name (ibfd, pupdate->name); + if (pupdate->section == NULL) + { + non_fatal (_("error: %s not found, can't be updated"), pupdate->name); + return FALSE; + } + + osec = pupdate->section->output_section; + if (!bfd_set_section_size (osec, pupdate->size)) + { + bfd_nonfatal_message (NULL, obfd, osec, NULL); + return FALSE; + } + } + } + + merged_note_section * merged_note_sections = NULL; + if (merge_notes) + { + /* This palaver is necessary because we must set the output + section size first, before its contents are ready. */ + for (osec = ibfd->sections; osec != NULL; osec = osec->next) + { + if (! is_mergeable_note_section (ibfd, osec)) + continue; + + /* If the section is going to be completly deleted then + do not bother to merge it. */ + if (osec->output_section == NULL) + continue; + + bfd_size_type size = bfd_section_size (osec); + + if (size == 0) + { + bfd_nonfatal_message (NULL, ibfd, osec, + _("warning: note section is empty")); + continue; + } + + merged_note_section * merged = xmalloc (sizeof * merged); + merged->contents = NULL; + if (! bfd_get_full_section_contents (ibfd, osec, & merged->contents)) + { + bfd_nonfatal_message (NULL, ibfd, osec, + _("warning: could not load note section")); + free (merged); + continue; + } + + merged->size = merge_gnu_build_notes (ibfd, osec, size, + merged->contents); + + /* FIXME: Once we have read the contents in, we must write + them out again. So even if the mergeing has achieved + nothing we still add this entry to the merge list. */ + + if (size != merged->size + && !bfd_set_section_size (osec->output_section, merged->size)) + { + bfd_nonfatal_message (NULL, obfd, osec, + _("warning: failed to set merged notes size")); + free (merged->contents); + free (merged); + continue; + } + + /* Add section to list of merged sections. */ + merged->sec = osec; + merged->next = merged_note_sections; + merged_note_sections = merged; + } + } + + if (dump_sections != NULL) + { + struct section_add * pdump; + + for (pdump = dump_sections; pdump != NULL; pdump = pdump->next) + { + osec = bfd_get_section_by_name (ibfd, pdump->name); + if (osec == NULL) + { + bfd_nonfatal_message (NULL, ibfd, NULL, + _("can't dump section '%s' - it does not exist"), + pdump->name); + continue; + } + + if ((bfd_section_flags (osec) & SEC_HAS_CONTENTS) == 0) + { + bfd_nonfatal_message (NULL, ibfd, osec, + _("can't dump section - it has no contents")); + continue; + } + + bfd_size_type size = bfd_section_size (osec); + if (size == 0) + { + bfd_nonfatal_message (NULL, ibfd, osec, + _("can't dump section - it is empty")); + continue; + } + + FILE * f; + f = fopen (pdump->filename, FOPEN_WB); + if (f == NULL) + { + bfd_nonfatal_message (pdump->filename, NULL, NULL, + _("could not open section dump file")); + continue; + } + + bfd_byte *contents; + if (bfd_malloc_and_get_section (ibfd, osec, &contents)) + { + if (fwrite (contents, 1, size, f) != size) + { + non_fatal (_("error writing section contents to %s (error: %s)"), + pdump->filename, + strerror (errno)); + free (contents); + fclose (f); + return FALSE; } } + else + bfd_nonfatal_message (NULL, ibfd, osec, + _("could not retrieve section contents")); + + fclose (f); + free (contents); } } if (gnu_debuglink_filename != NULL) { - gnu_debuglink_section = bfd_create_gnu_debuglink_section - (obfd, gnu_debuglink_filename); - - if (gnu_debuglink_section == NULL) + /* PR 15125: Give a helpful warning message if + the debuglink section already exists, and + allow the rest of the copy to complete. */ + if (bfd_get_section_by_name (obfd, ".gnu_debuglink")) { - bfd_nonfatal (gnu_debuglink_filename); - return FALSE; + non_fatal (_("%s: debuglink section already exists"), + bfd_get_filename (obfd)); + gnu_debuglink_filename = NULL; } + else + { + gnu_debuglink_section = bfd_create_gnu_debuglink_section + (obfd, gnu_debuglink_filename); + + if (gnu_debuglink_section == NULL) + { + bfd_nonfatal_message (NULL, obfd, NULL, + _("cannot create debug link section `%s'"), + gnu_debuglink_filename); + return FALSE; + } - /* Special processing for PE format files. We - have no way to distinguish PE from COFF here. */ - if (bfd_get_flavour (obfd) == bfd_target_coff_flavour) - { - bfd_vma debuglink_vma; - asection * highest_section; - asection * sec; - - /* The PE spec requires that all sections be adjacent and sorted - in ascending order of VMA. It also specifies that debug - sections should be last. This is despite the fact that debug - sections are not loaded into memory and so in theory have no - use for a VMA. - - This means that the debuglink section must be given a non-zero - VMA which makes it contiguous with other debug sections. So - walk the current section list, find the section with the - highest VMA and start the debuglink section after that one. */ - for (sec = obfd->sections, highest_section = NULL; - sec != NULL; - sec = sec->next) - if (sec->vma > 0 - && (highest_section == NULL - || sec->vma > highest_section->vma)) - highest_section = sec; - - if (highest_section) - debuglink_vma = BFD_ALIGN (highest_section->vma - + highest_section->size, - /* FIXME: We ought to be using - COFF_PAGE_SIZE here or maybe - bfd_get_section_alignment() (if it - was set) but since this is for PE - and we know the required alignment - it is easier just to hard code it. */ - 0x1000); - else - /* Umm, not sure what to do in this case. */ - debuglink_vma = 0x1000; + /* Special processing for PE format files. We + have no way to distinguish PE from COFF here. */ + if (bfd_get_flavour (obfd) == bfd_target_coff_flavour) + { + bfd_vma debuglink_vma; + asection * highest_section; + + /* The PE spec requires that all sections be adjacent and sorted + in ascending order of VMA. It also specifies that debug + sections should be last. This is despite the fact that debug + sections are not loaded into memory and so in theory have no + use for a VMA. + + This means that the debuglink section must be given a non-zero + VMA which makes it contiguous with other debug sections. So + walk the current section list, find the section with the + highest VMA and start the debuglink section after that one. */ + for (osec = obfd->sections, highest_section = NULL; + osec != NULL; + osec = osec->next) + if (osec->vma > 0 + && (highest_section == NULL + || osec->vma > highest_section->vma)) + highest_section = osec; + + if (highest_section) + debuglink_vma = BFD_ALIGN (highest_section->vma + + highest_section->size, + /* FIXME: We ought to be using + COFF_PAGE_SIZE here or maybe + bfd_section_alignment() (if it + was set) but since this is for PE + and we know the required alignment + it is easier just to hard code it. */ + 0x1000); + else + /* Umm, not sure what to do in this case. */ + debuglink_vma = 0x1000; - bfd_set_section_vma (obfd, gnu_debuglink_section, debuglink_vma); + bfd_set_section_vma (gnu_debuglink_section, debuglink_vma); + } } } - if (bfd_count_sections (obfd) != 0 + c = bfd_count_sections (obfd); + if (c != 0 && (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 @@ -1558,14 +3079,13 @@ copy_object (bfd *ibfd, bfd *obfd) increasing the section sizes as required to fill the gaps. We write out the gap contents below. */ - c = bfd_count_sections (obfd); - osections = xmalloc (c * sizeof (asection *)); + osections = (asection **) xmalloc (c * sizeof (asection *)); set = osections; bfd_map_over_sections (obfd, get_sections, &set); qsort (osections, c, sizeof (asection *), compare_section_lma); - gaps = xmalloc (c * sizeof (bfd_size_type)); + gaps = (bfd_size_type *) xmalloc (c * sizeof (bfd_size_type)); memset (gaps, 0, c * sizeof (bfd_size_type)); if (gap_fill_set) @@ -1576,22 +3096,21 @@ copy_object (bfd *ibfd, bfd *obfd) bfd_size_type size; bfd_vma gap_start, gap_stop; - flags = bfd_get_section_flags (obfd, osections[i]); + flags = bfd_section_flags (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]); + size = bfd_section_size (osections[i]); + gap_start = bfd_section_lma (osections[i]) + size; + gap_stop = bfd_section_lma (osections[i + 1]); if (gap_start < gap_stop) { - if (! bfd_set_section_size (obfd, osections[i], - size + (gap_stop - gap_start))) + if (!bfd_set_section_size (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 ())); + bfd_nonfatal_message (NULL, obfd, osections[i], + _("Can't fill gap after section")); status = 1; break; } @@ -1607,16 +3126,14 @@ copy_object (bfd *ibfd, bfd *obfd) bfd_vma lma; bfd_size_type size; - lma = bfd_section_lma (obfd, osections[c - 1]); - size = bfd_section_size (obfd, osections[c - 1]); + lma = bfd_section_lma (osections[c - 1]); + size = bfd_section_size (osections[c - 1]); if (lma + size < pad_to) { - if (! bfd_set_section_size (obfd, osections[c - 1], - pad_to - lma)) + if (!bfd_set_section_size (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 ())); + bfd_nonfatal_message (NULL, obfd, osections[c - 1], + _("can't add padding")); status = 1; } else @@ -1633,28 +3150,32 @@ copy_object (bfd *ibfd, bfd *obfd) have been created, but before their contents are set. */ dhandle = NULL; if (convert_debugging) - dhandle = read_debugging_info (ibfd, isympp, symcount); + dhandle = read_debugging_info (ibfd, isympp, symcount, FALSE); if (strip_symbols == STRIP_DEBUG || strip_symbols == STRIP_ALL || strip_symbols == STRIP_UNNEEDED || strip_symbols == STRIP_NONDEBUG + || strip_symbols == STRIP_DWO + || strip_symbols == STRIP_NONDWO || discard_locals != LOCALS_UNDEF || localize_hidden - || strip_specific_list != NULL - || keep_specific_list != NULL - || localize_specific_list != NULL - || globalize_specific_list != NULL - || keepglobal_specific_list != NULL - || weaken_specific_list != NULL + || htab_elements (strip_specific_htab) != 0 + || htab_elements (keep_specific_htab) != 0 + || htab_elements (localize_specific_htab) != 0 + || htab_elements (globalize_specific_htab) != 0 + || htab_elements (keepglobal_specific_htab) != 0 + || htab_elements (weaken_specific_htab) != 0 + || htab_elements (redefine_specific_htab) != 0 || prefix_symbols_string || sections_removed || sections_copied || convert_debugging || change_leading_char || remove_leading_char - || redefine_sym_list - || weaken) + || section_rename_list + || weaken + || add_symbols) { /* Mark symbols used in output relocations so that they are kept, even if they are local labels or static symbols. @@ -1665,16 +3186,32 @@ copy_object (bfd *ibfd, bfd *obfd) ignore input sections which have no corresponding output section. */ if (strip_symbols != STRIP_ALL) - bfd_map_over_sections (ibfd, - mark_symbols_used_in_relocations, - isympp); - osympp = xmalloc ((symcount + 1) * sizeof (asymbol *)); + { + bfd_set_error (bfd_error_no_error); + bfd_map_over_sections (ibfd, + mark_symbols_used_in_relocations, + isympp); + if (bfd_get_error () != bfd_error_no_error) + { + status = 1; + return FALSE; + } + } + + osympp = (asymbol **) xmalloc ((symcount + add_symbols + 1) * sizeof (asymbol *)); symcount = filter_symbols (ibfd, obfd, osympp, isympp, symcount); } if (convert_debugging && dhandle != NULL) { - if (! write_debugging_info (obfd, dhandle, &symcount, &osympp)) + bfd_boolean res; + + res = write_debugging_info (obfd, dhandle, &symcount, &osympp); + + free (dhandle); + dhandle = NULL; /* Paranoia... */ + + if (! res) { status = 1; return FALSE; @@ -1683,6 +3220,9 @@ copy_object (bfd *ibfd, bfd *obfd) bfd_set_symtab (obfd, osympp, symcount); + /* This has to happen before section positions are set. */ + bfd_map_over_sections (ibfd, copy_relocations_in_section, obfd); + /* This has to happen after the symbol table has been set. */ bfd_map_over_sections (ibfd, copy_section, obfd); @@ -1695,18 +3235,105 @@ copy_object (bfd *ibfd, bfd *obfd) if (! bfd_set_section_contents (obfd, padd->section, padd->contents, 0, padd->size)) { - bfd_nonfatal (bfd_get_filename (obfd)); + bfd_nonfatal_message (NULL, obfd, padd->section, NULL); + return FALSE; + } + } + } + + if (update_sections != NULL) + { + struct section_add *pupdate; + + for (pupdate = update_sections; + pupdate != NULL; + pupdate = pupdate->next) + { + osec = pupdate->section->output_section; + if (! bfd_set_section_contents (obfd, osec, pupdate->contents, + 0, pupdate->size)) + { + bfd_nonfatal_message (NULL, obfd, osec, NULL); + return FALSE; + } + } + } + + if (merged_note_sections != NULL) + { + merged_note_section * merged = NULL; + + for (osec = obfd->sections; osec != NULL; osec = osec->next) + { + if (! is_mergeable_note_section (obfd, osec)) + continue; + + if (merged == NULL) + merged = merged_note_sections; + + /* It is likely that output sections are in the same order + as the input sections, but do not assume that this is + the case. */ + if (strcmp (bfd_section_name (merged->sec), + bfd_section_name (osec)) != 0) + { + for (merged = merged_note_sections; + merged != NULL; + merged = merged->next) + if (strcmp (bfd_section_name (merged->sec), + bfd_section_name (osec)) == 0) + break; + + if (merged == NULL) + { + bfd_nonfatal_message + (NULL, obfd, osec, + _("error: failed to locate merged notes")); + continue; + } + } + + if (merged->contents == NULL) + { + bfd_nonfatal_message + (NULL, obfd, osec, + _("error: failed to merge notes")); + continue; + } + + if (! bfd_set_section_contents (obfd, osec, merged->contents, 0, + merged->size)) + { + bfd_nonfatal_message + (NULL, obfd, osec, + _("error: failed to copy merged notes into output")); return FALSE; } + + merged = merged->next; + } + + /* Free the memory. */ + merged_note_section * next; + for (merged = merged_note_sections; merged != NULL; merged = next) + { + next = merged->next; + free (merged->contents); + free (merged); } } + else if (merge_notes && ! is_strip) + non_fatal (_("%s: Could not find any mergeable note sections"), + bfd_get_filename (ibfd)); if (gnu_debuglink_filename != NULL) { if (! bfd_fill_in_gnu_debuglink_section (obfd, gnu_debuglink_section, gnu_debuglink_filename)) { - bfd_nonfatal (gnu_debuglink_filename); + bfd_nonfatal_message (NULL, obfd, NULL, + _("cannot fill debug link section `%s'"), + gnu_debuglink_filename); return FALSE; } } @@ -1714,12 +3341,11 @@ copy_object (bfd *ibfd, bfd *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 = xmalloc (max_gap); + buf = (bfd_byte *) xmalloc (max_gap); memset (buf, gap_fill, max_gap); c = bfd_count_sections (obfd); @@ -1731,7 +3357,7 @@ copy_object (bfd *ibfd, bfd *obfd) file_ptr off; left = gaps[i]; - off = bfd_section_size (obfd, osections[i]) - left; + off = bfd_section_size (osections[i]) - left; while (left > 0) { @@ -1745,7 +3371,8 @@ copy_object (bfd *ibfd, bfd *obfd) if (! bfd_set_section_contents (obfd, osections[i], buf, off, now)) { - bfd_nonfatal (bfd_get_filename (obfd)); + bfd_nonfatal_message (NULL, obfd, osections[i], NULL); + free (buf); return FALSE; } @@ -1754,12 +3381,11 @@ copy_object (bfd *ibfd, bfd *obfd) } } } - } - /* Do not copy backend data if --extract-symbol is passed; anything - that needs to look at the section contents will fail. */ - if (extract_symbol) - return TRUE; + free (buf); + free (gaps); + gaps = NULL; + } /* Allow the BFD backend to copy any private data it understands from the input BFD to the output BFD. This is done last to @@ -1767,9 +3393,8 @@ copy_object (bfd *ibfd, bfd *obfd) 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 ())); + bfd_nonfatal_message (NULL, obfd, NULL, + _("error copying private BFD data")); return FALSE; } @@ -1803,7 +3428,8 @@ copy_object (bfd *ibfd, bfd *obfd) static void copy_archive (bfd *ibfd, bfd *obfd, const char *output_target, - bfd_boolean force_output_target) + bfd_boolean force_output_target, + const bfd_arch_info_type *input_arch) { struct name_list { @@ -1813,22 +3439,55 @@ copy_archive (bfd *ibfd, bfd *obfd, const char *output_target, } *list, *l; bfd **ptr = &obfd->archive_head; bfd *this_element; - char * dir; + char *dir; + const char *filename; + + /* PR 24281: It is not clear what should happen when copying a thin archive. + One part is straight forward - if the output archive is in a different + directory from the input archive then any relative paths in the library + should be adjusted to the new location. But if any transformation + options are active (eg strip, rename, add, etc) then the implication is + that these should be applied to the files pointed to by the archive. + But since objcopy is not destructive, this means that new files must be + created, and there is no guidance for the names of the new files. (Plus + this conflicts with one of the goals of thin libraries - only taking up + a minimal amount of space in the file system). + + So for now we fail if an attempt is made to copy such libraries. */ + if (ibfd->is_thin_archive) + { + status = 1; + bfd_set_error (bfd_error_invalid_operation); + bfd_nonfatal_message (NULL, ibfd, NULL, + _("sorry: copying thin archives is not currently supported")); + return; + } /* Make a temp directory to hold the contents. */ dir = make_tempdir (bfd_get_filename (obfd)); if (dir == NULL) - fatal (_("cannot create tempdir for archive copying (error: %s)"), + fatal (_("cannot create tempdir for archive copying (error: %s)"), strerror (errno)); - obfd->has_armap = ibfd->has_armap; + if (strip_symbols == STRIP_ALL) + obfd->has_armap = FALSE; + else + obfd->has_armap = ibfd->has_armap; + obfd->is_thin_archive = ibfd->is_thin_archive; + + if (deterministic) + obfd->flags |= BFD_DETERMINISTIC_OUTPUT; list = NULL; this_element = bfd_openr_next_archived_file (ibfd, NULL); if (!bfd_set_format (obfd, bfd_get_format (ibfd))) - RETURN_NONFATAL (bfd_get_filename (obfd)); + { + status = 1; + bfd_nonfatal_message (NULL, obfd, NULL, NULL); + goto cleanup_and_exit; + } while (!status && this_element != NULL) { @@ -1837,7 +3496,18 @@ copy_archive (bfd *ibfd, bfd *obfd, const char *output_target, bfd *last_element; struct stat buf; int stat_status = 0; - bfd_boolean delete = TRUE; + bfd_boolean del = TRUE; + bfd_boolean ok_object; + + /* PR binutils/17533: Do not allow directory traversal + outside of the current directory tree by archive members. */ + if (! is_valid_archive_path (bfd_get_filename (this_element))) + { + non_fatal (_("illegal pathname found in archive member: %s"), + bfd_get_filename (this_element)); + status = 1; + goto cleanup_and_exit; + } /* Create an output file for this member. */ output_name = concat (dir, "/", @@ -1846,17 +3516,23 @@ copy_archive (bfd *ibfd, bfd *obfd, const char *output_target, /* If the file already exists, make another temp dir. */ if (stat (output_name, &buf) >= 0) { - output_name = make_tempdir (output_name); - if (output_name == NULL) - fatal (_("cannot create tempdir for archive copying (error: %s)"), - strerror (errno)); + char * tmpdir = make_tempdir (output_name); + + free (output_name); + if (tmpdir == NULL) + { + non_fatal (_("cannot create tempdir for archive copying (error: %s)"), + strerror (errno)); + status = 1; + goto cleanup_and_exit; + } - l = xmalloc (sizeof (struct name_list)); - l->name = output_name; + l = (struct name_list *) xmalloc (sizeof (struct name_list)); + l->name = tmpdir; l->next = list; l->obfd = NULL; list = l; - output_name = concat (output_name, "/", + output_name = concat (tmpdir, "/", bfd_get_filename (this_element), (char *) 0); } @@ -1869,56 +3545,58 @@ copy_archive (bfd *ibfd, bfd *obfd, const char *output_target, bfd_get_filename (this_element)); } - l = xmalloc (sizeof (struct name_list)); + l = (struct name_list *) xmalloc (sizeof (struct name_list)); l->name = output_name; l->next = list; l->obfd = NULL; list = l; - if (bfd_check_format (this_element, bfd_object)) - { - /* PR binutils/3110: Cope with archives - containing multiple target types. */ - if (force_output_target) - output_bfd = bfd_openw (output_name, output_target); - else - output_bfd = bfd_openw (output_name, bfd_get_target (this_element)); + ok_object = bfd_check_format (this_element, bfd_object); + if (!ok_object) + bfd_nonfatal_message (NULL, this_element, NULL, + _("Unable to recognise the format of file")); - if (output_bfd == NULL) - RETURN_NONFATAL (output_name); + /* PR binutils/3110: Cope with archives + containing multiple target types. */ + if (force_output_target || !ok_object) + output_bfd = bfd_openw (output_name, output_target); + else + output_bfd = bfd_openw (output_name, bfd_get_target (this_element)); + + if (output_bfd == NULL) + { + bfd_nonfatal_message (output_name, NULL, NULL, NULL); + status = 1; + goto cleanup_and_exit; + } - delete = ! copy_object (this_element, output_bfd); + if (ok_object) + { + del = !copy_object (this_element, output_bfd, input_arch); - if (! delete - || bfd_get_arch (this_element) != bfd_arch_unknown) + if (del && bfd_get_arch (this_element) == bfd_arch_unknown) + /* Try again as an unknown object file. */ + ok_object = FALSE; + else if (!bfd_close (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; - } + bfd_nonfatal_message (output_name, NULL, NULL, NULL); + /* Error in new object file. Don't change archive. */ + status = 1; } - else - goto copy_unknown_element; } - else - { - non_fatal (_("Unable to recognise the format of the input file `%s'"), - bfd_get_archive_filename (this_element)); - output_bfd = bfd_openw (output_name, output_target); -copy_unknown_element: - delete = !copy_unknown_object (this_element, output_bfd); + if (!ok_object) + { + del = !copy_unknown_object (this_element, output_bfd); if (!bfd_close_all_done (output_bfd)) { - bfd_nonfatal (bfd_get_filename (output_bfd)); + bfd_nonfatal_message (output_name, NULL, NULL, NULL); /* Error in new object file. Don't change archive. */ status = 1; } } - if (delete) + if (del) { unlink (output_name); status = 1; @@ -1945,38 +3623,72 @@ copy_unknown_element: } *ptr = NULL; + filename = bfd_get_filename (obfd); if (!bfd_close (obfd)) - RETURN_NONFATAL (bfd_get_filename (obfd)); + { + status = 1; + bfd_nonfatal_message (filename, NULL, NULL, NULL); + } + filename = bfd_get_filename (ibfd); if (!bfd_close (ibfd)) - RETURN_NONFATAL (bfd_get_filename (ibfd)); + { + status = 1; + bfd_nonfatal_message (filename, NULL, NULL, NULL); + } + cleanup_and_exit: /* Delete all the files that we opened. */ - for (l = list; l != NULL; l = l->next) + { + struct name_list * next; + + for (l = list; l != NULL; l = next) + { + if (l->obfd == NULL) + rmdir (l->name); + else + { + bfd_close (l->obfd); + unlink (l->name); + } + next = l->next; + free (l); + } + } + + rmdir (dir); +} + +static void +set_long_section_mode (bfd *output_bfd, bfd *input_bfd, enum long_section_name_handling style) +{ + /* This is only relevant to Coff targets. */ + if (bfd_get_flavour (output_bfd) == bfd_target_coff_flavour) { - if (l->obfd == NULL) - rmdir (l->name); - else - { - bfd_close (l->obfd); - unlink (l->name); - } + if (style == KEEP + && bfd_get_flavour (input_bfd) == bfd_target_coff_flavour) + style = bfd_coff_long_section_names (input_bfd) ? ENABLE : DISABLE; + bfd_coff_set_long_section_names (output_bfd, style != DISABLE); } - rmdir (dir); } /* The top-level control. */ static void copy_file (const char *input_filename, const char *output_filename, - const char *input_target, const char *output_target) + const char *input_target, const char *output_target, + const bfd_arch_info_type *input_arch) { bfd *ibfd; char **obj_matching; char **core_matching; + off_t size = get_file_size (input_filename); - if (get_file_size (input_filename) < 1) + if (size < 1) { + if (size == 0) + non_fatal (_("error: the input file '%s' is empty"), + input_filename); status = 1; return; } @@ -1985,7 +3697,43 @@ copy_file (const char *input_filename, const char *output_filename, non-object file, failures are nonfatal. */ ibfd = bfd_openr (input_filename, input_target); if (ibfd == NULL) - RETURN_NONFATAL (input_filename); + { + bfd_nonfatal_message (input_filename, NULL, NULL, NULL); + status = 1; + return; + } + + switch (do_debug_sections) + { + case compress: + case compress_zlib: + case compress_gnu_zlib: + case compress_gabi_zlib: + ibfd->flags |= BFD_COMPRESS; + /* Don't check if input is ELF here since this information is + only available after bfd_check_format_matches is called. */ + if (do_debug_sections != compress_gnu_zlib) + ibfd->flags |= BFD_COMPRESS_GABI; + break; + case decompress: + ibfd->flags |= BFD_DECOMPRESS; + break; + default: + break; + } + + switch (do_elf_stt_common) + { + case elf_stt_common: + ibfd->flags |= BFD_CONVERT_ELF_COMMON | BFD_USE_ELF_STT_COMMON; + break; + break; + case no_elf_stt_common: + ibfd->flags |= BFD_CONVERT_ELF_COMMON; + break; + default: + break; + } if (bfd_check_format (ibfd, bfd_archive)) { @@ -1993,7 +3741,7 @@ copy_file (const char *input_filename, const char *output_filename, bfd *obfd; /* bfd_get_target does not return the correct value until - bfd_check_format succeeds. */ + bfd_check_format succeeds. */ if (output_target == NULL) { output_target = bfd_get_target (ibfd); @@ -2004,9 +3752,23 @@ copy_file (const char *input_filename, const char *output_filename, obfd = bfd_openw (output_filename, output_target); if (obfd == NULL) - RETURN_NONFATAL (output_filename); + { + bfd_nonfatal_message (output_filename, NULL, NULL, NULL); + status = 1; + return; + } - copy_archive (ibfd, obfd, output_target, force_output_target); + if (gnu_debuglink_filename != NULL) + { + non_fatal (_("--add-gnu-debuglink ignored for archive %s"), + bfd_get_filename (ibfd)); + gnu_debuglink_filename = NULL; + } + + /* This is a no-op on non-Coff targets. */ + set_long_section_mode (obfd, ibfd, long_section_names); + + copy_archive (ibfd, obfd, output_target, force_output_target, input_arch); } else if (bfd_check_format_matches (ibfd, bfd_object, &obj_matching)) { @@ -2014,23 +3776,40 @@ copy_file (const char *input_filename, const char *output_filename, do_copy: /* bfd_get_target does not return the correct value until - bfd_check_format succeeds. */ + 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); - - if (! copy_object (ibfd, obfd)) + { + bfd_nonfatal_message (output_filename, NULL, NULL, NULL); + status = 1; + return; + } + /* This is a no-op on non-Coff targets. */ + set_long_section_mode (obfd, ibfd, long_section_names); + + if (! copy_object (ibfd, obfd, input_arch)) status = 1; - if (!bfd_close (obfd)) - RETURN_NONFATAL (output_filename); + /* PR 17512: file: 0f15796a. + If the file could not be copied it may not be in a writeable + state. So use bfd_close_all_done to avoid the possibility of + writing uninitialised data into the file. */ + if (! (status ? bfd_close_all_done (obfd) : bfd_close (obfd))) + { + status = 1; + bfd_nonfatal_message (output_filename, NULL, NULL, NULL); + return; + } if (!bfd_close (ibfd)) - RETURN_NONFATAL (input_filename); - + { + status = 1; + bfd_nonfatal_message (input_filename, NULL, NULL, NULL); + return; + } } else { @@ -2050,7 +3829,7 @@ copy_file (const char *input_filename, const char *output_filename, if (obj_error != core_error) bfd_set_error (obj_error); - bfd_nonfatal (input_filename); + bfd_nonfatal_message (input_filename, NULL, NULL, NULL); if (obj_error == bfd_error_file_ambiguously_recognized) { @@ -2073,51 +3852,46 @@ static void add_section_rename (const char * old_name, const char * new_name, flagword flags) { - section_rename * rename; + section_rename * srename; /* Check for conflicts first. */ - for (rename = section_rename_list; rename != NULL; rename = rename->next) - if (strcmp (rename->old_name, old_name) == 0) + for (srename = section_rename_list; srename != NULL; srename = srename->next) + if (strcmp (srename->old_name, old_name) == 0) { /* Silently ignore duplicate definitions. */ - if (strcmp (rename->new_name, new_name) == 0 - && rename->flags == flags) + if (strcmp (srename->new_name, new_name) == 0 + && srename->flags == flags) return; fatal (_("Multiple renames of section %s"), old_name); } - rename = xmalloc (sizeof (* rename)); + srename = (section_rename *) xmalloc (sizeof (* srename)); - rename->old_name = old_name; - rename->new_name = new_name; - rename->flags = flags; - rename->next = section_rename_list; + srename->old_name = old_name; + srename->new_name = new_name; + srename->flags = flags; + srename->next = section_rename_list; - section_rename_list = rename; + section_rename_list = srename; } /* Check the section rename list for a new name of the input section - ISECTION. Return the new name if one is found. - Also set RETURNED_FLAGS to the flags to be used for this section. */ + called OLD_NAME. Returns the new name if one is found and sets + RETURNED_FLAGS if non-NULL to the flags to be used for this section. */ static const char * -find_section_rename (bfd * ibfd ATTRIBUTE_UNUSED, sec_ptr isection, - flagword * returned_flags) +find_section_rename (const char *old_name, flagword *returned_flags) { - const char * old_name = bfd_section_name (ibfd, isection); - section_rename * rename; + const section_rename *srename; - /* Default to using the flags of the input section. */ - * returned_flags = bfd_get_section_flags (ibfd, isection); - - for (rename = section_rename_list; rename != NULL; rename = rename->next) - if (strcmp (rename->old_name, old_name) == 0) + for (srename = section_rename_list; srename != NULL; srename = srename->next) + if (strcmp (srename->old_name, old_name) == 0) { - if (rename->flags != (flagword) -1) - * returned_flags = rename->flags; + if (returned_flags != NULL && srename->flags != (flagword) -1) + *returned_flags = srename->flags; - return rename->new_name; + return srename->new_name; } return old_name; @@ -2129,24 +3903,18 @@ find_section_rename (bfd * ibfd ATTRIBUTE_UNUSED, sec_ptr isection, static void setup_bfd_headers (bfd *ibfd, bfd *obfd) { - const char *err; - /* Allow the BFD backend to copy any private data it understands from the input section to the output section. */ if (! bfd_copy_private_header_data (ibfd, obfd)) { - err = _("private header data"); - goto loser; + status = 1; + bfd_nonfatal_message (NULL, ibfd, NULL, + _("error in private header data")); + return; } /* All went well. */ return; - -loser: - non_fatal (_("%s: error in %s: %s"), - bfd_get_filename (ibfd), - err, bfd_errmsg (bfd_get_error ())); - status = 1; } /* Create a section in OBFD with the same @@ -2155,7 +3923,7 @@ loser: static void setup_section (bfd *ibfd, sec_ptr isection, void *obfdarg) { - bfd *obfd = obfdarg; + bfd *obfd = (bfd *) obfdarg; struct section_list *p; sec_ptr osection; bfd_size_type size; @@ -2165,20 +3933,25 @@ setup_section (bfd *ibfd, sec_ptr isection, void *obfdarg) const char *err; const char * name; char *prefix = NULL; + bfd_boolean make_nobits; + unsigned int alignment; if (is_strip_section (ibfd, isection)) return; - p = find_section_list (bfd_section_name (ibfd, isection), FALSE); - if (p != NULL) - p->used = TRUE; - /* Get the, possibly new, name of the output section. */ - name = find_section_rename (ibfd, isection, & flags); + name = bfd_section_name (isection); + flags = bfd_section_flags (isection); + if (bfd_get_flavour (ibfd) != bfd_get_flavour (obfd)) + { + flags &= bfd_applicable_section_flags (ibfd); + flags &= bfd_applicable_section_flags (obfd); + } + name = find_section_rename (name, &flags); /* Prefix sections. */ - if ((prefix_alloc_sections_string) - && (bfd_get_section_flags (ibfd, isection) & SEC_ALLOC)) + if (prefix_alloc_sections_string + && (bfd_section_flags (isection) & SEC_ALLOC) != 0) prefix = prefix_alloc_sections_string; else if (prefix_sections_string) prefix = prefix_sections_string; @@ -2187,164 +3960,251 @@ setup_section (bfd *ibfd, sec_ptr isection, void *obfdarg) { char *n; - n = xmalloc (strlen (prefix) + strlen (name) + 1); + n = (char *) xmalloc (strlen (prefix) + strlen (name) + 1); strcpy (n, prefix); strcat (n, name); name = n; } - if (p != NULL && p->set_flags) + make_nobits = FALSE; + + p = find_section_list (bfd_section_name (isection), FALSE, + SECTION_CONTEXT_SET_FLAGS); + if (p != NULL) flags = p->flags | (flags & (SEC_HAS_CONTENTS | SEC_RELOC)); else if (strip_symbols == STRIP_NONDEBUG - && obfd->xvec->flavour != bfd_target_elf_flavour - && (flags & SEC_ALLOC) != 0) - flags &= ~(SEC_HAS_CONTENTS | SEC_LOAD); + && (flags & (SEC_ALLOC | SEC_GROUP)) != 0 + && !is_nondebug_keep_contents_section (ibfd, isection)) + { + flags &= ~(SEC_HAS_CONTENTS | SEC_LOAD | SEC_GROUP); + if (bfd_get_flavour (obfd) == bfd_target_elf_flavour) + { + make_nobits = TRUE; + + /* Twiddle the input section flags so that it seems to + elf.c:copy_private_bfd_data that section flags have not + changed between input and output sections. This hack + prevents wholesale rewriting of the program headers. */ + isection->flags &= ~(SEC_HAS_CONTENTS | SEC_LOAD | SEC_GROUP); + } + } osection = bfd_make_section_anyway_with_flags (obfd, name, flags); if (osection == NULL) { - err = _("making"); + err = _("failed to create output section"); goto loser; } - if (strip_symbols == STRIP_NONDEBUG - && obfd->xvec->flavour == bfd_target_elf_flavour - && (flags & SEC_ALLOC) != 0 - && elf_section_type (osection) != SHT_NOTE - && (ibfd->xvec->flavour != bfd_target_elf_flavour - || elf_section_type (isection) != SHT_NOTE) - && (p == NULL || !p->set_flags)) + if (make_nobits) elf_section_type (osection) = SHT_NOBITS; - size = bfd_section_size (ibfd, isection); + size = bfd_section_size (isection); + size = bfd_convert_section_size (ibfd, isection, obfd, size); if (copy_byte >= 0) - size = (size + interleave - 1) / interleave; + size = (size + interleave - 1) / interleave * copy_width; else if (extract_symbol) size = 0; - if (! bfd_set_section_size (obfd, osection, size)) + if (!bfd_set_section_size (osection, size)) { - err = _("size"); + err = _("failed to set 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; + vma = bfd_section_vma (isection); + p = find_section_list (bfd_section_name (isection), FALSE, + SECTION_CONTEXT_ALTER_VMA | SECTION_CONTEXT_SET_VMA); + if (p != NULL) + { + if (p->context & SECTION_CONTEXT_SET_VMA) + vma = p->vma_val; + else + vma += p->vma_val; + } else vma += change_section_address; - if (! bfd_set_section_vma (obfd, osection, extract_symbol ? 0 : vma)) + if (!bfd_set_section_vma (osection, vma)) { - err = _("vma"); + err = _("failed to set vma"); goto loser; } lma = isection->lma; - if ((p != NULL) && p->change_lma != CHANGE_IGNORE) + p = find_section_list (bfd_section_name (isection), FALSE, + SECTION_CONTEXT_ALTER_LMA | SECTION_CONTEXT_SET_LMA); + if (p != NULL) { - if (p->change_lma == CHANGE_MODIFY) + if (p->context & SECTION_CONTEXT_ALTER_LMA) lma += p->lma_val; - else if (p->change_lma == CHANGE_SET) - lma = p->lma_val; else - abort (); + lma = p->lma_val; } else lma += change_section_address; - osection->lma = extract_symbol ? 0 : lma; + osection->lma = lma; + p = find_section_list (bfd_section_name (isection), FALSE, + SECTION_CONTEXT_SET_ALIGNMENT); + if (p != NULL) + alignment = p->alignment; + else + alignment = bfd_section_alignment (isection); + /* 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))) + if (!bfd_set_section_alignment (osection, alignment)) { - err = _("alignment"); + err = _("failed to set alignment"); goto loser; } /* Copy merge entity size. */ osection->entsize = isection->entsize; + /* Copy compress status. */ + osection->compress_status = isection->compress_status; + /* 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 = extract_symbol ? vma : 0; + isection->output_offset = 0; - /* Do not copy backend data if --extract-symbol is passed; anything - that needs to look at the section contents will fail. */ - if (extract_symbol) - return; + if ((isection->flags & SEC_GROUP) != 0) + { + asymbol *gsym = group_signature (isection); + + if (gsym != NULL) + { + gsym->flags |= BSF_KEEP; + if (bfd_get_flavour (ibfd) == bfd_target_elf_flavour) + elf_group_id (isection) = gsym; + } + } /* 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"); + err = _("failed to copy private data"); goto loser; } - else if ((isection->flags & SEC_GROUP) != 0) - { - asymbol *gsym = group_signature (isection); - - if (gsym != NULL) - gsym->flags |= BSF_KEEP; - } /* 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 ())); + loser: status = 1; + bfd_nonfatal_message (NULL, obfd, osection, err); } -/* 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. */ +/* Return TRUE if input section ISECTION should be skipped. */ -static void -copy_section (bfd *ibfd, sec_ptr isection, void *obfdarg) +static bfd_boolean +skip_section (bfd *ibfd, sec_ptr isection, bfd_boolean skip_copy) { - bfd *obfd = obfdarg; - struct section_list *p; - arelent **relpp; - long relcount; sec_ptr osection; bfd_size_type size; - long relsize; flagword flags; /* If we have already failed earlier on, do not keep on generating complaints now. */ if (status != 0) - return; + return TRUE; + + if (extract_symbol) + return TRUE; if (is_strip_section (ibfd, isection)) - return; + return TRUE; - flags = bfd_get_section_flags (ibfd, isection); + if (is_update_section (ibfd, isection)) + return TRUE; + + /* When merging a note section we skip the copying of the contents, + but not the copying of the relocs associated with the contents. */ + if (skip_copy && is_mergeable_note_section (ibfd, isection)) + return TRUE; + + flags = bfd_section_flags (isection); if ((flags & SEC_GROUP) != 0) - return; + return TRUE; osection = isection->output_section; - size = bfd_get_section_size (isection); + size = bfd_section_size (isection); if (size == 0 || osection == 0) + return TRUE; + + return FALSE; +} + +/* Add section SECTION_PATTERN to the list of sections that will have their + relocations removed. */ + +static void +handle_remove_relocations_option (const char *section_pattern) +{ + find_section_list (section_pattern, TRUE, SECTION_CONTEXT_REMOVE_RELOCS); +} + +/* Return TRUE if ISECTION from IBFD should have its relocations removed, + otherwise return FALSE. If the user has requested that relocations be + removed from a section that does not have relocations then this + function will still return TRUE. */ + +static bfd_boolean +discard_relocations (bfd *ibfd ATTRIBUTE_UNUSED, asection *isection) +{ + return (find_section_list (bfd_section_name (isection), FALSE, + SECTION_CONTEXT_REMOVE_RELOCS) != NULL); +} + +/* Wrapper for dealing with --remove-section (-R) command line arguments. + A special case is detected here, if the user asks to remove a relocation + section (one starting with ".rela" or ".rel") then this removal must + be done using a different technique in a relocatable object. */ + +static void +handle_remove_section_option (const char *section_pattern) +{ + find_section_list (section_pattern, TRUE, SECTION_CONTEXT_REMOVE); + if (strncmp (section_pattern, ".rel", 4) == 0) + { + section_pattern += 4; + if (*section_pattern == 'a') + section_pattern++; + if (*section_pattern) + handle_remove_relocations_option (section_pattern); + } + sections_removed = TRUE; +} + +/* Copy relocations in input section ISECTION of IBFD to an output + section with the same name in OBFDARG. If stripping then don't + copy any relocation info. */ + +static void +copy_relocations_in_section (bfd *ibfd, sec_ptr isection, void *obfdarg) +{ + bfd *obfd = (bfd *) obfdarg; + long relsize; + arelent **relpp; + long relcount; + sec_ptr osection; + + if (skip_section (ibfd, isection, FALSE)) return; - p = find_section_list (bfd_get_section_name (ibfd, isection), FALSE); + osection = isection->output_section; - /* Core files do not need to be relocated. */ - if (bfd_get_format (obfd) == bfd_core) + /* Core files and DWO files do not need to be relocated. */ + if (bfd_get_format (obfd) == bfd_core + || strip_symbols == STRIP_NONDWO + || discard_relocations (ibfd, isection)) relsize = 0; else { @@ -2356,18 +4216,40 @@ copy_section (bfd *ibfd, sec_ptr isection, void *obfdarg) if (relsize == -1 && bfd_get_error () == bfd_error_invalid_operation) relsize = 0; else - RETURN_NONFATAL (bfd_get_filename (ibfd)); + { + status = 1; + bfd_nonfatal_message (NULL, ibfd, isection, NULL); + return; + } } } if (relsize == 0) - bfd_set_reloc (obfd, osection, NULL, 0); + { + bfd_set_reloc (obfd, osection, NULL, 0); + osection->flags &= ~SEC_RELOC; + } else { - relpp = xmalloc (relsize); - relcount = bfd_canonicalize_reloc (ibfd, isection, relpp, isympp); - if (relcount < 0) - RETURN_NONFATAL (bfd_get_filename (ibfd)); + if (isection->orelocation != NULL) + { + /* Some other function has already set up the output relocs + for us, so scan those instead of the default relocs. */ + relcount = isection->reloc_count; + relpp = isection->orelocation; + } + else + { + relpp = (arelent **) xmalloc (relsize); + relcount = bfd_canonicalize_reloc (ibfd, isection, relpp, isympp); + if (relcount < 0) + { + status = 1; + bfd_nonfatal_message (NULL, ibfd, isection, + _("relocation count is negative")); + return; + } + } if (strip_symbols == STRIP_ALL) { @@ -2377,31 +4259,67 @@ copy_section (bfd *ibfd, sec_ptr isection, void *obfdarg) long temp_relcount = 0; long i; - temp_relpp = xmalloc (relsize); + 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]; + { + /* PR 17512: file: 9e907e0c. */ + if (relpp[i]->sym_ptr_ptr + /* PR 20096 */ + && * relpp[i]->sym_ptr_ptr) + if (is_specified_symbol (bfd_asymbol_name (*relpp[i]->sym_ptr_ptr), + keep_specific_htab)) + temp_relpp [temp_relcount++] = relpp [i]; + } relcount = temp_relcount; - free (relpp); + if (isection->orelocation == NULL) + free (relpp); relpp = temp_relpp; } bfd_set_reloc (obfd, osection, relcount == 0 ? NULL : relpp, relcount); if (relcount == 0) - free (relpp); + { + osection->flags &= ~SEC_RELOC; + free (relpp); + } } +} - if (extract_symbol) +/* Copy the data of input section ISECTION of IBFD + to an output section with the same name in OBFD. */ + +static void +copy_section (bfd *ibfd, sec_ptr isection, void *obfdarg) +{ + bfd *obfd = (bfd *) obfdarg; + struct section_list *p; + sec_ptr osection; + bfd_size_type size; + + if (skip_section (ibfd, isection, TRUE)) return; - if (bfd_get_section_flags (ibfd, isection) & SEC_HAS_CONTENTS - && bfd_get_section_flags (obfd, osection) & SEC_HAS_CONTENTS) + osection = isection->output_section; + /* The output SHF_COMPRESSED section size is different from input if + ELF classes of input and output aren't the same. We can't use + the output section size since --interleave will shrink the output + section. Size will be updated if the section is converted. */ + size = bfd_section_size (isection); + + if (bfd_section_flags (isection) & SEC_HAS_CONTENTS + && bfd_section_flags (osection) & SEC_HAS_CONTENTS) { - void *memhunk = xmalloc (size); + bfd_byte *memhunk = NULL; - if (!bfd_get_section_contents (ibfd, isection, memhunk, 0, size)) - RETURN_NONFATAL (bfd_get_filename (ibfd)); + if (!bfd_get_full_section_contents (ibfd, isection, &memhunk) + || !bfd_convert_section_contents (ibfd, isection, obfd, + &memhunk, &size)) + { + status = 1; + bfd_nonfatal_message (NULL, ibfd, isection, NULL); + free (memhunk); + return; + } if (reverse_bytes) { @@ -2426,29 +4344,52 @@ copy_section (bfd *ibfd, sec_ptr isection, void *obfdarg) else /* User must pad the section up in order to do this. */ fatal (_("cannot reverse bytes: length of section %s must be evenly divisible by %d"), - bfd_section_name (ibfd, isection), reverse_bytes); + bfd_section_name (isection), reverse_bytes); } if (copy_byte >= 0) { /* Keep only every `copy_byte'th byte in MEMHUNK. */ char *from = (char *) memhunk + copy_byte; - char *to = memhunk; + char *to = (char *) memhunk; char *end = (char *) memhunk + size; + int i; + + /* If the section address is not exactly divisible by the interleave, + then we must bias the from address. If the copy_byte is less than + the bias, then we must skip forward one interleave, and increment + the final lma. */ + int extra = isection->lma % interleave; + from -= extra; + if (copy_byte < extra) + from += interleave; for (; from < end; from += interleave) - *to++ = *from; + for (i = 0; i < copy_width; i++) + { + if (&from[i] >= end) + break; + *to++ = from[i]; + } - size = (size + interleave - 1 - copy_byte) / interleave; + size = (size + interleave - 1 - copy_byte) / interleave * copy_width; osection->lma /= interleave; + if (copy_byte < extra) + osection->lma++; } if (!bfd_set_section_contents (obfd, osection, memhunk, 0, size)) - RETURN_NONFATAL (bfd_get_filename (obfd)); - + { + status = 1; + bfd_nonfatal_message (NULL, obfd, osection, NULL); + free (memhunk); + return; + } free (memhunk); } - else if (p != NULL && p->set_flags && (p->flags & SEC_HAS_CONTENTS) != 0) + else if ((p = find_section_list (bfd_section_name (isection), + FALSE, SECTION_CONTEXT_SET_FLAGS)) != NULL + && (p->flags & SEC_HAS_CONTENTS) != 0) { void *memhunk = xmalloc (size); @@ -2460,7 +4401,12 @@ copy_section (bfd *ibfd, sec_ptr isection, void *obfdarg) memset (memhunk, 0, size); if (! bfd_set_section_contents (obfd, osection, memhunk, 0, size)) - RETURN_NONFATAL (bfd_get_filename (obfd)); + { + status = 1; + bfd_nonfatal_message (NULL, obfd, osection, NULL); + free (memhunk); + return; + } free (memhunk); } } @@ -2471,26 +4417,26 @@ copy_section (bfd *ibfd, sec_ptr isection, void *obfdarg) static void get_sections (bfd *obfd ATTRIBUTE_UNUSED, asection *osection, void *secppparg) { - asection ***secppp = secppparg; + asection ***secppp = (asection ***) secppparg; **secppp = osection; ++(*secppp); } -/* Sort sections by VMA. This is called via qsort, and is used when +/* Sort sections by LMA. 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 (const void *arg1, const void *arg2) { - const asection *const *sec1 = arg1; - const asection *const *sec2 = 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; + flags1 = sec1->flags; + flags2 = sec2->flags; if ((flags1 & SEC_HAS_CONTENTS) == 0 || (flags1 & SEC_LOAD) == 0) { @@ -2506,17 +4452,21 @@ compare_section_lma (const void *arg1, const void *arg2) } /* Sort sections by LMA. */ - if ((*sec1)->lma > (*sec2)->lma) + if (sec1->lma > sec2->lma) return 1; - else if ((*sec1)->lma < (*sec2)->lma) + if (sec1->lma < sec2->lma) return -1; /* Sort sections with the same LMA by size. */ - if (bfd_get_section_size (*sec1) > bfd_get_section_size (*sec2)) + if (bfd_section_size (sec1) > bfd_section_size (sec2)) return 1; - else if (bfd_get_section_size (*sec1) < bfd_get_section_size (*sec2)) + if (bfd_section_size (sec1) < bfd_section_size (sec2)) return -1; + if (sec1->id > sec2->id) + return 1; + if (sec1->id < sec2->id) + return -1; return 0; } @@ -2528,7 +4478,7 @@ compare_section_lma (const void *arg1, const void *arg2) static void mark_symbols_used_in_relocations (bfd *ibfd, sec_ptr isection, void *symbolsarg) { - asymbol **symbols = symbolsarg; + asymbol **symbols = (asymbol **) symbolsarg; long relsize; arelent **relpp; long relcount, i; @@ -2549,7 +4499,7 @@ mark_symbols_used_in_relocations (bfd *ibfd, sec_ptr isection, void *symbolsarg) if (relsize == 0) return; - relpp = xmalloc (relsize); + relpp = (arelent **) xmalloc (relsize); relcount = bfd_canonicalize_reloc (ibfd, isection, relpp, symbols); if (relcount < 0) bfd_fatal (bfd_get_filename (ibfd)); @@ -2558,7 +4508,10 @@ mark_symbols_used_in_relocations (bfd *ibfd, sec_ptr isection, void *symbolsarg) 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 + /* See PRs 20923 and 20930 for reproducers for the NULL tests. */ + if (relpp[i]->sym_ptr_ptr != NULL + && * relpp[i]->sym_ptr_ptr != NULL + && *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; @@ -2575,13 +4528,10 @@ write_debugging_info (bfd *obfd, void *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) { - bfd_byte *syms, *strings; + bfd_byte *syms, *strings = NULL; bfd_size_type symsize, stringsize; asection *stabsec, *stabstrsec; flagword flags; @@ -2596,39 +4546,49 @@ write_debugging_info (bfd *obfd, void *dhandle, stabstrsec = bfd_make_section_with_flags (obfd, ".stabstr", flags); 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)) - { - non_fatal (_("%s: can't create debugging section: %s"), - bfd_get_filename (obfd), - bfd_errmsg (bfd_get_error ())); + || !bfd_set_section_size (stabsec, symsize) + || !bfd_set_section_size (stabstrsec, stringsize) + || !bfd_set_section_alignment (stabsec, 2) + || !bfd_set_section_alignment (stabstrsec, 0)) + { + bfd_nonfatal_message (NULL, obfd, NULL, + _("can't create debugging section")); + free (strings); 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. */ + 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, 0, symsize) || ! bfd_set_section_contents (obfd, stabstrsec, strings, 0, stringsize)) { - non_fatal (_("%s: can't set debugging section contents: %s"), - bfd_get_filename (obfd), - bfd_errmsg (bfd_get_error ())); + bfd_nonfatal_message (NULL, obfd, NULL, + _("can't set debugging section contents")); + free (strings); 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)); + bfd_nonfatal_message (NULL, obfd, NULL, + _("don't know how to write debugging information for %s"), + bfd_get_target (obfd)); return FALSE; } +/* If neither -D nor -U was specified explicitly, + then use the configured default. */ +static void +default_deterministic (void) +{ + if (deterministic < 0) + deterministic = DEFAULT_AR_DETERMINISTIC; +} + static int strip_main (int argc, char *argv[]) { @@ -2638,10 +4598,10 @@ strip_main (int argc, char *argv[]) bfd_boolean formats_info = FALSE; int c; int i; - struct section_list *p; char *output_file = NULL; + bfd_boolean merge_notes_set = FALSE; - while ((c = getopt_long (argc, argv, "I:O:F:K:N:R:o:sSpdgxXHhVvw", + while ((c = getopt_long (argc, argv, "I:O:F:K:MN:R:o:sSpdgxXHhVvwDU", strip_options, (int *) 0)) != EOF) { switch (c) @@ -2656,9 +4616,13 @@ strip_main (int argc, char *argv[]) input_target = output_target = optarg; break; case 'R': - p = find_section_list (optarg, TRUE); - p->remove = TRUE; - sections_removed = TRUE; + handle_remove_section_option (optarg); + break; + case OPTION_KEEP_SECTION: + find_section_list (optarg, TRUE, SECTION_CONTEXT_KEEP); + break; + case OPTION_REMOVE_RELOCS: + handle_remove_relocations_option (optarg); break; case 's': strip_symbols = STRIP_ALL; @@ -2668,14 +4632,25 @@ strip_main (int argc, char *argv[]) case 'd': /* Historic BSD alias for -g. Used by early NetBSD. */ strip_symbols = STRIP_DEBUG; break; + case OPTION_STRIP_DWO: + strip_symbols = STRIP_DWO; + break; case OPTION_STRIP_UNNEEDED: strip_symbols = STRIP_UNNEEDED; break; case 'K': - add_specific_symbol (optarg, &keep_specific_list); + add_specific_symbol (optarg, keep_specific_htab); + break; + case 'M': + merge_notes = TRUE; + merge_notes_set = TRUE; + break; + case OPTION_NO_MERGE_NOTES: + merge_notes = FALSE; + merge_notes_set = TRUE; break; case 'N': - add_specific_symbol (optarg, &strip_specific_list); + add_specific_symbol (optarg, strip_specific_htab); break; case 'o': output_file = optarg; @@ -2683,6 +4658,12 @@ strip_main (int argc, char *argv[]) case 'p': preserve_dates = TRUE; break; + case 'D': + deterministic = TRUE; + break; + case 'U': + deterministic = FALSE; + break; case 'x': discard_locals = LOCALS_ALL; break; @@ -2718,6 +4699,16 @@ strip_main (int argc, char *argv[]) } } + /* If the user has not expressly chosen to merge/not-merge ELF notes + then enable the merging unless we are stripping debug or dwo info. */ + if (! merge_notes_set + && (strip_symbols == STRIP_UNDEF + || strip_symbols == STRIP_ALL + || strip_symbols == STRIP_UNNEEDED + || strip_symbols == STRIP_NONDEBUG + || strip_symbols == STRIP_NONDWO)) + merge_notes = TRUE; + if (formats_info) { display_info (); @@ -2727,10 +4718,12 @@ strip_main (int argc, char *argv[]) if (show_version) print_version ("strip"); + default_deterministic (); + /* Default is to strip all symbols. */ if (strip_symbols == STRIP_UNDEF && discard_locals == LOCALS_UNDEF - && strip_specific_list == NULL) + && htab_elements (strip_specific_htab) == 0) strip_symbols = STRIP_ALL; if (output_target == NULL) @@ -2758,29 +4751,32 @@ strip_main (int argc, char *argv[]) It has already been checked in get_file_size(). */ stat (argv[i], &statbuf); - if (output_file == NULL || strcmp (argv[i], output_file) == 0) + if (output_file == NULL + || filename_cmp (argv[i], output_file) == 0) tmpname = make_tempname (argv[i]); else tmpname = output_file; if (tmpname == NULL) { - non_fatal (_("could not create temporary file to hold stripped copy of '%s'"), - argv[i]); + bfd_nonfatal_message (argv[i], NULL, NULL, + _("could not create temporary file to hold stripped copy")); status = 1; continue; } status = 0; - copy_file (argv[i], tmpname, input_target, output_target); + copy_file (argv[i], tmpname, input_target, output_target, NULL); if (status == 0) { if (preserve_dates) set_times (tmpname, &statbuf); if (output_file != tmpname) - smart_rename (tmpname, output_file ? output_file : argv[i], - preserve_dates); - status = hold_status; + status = (smart_rename (tmpname, + output_file ? output_file : argv[i], + preserve_dates) != 0); + if (status == 0) + status = hold_status; } else unlink_if_ordinary (tmpname); @@ -2791,10 +4787,195 @@ strip_main (int argc, char *argv[]) return status; } +/* Set up PE subsystem. */ + +static void +set_pe_subsystem (const char *s) +{ + const char *version, *subsystem; + size_t i; + static const struct + { + const char *name; + const char set_def; + const short value; + } + v[] = + { + { "native", 0, IMAGE_SUBSYSTEM_NATIVE }, + { "windows", 0, IMAGE_SUBSYSTEM_WINDOWS_GUI }, + { "console", 0, IMAGE_SUBSYSTEM_WINDOWS_CUI }, + { "posix", 0, IMAGE_SUBSYSTEM_POSIX_CUI }, + { "wince", 0, IMAGE_SUBSYSTEM_WINDOWS_CE_GUI }, + { "efi-app", 1, IMAGE_SUBSYSTEM_EFI_APPLICATION }, + { "efi-bsd", 1, IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER }, + { "efi-rtd", 1, IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER }, + { "sal-rtd", 1, IMAGE_SUBSYSTEM_SAL_RUNTIME_DRIVER }, + { "xbox", 0, IMAGE_SUBSYSTEM_XBOX } + }; + short value; + char *copy; + int set_def = -1; + + /* Check for the presence of a version number. */ + version = strchr (s, ':'); + if (version == NULL) + subsystem = s; + else + { + int len = version - s; + copy = xstrdup (s); + subsystem = copy; + copy[len] = '\0'; + version = copy + 1 + len; + pe_major_subsystem_version = strtoul (version, ©, 0); + if (*copy == '.') + pe_minor_subsystem_version = strtoul (copy + 1, ©, 0); + if (*copy != '\0') + non_fatal (_("%s: bad version in PE subsystem"), s); + } + + /* Check for numeric subsystem. */ + value = (short) strtol (subsystem, ©, 0); + if (*copy == '\0') + { + for (i = 0; i < ARRAY_SIZE (v); i++) + if (v[i].value == value) + { + pe_subsystem = value; + set_def = v[i].set_def; + break; + } + } + else + { + /* Search for subsystem by name. */ + for (i = 0; i < ARRAY_SIZE (v); i++) + if (strcmp (subsystem, v[i].name) == 0) + { + pe_subsystem = v[i].value; + set_def = v[i].set_def; + break; + } + } + + switch (set_def) + { + case -1: + fatal (_("unknown PE subsystem: %s"), s); + break; + case 0: + break; + default: + if (pe_file_alignment == (bfd_vma) -1) + pe_file_alignment = PE_DEF_FILE_ALIGNMENT; + if (pe_section_alignment == (bfd_vma) -1) + pe_section_alignment = PE_DEF_SECTION_ALIGNMENT; + break; + } + if (s != subsystem) + free ((char *) subsystem); +} + +/* Convert EFI target to PEI target. */ + +static void +convert_efi_target (char *efi) +{ + efi[0] = 'p'; + efi[1] = 'e'; + efi[2] = 'i'; + + if (strcmp (efi + 4, "ia32") == 0) + { + /* Change ia32 to i386. */ + efi[5]= '3'; + efi[6]= '8'; + efi[7]= '6'; + } + else if (strcmp (efi + 4, "x86_64") == 0) + { + /* Change x86_64 to x86-64. */ + efi[7] = '-'; + } +} + +/* Allocate and return a pointer to a struct section_add, initializing the + structure using ARG, a string in the format "sectionname=filename". + The returned structure will have its next pointer set to NEXT. The + OPTION field is the name of the command line option currently being + parsed, and is only used if an error needs to be reported. */ + +static struct section_add * +init_section_add (const char *arg, + struct section_add *next, + const char *option) +{ + struct section_add *pa; + const char *s; + + s = strchr (arg, '='); + if (s == NULL) + fatal (_("bad format for %s"), option); + + pa = (struct section_add *) xmalloc (sizeof (struct section_add)); + pa->name = xstrndup (arg, s - arg); + pa->filename = s + 1; + pa->next = next; + pa->contents = NULL; + pa->size = 0; + + return pa; +} + +/* Load the file specified in PA, allocating memory to hold the file + contents, and store a pointer to the allocated memory in the contents + field of PA. The size field of PA is also updated. All errors call + FATAL. */ + +static void +section_add_load_file (struct section_add *pa) +{ + size_t off, alloc; + FILE *f; + + /* We don't use get_file_size so that we can do + --add-section .note.GNU_stack=/dev/null + get_file_size doesn't work on /dev/null. */ + + f = fopen (pa->filename, FOPEN_RB); + if (f == NULL) + fatal (_("cannot open: %s: %s"), + pa->filename, strerror (errno)); + + off = 0; + alloc = 4096; + pa->contents = (bfd_byte *) xmalloc (alloc); + while (!feof (f)) + { + off_t got; + + if (off == alloc) + { + alloc <<= 1; + pa->contents = (bfd_byte *) xrealloc (pa->contents, alloc); + } + + got = fread (pa->contents + off, 1, alloc - off, f); + if (ferror (f)) + fatal (_("%s: fread failed"), pa->filename); + + off += got; + } + + pa->size = off; + + fclose (f); +} + static int copy_main (int argc, char *argv[]) { - char * binary_architecture = NULL; char *input_filename = NULL; char *output_filename = NULL; char *tmpname; @@ -2803,11 +4984,13 @@ copy_main (int argc, char *argv[]) bfd_boolean show_version = FALSE; bfd_boolean change_warn = TRUE; bfd_boolean formats_info = FALSE; + bfd_boolean use_globalize = FALSE; + bfd_boolean use_keep_global = FALSE; int c; - struct section_list *p; struct stat statbuf; + const bfd_arch_info_type *input_arch = NULL; - while ((c = getopt_long (argc, argv, "b:B:i:I:j:K:N:s:O:d:F:L:G:R:SpgxXHhVvW:w", + while ((c = getopt_long (argc, argv, "b:B:i:I:j:K:MN:s:O:d:F:L:G:R:SpgxXHhVvW:wDU", copy_options, (int *) 0)) != EOF) { switch (c) @@ -2819,13 +5002,26 @@ copy_main (int argc, char *argv[]) break; case 'B': - binary_architecture = optarg; + input_arch = bfd_scan_arch (optarg); + if (input_arch == NULL) + fatal (_("architecture %s unknown"), optarg); break; case 'i': - interleave = atoi (optarg); - if (interleave < 1) - fatal (_("interleave must be positive")); + if (optarg) + { + interleave = atoi (optarg); + if (interleave < 1) + fatal (_("interleave must be positive")); + } + else + interleave = 4; + break; + + case OPTION_INTERLEAVE_WIDTH: + copy_width = atoi (optarg); + if (copy_width < 1) + fatal(_("interleave width must be positive")); break; case 'I': @@ -2843,19 +5039,20 @@ copy_main (int argc, char *argv[]) break; case 'j': - p = find_section_list (optarg, TRUE); - if (p->remove) - fatal (_("%s both copied and removed"), optarg); - p->copy = TRUE; + find_section_list (optarg, TRUE, SECTION_CONTEXT_COPY); 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; + handle_remove_section_option (optarg); + break; + + case OPTION_KEEP_SECTION: + find_section_list (optarg, TRUE, SECTION_CONTEXT_KEEP); + break; + + case OPTION_REMOVE_RELOCS: + handle_remove_relocations_option (optarg); break; case 'S': @@ -2866,6 +5063,10 @@ copy_main (int argc, char *argv[]) strip_symbols = STRIP_DEBUG; break; + case OPTION_STRIP_DWO: + strip_symbols = STRIP_DWO; + break; + case OPTION_STRIP_UNNEEDED: strip_symbols = STRIP_UNNEEDED; break; @@ -2879,41 +5080,59 @@ copy_main (int argc, char *argv[]) break; case OPTION_ADD_GNU_DEBUGLINK: + long_section_names = ENABLE ; gnu_debuglink_filename = optarg; break; case 'K': - add_specific_symbol (optarg, &keep_specific_list); + add_specific_symbol (optarg, keep_specific_htab); + break; + + case 'M': + merge_notes = TRUE; + break; + case OPTION_NO_MERGE_NOTES: + merge_notes = FALSE; break; case 'N': - add_specific_symbol (optarg, &strip_specific_list); + add_specific_symbol (optarg, strip_specific_htab); break; case OPTION_STRIP_UNNEEDED_SYMBOL: - add_specific_symbol (optarg, &strip_unneeded_list); + add_specific_symbol (optarg, strip_unneeded_htab); break; case 'L': - add_specific_symbol (optarg, &localize_specific_list); + add_specific_symbol (optarg, localize_specific_htab); break; case OPTION_GLOBALIZE_SYMBOL: - add_specific_symbol (optarg, &globalize_specific_list); + use_globalize = TRUE; + add_specific_symbol (optarg, globalize_specific_htab); break; case 'G': - add_specific_symbol (optarg, &keepglobal_specific_list); + use_keep_global = TRUE; + add_specific_symbol (optarg, keepglobal_specific_htab); break; case 'W': - add_specific_symbol (optarg, &weaken_specific_list); + add_specific_symbol (optarg, weaken_specific_htab); break; case 'p': preserve_dates = TRUE; break; + case 'D': + deterministic = TRUE; + break; + + case 'U': + deterministic = FALSE; + break; + case 'w': wildcard = TRUE; break; @@ -2943,52 +5162,67 @@ copy_main (int argc, char *argv[]) break; case OPTION_ADD_SECTION: + add_sections = init_section_add (optarg, add_sections, + "--add-section"); + section_add_load_file (add_sections); + break; + + case OPTION_UPDATE_SECTION: + update_sections = init_section_add (optarg, update_sections, + "--update-section"); + section_add_load_file (update_sections); + break; + + case OPTION_DUMP_SECTION: + dump_sections = init_section_add (optarg, dump_sections, + "--dump-section"); + break; + + case OPTION_ADD_SYMBOL: { - const char *s; - off_t size; - struct section_add *pa; - int len; - char *name; - FILE *f; + char *s, *t; + struct addsym_node *newsym = xmalloc (sizeof *newsym); + newsym->next = NULL; s = strchr (optarg, '='); - if (s == NULL) - fatal (_("bad format for %s"), "--add-section"); + fatal (_("bad format for %s"), "--add-symbol"); + t = strchr (s + 1, ':'); - size = get_file_size (s + 1); - if (size < 1) + newsym->symdef = xstrndup (optarg, s - optarg); + if (t) { - status = 1; - break; + newsym->section = xstrndup (s + 1, t - (s + 1)); + newsym->symval = strtol (t + 1, NULL, 0); + } + else + { + newsym->section = NULL; + newsym->symval = strtol (s + 1, NULL, 0); + t = s; } - pa = xmalloc (sizeof (struct section_add)); - - len = s - optarg; - name = xmalloc (len + 1); - strncpy (name, optarg, len); - name[len] = '\0'; - pa->name = name; - - pa->filename = s + 1; - pa->size = size; - pa->contents = xmalloc (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); + t = strchr (t + 1, ','); + newsym->othersym = NULL; + if (t) + newsym->flags = parse_symflags (t+1, &newsym->othersym); + else + newsym->flags = BSF_GLOBAL; - pa->next = add_sections; - add_sections = pa; + /* Keep 'othersym' symbols at the front of the list. */ + if (newsym->othersym) + { + newsym->next = add_sym_list; + if (!add_sym_list) + add_sym_tail = &newsym->next; + add_sym_list = newsym; + } + else + { + *add_sym_tail = newsym; + add_sym_tail = &newsym->next; + } + add_symbols++; } break; @@ -3000,23 +5234,27 @@ copy_main (int argc, char *argv[]) case OPTION_CHANGE_SECTION_LMA: case OPTION_CHANGE_SECTION_VMA: { + struct section_list * p; + unsigned int context = 0; 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"; + context = SECTION_CONTEXT_ALTER_LMA | SECTION_CONTEXT_ALTER_VMA; break; case OPTION_CHANGE_SECTION_LMA: option = "--change-section-lma"; + context = SECTION_CONTEXT_ALTER_LMA; break; case OPTION_CHANGE_SECTION_VMA: option = "--change-section-vma"; + context = SECTION_CONTEXT_ALTER_VMA; break; } @@ -3031,38 +5269,46 @@ copy_main (int argc, char *argv[]) fatal (_("bad format for %s"), option); } } + else + { + /* Correct the context. */ + switch (c) + { + case OPTION_CHANGE_SECTION_ADDRESS: + context = SECTION_CONTEXT_SET_LMA | SECTION_CONTEXT_SET_VMA; + break; + case OPTION_CHANGE_SECTION_LMA: + context = SECTION_CONTEXT_SET_LMA; + break; + case OPTION_CHANGE_SECTION_VMA: + context = SECTION_CONTEXT_SET_VMA; + break; + } + } len = s - optarg; - name = xmalloc (len + 1); + name = (char *) xmalloc (len + 1); strncpy (name, optarg, len); name[len] = '\0'; - p = find_section_list (name, TRUE); + p = find_section_list (name, TRUE, context); val = parse_vma (s + 1, option); - - switch (*s) - { - case '=': what = CHANGE_SET; break; - case '-': val = - val; /* Drop through. */ - case '+': what = CHANGE_MODIFY; break; - } + if (*s == '-') + val = - val; switch (c) { case OPTION_CHANGE_SECTION_ADDRESS: - p->change_vma = what; - p->vma_val = val; - /* Drop through. */ + p->vma_val = val; + /* Fall through. */ case OPTION_CHANGE_SECTION_LMA: - p->change_lma = what; - p->lma_val = val; + p->lma_val = val; break; case OPTION_CHANGE_SECTION_VMA: - p->change_vma = what; - p->vma_val = val; + p->vma_val = val; break; } } @@ -3081,10 +5327,43 @@ copy_main (int argc, char *argv[]) change_leading_char = TRUE; break; + case OPTION_COMPRESS_DEBUG_SECTIONS: + if (optarg) + { + if (strcasecmp (optarg, "none") == 0) + do_debug_sections = decompress; + else if (strcasecmp (optarg, "zlib") == 0) + do_debug_sections = compress_zlib; + else if (strcasecmp (optarg, "zlib-gnu") == 0) + do_debug_sections = compress_gnu_zlib; + else if (strcasecmp (optarg, "zlib-gabi") == 0) + do_debug_sections = compress_gabi_zlib; + else + fatal (_("unrecognized --compress-debug-sections type `%s'"), + optarg); + } + else + do_debug_sections = compress; + break; + case OPTION_DEBUGGING: convert_debugging = TRUE; break; + case OPTION_DECOMPRESS_DEBUG_SECTIONS: + do_debug_sections = decompress; + break; + + case OPTION_ELF_STT_COMMON: + if (strcasecmp (optarg, "yes") == 0) + do_elf_stt_common = elf_stt_common; + else if (strcasecmp (optarg, "no") == 0) + do_elf_stt_common = no_elf_stt_common; + else + fatal (_("unrecognized --elf-stt-common= option `%s'"), + optarg); + break; + case OPTION_GAP_FILL: { bfd_vma gap_fill_vma; @@ -3119,7 +5398,7 @@ copy_main (int argc, char *argv[]) case OPTION_REDEFINE_SYM: { - /* Push this redefinition onto redefine_symbol_list. */ + /* Insert this redefinition onto redefine_specific_htab. */ int len; const char *s; @@ -3131,16 +5410,16 @@ copy_main (int argc, char *argv[]) fatal (_("bad format for %s"), "--redefine-sym"); len = s - optarg; - source = xmalloc (len + 1); + source = (char *) xmalloc (len + 1); strncpy (source, optarg, len); source[len] = '\0'; nextarg = s + 1; len = strlen (nextarg); - target = xmalloc (len + 1); + target = (char *) xmalloc (len + 1); strcpy (target, nextarg); - redefine_list_append ("--redefine-sym", source, target); + add_redefine_and_check ("--redefine-sym", source, target); free (source); free (target); @@ -3153,6 +5432,7 @@ copy_main (int argc, char *argv[]) case OPTION_SET_SECTION_FLAGS: { + struct section_list *p; const char *s; int len; char *name; @@ -3162,17 +5442,56 @@ copy_main (int argc, char *argv[]) fatal (_("bad format for %s"), "--set-section-flags"); len = s - optarg; - name = xmalloc (len + 1); + name = (char *) xmalloc (len + 1); strncpy (name, optarg, len); name[len] = '\0'; - p = find_section_list (name, TRUE); + p = find_section_list (name, TRUE, SECTION_CONTEXT_SET_FLAGS); - p->set_flags = TRUE; p->flags = parse_flags (s + 1); } break; + case OPTION_SET_SECTION_ALIGNMENT: + { + struct section_list *p; + const char *s; + int len; + char *name; + int palign, align; + + s = strchr (optarg, '='); + if (s == NULL) + fatal (_("bad format for --set-section-alignment: argument needed")); + + align = atoi (s + 1); + if (align <= 0) + fatal (_("bad format for --set-section-alignment: numeric argument needed")); + + /* Convert integer alignment into a power-of-two alignment. */ + palign = 0; + while ((align & 1) == 0) + { + align >>= 1; + ++palign; + } + + if (align != 1) + /* Number has more than on 1, i.e. wasn't a power of 2. */ + fatal (_("bad format for --set-section-alignment: alignment is not a power of two")); + + /* Add the alignment setting to the section list. */ + len = s - optarg; + name = (char *) xmalloc (len + 1); + strncpy (name, optarg, len); + name[len] = '\0'; + + p = find_section_list (name, TRUE, SECTION_CONTEXT_SET_ALIGNMENT); + if (p) + p->alignment = palign; + } + break; + case OPTION_RENAME_SECTION: { flagword flags; @@ -3189,7 +5508,7 @@ copy_main (int argc, char *argv[]) if (len == 0) fatal (_("bad format for %s"), "--rename-section"); - old_name = xmalloc (len + 1); + old_name = (char *) xmalloc (len + 1); strncpy (old_name, optarg, len); old_name[len] = 0; @@ -3209,7 +5528,7 @@ copy_main (int argc, char *argv[]) if (len == 0) fatal (_("bad format for %s"), "--rename-section"); - new_name = xmalloc (len + 1); + new_name = (char *) xmalloc (len + 1); strncpy (new_name, eq, len); new_name[len] = 0; @@ -3223,23 +5542,26 @@ copy_main (int argc, char *argv[]) break; case OPTION_SREC_LEN: - Chunk = parse_vma (optarg, "--srec-len"); + _bfd_srec_len = parse_vma (optarg, "--srec-len"); break; case OPTION_SREC_FORCES3: - S3Forced = TRUE; + _bfd_srec_forceS3 = TRUE; break; case OPTION_STRIP_SYMBOLS: - add_specific_symbols (optarg, &strip_specific_list); + add_specific_symbols (optarg, strip_specific_htab, + &strip_specific_buffer); break; case OPTION_STRIP_UNNEEDED_SYMBOLS: - add_specific_symbols (optarg, &strip_unneeded_list); + add_specific_symbols (optarg, strip_unneeded_htab, + &strip_unneeded_buffer); break; case OPTION_KEEP_SYMBOLS: - add_specific_symbols (optarg, &keep_specific_list); + add_specific_symbols (optarg, keep_specific_htab, + &keep_specific_buffer); break; case OPTION_LOCALIZE_HIDDEN: @@ -3247,19 +5569,36 @@ copy_main (int argc, char *argv[]) break; case OPTION_LOCALIZE_SYMBOLS: - add_specific_symbols (optarg, &localize_specific_list); + add_specific_symbols (optarg, localize_specific_htab, + &localize_specific_buffer); + break; + + case OPTION_LONG_SECTION_NAMES: + if (!strcmp ("enable", optarg)) + long_section_names = ENABLE; + else if (!strcmp ("disable", optarg)) + long_section_names = DISABLE; + else if (!strcmp ("keep", optarg)) + long_section_names = KEEP; + else + fatal (_("unknown long section names option '%s'"), optarg); break; case OPTION_GLOBALIZE_SYMBOLS: - add_specific_symbols (optarg, &globalize_specific_list); + use_globalize = TRUE; + add_specific_symbols (optarg, globalize_specific_htab, + &globalize_specific_buffer); break; case OPTION_KEEPGLOBAL_SYMBOLS: - add_specific_symbols (optarg, &keepglobal_specific_list); + use_keep_global = TRUE; + add_specific_symbols (optarg, keepglobal_specific_htab, + &keepglobal_specific_buffer); break; case OPTION_WEAKEN_SYMBOLS: - add_specific_symbols (optarg, &weaken_specific_list); + add_specific_symbols (optarg, weaken_specific_htab, + &weaken_specific_buffer); break; case OPTION_ALT_MACH_CODE: @@ -3300,23 +5639,86 @@ copy_main (int argc, char *argv[]) bfd_flags_to_set &= ~D_PAGED; break; + case OPTION_EXTRACT_DWO: + strip_symbols = STRIP_NONDWO; + break; + case OPTION_EXTRACT_SYMBOL: extract_symbol = TRUE; break; case OPTION_REVERSE_BYTES: - { - int prev = reverse_bytes; + { + int prev = reverse_bytes; + + reverse_bytes = atoi (optarg); + if ((reverse_bytes <= 0) || ((reverse_bytes % 2) != 0)) + fatal (_("number of bytes to reverse must be positive and even")); + + if (prev && prev != reverse_bytes) + non_fatal (_("Warning: ignoring previous --reverse-bytes value of %d"), + prev); + break; + } + + case OPTION_FILE_ALIGNMENT: + pe_file_alignment = parse_vma (optarg, "--file-alignment"); + break; + + case OPTION_HEAP: + { + char *end; + pe_heap_reserve = strtoul (optarg, &end, 0); + if (end == optarg + || (*end != '.' && *end != '\0')) + non_fatal (_("%s: invalid reserve value for --heap"), + optarg); + else if (*end != '\0') + { + pe_heap_commit = strtoul (end + 1, &end, 0); + if (*end != '\0') + non_fatal (_("%s: invalid commit value for --heap"), + optarg); + } + } + break; + + case OPTION_IMAGE_BASE: + pe_image_base = parse_vma (optarg, "--image-base"); + break; + + case OPTION_PE_SECTION_ALIGNMENT: + pe_section_alignment = parse_vma (optarg, + "--section-alignment"); + break; + + case OPTION_SUBSYSTEM: + set_pe_subsystem (optarg); + break; - reverse_bytes = atoi (optarg); - if ((reverse_bytes <= 0) || ((reverse_bytes % 2) != 0)) - fatal (_("number of bytes to reverse must be positive and even")); + case OPTION_STACK: + { + char *end; + pe_stack_reserve = strtoul (optarg, &end, 0); + if (end == optarg + || (*end != '.' && *end != '\0')) + non_fatal (_("%s: invalid reserve value for --stack"), + optarg); + else if (*end != '\0') + { + pe_stack_commit = strtoul (end + 1, &end, 0); + if (*end != '\0') + non_fatal (_("%s: invalid commit value for --stack"), + optarg); + } + } + break; - if (prev && prev != reverse_bytes) - non_fatal (_("Warning: ignoring previous --reverse-bytes value of %d"), - prev); - break; - } + case OPTION_VERILOG_DATA_WIDTH: + VerilogDataWidth = parse_vma (optarg, "--verilog-data-width"); + if (VerilogDataWidth < 1) + fatal (_("verilog data width must be at least 1 byte")); + break; case 0: /* We've been given a long option. */ @@ -3331,6 +5733,9 @@ copy_main (int argc, char *argv[]) } } + if (use_globalize && use_keep_global) + fatal(_("--globalize-symbol(s) is incompatible with -G/--keep-global-symbol(s)")); + if (formats_info) { display_info (); @@ -3340,9 +5745,15 @@ copy_main (int argc, char *argv[]) if (show_version) print_version ("objcopy"); + if (interleave && copy_byte == -1) + fatal (_("interleave start byte must be set with --byte")); + if (copy_byte >= interleave) fatal (_("byte number must be less than interleave")); + if (copy_width > interleave - copy_byte) + fatal (_("interleave width must be less than or equal to interleave - byte`")); + if (optind == argc || optind + 2 < argc) copy_usage (stderr, 1); @@ -3350,6 +5761,8 @@ copy_main (int argc, char *argv[]) if (optind + 1 < argc) output_filename = argv[optind + 1]; + default_deterministic (); + /* Default is to strip no symbols. */ if (strip_symbols == STRIP_UNDEF && discard_locals == LOCALS_UNDEF) strip_symbols = STRIP_NONE; @@ -3357,27 +5770,57 @@ copy_main (int argc, char *argv[]) if (output_target == NULL) output_target = input_target; - if (binary_architecture != NULL) + /* Convert input EFI target to PEI target. */ + if (input_target != NULL + && strncmp (input_target, "efi-", 4) == 0) { - if (input_target && strcmp (input_target, "binary") == 0) - { - const bfd_arch_info_type * temp_arch_info; + char *efi; - temp_arch_info = bfd_scan_arch (binary_architecture); + efi = xstrdup (output_target + 4); + if (strncmp (efi, "bsdrv-", 6) == 0 + || strncmp (efi, "rtdrv-", 6) == 0) + efi += 2; + else if (strncmp (efi, "app-", 4) != 0) + fatal (_("unknown input EFI target: %s"), input_target); - if (temp_arch_info != NULL) - { - bfd_external_binary_architecture = temp_arch_info->arch; - bfd_external_machine = temp_arch_info->mach; - } - else - fatal (_("architecture %s unknown"), binary_architecture); + input_target = efi; + convert_efi_target (efi); + } + + /* Convert output EFI target to PEI target. */ + if (output_target != NULL + && strncmp (output_target, "efi-", 4) == 0) + { + char *efi; + + efi = xstrdup (output_target + 4); + if (strncmp (efi, "app-", 4) == 0) + { + if (pe_subsystem == -1) + pe_subsystem = IMAGE_SUBSYSTEM_EFI_APPLICATION; } - else + else if (strncmp (efi, "bsdrv-", 6) == 0) + { + if (pe_subsystem == -1) + pe_subsystem = IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER; + efi += 2; + } + else if (strncmp (efi, "rtdrv-", 6) == 0) { - non_fatal (_("Warning: input target 'binary' required for binary architecture parameter.")); - non_fatal (_(" Argument %s ignored"), binary_architecture); + if (pe_subsystem == -1) + pe_subsystem = IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER; + efi += 2; } + else + fatal (_("unknown output EFI target: %s"), output_target); + + if (pe_file_alignment == (bfd_vma) -1) + pe_file_alignment = PE_DEF_FILE_ALIGNMENT; + if (pe_section_alignment == (bfd_vma) -1) + pe_section_alignment = PE_DEF_SECTION_ALIGNMENT; + + output_target = efi; + convert_efi_target (efi); } if (preserve_dates) @@ -3387,7 +5830,8 @@ copy_main (int argc, char *argv[]) /* If there is no destination file, or the source and destination files are the same, then create a temp and rename the result into the input. */ - if (output_filename == NULL || strcmp (input_filename, output_filename) == 0) + if (output_filename == NULL + || filename_cmp (input_filename, output_filename) == 0) tmpname = make_tempname (input_filename); else tmpname = output_filename; @@ -3396,24 +5840,30 @@ copy_main (int argc, char *argv[]) fatal (_("warning: could not create temporary file whilst copying '%s', (error: %s)"), input_filename, strerror (errno)); - copy_file (input_filename, tmpname, input_target, output_target); + copy_file (input_filename, tmpname, input_target, output_target, input_arch); if (status == 0) { if (preserve_dates) set_times (tmpname, &statbuf); if (tmpname != output_filename) - smart_rename (tmpname, input_filename, preserve_dates); + status = (smart_rename (tmpname, input_filename, + preserve_dates) != 0); } else unlink_if_ordinary (tmpname); + if (tmpname != output_filename) + free (tmpname); + if (change_warn) { + struct section_list *p; + for (p = change_sections; p != NULL; p = p->next) { if (! p->used) { - if (p->change_vma != CHANGE_IGNORE) + if (p->context & (SECTION_CONTEXT_SET_VMA | SECTION_CONTEXT_ALTER_VMA)) { char buff [20]; @@ -3422,12 +5872,12 @@ copy_main (int argc, char *argv[]) /* xgettext:c-format */ non_fatal (_("%s %s%c0x%s never used"), "--change-section-vma", - p->name, - p->change_vma == CHANGE_SET ? '=' : '+', + p->pattern, + p->context & SECTION_CONTEXT_SET_VMA ? '=' : '+', buff); } - if (p->change_lma != CHANGE_IGNORE) + if (p->context & (SECTION_CONTEXT_SET_LMA | SECTION_CONTEXT_ALTER_LMA)) { char buff [20]; @@ -3436,14 +5886,35 @@ copy_main (int argc, char *argv[]) /* xgettext:c-format */ non_fatal (_("%s %s%c0x%s never used"), "--change-section-lma", - p->name, - p->change_lma == CHANGE_SET ? '=' : '+', + p->pattern, + p->context & SECTION_CONTEXT_SET_LMA ? '=' : '+', buff); } } } } + if (strip_specific_buffer) + free (strip_specific_buffer); + + if (strip_unneeded_buffer) + free (strip_unneeded_buffer); + + if (keep_specific_buffer) + free (keep_specific_buffer); + + if (localize_specific_buffer) + free (globalize_specific_buffer); + + if (globalize_specific_buffer) + free (globalize_specific_buffer); + + if (keepglobal_specific_buffer) + free (keepglobal_specific_buffer); + + if (weaken_specific_buffer) + free (weaken_specific_buffer); + return 0; } @@ -3469,7 +5940,8 @@ main (int argc, char *argv[]) strip_symbols = STRIP_UNDEF; discard_locals = LOCALS_UNDEF; - bfd_init (); + if (bfd_init () != BFD_INIT_MAGIC) + fatal (_("fatal error: libbfd ABI mismatch")); set_default_bfd_target (); if (is_strip < 0) @@ -3486,6 +5958,11 @@ main (int argc, char *argv[]) is_strip = (i >= 5 && FILENAME_CMP (program_name + i - 5, "strip") == 0); } + create_symbol_htabs (); + + if (argv != NULL) + bfd_set_error_program_name (argv[0]); + if (is_strip) strip_main (argc, argv); else