diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ChangeLog | 1371 | ||||
-rw-r--r-- | src/Makefile | 638 | ||||
-rw-r--r-- | src/Makefile.am | 141 | ||||
-rw-r--r-- | src/Makefile.in | 487 | ||||
-rw-r--r-- | src/addr2line.c | 490 | ||||
-rw-r--r-- | src/ar.c | 1518 | ||||
-rw-r--r-- | src/arlib.c | 279 | ||||
-rw-r--r-- | src/arlib.h | 96 | ||||
-rw-r--r-- | src/arlib2.c | 50 | ||||
-rw-r--r-- | src/debugpred.h | 53 | ||||
-rw-r--r-- | src/elf32-i386.script | 36 | ||||
-rw-r--r-- | src/elfcmp.c | 751 | ||||
-rw-r--r-- | src/elflint.c | 3293 | ||||
-rw-r--r-- | src/findtextrel.c | 611 | ||||
-rw-r--r-- | src/i386_ld.c | 549 | ||||
-rw-r--r-- | src/ld.c | 360 | ||||
-rw-r--r-- | src/ld.h | 110 | ||||
-rw-r--r-- | src/ldgeneric.c | 1491 | ||||
-rw-r--r-- | src/ldlex.c | 3060 | ||||
-rw-r--r-- | src/ldlex.l | 38 | ||||
-rw-r--r-- | src/ldscript.c | 1924 | ||||
-rw-r--r-- | src/ldscript.h | 143 | ||||
-rw-r--r-- | src/ldscript.y | 82 | ||||
-rw-r--r-- | src/make-debug-archive.in | 132 | ||||
-rw-r--r-- | src/nm.c | 215 | ||||
-rw-r--r-- | src/objdump.c | 815 | ||||
-rw-r--r-- | src/ranlib.c | 309 | ||||
-rw-r--r-- | src/readelf.c | 3836 | ||||
-rw-r--r-- | src/sectionhash.c | 32 | ||||
-rw-r--r-- | src/sectionhash.h | 28 | ||||
-rw-r--r-- | src/size.c | 220 | ||||
-rw-r--r-- | src/strings.c | 744 | ||||
-rw-r--r-- | src/strip.c | 975 | ||||
-rw-r--r-- | src/symbolhash.c | 28 | ||||
-rw-r--r-- | src/symbolhash.h | 28 | ||||
-rw-r--r-- | src/unaligned.h | 36 | ||||
-rw-r--r-- | src/unstrip.c | 2317 | ||||
-rw-r--r-- | src/versionhash.c | 28 | ||||
-rw-r--r-- | src/versionhash.h | 28 | ||||
-rw-r--r-- | src/xelf.h | 30 | ||||
-rw-r--r-- | src/ylwrap | 2 |
41 files changed, 21081 insertions, 6293 deletions
diff --git a/src/ChangeLog b/src/ChangeLog index e5b44049..b14d58bc 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,1374 @@ +2008-12-11 Roland McGrath <roland@redhat.com> + + * nm.c (sym_name): New function. + (show_symbols_sysv): Use it in place of elf_strptr. + (show_symbols_bsd, show_symbols_posix): Likewise. + Fixes RHBZ#476136. + + * nm.c (show_symbols_sysv): Use an alloca'd backup section name when + elf_strptr fails. + +2008-12-02 Roland McGrath <roland@redhat.com> + + * readelf.c (count_dwflmod, process_file): Don't presume encoding of + nonzero OFFSET argument to dwfl_getmodules. + +2008-08-07 Roland McGrath <roland@redhat.com> + + * addr2line.c (main): Pass string to handle_address. + (see_one_module): New function, subroutine of handle_address. + (find_symbol): Likewise. + (handle_address): Take string argument rather than address. + Convert plain number, or handle strings like "(section)+offset" + or "symbol+offset". + +2008-08-01 Roland McGrath <roland@redhat.com> + + * readelf.c (handle_core_item): Handle 'B' type for 1-origin bitset. + For 'b' and 'B', print <x-y,z> or ~<x,y-z> rather than 1/0 string. + + * readelf.c (convert): Take new argument SIZE. + (handle_core_register, handle_core_item): Update callers. + (handle_core_item): Take new arg REPEATED_SIZE. + (handle_core_items): Special case for a singleton item, + let handle_core_item handle repeats if it wants to. + + * readelf.c (handle_core_items): Give abridged output + for identical groups repeated more than twice. + +2008-07-04 Roland McGrath <roland@redhat.com> + + * readelf.c (handle_core_items): Handle ELF_T_ADDR. + +2008-04-10 Roland McGrath <roland@redhat.com> + + * strip.c (handle_elf): Don't keep sections that kept symbol tables + refer to. Instead, just be sure to preserve the original symbol + table in the debug file so those symbols go with their sections and + can be elided from the stripped version of the symbol table. + + * strip.c (handle_elf): When a discarded section kept in the debug + file refers to a nondiscard section via sh_link/sh_info, preserve + that nondiscarded section unmodified in the debug file as well. + Skip adjustment of discarded sections symbol table references when + that symbol table is copied in this way. + + * elflint.c (check_symtab): Don't crash from missing symbol names + after diagnosing bogus strtab. + + * strip.c (handle_elf): Cosmetic cleanup in special section contents + adjustment for symtab changes. + +2008-03-31 Roland McGrath <roland@redhat.com> + + * elflint.c (check_sections): Add checks on SHF_EXECINSTR sections: + must be SHT_PROGBITS, must not be SHF_WRITE. Let backend hook + excuse a special section. + +2008-03-27 Roland McGrath <roland@redhat.com> + + * elflint.c (check_sections): Check that executability and writability + of sections is reflected in segment p_flags. + +2008-03-26 Roland McGrath <roland@redhat.com> + + * elflint.c (check_program_header): Accept PT_GNU_RELRO p_flags + that matches its PT_LOAD's p_flags &~ PF_W. On sparc, PF_X really + is valid in RELRO. + +2008-02-29 Roland McGrath <roland@redhat.com> + + * readelf.c (print_attributes): Add a cast. + * elflint.c (check_attributes): Likewise. + + * unaligned.h (add_8ubyte_unaligned): Cast PTR argument for parity + with [UNALIGNED_ACCESS_CLASS == BYTE_ORDER] definition. + (add_4ubyte_unaligned, add_2ubyte_unaligned): Likewise. + +2008-02-03 Ulrich Drepper <drepper@redhat.com> + + * i386_ld.c (elf_i386_count_relocations): Implement R_386_TLS_GD + when linked into executable. + (elf_i386_create_relocations): Likewise. + +2008-02-20 Roland McGrath <roland@redhat.com> + + * readelf.c (print_attributes): New function. + (process_elf_file): Call it under -A. + + * elflint.c (check_attributes): Implement it for real. + +2008-02-19 Roland McGrath <roland@redhat.com> + + * elflint.c (special_sections): Handle .gnu.attributes section. + (check_sections): Likewise. + (check_attributes): New function. + +2008-02-10 Roland McGrath <roland@redhat.com> + + * elfcmp.c (main): Ignore sh_offset differences in non-SHF_ALLOC + sections and ET_REL files. + +2008-02-02 Ulrich Drepper <drepper@redhat.com> + + * elf32-i386.script: Add .eh_frame_hdr, .tdata, and .tbss sections. + * i386_ld.c (elf_i386_count_relocations): Handle R_386_TLS_LDO_32 + and R_386_TLS_LE. + (elf_i386_create_relocations): Likewise. + * ld.h (struct ld_state): Add need_tls, tls_start, and tls_tcb + elements. + * ldgeneric.c (add_section): If TLS section is used, set need_tls flag. + (ld_generic_create_outfile): Add PT_TLS entry to program header. + Fix generation of PT_GNU_STACK entry. + +2008-02-01 Ulrich Drepper <drepper@redhat.com> + + * ld.c (replace_args): Prevent loop over replacements if the parameter + is only two characters long. + + * ld.c: Recognize sha1 argument for --build-id parameter. + * ldgeneric.c (create_build_id_section): Handle sha1. + (compute_hash_sum): New function. Broken out of compute_build_id. + Take hash function and context as parameters. + (compute_build_id): Use compute_hash_sum for md5 and the new sha1 + implementation. + +2008-01-31 Ulrich Drepper <drepper@redhat.com> + + * elf32-i386.script: Add .note.ABI-tag and .note.gnu.build-id sections. + * ld.c: Recognize --build-id command line parameter. + * ld.h: Define scn_dot_note_gnu_build_id. + (struct ld_state): Add build_id and buildidscnidx elements. + * ldgeneric.c: Implement --build-id command line parameter. + * ldlex.l (ID): Recognize - as valid character after the first one. + +2008-01-29 Ulrich Drepper <drepper@redhat.com> + + * ld.c (replace_args): New function. + (main): Use it to rewrite old-style parameters. + + * elf32-i386.script: Add .gnu.hash section. + * ldgeneric.c (optimal_bucket_size): A tiny bit more efficient. + (fillin_special_symbol): Initialize st_size. + (sortfct_hashval): New function. + (create_gnu_hash): New function. + (create_hash): New function. + (ld_generic_create_outfile): Use the new functions to create the + hash tables. + + * elflint.c (check_gnu_hash): Fix index value printed in error message. + +2008-01-24 Ulrich Drepper <drepper@redhat.com> + + * elflint.c (check_group): Check that signature symbol for section + group is not an empty string. + * ldgeneric.c: Remove magic assignment of indeces in the dynsym + section. Start implementation of --hash-style. + * i386_ld.c: Likewise. + * ld.c: Recognize --hash-style. + * ld.h (struct scninfo): Add comdat_group. + Add additional parameter to finalize_plt callback. + +2008-01-22 Ulrich Drepper <drepper@redhat.com> + + * ld.h (struct callbacks): Add initialize_gotplt. + (struct scnhead): Add scn_dot_gotplt. + (struct ld_state): Add gotpltscnidx. + * i386_ld.c (elf_i386_initialize_plt): Minor optimization. + (elf_i386_initialize_pltrel): Likewise. + (elf_i386_initialize_got): There is now a separate .got.plt, so + don't do the PLT-related work here. Initialize d_type. + (elf_i386_initialize_gotplt): New function. + (elf_i386_plt0): Use ud2a after indirect jump. + (elf_i386_pic_plt0_entry): Likewise. + (elf_i386_finalize_plt): Reference now .got.plt. + (elf_i386_count_relocations): For GOT entries which need no relocation + don't bump nrel_got. + (elf_i386_create_relocations): Also get .got.plt. Rewrite R-386_GOT32 + handling for split .got/.got.plt. + (elf_i386_ld_init): Initialize callbacks.initialize_gotplt. + * elf32-i386.script: Sort sections for security. There are no .got + input sections. Add .got.plt. + * ldgeneric.c (ld_generic_generate_sections): Add .got.plt section. + (ld_generic_create_outfile): Initialize .got.plt section. + Use .got.plt address for _GLOBAL_OFFSET_TABLE_ symbol and DT_PLTGOT. + +2008-01-19 Ulrich Drepper <drepper@redhat.com> + + * i386_ld.c (elf_i386_count_relocations): PLT relocations for undefined + symbols are not carried over into statically linked output files. + Add dummy entries for more TLS relocations. + + * ld.c (options): Add long names for -( and -). + + * ldgeneric.c (check_definition): For newly found definitions don't + mark section as used if symbol is absolute. + (extract_from_archive): Only assign archive sequence number the first + time the archive is handled. Update ld_state.last_archive_used + if any symbol was used. Remove nround variable. + (file_process2): When using symbol from an archive, update + ld_state.group_start_archive, ld_state.archives, and + ld_state.tailarchives. + (ld_generic_file_process): If group is not handled anymore, after + freeing ELF handles for the archives, clear ld_state.archives and + *nextp. Fix wrong logic in recognizing first iteration of group + loop. When clearing flags, also clear ld_state.group_start_archive. + +2008-01-11 Ulrich Drepper <drepper@redhat.com> + + * objdump.c (show_disasm): Adjust disassembler format string for + removal of %e. + +2008-01-04 Roland McGrath <roland@redhat.com> + + * readelf.c (handle_core_items): Take new arg DESCSZ; if nonzero, + a size greater than the items cover means multiple sets of items. + (handle_core_note): Update caller. + +2008-01-04 Roland McGrath <roland@redhat.com> + + * strip.c (handle_elf): Move SHDRIDX defn to silence gcc warning. + +2008-01-03 Roland McGrath <roland@redhat.com> + + * ld.h (linked_from_dso_p): Use __attribute__ ((__gnu_inline__)). + + * elflint.c (check_dynamic): Remove duplicate initializer. + +2008-01-02 Ulrich Drepper <drepper@redhat.com> + + * addr2line.c: Update copyright year. + * ar.c: Likewise. + * elfcmp.c: Likewise. + * elflint.c: Likewise. + * findtextrel.c: Likewise. + * ld.c: Likewise. + * nm.c: Likewise. + * objdump.c: Likewise. + * ranlib.c: Likewise. + * readelf.c: Likewise. + * size.c: Likewise. + * strings.c: Likewise. + * strip.c: Likewise. + * unstrip.c: Likewise. + +2007-12-30 Ulrich Drepper <drepper@redhat.com> + + * objdump (show_disasm): Use %e after third parameter. + +2007-12-21 Ulrich Drepper <drepper@redhat.com> + + * strip.c: Fix wrong parenthesis in a few branch predictions. + * strings.c: Likewise. + +2007-12-20 Ulrich Drepper <drepper@redhat.com> + + * Makefile.am (DEFS): Add DEBUGPRED. + * addr2line.c: Include debugpred.h. + * ar.c: Likewise. + * elfcmp.c: Likewise. + * elflint.c: Likewise. + * findtextrel.c: Likewise. + * nm.c: Likewise. + * objdump.c: Likewise. + * ranlib.c: Likewise. + * readelf.c: Likewise. + * size.c: Likewise. + * strings.c: Likewise. + * strip.c: Likewise. + * unstrip.c: Likewise. + * debugpred.h: New file. + + * readelf.c (handle_relocs_rel): Use elf_scnshndx. + (handle_relocs_rela): Likewise. + + * readelf.c: Add lots of likely/unlikely. + + * elflint.c: Minor cleanups. + +2007-11-19 Roland McGrath <roland@redhat.com> + + * readelf.c (print_ops): Handle all bad op codes gracefully. + Print their numbers instead of just ???. + +2007-11-09 Roland McGrath <roland@redhat.com> + + * readelf.c (attr_callback): Handle DW_AT_data_location. + Handle block forms to mean a DWARF expression for DW_AT_allocated, + DW_AT_associated, DW_AT_bit_size, DW_AT_bit_offset, DW_AT_bit_stride, + DW_AT_byte_size, DW_AT_byte_stride, DW_AT_count, DW_AT_lower_bound, + DW_AT_upper_bound. + +2007-10-20 Roland McGrath <roland@redhat.com> + + * unstrip.c (options): Update -R description. + (struct symbol): Put symbol details a union with a size_t pointer + `duplicate'. + (compare_symbols_output): Use null ->name as marker for discard + symbols, not zero *->map. + (copy_elided_sections): Record forwarding pointers for discarded + duplicates and fill SYMNDX_MAP elements through them. + + * readelf.c (process_file): Set offline_next_address to 0 at start. + (struct process_dwflmod_args): New type. + (process_dwflmod): Take args in it, pass fd to process_elf_file. + (process_file): Update caller; dup FD for passing to libdwfl. + (process_elf_file): Take new arg FD. For ET_REL file when + displaying data affected by libdwfl relocation, open a new Elf handle. + +2007-10-17 Roland McGrath <roland@redhat.com> + + * readelf.c (print_debug_line_section): For invalid data inside a + unit with plausible length, keep printing at the next unit boundary. + + * readelf.c (attr_callback): Use dwarf_formref_die, not dwarf_formref. + +2007-10-16 Roland McGrath <roland@redhat.com> + + * readelf.c (hex_dump): Fix rounding error in whitespace calculation. + +2007-10-15 Roland McGrath <roland@redhat.com> + + * make-debug-archive.in: New file. + * Makefile.am (EXTRA_DIST): Add it. + (make-debug-archive): New target. + (bin_SCRIPTS, CLEANFILES): Add it. + +2007-10-10 Roland McGrath <roland@redhat.com> + + * elflint.c (special_sections): Add new attrflag value exact_or_gnuld. + Use it to check MERGE|STRINGS for .debug_str. + (check_sections): Handle exact_or_gnuld. + +2007-10-08 Roland McGrath <roland@redhat.com> + + * readelf.c (handle_core_item): Handle 'T'|0x80 to indicate + 64-bit struct timeval with 32-bit tv_usec. + +2007-10-07 Roland McGrath <roland@redhat.com> + + * readelf.c (check_archive_index): New function. + (process_file): Call it. Change signature to take only fd and name. + Use libdwfl to open the file, then iterate on its modules (multiple + for an archive) to print file name and call process_elf_file. + (main): Update caller. Let process_file do elf_begin. + (count_dwflmod, process_dwflmod, find_no_debuginfo): New functions. + (process_elf_file): Take only Dwfl_Module * argument. + Don't print the file name here. + (print_debug_*_section): Take Dwfl_Module * argument. + (print_debug): Likewise. Update caller. + (format_dwarf_addr): New function. + (print_debug_ranges_section): Use it. + (attr_callback): Likewise. + (print_debug_line_section, print_debug_loc_section): Likewise. + + * readelf.c (print_debug_ranges_section): Translate all strings. + (print_debug_loc_section): Likewise. + + * unstrip.c (copy_elided_sections): Initialize SEC. + + * ar.c (do_oper_insert): Put trailing / on short names. + + * arlib.h (MAX_AR_NAME_LEN): Decrease by one. + + * arlib2.c (arlib_add_long_name): Adjust for header size. + + * arlib.c (arlib_finalize): Pad long name table to keep size even. + + * ar.c (do_oper_insert): Use write_retry for padding write. + + * ar.c (do_oper_insert): Initialize CUR_OFF in no_old case. + Unconditionally set FOUND[CNT]->elf when setting ->mem. + (remember_long_name): New function. + (do_oper_insert): Call it. Correctly use length of basename, + not original name. Don't store long name twice for new member. + +2007-10-06 Roland McGrath <roland@redhat.com> + + * elflint.c (check_note): Skip empty segment. + (check_note_section): Skip empty section. + + * unstrip.c (options, parse_opt, struct arg_info): Grok -R/--relocate. + (handle_output_dir_module, handle_implicit_modules): Pass it down. + (handle_dwfl_module): When set, use ET_REL already loaded by Dwfl. + (compare_alloc_sections): Take new arg REL, ignore address if true. + (compare_sections): Likewise, pass it down. + (compare_sections_rel, compare_sections_nonrel): New functions. + (find_alloc_sections_prelink, copy_elided_sections): Use them + instead of compare_sections. + (sections_match): New function, broken out of ... + (find_alloc_section): ... here. + (copy_elided_sections): Reorganize section match-up logic. + Use sections_match for SHF_ALLOC in ET_REL. + For ET_REL, let the nonzero sh_addr from the debug file dominate. + + * unstrip.c (add_new_section_symbols): Take new arg REL. + When true, do not update section symbol values. + (collect_symbols): Likewise. Update section symbols with address + of chosen output section, not original section. + (check_symtab_section_symbols, copy_elided_sections): Update callers. + + * unstrip.c (compare_alloc_sections): At the same address, preserve + original section order. + + * elflint.c (special_sections): Don't require MERGE|STRINGS for + .debug_str, it didn't always have them with older tools. + + * elflint.c (check_symtab, check_one_reloc): Ignore sh_addr in ET_REL. + +2007-10-05 Roland McGrath <roland@redhat.com> + + * elflint.c (check_symtab): Allow SHN_UNDEF _GLOBAL_OFFSET_TABLE_ in + ET_REL file. + + * elflint.c (check_symtab): For _GLOBAL_OFFSET_TABLE_, diagnose + SHN_UNDEF as "bad section". Use shndx value in messages. + + * elflint.c (special_sections): Add ".debug_str". Decrement namelen + for ".debug" so it matches as a prefix. + (IS_KNOWN_SPECIAL): New macro. + (check_sections): Use it for ".plt" match. Cite wrong SHT_NOBITS + type even under -d, for a .debug* or .shstrtab section. + + * readelf.c (print_ops): Use hex for address operand. + +2007-10-04 Roland McGrath <roland@redhat.com> + + * unstrip.c (copy_elided_sections): Initialize NDX_SECTION element for + .gnu_debuglink section to SHN_UNDEF. Drop STT_SECTION symbols for + sections mapped to SHN_UNDEF. + +2007-10-04 Ulrich Drepper <drepper@redhat.com> + + * readelf.c (dump_archive_index): Avoid warning about uninitialized + variable with older glibc versions. + Add some branch prediction. + +2007-10-04 Roland McGrath <roland@redhat.com> + + * readelf.c (print_archive_index): New variable. + (options, parse_opt): Accept -c/--archive-index to set it. + (dump_archive_index): New function. + (process_file): Take new arg WILL_PRINT_ARCHIVE_INDEX. + Call dump_archive_index on archives if set. + (main): Update caller. + (any_control_option): Give it file scope, moved out of ... + (parse_opt): ... here. + +2007-10-03 Roland McGrath <roland@redhat.com> + + * unstrip.c (struct arg_info): Add `list' flag. + (options, parse_opt): Grok -n/--list to set it. + (list_module): New function. + (handle_implicit_modules): Call it under -n. + + * elflint.c (check_note_section): New function. + (check_sections): Call it for SHT_NOTE. + + * readelf.c (handle_notes): Use sections when available. + + * elflint.c (check_note_data): New function, broken out of ... + (check_note): ... here. Call it and elf_getdata_rawchunk. + + * readelf.c (handle_auxv_note): Take offset as argument, not buffer. + Use elf_getdata_rawchunk and gelf_getauxv. + (handle_notes_data): New function, broken out of ... + (handle_notes): ... here. Call it and elf_getdata_rawchunk. + +2007-10-01 Roland McGrath <roland@redhat.com> + + * readelf.c (hex_dump): Fix transposed subtraction generating spaces. + + * readelf.c (hex_dump): Fix line header to be hex instead of decimal. + +2007-09-10 Roland McGrath <roland@redhat.com> + + * readelf.c (options): Give -p optional argument, alias --string-dump. + (string_sections, string_sections_tail): New static variables. + (parse_opt): Set them when -p has an argument. + (print_string_section): New function, broken out of ... + (print_strings): ... here. Call it. + (dump_data_section): New function, broken out of ... + (dump_data): ... here. Call it. + (for_each_section_argument): New function, broken out of ... + (dump_data): ... here. Call it. + (dump_strings): New function. + +2007-08-31 Roland McGrath <roland@redhat.com> + + * readelf.c (print_strings): Typo fix. + +2007-08-23 Roland McGrath <roland@redhat.com> + + * readelf.c (printf_with_wrap): Function removed. + (REGISTER_WRAP_COLUMN): New macro. + (handle_core_register): Use print_core_item instead. + (struct register_info): New type. + (compare_registers, compare_register_sets): New functions. + (register_bitpos, compare_sets_by_info): New functions. + (handle_core_registers): Use those to segregate and sort registers + for display. + + * readelf.c (ITEM_WRAP_COLUMN): New macro. + (print_core_item): New function. + (handle_core_item): Use it instead of printf_with_wrap. + (compare_core_items, compare_core_item_groups): New functions. + (handle_core_items): Use them. Sort by group and force line breaks + between groups. + + * readelf.c (handle_core_registers, handle_core_items): New functions, + broken out of ... + (handle_core_note): ... here. Call them. + +2007-08-22 Roland McGrath <roland@redhat.com> + + * unstrip.c (new_shstrtab): New function, broken out of ... + (copy_elided_sections): ... here. + +2007-08-20 Roland McGrath <roland@redhat.com> + + Avoid local function trampolines in nm binary. + * nm.c (sort_by_address): Move to a static function instead of local + inside show_symbols. + (sort_by_name_strtab): New static variable. + (sort_by_name): Use it. Move to a static function instead of local + inside show_symbols. + (show_symbols): Set sort_by_name_strtab. + +2007-08-19 Roland McGrath <roland@redhat.com> + + * readelf.c (handle_auxv_note): New function. + (handle_notes): Call it. + + * readelf.c (printf_with_wrap, convert): New functions. + (handle_core_item, (handle_core_register): New functions. + (handle_notes): Call those with details from ebl_core_note. + +2007-08-12 Roland McGrath <roland@redhat.com> + + * elflint.c (check_note): Accept type 0 with name "Linux". + + * elflint.c (special_sections): Accept SHF_ALLOC for ".note". + + * elflint.c (section_flags_string): Return "none" for 0, not "". + +2007-08-11 Roland McGrath <roland@redhat.com> + + * elflint.c (check_note): Accept NT_GNU_HWCAP, NT_GNU_BUILD_ID. + +2007-08-04 Ulrich Drepper <drepper@redhat.com> + + * readelf.c (hex_dump): Use isprint to determine whether to print + character itself or full stop character. + (dump_data): No need to check endp for NULL after strtol call. + +2007-08-03 Roland McGrath <roland@redhat.com> + + * readelf.c (print_string_sections): New variable. + (options, parse_opt): Handle --strings/-p to set it. + (print_strings): New function. + (process_elf_file): Call it under -p. + + * readelf.c (options): Add hidden aliases --segments, --sections, + as taken by binutils readelf. + +2007-08-01 Roland McGrath <roland@redhat.com> + + * readelf.c (dump_data_sections, dump_data_sections_tail): + New variables. + (options, parse_opt): Handle --hex-dump/-x, set them. + (hex_dump): New function. + (dump_data): New function, call it. + (process_elf_file): Call it. + +2007-07-25 Roland McGrath <roland@redhat.com> + + * addr2line.c (show_symbols): New variable. + (print_addrsym): New function. + (handle_address): Call it. + (options, parse_opt): Handle -S/--symbols. + +2007-06-05 Ulrich Drepper <drepper@redhat.com> + + * addr2line.c: Update for latest autoconf header. + * ar.c: Likewise. + * elfcmp.c: Likewise. + * elflint.c: Likewise. + * findtextrel.c: Likewise. + * ld.c: Likewise. + * ldgeneric.c: Likewise. + * nm.c: Likewise. + * objdump.c: Likewise. + * ranlib.c: Likewise. + * readelf.c: Likewise. + * size.c: Likewise. + * strings.c: Likewise. + * strip.c: Likewise. + * unstrip.c: Likewise. + +2007-05-18 Roland McGrath <roland@redhat.com> + + * unstrip.c (copy_elided_sections): Match up non-NOBITS sections with + stripped file, so as not to duplicate a section copied in both. + + * strip.c (handle_elf): Keep SHT_NOTE section copies in the debug file. + +2007-05-17 Roland McGrath <roland@redhat.com> + + * unstrip.c (copy_elided_sections): Don't call gelf_newphdr for 0. + + * unstrip.c (handle_file): Tweak BIAS != 0 warning. + + * unstrip.c (handle_file): Take new arg CREATE_DIRS. If set, + call make_directories here. + (handle_explicit_files): Take new arg CREATE_DIRS, pass it down. + (handle_dwfl_module): Likewise. + (handle_implicit_modules): Update callers. + (handle_output_dir_module): Likewise. Don't do make_directories here. + + * unstrip.c (get_section_name): New function, broken out of ... + (copy_elided_sections): here. Update callers. + (find_alloc_section): Broken out of ... + (copy_elided_sections): ... here. Update caller. + (symtab_count_leading_section_symbols): Take new arg NEWSYMDATA, + update STT_SECTION symbols' st_value fields as a side effect. + (check_symtab_section_symbols): Update caller. + (add_new_section_symbols): Set st_value in symbols added. + (collect_symbols): Reset S->value for STT_SECTION symbols recorded. + Take new arg SPLIT_BSS. Adjust S->shndx recorded for symbols moved + from .bss to .dynbss. + (find_alloc_sections_prelink): New function. Associate debug file + allocated SHT_NOBITS shdrs with stripped moved by prelink via + .gnu.prelink_undo information. + (copy_elided_sections): Call it when we couldn't find every allocated + section. Don't use a debug file non-NOBITS section if SHF_ALLOC. + Take STRIPPED_EHDR arg instead of E_TYPE and PHNUM. + (handle_file): Update callers. + + * unstrip.c (copy_elided_sections): Ignore unfound unallocated section + named ".comment". + + * elflint.c (check_sections): Fix association of segments with + sections when p_memsz > p_filesz. + +2007-04-29 Roland McGrath <roland@redhat.com> + + * addr2line.c (options, main): Tweak argp group settings to fix + usage output. + +2007-04-28 Roland McGrath <roland@redhat.com> + + * strip.c (handle_elf): Update debug file's SHT_NOBITS sections' + sizes to match sections adjusted in the stripped file. + +2007-04-24 Roland McGrath <roland@redhat.com> + + * elfcmp.c (OPT_HASH_INEXACT): New macro. + (hash_inexact): New variable. + (options, parse_opt): Add --hash-inexact option to set it. + (hash_content_equivalent): New function. + (main): Call it for differing SHT_HASH sections under --hash-inexact. + +2007-04-23 Roland McGrath <roland@redhat.com> + + * unstrip.c: New file. + * Makefile.am (bin_PROGRAMS): Add it. + (unstrip_LDADD): New variable. + + * strip.c (options): Allow --output for -o. + +2007-02-15 Ulrich Drepper <drepper@redhat.com> + + * readelf.c: Remove unused code. Add a few consts. + +2007-02-15 Roland McGrath <roland@redhat.com> + + * readelf.c (print_debug): Fix brainos in SHDR test. + +2007-02-05 Roland McGrath <roland@redhat.com> + + * ar.c: Include <limits.h>, since we use LONG_MAX. + +2007-02-05 Ulrich Drepper <drepper@redhat.com> + + * ar.c: Add ugly hack to work around gcc complaining that we + ignore fchown's return value. + (do_oper_insert): Handle error when writing padding. + * ranlib.c: Add fchown complain work around. + + * arlib.c: Make symtab a global variable. Change all users. + * arlib2.c: Likewise. + * ranlib.c: Likewise. + * ar.c: Likewise. + * arlib.h: Declare it. + +2007-01-11 Roland McGrath <roland@redhat.com> + + * elflint.c (check_sections): Use ebl_machine_section_flag_check on + SHF_MASKPROC bits separately from generic sh_flags validation. + +2007-02-04 Ulrich Drepper <drepper@redhat.com> + + * ar.c: New file. + * arlib.c: New file. + * arlib2.c: New file. + * arlib.h: New file. + * Makefile (noinst_LIBRARIES): Add libar. + (libar_a_SOURCES): Define. + (ar_LDADD): Define. + (CFLAGS_ar): Define. + * ranlib.c: Change to use arlib. + + * elflint.c (check_symtab): Work around GNU ld bug which omits + sections but not symbols in those sections. + +2007-01-10 Ulrich Drepper <drepper@redhat.com> + + * addr2line.c: Update copyright year. + * elfcmp.c: Likewise. + * elflint.c: Likewise. + * findtextrel.c: Likewise. + * ld.c: Likewise. + * nm.c: Likewise. + * objdump.c: Likewise. + * ranlib.c: Likewise. + * readelf.c: Likewise. + * size.c: Likewise. + * strings.c: Likewise. + * strip.c: Likewise. + +2006-12-09 Ulrich Drepper <drepper@redhat.com> + + * elflint.c (compare_hash_gnu_hash): New function. Report if the + two hash tables have different content (module expected omission + of undefined symbols). + +2006-10-31 Roland McGrath <roland@redhat.com> + + * elflint.c (check_program_header): Don't complain about + p_filesz > p_memsz if p_memsz is zero and p_type is PT_NOTE. + +2006-09-19 Jakub Jelinek <jakub@redhat.com> + + * strip.c (process_file): Disallow -f on archives. + +2006-10-09 Roland McGrath <roland@redhat.com> + + * Makefile.am (libld_elf_i386.so): Use $(LINK), not $(CC). + +2006-08-29 Roland McGrath <roland@redhat.com> + + * Makefile.am (MAINTAINERCLEANFILES): New variable. + + * readelf.c (handle_relocs_rel): Typo fix, test DESTSHDR properly. + Reported by Christian Aichinger <Greek0@gmx.net>. + + * elflint.c (valid_e_machine): Add EM_ALPHA. + Reported by Christian Aichinger <Greek0@gmx.net>. + +2006-08-08 Ulrich Drepper <drepper@redhat.com> + + * elflint.c (check_dynamic): Don't require DT_HASH for DT_SYMTAB. + Keep track of which "high DT" entries are present. + Check that either old or GNU-style hash table is present. + If GNU-style hash table is used a symbol table is mandatory. + Check that if any prelink entry is present all of them are. + (check_gnu_hash): Only fail for undefined symbols in GNU-style hash + table if they don't refer to functions. + +2006-07-17 Roland McGrath <roland@redhat.com> + + * elflint.c (struct version_namelist): Use GElf_Versym for `ndx' field. + (add_version): Likewise for argument. + (check_versym): Cast constant to GElf_Versym for comparison. + +2006-07-12 Roland McGrath <roland@redhat.com> + + * readelf.c (handle_gnu_hash): Add casts for machines where + Elf32_Word != unsigned int. + +2006-07-12 Ulrich Drepper <drepper@redhat.com> + + * elflint.c (check_sysv_hash64): Fix printf format. + +2006-07-11 Roland McGrath <roland@redhat.com> + + * addr2line.c (options): English fix in -f doc string. + + * addr2line.c (use_comp_dir): New variable. + (options, parse_opt): Grok -A/--absolute to set it. + (handle_address): If set, prepend dwfl_line_comp_dir results to + relative file names. + +2006-07-06 Ulrich Drepper <drepper@redhat.com> + + * elflint.c: Adjust for latest new hash table format. + * readelf.c: Likewise. + + * elflint.c (check_versym): Ignore hidden bit when comparing version + numbers. + +2006-07-05 Ulrich Drepper <drepper@redhat.com> + + * ldgeneric.c (ld_generic_create_outfile): Correctly recognize + discarded COMDAT symbols. + + * i386_ld.c (elf_i386_count_relocations): Lot of corrections. + (elf_i386_create_relocations): Likewise. + * ld.h (struct symbol): Add local and hidden bits. + * ld.c (create_special_section_symbol): These synthsized symbols + are local and hidden. + * ldgeneric.c (file_process2): Check whether input file matches + the emulation. + (fillin_special_symbol): Create symbols as local and/or hidden + if requested. + (ld_generic_create_outfile): Make local copy of symbol. + Don't hide global, defined symbols in dynamic symbol table unless + requested. Synthetic symbols have no version information. + + * elflint.c: Add support for checking 64-bit SysV-style hash tables. + * readelf.c: Add support for printing 64-bit SysV-style hash tables. + +2006-07-04 Ulrich Drepper <drepper@redhat.com> + + * elflint.c (is_rel_dyn): Fix and extend DT_RELCOUNT/DT_RELACOUNT + testing. + +2006-07-03 Ulrich Drepper <drepper@redhat.com> + + * elflint.c: Add testing of DT_GNU_HASH. + * readelf.c: Implement showing histogram for DT_GNU_HASH section. + + * Makefile.am: Add hacks to create dependency files for non-generic + linker. + +2006-06-12 Ulrich Drepper <drepper@redhat.com> + + * ldgeneric.c (ld_generic_generate_sections): Don't create .interp + section if creating a DSO and no interpreter is given. + (ld_generic_create_outfile): Don't store reference to symbols in + discarded COMDAT groups. Don't create PHDR and INTERP program header + for DSO if no interpreter is specified. + (create_verneed_data): Pretty printing. + + * ldscript.y (content): If a DSO is created don't set default + interpreter from linker script. + + * i386_ld.c (elf_i386_count_relocations): Do not add relocations + for symbols in discarded COMDAT groups. + (elf_i386_create_relocations): Likewise. + * ld.h (struct scninfo): Add unused_comdat. + * ldgeneric.c (add_section): Also check group signature when + matching COMDAT sections. + (add_relocatable_file): Ignore symbols in COMDAT group which are + discarded. + + * elflint.c (check_one_reloc): For *_NONE relocs only check type + and symbol reference. + +2006-06-11 Ulrich Drepper <drepper@redhat.com> + + * elflint.c (check_dynamic): Fix checking value of tags which are + offsets in the string section. Make sure DT_STRTAB points to the + section referenced in sh_link. + + * ld.c (options): Add headers. Add short option 'R' for '--rpath'. + + * ld.c: Recognize --eh-frame-hdr option. + * ld.h (struct ld_state): Add eh_frame_hdr field. + * ldgeneric.c (struct unw_eh_frame_hdr): Define. + + * ldgeneric.c (add_section): Use ebl_sh_flags_combine instead of + SH_FLAGS_COMBINE. + (add_relocatable_file): Minor optimization of last change. + (match_section): Don't preserve SHF_GROUP flag any longer. + +2006-06-10 Ulrich Drepper <drepper@redhat.com> + + * ld.c (parse_z_option): Recognize execstack and noexecstack. + Handle record and ignore as position dependent options. + (parse_z_option_2): Handle ignore and record here. + * ld.h (struct ld_state): Add execstack field. + * ldgeneric.c (add_relocatable_file): Recognize .note.GNU-stack + sections. + (ld_generic_create_outfile): Fix program header creation in native + linker. Add PT_GNU_STACK program header. + +2006-06-09 Ulrich Drepper <drepper@redhat.com> + + * i386_ld.c (elf_i386_finalize_plt): Don't change symbol table entries + for PLT entries if there is no local definition. + + * ld.c (parse_option): Handle -z ignore like --as-needed and + -z record like --no-as-needed. + * ld.h (struct ld_state): Remove ignore_unused_dsos field. + * ldgeneric.c (new_generated_scn): Always compute ndt_needed by + looping over DSOs. When deciding about adding DT_NEEDED entries + use ->as_needed instead of ignore_unused_dsos. + +2006-05-31 Ulrich Drepper <drepper@redhat.com> + + * ld.c: Recognize --as-needed and --no-as-needed options. + * ld.h (struct usedfile): Add as_needed field. + (struct ld_state): Likewise. + * ldgeneric.c (ld_handle_filename_list): Copy as_needed flag from + the list. + * ldscript.y (filename_id_list): Split to correctly parse all + combinations. + (mark_as_needed): Fix loop. + +2006-05-28 Ulrich Drepper <drepper@redhat.com> + + * addr2line.c (print_dwarf_function): Use unsigned type for lineno + and colno. + +2006-05-27 Ulrich Drepper <drepper@redhat.com> + + * readelf.c (handle_relocs_rela): Better notations for addon value. + (print_ehdr): Distinguish e_ident[EI_VERSION] from e_version. + +2006-04-04 Ulrich Drepper <drepper@redhat.com> + + * addr2line.c: Update copyright year. + * elfcmp.c: Likewise. + * elflint.c: Likewise. + * findtextrel.c: Likewise. + * ld.c: Likewise. + * nm.c: Likewise. + * objdump.c: Likewise. + * ranlib.c: Likewise. + * readelf.c: Likewise. + * size.c: Likewise. + * strings.c: Likewise. + * strip.c: Likewise. + +2006-03-09 Roland McGrath <roland@redhat.com> + + * Makefile.am (AM_LDFLAGS): New variable. + +2006-03-01 Roland McGrath <roland@redhat.com> + + * readelf.c (dwarf_tag_string, dwarf_attr_string): Update name tables + for dwarf.h changes matching 3.0 spec. + (dwarf_encoding_string, dwarf_lang_string, print_ops): Likewise. + +2005-12-04 Ulrich Drepper <drepper@redhat.com> + + * elflint.c (check_one_reloc): If relocation section is not loaded, + don't check whether the relocations modify read-only sections or + loaded and unloaded sections. + +2005-11-28 Ulrich Drepper <drepper@redhat.com> + + * elflint.c (check_one_reloc): Take additional parameters. Use + them to determine whether relocation is valid in this type of + file. DSOs and executables can contain relocation sections in + unloaded sections which just show the relocations the linker + applied. Adjust all callers. + (check_program_header): Check that PT_PHDR is loaded and that offset + matches the one in the ELF header. + +2005-10-26 Roland McGrath <roland@redhat.com> + + * nm.c (get_var_range): dwarf_getloclist -> dwarf_getlocation. + +2005-09-03 Ulrich Drepper <drepper@redhat.com> + + * strip.c (handle_elf): Unify some error messages. + * ld.c (main): Likewise. + * ldgeneric.c (open_elf): Likewise. + * elfcmp.c (main): Likewise. + * elflint.c (check_elf_header): Likewise. + + * size.c (process_file): Fix typo in error message. + + * readelf.c: Lots of little cleanups. Use _unlocked functions. + +2005-09-02 Ulrich Drepper <drepper@redhat.com> + + * strings.c (main): Reset elfmap variable after munmap call. + [_MUDFLAP] (map_file): Simplify mudflap debugging by not using mmap. + +2005-08-28 Ulrich Drepper <drepper@redhat.com> + + * ranlib.c: Don't define pread_retry and write_retry here. + + * Makefile.an [BUILD_STATIC] (libdw): Add -ldl. + (CLEANFILES): Add *.gcno *.gcda *.gconv. + + * strings.c (process_chunk): Reorder expressions in conditional + (process_chunk_mb): Likewise. + + * strings.c: New file. + * Makefile.am (bin_PROGRAMS): Add strings. + (strings_no_Wstring): Define. + (strings_LDADD): Define. + +2005-08-27 Roland McGrath <roland@redhat.com> + + * addr2line.c (dwarf_diename_integrate): Function removed. + (print_dwarf_function): Use plain dwarf_diename. + +2005-08-24 Ulrich Drepper <drepper@redhat.com> + + * elflint.c (check_versym): Versioned symbols should not have + local binding. + +2005-08-15 Ulrich Drepper <drepper@redhat.com> + + * elflint.c (check_versym): Allow VER_NDX_LOCAL symbols to be + undefined. + + * Makefile.am: Add rules to build ranlib. + * ranlib.c: New file. + +2005-08-14 Roland McGrath <roland@redhat.com> + + * elflint.c (check_sections): Use ebl_section_type_name and allow any + sh_type it recognizes. + + * elflint.c (check_sections): Print unknown flags in hex, don't + truncate high bits. Print section number and name for unknown type. + +2005-08-13 Roland McGrath <roland@redhat.com> + + * elflint.c (check_program_header): Use ebl_segment_type_name and + allow any p_type it recognizes. Include p_type value in error + message for unknown type. + +2005-08-13 Ulrich Drepper <drepper@redhat.com> + + * elflint.c (check_symtab): Simplify last change a bit. Pass ehdr + to ebl_check_special_symbol. + (check_sections): Pass ehdr to ebl_bss_plt_p. + +2005-08-12 Roland McGrath <roland@redhat.com> + + * elflint.c (check_symtab): Check that _GLOBAL_OFFSET_TABLE_ st_shndx + refers to the right section if it's not SHN_ABS. + Let ebl_check_special_symbol override _G_O_T_ value and size checks. + + * elflint.c (check_sections): Don't complain about a non-NOBITS + section taking no segment space, if it's sh_size is 0. + + * elflint.c (check_sections): Use ebl_bss_plt_p to see if .plt should + be PROGBITS or NOBITS. + + * elflint.c (check_symtab): Use ebl_check_special_symbol to override + standard st_value and st_size checks. + +2005-07-28 Roland McGrath <roland@redhat.com> + + * addr2line.c (options, parse_opt): Don't handle -e here. + (executable): Variable removed. + (argp_children): New static variable. + (argp): Use it. Make const. + (main): Fill in argp_children from dwfl_standard_argp (). + Let libdwfl handle file selection, pass Dwfl handle to handle_address. + (print_dwarf_function): New function. Try to figure out inline chain. + (elf_getname): Function removed, libdwfl does it for us. + (handle_address): Take Dwfl handle instead of Elf, Dwarf handles. + Use dwfl_module_addrname instead of elf_getname. + Use dwfl_module_getsrc and dwfl_lineinfo instead of libdw calls. + * Makefile.am (INCLUDES): Add libdwfl directory to path. + +2005-08-10 Ulrich Drepper <drepper@redhat.com> + + * strip.c (parse_opt): STATE parameter is now used. + Various little cleanups. + + * readelf.c (print_debug_line_section): Correct fallout of renaming + of DW_LNS_set_epilog_begin. + +2005-08-08 Roland McGrath <roland@redhat.com> + + * strip.c (options, parse_opt): Grok -R .comment for compatibility + with binutils strip. Likewise -d, -S, as aliases for -g. + Likewise ignore -s/--strip-all. + +2005-08-07 Roland McGrath <roland@redhat.com> + + * strip.c (process_file): Open read-only when using a different output + file. + +2005-08-06 Ulrich Drepper <drepper@redhat.com> + + * elflint.c (in_nobits_scn): New function. + (check_versym): Allow references for defined symbols against versions + of other DSOs also for symbols in nobits sections. + Move a few variables around. + + * Makefile.am (AM_CFLAGS): Avoid duplication. + Link with statis libs if BUILD_STATIC. + +2005-08-05 Ulrich Drepper <drepper@redhat.com> + + * elflint.c: Many, many more tests. Mostly related to symbol + versioning. Those sections should now be completely checked. + + * readelf.c (print_dynamic): Use gelf_offscn. + +2005-08-04 Ulrich Drepper <drepper@redhat.com> + + * elflint.c: Add lots more tests: more extension symbol table sanity, + versioning section tests, hash table tests. General cleanup. + +2005-08-02 Ulrich Drepper <drepper@redhat.com> + + * objdump.c: New file. + * Makefile.am (bin_PROGRAMS): Add objdump. + (objdump_LDADD): Define. + + * elflint.c (check_reloc_shdr): New function split out from check_rela + and check_rel. + (check_one_reloc): New function. Likewise. + (check_rela): Use check_reloc_shdr and check_one_reloc. + (check_rel): Likewise. + (check_program_header): Check that PT_DYNAMIC entry matches .dynamic + section. + Add checks that relocations against read-only segments are flagged, + that the text relocation flag is not set unnecessarily, and that + relocations in one section are either against loaded or not-loaded + segments. + +2005-08-01 Ulrich Drepper <drepper@redhat.com> + + * elfcmp.c (main): Ignore section count and section name string table + section index. + +2005-07-27 Roland McGrath <roland@redhat.com> + + * elfcmp.c: Include <locale.h>. + +2005-07-27 Ulrich Drepper <drepper@redhat.com> + + * elfcmp.c: Print name and index of differing section. + +2005-07-24 Ulrich Drepper <drepper@redhat.com> + + * elfcmp.c: Implement comparing gaps between sections. + +2005-07-23 Ulrich Drepper <drepper@redhat.com> + + * elflint.c: Include libeblP.h instead of libebl.h. + * nm.c: Likewise. + * readelf.c: Likewise. + * elfcmp.c: Likewise. + + * elfcmp.c (main): Compare individual ELF header fields, excluding + e_shoff instead of the whole struct at once. + Use ebl_section_strip_p instead of SECTION_STRIP_P. + * strip.c: Use ebl_section_strip_p instead of SECTION_STRIP_P. + +2005-07-22 Ulrich Drepper <drepper@redhat.com> + + * elfcmp.c (main): Take empty section into account when comparing + section content. + + * elflint.c (check_dynamic): Check that d_tag value is >= 0 before + using it. + +2005-07-21 Ulrich Drepper <drepper@redhat.com> + + * elfcmp.c: New file. + * Makefile.am (bin_PROGRAMS): Add elfcmp. + (elfcmp_LDADD): Define. + + * elflint.c (check_rela): Check that copy relocations only reference + object symbols or symbols with unknown type. + (check_rel): Likewise. + +2005-06-08 Roland McGrath <roland@redhat.com> + + * readelf.c (print_ops): Add consts. + +2005-05-31 Roland McGrath <roland@redhat.com> + + * readelf.c (print_debug_abbrev_section): Don't bail after first CU's + abbreviations. Print a header line before each CU section. + + * readelf.c (print_debug_loc_section): Fix indentation for larger + address size. + +2005-05-30 Roland McGrath <roland@redhat.com> + + * readelf.c (print_debug_line_section): Print section offset of each + CU's table, so they are easy to find from seeing the stmt_list value. + + * readelf.c (dwarf_attr_string): Add all attributes in <dwarf.h>. + (attr_callback): Grok DW_AT_ranges and print offset in hex. + + * readelf.c (attr_callback): Add 2 to addrsize * 2 for %#0* format. + (print_debug_ranges_section, print_debug_loc_section): Likewise. + + * readelf.c (print_ops): Take different args for indentation control. + (attr_callback): Caller updated. + Grok several more block-form attributes as being location expressions. + For those same attributes with udata forms, format output differently + for location list offset. + (print_debug_loc_section): Implement it for real. + + * readelf.c (options): Mention ranges for --debug-dump. + (enum section_e): Add section_ranges. + (parse_opt): Grok "ranges" for -w/--debug-dump. + (print_debug_ranges_section): New function. + (print_debug): Handle .debug_ranges section. + +2005-05-30 Ulrich Drepper <drepper@redhat.com> + + * readelf.c (handle_notes): At least x86-64 need not have the note + section values aligned to 8 bytes. + +2005-05-18 Ulrich Drepper <drepper@redhat.com> + + * readelf.c (dwarf_tag_string): Add new tags. + +2005-05-08 Roland McGrath <roland@redhat.com> + + * strip.c (handle_elf): Don't translate hash and versym data formats, + elf_getdata already did it for us. + +2005-05-07 Ulrich Drepper <drepper@redhat.com> + + * Makefile.am (findtextrel_LDADD): Add $(libmudflap). + (addr2line_LDADD): Likewise. + +2005-05-03 Roland McGrath <roland@redhat.com> + + * strip.c (handle_elf): Apply symbol table fixups to discarded + relocation sections when they are being saved in the debug file. + + * strip.c (handle_elf): Pass EHDR->e_ident[EI_DATA] to gelf_xlatetom + and gelf_xlatetof, not the native byte order. + + * strip.c (parse_opt): Give error if -f or -o is repeated. + (main): Exit if argp_parse returns nonzero. + + * strip.c (debug_fname_embed): New variable. + (options, parse_opt): New option -F to set it. + +2005-05-07 Ulrich Drepper <drepper@redhat.com> + + * readelf.c (parse_opt): Make any_control_option variable + local. Simplify some tests. + +2005-05-03 Roland McGrath <roland@redhat.com> + + * strip.c (crc32_file): Function removed (now in ../lib). + +2005-05-03 Roland McGrath <roland@redhat.com> + + * elflint.c (is_debuginfo): New variable. + (options, parse_opt): New option --debuginfo/-d to set it. + (check_sections): If is_debuginfo, don't complain about SHT_NOBITS. + (check_note): If is_debuginfo, don't try to get note contents. + +2005-04-24 Ulrich Drepper <drepper@redhat.com> + + * readelf.c (print_debug_abbrev_section): Don't print error when end of + section reached. + +2005-04-14 Ulrich Drepper <drepper@redhat.com> + + * readelf.c (dwarf_encoding_string): New function. + (dwarf_inline_string): New function. + (dwarf_access_string): New function. + (dwarf_visibility_string): New function. + (dwarf_virtuality_string): New function. + (dwarf_identifier_case_string): New function. + (dwarf_calling_convention_string): New function. + (dwarf_ordering_string): New function. + (dwarf_discr_list_string): New function. + (attr_callback): Decode man more attribute values. + +2005-04-01 Ulrich Drepper <drepper@redhat.com> + + * addr2line.c: Finish implementation of -f option. + +2005-03-29 Ulrich Drepper <drepper@redhat.com> + + * addr2line.c: New file. + * Makefile.am (bin_PROGRAMS): Add addr2line. + Define addr2line_LDADD. + + * findtextrel.c: Use new dwarf_addrdie function. + + * findtextrel.c: Fix usage message and re-add accidentally removed + line. + +2005-03-28 Ulrich Drepper <drepper@redhat.com> + + * findtextrel.c: New file. + * Makefile: Add rules to build findtextrel. + +2005-02-15 Ulrich Drepper <drepper@redhat.com> + + * ldlex.l: Provide ECHO definition to avoid warning. + + * elflint.c (check_program_header): Fix typo in RELRO test. + + * Makefile.am (AM_CFLAGS): Add more warning options. + * elflint.c: Fix warnings introduced by the new warning options. + * i386_ld.c: Likewise. + * ld.c: Likewise. + * ld.h: Likewise. + * ldgeneric.c: Likewise. + * nm.c: Likewise. + * readelf.c: Likewise. + * sectionhash.c: Likewise. + * size.c: Likewise. + * string.c: Likewise. + +2005-02-05 Ulrich Drepper <drepper@redhat.com> + + * Makefile.am: Check for text relocations in constructed DSOs. + + * Makefile.am [MUDFLAP] (AM_CFLAGS): Add -fmudflap. Link all apps + with -lmudflap. + + * ldscript.y: Add as_needed handling. + * ldlex.l: Recognize AS_NEEDED token. + * ld.h (struct filename_list): Add as_needed flag. + +2005-02-04 Ulrich Drepper <drepper@redhat.com> + + * elflint.c (check_symtab): Correctly determine size of GOT section. + +2005-01-19 Ulrich Drepper <drepper@redhat.com> + + * ld.c: Remove unnecessary more_help function. Print bug report + address using argp. + * strip.c: Likewise. + * size.c: Likewise. + * nm.c: Likewise. + * readelf.c: Likewise. + * elflint.c: Likewise. + + * elflint.c (main): Don't check for parameter problems here. + (parse_opt): Do it here, where we get informed about some of them + anyway. + + * readelf.c (main): Don't check for parameter problems here. + (parse_opt): Do it here, where we get informed about some of them + anyway. + +2005-01-11 Ulrich Drepper <drepper@redhat.com> + + * strip.c: Update copyright year. + * readelf.c: Likewise. + * size.c: Likewise. + * nm.c: Likewise. + * ld.c: Likewise. + * elflint.c: Likewise. + + * elflint.c (check_symtab): Don't warn about wrong size for + _DYNAMIC and __GLOBAL_OFFSET_TABLE__ for --gnu-ld. + +2004-10-05 Ulrich Drepper <drepper@redhat.com> + + * readelf.c (print_phdr): In section mapping, also indicate + sections in read-only segments. + 2004-09-25 Ulrich Drepper <drepper@redhat.com> * readelf.c: Make compile with gcc 4.0. diff --git a/src/Makefile b/src/Makefile deleted file mode 100644 index f147eee2..00000000 --- a/src/Makefile +++ /dev/null @@ -1,638 +0,0 @@ -# Makefile.in generated by automake 1.9.2 from Makefile.am. -# src/Makefile. Generated from Makefile.in by configure. - -# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004 Free Software Foundation, Inc. -# This Makefile.in is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - - - - - -SOURCES = $(libld_elf_a_SOURCES) $(libld_elf_i386_pic_a_SOURCES) elflint.c $(ld_SOURCES) $(libld_elf_i386_so_SOURCES) nm.c readelf.c size.c strip.c - -srcdir = . -top_srcdir = .. - -pkgdatadir = $(datadir)/elfutils -pkglibdir = $(libdir)/elfutils -pkgincludedir = $(includedir)/elfutils -top_builddir = .. -am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd -INSTALL = /usr/bin/install -c -install_sh_DATA = $(install_sh) -c -m 644 -install_sh_PROGRAM = $(install_sh) -c -install_sh_SCRIPT = $(install_sh) -c -INSTALL_HEADER = $(INSTALL_DATA) -transform = $(program_transform_name) -NORMAL_INSTALL = : -PRE_INSTALL = : -POST_INSTALL = : -NORMAL_UNINSTALL = : -PRE_UNINSTALL = : -POST_UNINSTALL = : -build_triplet = i686-pc-linux-gnu -host_triplet = i686-pc-linux-gnu -bin_PROGRAMS = readelf$(EXEEXT) nm$(EXEEXT) size$(EXEEXT) \ - strip$(EXEEXT) ld$(EXEEXT) elflint$(EXEEXT) -noinst_PROGRAMS = $(am__EXEEXT_1) -#am__append_1 = libld_elf.a -subdir = src -DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \ - $(srcdir)/Makefile.in ChangeLog ldlex.c ldscript.c ylwrap -ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/gettext.m4 \ - $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/lib-ld.m4 \ - $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ - $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/configure.ac -am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ - $(ACLOCAL_M4) -mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs -CONFIG_HEADER = $(top_builddir)/config.h -CONFIG_CLEAN_FILES = -LIBRARIES = $(noinst_LIBRARIES) -AR = ar -ARFLAGS = cru -libld_elf_a_AR = $(AR) $(ARFLAGS) -libld_elf_a_LIBADD = -am_libld_elf_a_OBJECTS = $(base_cpu)_ld.$(OBJEXT) -libld_elf_a_OBJECTS = $(am_libld_elf_a_OBJECTS) -libld_elf_i386_pic_a_AR = $(AR) $(ARFLAGS) -libld_elf_i386_pic_a_LIBADD = -libld_elf_i386_pic_a_OBJECTS = $(am_libld_elf_i386_pic_a_OBJECTS) -am__installdirs = "$(DESTDIR)$(bindir)" -binPROGRAMS_INSTALL = $(INSTALL_PROGRAM) -am__EXEEXT_1 = libld_elf_i386.so$(EXEEXT) -PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS) -elflint_SOURCES = elflint.c -elflint_OBJECTS = elflint.$(OBJEXT) -am__DEPENDENCIES_1 = ../libebl/libebl.a -am__DEPENDENCIES_2 = ../libelf/libelf.so -#am__DEPENDENCIES_2 = ../libelf/libelf.a -am__DEPENDENCIES_3 = ../lib/libeu.a -elflint_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) \ - $(am__DEPENDENCIES_3) -am_ld_OBJECTS = ld.$(OBJEXT) ldgeneric.$(OBJEXT) ldlex.$(OBJEXT) \ - ldscript.$(OBJEXT) symbolhash.$(OBJEXT) sectionhash.$(OBJEXT) \ - versionhash.$(OBJEXT) -ld_OBJECTS = $(am_ld_OBJECTS) -#am__DEPENDENCIES_4 = libld_elf.a -ld_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) \ - $(am__DEPENDENCIES_3) $(am__DEPENDENCIES_4) -am_libld_elf_i386_so_OBJECTS = -libld_elf_i386_so_OBJECTS = $(am_libld_elf_i386_so_OBJECTS) -libld_elf_i386_so_LDADD = $(LDADD) -nm_SOURCES = nm.c -nm_OBJECTS = nm.$(OBJEXT) -am__DEPENDENCIES_5 = ../libdw/libdw.so -#am__DEPENDENCIES_5 = ../libdw/libdw.a -nm_DEPENDENCIES = $(am__DEPENDENCIES_5) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_3) -readelf_SOURCES = readelf.c -readelf_OBJECTS = readelf.$(OBJEXT) -readelf_DEPENDENCIES = $(am__DEPENDENCIES_5) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_3) -size_SOURCES = size.c -size_OBJECTS = size.$(OBJEXT) -size_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_3) -strip_SOURCES = strip.c -strip_OBJECTS = strip.$(OBJEXT) -strip_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) \ - $(am__DEPENDENCIES_3) -DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) -depcomp = $(SHELL) $(top_srcdir)/config/depcomp -am__depfiles_maybe = depfiles -COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ - $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -CCLD = $(CC) -LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ -LEXCOMPILE = $(LEX) $(LFLAGS) $(AM_LFLAGS) -YACCCOMPILE = $(YACC) $(YFLAGS) $(AM_YFLAGS) -SOURCES = $(libld_elf_a_SOURCES) $(libld_elf_i386_pic_a_SOURCES) \ - elflint.c $(ld_SOURCES) $(libld_elf_i386_so_SOURCES) nm.c \ - readelf.c size.c strip.c -DIST_SOURCES = $(libld_elf_a_SOURCES) $(libld_elf_i386_pic_a_SOURCES) \ - elflint.c $(ld_SOURCES) $(libld_elf_i386_so_SOURCES) nm.c \ - readelf.c size.c strip.c -HEADERS = $(noinst_HEADERS) -ETAGS = etags -CTAGS = ctags -DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) -ACLOCAL = ${SHELL} /work/elfutils/stock/elfutils-0.97/config/missing --run aclocal-1.9 -AMDEP_FALSE = # -AMDEP_TRUE = -AMTAR = ${SHELL} /work/elfutils/stock/elfutils-0.97/config/missing --run tar -AUTOCONF = ${SHELL} /work/elfutils/stock/elfutils-0.97/config/missing --run autoconf -AUTOHEADER = ${SHELL} /work/elfutils/stock/elfutils-0.97/config/missing --run autoheader -AUTOMAKE = ${SHELL} /work/elfutils/stock/elfutils-0.97/config/missing --run automake-1.9 -AWK = gawk -CC = gcc -CCDEPMODE = depmode=gcc3 -CFLAGS = -g -O2 -CPP = gcc -E -CPPFLAGS = -CYGPATH_W = echo -DATADIRNAME = ${prefix}/share -DEFS = -D_GNU_SOURCE -DHAVE_CONFIG_H $(YYDEBUG) \ - -DSRCDIR=\"$(shell cd $(srcdir);pwd)\" -DOBJDIR=\"$(shell pwd)\" - -DEPDIR = .deps -ECHO_C = -ECHO_N = -n -ECHO_T = -EGREP = grep -E -EXEEXT = -GMSGFMT = /usr/bin/msgfmt -INSTALL_DATA = ${INSTALL} -m 644 -INSTALL_PROGRAM = ${INSTALL} -INSTALL_SCRIPT = ${INSTALL} -INSTALL_STRIP_PROGRAM = ${SHELL} $(install_sh) -c -s -INTLLIBS = -LDFLAGS = -LEX = flex -LEXLIB = -lfl -LEX_OUTPUT_ROOT = lex.yy -LIBICONV = -liconv -LIBINTL = -LIBOBJS = -LIBS = -LOCALEDIR = ${prefix}/share -LTLIBICONV = -liconv -LTLIBINTL = -LTLIBOBJS = -MAKEINFO = ${SHELL} /work/elfutils/stock/elfutils-0.97/config/missing --run makeinfo -MKINSTALLDIRS = config/mkinstalldirs -MSGFMT = /usr/bin/msgfmt -MSGMERGE = /usr/bin/msgmerge -MUDFLAP_FALSE = -MUDFLAP_TRUE = # -NATIVE_LD_FALSE = -NATIVE_LD_TRUE = # -OBJEXT = o -PACKAGE = elfutils -PACKAGE_BUGREPORT = http://bugzilla.redhat.com/bugzilla/ -PACKAGE_NAME = Red Hat elfutils -PACKAGE_STRING = Red Hat elfutils 0.97 -PACKAGE_TARNAME = elfutils -PACKAGE_VERSION = 0.97 -PATH_SEPARATOR = : -POSUB = po -RANLIB = ranlib -SET_MAKE = -SHELL = /bin/sh -STRIP = -USE_NLS = yes -VERSION = 0.97 -XGETTEXT = /usr/bin/xgettext -YACC = bison -y -d -ac_ct_CC = gcc -ac_ct_RANLIB = ranlib -ac_ct_STRIP = -am__fastdepCC_FALSE = # -am__fastdepCC_TRUE = -am__include = include -am__leading_dot = . -am__quote = -am__tar = ${AMTAR} chof - "$$tardir" -am__untar = ${AMTAR} xf - -base_cpu = none -bindir = ${exec_prefix}/bin -build = i686-pc-linux-gnu -build_alias = -build_cpu = i686 -build_os = linux-gnu -build_vendor = pc -datadir = ${prefix}/share -exec_prefix = ${prefix} -host = i686-pc-linux-gnu -host_alias = -host_cpu = i686 -host_os = linux-gnu -host_vendor = pc -includedir = ${prefix}/include -infodir = ${prefix}/info -install_sh = /work/elfutils/stock/elfutils-0.97/config/install-sh -libdir = ${exec_prefix}/lib -libexecdir = ${exec_prefix}/libexec -localstatedir = ${prefix}/var -mandir = ${prefix}/man -mkdir_p = mkdir -p -- -oldincludedir = /usr/include -prefix = /work/elfutils/google/linux-install-elfutils-0.97/ -program_transform_name = s,x,x, -sbindir = ${exec_prefix}/sbin -sharedstatedir = ${prefix}/com -sysconfdir = ${prefix}/etc -target_alias = -AM_CFLAGS = -Wall -Wshadow -std=gnu99 \ - $(if $($(*F)_no_Werror),,-Werror) $(native_ld_cflags) - -#AM_CFLAGS = -Wall -Wshadow -std=gnu99 \ -# $(native_ld_cflags) - -#AM_LDFLAGS = -fmudflap -INCLUDES = -I$(srcdir) -I$(srcdir)/../libelf -I$(srcdir)/../libebl -I$(srcdir)/../lib -I$(srcdir)/../libdw -I.. -AM_YFLAGS = -pld -AM_LFLAGS = -Pld -olex.yy.c -native_ld = @native_ld@ -ld_dsos = libld_elf_i386_pic.a -noinst_LIBRARIES = libld_elf.a $(ld_dsos) -#noinst_LIBRARIES = libld_elf.a -#native_ld_cflags = -DBASE_ELF_NAME=elf_$(base_cpu) -ld_SOURCES = ld.c ldgeneric.c ldlex.l ldscript.y symbolhash.c sectionhash.c \ - versionhash.c - -noinst_HEADERS = ld.h symbolhash.h sectionhash.h versionhash.h \ - ldscript.h xelf.h unaligned.h - -EXTRA_DIST = elf32-i386.script libld_elf_i386.map $(ld_modules) -ld_modules = i386_ld.c -libdw = ../libdw/libdw.so -#libdw = ../libdw/libdw.a -libelf = ../libelf/libelf.so -#libelf = ../libelf/libelf.a -libebl = ../libebl/libebl.a -libeu = ../lib/libeu.a -readelf_LDADD = $(libdw) $(libebl) $(libelf) $(libeu) -ldl -nm_LDADD = $(libdw) $(libebl) $(libelf) $(libeu) -ldl -size_LDADD = $(libelf) $(libeu) -strip_LDADD = $(libebl) $(libelf) $(libeu) -ldl -ld_LDADD = $(libebl) $(libelf) $(libeu) -ldl $(am__append_1) -ld_LDFLAGS = -rdynamic -elflint_LDADD = $(libebl) $(libelf) $(libeu) -ldl -ldlex_no_Werror = yes - -# Machine-specific linker code. -libld_elf_a_SOURCES = $(base_cpu)_ld.c -libld_elf_i386_pic_a_SOURCES = -am_libld_elf_i386_pic_a_OBJECTS = i386_ld.os -libld_elf_i386_so_SOURCES = -CLEANFILES = none_ld.os $(ld_modules:.c=.os) -all: all-am - -.SUFFIXES: -.SUFFIXES: .c .l .o .obj .y -$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) - @for dep in $?; do \ - case '$(am__configure_deps)' in \ - *$$dep*) \ - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ - && exit 0; \ - exit 1;; \ - esac; \ - done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnits src/Makefile'; \ - cd $(top_srcdir) && \ - $(AUTOMAKE) --gnits src/Makefile -.PRECIOUS: Makefile -Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status - @case '$?' in \ - *config.status*) \ - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ - *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ - esac; - -$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh - -$(top_srcdir)/configure: $(am__configure_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(ACLOCAL_M4): $(am__aclocal_m4_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh - -clean-noinstLIBRARIES: - -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) -libld_elf.a: $(libld_elf_a_OBJECTS) $(libld_elf_a_DEPENDENCIES) - -rm -f libld_elf.a - $(libld_elf_a_AR) libld_elf.a $(libld_elf_a_OBJECTS) $(libld_elf_a_LIBADD) - $(RANLIB) libld_elf.a -libld_elf_i386_pic.a: $(libld_elf_i386_pic_a_OBJECTS) $(libld_elf_i386_pic_a_DEPENDENCIES) - -rm -f libld_elf_i386_pic.a - $(libld_elf_i386_pic_a_AR) libld_elf_i386_pic.a $(libld_elf_i386_pic_a_OBJECTS) $(libld_elf_i386_pic_a_LIBADD) - $(RANLIB) libld_elf_i386_pic.a -install-binPROGRAMS: $(bin_PROGRAMS) - @$(NORMAL_INSTALL) - test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)" - @list='$(bin_PROGRAMS)'; for p in $$list; do \ - p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ - if test -f $$p \ - ; then \ - f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \ - echo " $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \ - $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \ - else :; fi; \ - done - -uninstall-binPROGRAMS: - @$(NORMAL_UNINSTALL) - @list='$(bin_PROGRAMS)'; for p in $$list; do \ - f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ - echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \ - rm -f "$(DESTDIR)$(bindir)/$$f"; \ - done - -clean-binPROGRAMS: - -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) - -clean-noinstPROGRAMS: - -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS) -elflint$(EXEEXT): $(elflint_OBJECTS) $(elflint_DEPENDENCIES) - @rm -f elflint$(EXEEXT) - $(LINK) $(elflint_LDFLAGS) $(elflint_OBJECTS) $(elflint_LDADD) $(LIBS) -ld$(EXEEXT): $(ld_OBJECTS) $(ld_DEPENDENCIES) - @rm -f ld$(EXEEXT) - $(LINK) $(ld_LDFLAGS) $(ld_OBJECTS) $(ld_LDADD) $(LIBS) -nm$(EXEEXT): $(nm_OBJECTS) $(nm_DEPENDENCIES) - @rm -f nm$(EXEEXT) - $(LINK) $(nm_LDFLAGS) $(nm_OBJECTS) $(nm_LDADD) $(LIBS) -readelf$(EXEEXT): $(readelf_OBJECTS) $(readelf_DEPENDENCIES) - @rm -f readelf$(EXEEXT) - $(LINK) $(readelf_LDFLAGS) $(readelf_OBJECTS) $(readelf_LDADD) $(LIBS) -size$(EXEEXT): $(size_OBJECTS) $(size_DEPENDENCIES) - @rm -f size$(EXEEXT) - $(LINK) $(size_LDFLAGS) $(size_OBJECTS) $(size_LDADD) $(LIBS) -strip$(EXEEXT): $(strip_OBJECTS) $(strip_DEPENDENCIES) - @rm -f strip$(EXEEXT) - $(LINK) $(strip_LDFLAGS) $(strip_OBJECTS) $(strip_LDADD) $(LIBS) - -mostlyclean-compile: - -rm -f *.$(OBJEXT) - -distclean-compile: - -rm -f *.tab.c - -include ./$(DEPDIR)/$(base_cpu)_ld.Po -include ./$(DEPDIR)/elflint.Po -include ./$(DEPDIR)/ld.Po -include ./$(DEPDIR)/ldgeneric.Po -include ./$(DEPDIR)/ldlex.Po -include ./$(DEPDIR)/ldscript.Po -include ./$(DEPDIR)/nm.Po -include ./$(DEPDIR)/readelf.Po -include ./$(DEPDIR)/sectionhash.Po -include ./$(DEPDIR)/size.Po -include ./$(DEPDIR)/strip.Po -include ./$(DEPDIR)/symbolhash.Po -include ./$(DEPDIR)/versionhash.Po - -.c.o: - if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ - then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi -# source='$<' object='$@' libtool=no \ -# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ -# $(COMPILE) -c $< - -.c.obj: - if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ - then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi -# source='$<' object='$@' libtool=no \ -# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ -# $(COMPILE) -c `$(CYGPATH_W) '$<'` - -.l.c: - $(LEXCOMPILE) $< - sed '/^#/ s|$(LEX_OUTPUT_ROOT)\.c|$@|' $(LEX_OUTPUT_ROOT).c >$@ - rm -f $(LEX_OUTPUT_ROOT).c - -.y.c: - $(YACCCOMPILE) $< - if test -f y.tab.h; then \ - to=`echo "$*_H" | sed \ - -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \ - -e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g'`; \ - sed -e "/^#/!b" -e "s/Y_TAB_H/$$to/g" -e "s|y\.tab\.h|$*.h|" \ - y.tab.h >$*.ht; \ - rm -f y.tab.h; \ - if cmp -s $*.ht $*.h; then \ - rm -f $*.ht ;\ - else \ - mv $*.ht $*.h; \ - fi; \ - fi - if test -f y.output; then \ - mv y.output $*.output; \ - fi - sed '/^#/ s|y\.tab\.c|$@|' y.tab.c >$@t && mv $@t $@ - rm -f y.tab.c -uninstall-info-am: - -ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) - list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | \ - $(AWK) ' { files[$$0] = 1; } \ - END { for (i in files) print i; }'`; \ - mkid -fID $$unique -tags: TAGS - -TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ - $(TAGS_FILES) $(LISP) - tags=; \ - here=`pwd`; \ - list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | \ - $(AWK) ' { files[$$0] = 1; } \ - END { for (i in files) print i; }'`; \ - if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ - test -n "$$unique" || unique=$$empty_fix; \ - $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ - $$tags $$unique; \ - fi -ctags: CTAGS -CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ - $(TAGS_FILES) $(LISP) - tags=; \ - here=`pwd`; \ - list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | \ - $(AWK) ' { files[$$0] = 1; } \ - END { for (i in files) print i; }'`; \ - test -z "$(CTAGS_ARGS)$$tags$$unique" \ - || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ - $$tags $$unique - -GTAGS: - here=`$(am__cd) $(top_builddir) && pwd` \ - && cd $(top_srcdir) \ - && gtags -i $(GTAGS_ARGS) $$here - -distclean-tags: - -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags - -distdir: $(DISTFILES) - @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ - topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ - list='$(DISTFILES)'; for file in $$list; do \ - case $$file in \ - $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ - $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ - esac; \ - if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ - dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ - if test "$$dir" != "$$file" && test "$$dir" != "."; then \ - dir="/$$dir"; \ - $(mkdir_p) "$(distdir)$$dir"; \ - else \ - dir=''; \ - fi; \ - if test -d $$d/$$file; then \ - if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ - cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ - fi; \ - cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ - else \ - test -f $(distdir)/$$file \ - || cp -p $$d/$$file $(distdir)/$$file \ - || exit 1; \ - fi; \ - done -check-am: all-am -check: check-am -all-am: Makefile $(LIBRARIES) $(PROGRAMS) $(HEADERS) -installdirs: - for dir in "$(DESTDIR)$(bindir)"; do \ - test -z "$$dir" || $(mkdir_p) "$$dir"; \ - done -install: install-am -install-exec: install-exec-am -install-data: install-data-am -uninstall: uninstall-am - -install-am: all-am - @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am - -installcheck: installcheck-am -install-strip: - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - `test -z '$(STRIP)' || \ - echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install -mostlyclean-generic: - -clean-generic: - -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) - -distclean-generic: - -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) - -maintainer-clean-generic: - @echo "This command is intended for maintainers to use" - @echo "it deletes files that may require special tools to rebuild." - -rm -f ldlex.c - -rm -f ldscript.c -clean: clean-am - -clean-am: clean-binPROGRAMS clean-generic clean-noinstLIBRARIES \ - clean-noinstPROGRAMS mostlyclean-am - -distclean: distclean-am - -rm -rf ./$(DEPDIR) - -rm -f Makefile -distclean-am: clean-am distclean-compile distclean-generic \ - distclean-tags - -dvi: dvi-am - -dvi-am: - -html: html-am - -info: info-am - -info-am: - -install-data-am: - -install-exec-am: install-binPROGRAMS - -install-info: install-info-am - -install-man: - -installcheck-am: installcheck-binPROGRAMS - -maintainer-clean: maintainer-clean-am - -rm -rf ./$(DEPDIR) - -rm -f Makefile -maintainer-clean-am: distclean-am maintainer-clean-generic - -mostlyclean: mostlyclean-am - -mostlyclean-am: mostlyclean-compile mostlyclean-generic - -pdf: pdf-am - -pdf-am: - -ps: ps-am - -ps-am: - -uninstall-am: uninstall-binPROGRAMS uninstall-info-am - -.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ - clean-generic clean-noinstLIBRARIES clean-noinstPROGRAMS ctags \ - distclean distclean-compile distclean-generic distclean-tags \ - distdir dvi dvi-am html html-am info info-am install \ - install-am install-binPROGRAMS install-data install-data-am \ - install-exec install-exec-am install-info install-info-am \ - install-man install-strip installcheck installcheck-am \ - installcheck-binPROGRAMS installdirs maintainer-clean \ - maintainer-clean-generic mostlyclean mostlyclean-compile \ - mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \ - uninstall-am uninstall-binPROGRAMS uninstall-info-am - - -ldlex.o: ldscript.c -ldscript.h: ldscript.c -libld_elf_i386.so: libld_elf_i386_pic.a libld_elf_i386.map - $(CC) -shared -o $@ -Wl,--whole-archive,$<,--no-whole-archive \ - $(libelf) $(libeu) \ - -Wl,--version-script,$(srcdir)/libld_elf_i386.map - -%.os: %.c %.o - if $(filter-out -fmudflap,$(COMPILE)) -c -o $@ -fpic -DPIC -DSHARED \ - -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" \ - `test -f '$<' || echo '$(srcdir)/'`$<; \ - then cat "$(DEPDIR)/$*.Tpo" >> "$(DEPDIR)/$*.Po"; \ - rm -f "$(DEPDIR)/$*.Tpo"; \ - else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; \ - fi - -# Special rule to make it possible to define libld_elf_a_SOURCES as we do. -# Otherwise make would complain. -.deps/none_ld.Po: none_ld.os - -: - -installcheck-binPROGRAMS: $(bin_PROGRAMS) - bad=0; pid=$$$$; list="$(bin_PROGRAMS)"; for p in $$list; do \ - case ' $(AM_INSTALLCHECK_STD_OPTIONS_EXEMPT) ' in \ - *" $$p "* | *" $(srcdir)/$$p "*) continue;; \ - esac; \ - f=`echo "$$p" | \ - sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ - for opt in --help --version; do \ - if LD_LIBRARY_PATH=$(DESTDIR)$(libdir) \ - $(DESTDIR)$(bindir)/$$f $$opt > c$${pid}_.out 2> c$${pid}_.err \ - && test -n "`cat c$${pid}_.out`" \ - && test -z "`cat c$${pid}_.err`"; then :; \ - else echo "$$f does not support $$opt" 1>&2; bad=1; fi; \ - done; \ - done; rm -f c$${pid}_.???; exit $$bad -# Tell versions [3.59,3.63) of GNU make to not export all variables. -# Otherwise a system limit (for SysV at least) may be exceeded. -.NOEXPORT: diff --git a/src/Makefile.am b/src/Makefile.am index 7b957eb5..f72bb458 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,31 +1,46 @@ ## Process this file with automake to create Makefile.in -## Configure input file for elfutils. ## -## Copyright (C) 1996-2002, 2003, 2004 Red Hat, Inc. +## Copyright (C) 1996-2002, 2003, 2004, 2005, 2006, 2007 Red Hat, Inc. +## This file is part of Red Hat elfutils. ## -## This program is Open Source software; you can redistribute it and/or -## modify it under the terms of the Open Software License version 1.0 as -## published by the Open Source Initiative. +## Red Hat elfutils is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by the +## Free Software Foundation; version 2 of the License. ## -## You should have received a copy of the Open Software License along -## with this program; if not, you may obtain a copy of the Open Software -## License version 1.0 from http://www.opensource.org/licenses/osl.php or -## by writing the Open Source Initiative c/o Lawrence Rosen, Esq., -## 3001 King Ranch Road, Ukiah, CA 95482. +## Red Hat elfutils is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. ## -DEFS = -D_GNU_SOURCE -DHAVE_CONFIG_H $(YYDEBUG) \ +## You should have received a copy of the GNU General Public License along +## with Red Hat elfutils; if not, write to the Free Software Foundation, +## Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. +## +## Red Hat elfutils is an included package of the Open Invention Network. +## An included package of the Open Invention Network is a package for which +## Open Invention Network licensees cross-license their patents. No patent +## license is granted, either expressly or impliedly, by designation as an +## included package. Should you wish to participate in the Open Invention +## Network licensing program, please visit www.openinventionnetwork.com +## <http://www.openinventionnetwork.com>. +## +DEFS = -D_GNU_SOURCE -DHAVE_CONFIG_H $(YYDEBUG) -DDEBUGPRED=@DEBUGPRED@ \ -DSRCDIR=\"$(shell cd $(srcdir);pwd)\" -DOBJDIR=\"$(shell pwd)\" if MUDFLAP -AM_CFLAGS = -Wall -Wshadow -std=gnu99 \ - $(native_ld_cflags) +AM_CFLAGS = -fmudflap else -AM_CFLAGS = -Wall -Wshadow -std=gnu99 \ - $(if $($(*F)_no_Werror),,-Werror) $(native_ld_cflags) +AM_CFLAGS = endif -if MUDFLAP -AM_LDFLAGS = -fmudflap -endif -INCLUDES = -I$(srcdir) -I$(srcdir)/../libelf -I$(srcdir)/../libebl -I$(srcdir)/../lib -I$(srcdir)/../libdw -I.. +AM_CFLAGS += -Wall -Wshadow -std=gnu99 $(native_ld_cflags) \ + $(if $($(*F)_no_Werror),,-Werror) \ + $(if $($(*F)_no_Wunused),,-Wunused -Wextra) \ + $(if $($(*F)_no_Wformat),,-Wformat=2) $(CFLAGS_$(*F)) + +INCLUDES = -I$(srcdir) -I$(srcdir)/../libelf -I$(srcdir)/../libebl \ + -I$(srcdir)/../libdw -I$(srcdir)/../libdwfl \ + -I$(srcdir)/../libasm -I$(srcdir)/../lib -I.. + +AM_LDFLAGS = -Wl,-rpath-link,../libelf:../libdw YACC = @YACC@ -d AM_YFLAGS = -pld @@ -36,64 +51,105 @@ AM_LFLAGS = -Pld -olex.yy.c native_ld = @native_ld@ base_cpu = @base_cpu@ -bin_PROGRAMS = readelf nm size strip ld elflint +bin_PROGRAMS = readelf nm size strip ld elflint findtextrel addr2line \ + elfcmp objdump ranlib strings ar unstrip ld_dsos = libld_elf_i386_pic.a if NATIVE_LD -noinst_LIBRARIES = libld_elf.a +noinst_LIBRARIES = libld_elf.a libar.a native_ld_cflags = -DBASE_ELF_NAME=elf_$(base_cpu) else -noinst_LIBRARIES = libld_elf.a $(ld_dsos) +noinst_LIBRARIES = libld_elf.a libar.a $(ld_dsos) noinst_PROGRAMS = $(ld_dsos:_pic.a=.so) endif +if NEVER +# We never build this library but we need to get the dependency files +# of all the linker backends that might be used in a non-generic linker. +noinst_LIBRARIES += libdummy.a +libdummy_a_SOURCES = i386_ld.c +endif + +textrel_check = if readelf -d $@ | fgrep -q TEXTREL; then exit 1; fi ld_SOURCES = ld.c ldgeneric.c ldlex.l ldscript.y symbolhash.c sectionhash.c \ versionhash.c +libar_a_SOURCES = arlib.c arlib2.c + noinst_HEADERS = ld.h symbolhash.h sectionhash.h versionhash.h \ ldscript.h xelf.h unaligned.h -EXTRA_DIST = elf32-i386.script libld_elf_i386.map $(ld_modules) +EXTRA_DIST = elf32-i386.script libld_elf_i386.map $(ld_modules) arlib.h \ + debugpred.h ld_modules = i386_ld.c +bin_SCRIPTS = make-debug-archive +EXTRA_DIST += make-debug-archive.in +CLEANFILES = make-debug-archive + if MUDFLAP -libdw = ../libdw/libdw.a +libmudflap = -lmudflap +endif + +if BUILD_STATIC +libasm = ../libasm/libasm.a +libdw = ../libdw/libdw.a $(libelf) $(libebl) -ldl libelf = ../libelf/libelf.a else +libasm = ../libasm/libasm.so libdw = ../libdw/libdw.so libelf = ../libelf/libelf.so endif libebl = ../libebl/libebl.a libeu = ../lib/libeu.a -readelf_LDADD = $(libdw) $(libebl) $(libelf) $(libeu) -ldl -nm_LDADD = $(libdw) $(libebl) $(libelf) $(libeu) -ldl -size_LDADD = $(libelf) $(libeu) -strip_LDADD = $(libebl) $(libelf) $(libeu) -ldl -ld_LDADD = $(libebl) $(libelf) $(libeu) -ldl +nm_no_Wformat = yes +size_no_Wformat = yes +strings_no_Wformat = yes +# XXX While the file is not finished, don't warn about this +ldgeneric_no_Wunused = yes + +readelf_LDADD = $(libdw) $(libebl) $(libelf) $(libeu) $(libmudflap) -ldl +nm_LDADD = $(libdw) $(libebl) $(libelf) $(libeu) $(libmudflap) -ldl +size_LDADD = $(libelf) $(libeu) $(libmudflap) +strip_LDADD = $(libebl) $(libelf) $(libeu) $(libmudflap) -ldl +ld_LDADD = $(libebl) $(libelf) $(libeu) $(libmudflap) -ldl if NATIVE_LD +# -ldl is always needed for libebl. ld_LDADD += libld_elf.a endif ld_LDFLAGS = -rdynamic -elflint_LDADD = $(libebl) $(libelf) $(libeu) -ldl +elflint_LDADD = $(libebl) $(libelf) $(libeu) $(libmudflap) -ldl +findtextrel_LDADD = $(libdw) $(libelf) $(libmudflap) +addr2line_LDADD = $(libdw) $(libmudflap) +elfcmp_LDADD = $(libebl) $(libelf) $(libmudflap) -ldl +objdump_LDADD = $(libasm) $(libebl) $(libelf) $(libeu) $(libmudflap) -ldl +ranlib_LDADD = libar.a $(libelf) $(libeu) $(libmudflap) +strings_LDADD = $(libelf) $(libeu) $(libmudflap) +ar_LDADD = libar.a $(libelf) $(libeu) $(libmudflap) +CFLAGS_ar = -DAR=\"$(shell echo ar|sed '$(transform)')\" +unstrip_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(libmudflap) -ldl ldlex.o: ldscript.c ldlex_no_Werror = yes ldscript.h: ldscript.c +if NATIVE_LD # Machine-specific linker code. -libld_elf_a_SOURCES = $(base_cpu)_ld.c - +libld_elf_a_SOURCES := $(base_cpu)_ld.c +else libld_elf_i386_pic_a_SOURCES = am_libld_elf_i386_pic_a_OBJECTS = i386_ld.os libld_elf_i386_so_SOURCES = libld_elf_i386.so: libld_elf_i386_pic.a libld_elf_i386.map - $(CC) -shared -o $@ -Wl,--whole-archive,$<,--no-whole-archive \ - $(libelf) $(libeu) \ - -Wl,--version-script,$(srcdir)/libld_elf_i386.map + $(LINK) -shared -o $@ -Wl,--whole-archive,$<,--no-whole-archive \ + $(libelf) $(libeu) \ + -Wl,--version-script,$(srcdir)/libld_elf_i386.map + $(textrel_check) +endif %.os: %.c %.o @@ -127,4 +183,17 @@ installcheck-binPROGRAMS: $(bin_PROGRAMS) done; \ done; rm -f c$${pid}_.???; exit $$bad -CLEANFILES = none_ld.os $(ld_modules:.c=.os) +CLEANFILES += none_ld.os $(ld_modules:.c=.os) *.gcno *.gcda *.gconv + +MAINTAINERCLEANFILES = ldlex.c ldscript.c ldscript.h + + +make-debug-archive: $(srcdir)/make-debug-archive.in + UNSTRIP=$(bindir)/`echo unstrip | sed '$(transform)'`; \ + AR=$(bindir)/`echo ar | sed '$(transform)'`; \ + sed -e "s,@UNSTRIP@,$$UNSTRIP,g" -e "s,@AR@,$$AR,g" \ + -e "s%[@]PACKAGE_NAME[@]%$(PACKAGE_NAME)%g" \ + -e "s%[@]PACKAGE_VERSION[@]%$(PACKAGE_VERSION)%g" \ + $(srcdir)/make-debug-archive.in > $@.new + chmod +x $@.new + mv -f $@.new $@ diff --git a/src/Makefile.in b/src/Makefile.in index 0eb72237..7822b10d 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -1,8 +1,8 @@ -# Makefile.in generated by automake 1.9.2 from Makefile.am. +# Makefile.in generated by automake 1.10.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004 Free Software Foundation, Inc. +# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. @@ -16,17 +16,12 @@ -SOURCES = $(libld_elf_a_SOURCES) $(libld_elf_i386_pic_a_SOURCES) elflint.c $(ld_SOURCES) $(libld_elf_i386_so_SOURCES) nm.c readelf.c size.c strip.c -srcdir = @srcdir@ -top_srcdir = @top_srcdir@ VPATH = @srcdir@ pkgdatadir = $(datadir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ -top_builddir = .. am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd -INSTALL = @INSTALL@ install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c @@ -41,93 +36,143 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ bin_PROGRAMS = readelf$(EXEEXT) nm$(EXEEXT) size$(EXEEXT) \ - strip$(EXEEXT) ld$(EXEEXT) elflint$(EXEEXT) + strip$(EXEEXT) ld$(EXEEXT) elflint$(EXEEXT) \ + findtextrel$(EXEEXT) addr2line$(EXEEXT) elfcmp$(EXEEXT) \ + objdump$(EXEEXT) ranlib$(EXEEXT) strings$(EXEEXT) ar$(EXEEXT) \ + unstrip$(EXEEXT) @NATIVE_LD_FALSE@noinst_PROGRAMS = $(am__EXEEXT_1) -@NATIVE_LD_TRUE@am__append_1 = libld_elf.a +# We never build this library but we need to get the dependency files +# of all the linker backends that might be used in a non-generic linker. +@NEVER_TRUE@am__append_1 = libdummy.a +# -ldl is always needed for libebl. +@NATIVE_LD_TRUE@am__append_2 = libld_elf.a +@NATIVE_LD_TRUE@am_libld_elf_i386_pic_a_OBJECTS = subdir = src DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \ $(srcdir)/Makefile.in ChangeLog ldlex.c ldscript.c ylwrap ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/gettext.m4 \ - $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/lib-ld.m4 \ - $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ +am__aclocal_m4_deps = $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) -mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = LIBRARIES = $(noinst_LIBRARIES) AR = ar ARFLAGS = cru +libar_a_AR = $(AR) $(ARFLAGS) +libar_a_LIBADD = +am_libar_a_OBJECTS = arlib.$(OBJEXT) arlib2.$(OBJEXT) +libar_a_OBJECTS = $(am_libar_a_OBJECTS) +libdummy_a_AR = $(AR) $(ARFLAGS) +libdummy_a_LIBADD = +am__libdummy_a_SOURCES_DIST = i386_ld.c +@NEVER_TRUE@am_libdummy_a_OBJECTS = i386_ld.$(OBJEXT) +libdummy_a_OBJECTS = $(am_libdummy_a_OBJECTS) libld_elf_a_AR = $(AR) $(ARFLAGS) libld_elf_a_LIBADD = -am_libld_elf_a_OBJECTS = $(base_cpu)_ld.$(OBJEXT) +am__libld_elf_a_SOURCES_DIST = $(base_cpu)_ld.c +@NATIVE_LD_TRUE@am_libld_elf_a_OBJECTS = $(base_cpu)_ld.$(OBJEXT) libld_elf_a_OBJECTS = $(am_libld_elf_a_OBJECTS) libld_elf_i386_pic_a_AR = $(AR) $(ARFLAGS) libld_elf_i386_pic_a_LIBADD = libld_elf_i386_pic_a_OBJECTS = $(am_libld_elf_i386_pic_a_OBJECTS) -am__installdirs = "$(DESTDIR)$(bindir)" +am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(bindir)" binPROGRAMS_INSTALL = $(INSTALL_PROGRAM) am__EXEEXT_1 = libld_elf_i386.so$(EXEEXT) PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS) +addr2line_SOURCES = addr2line.c +addr2line_OBJECTS = addr2line.$(OBJEXT) +@BUILD_STATIC_FALSE@am__DEPENDENCIES_1 = ../libdw/libdw.so +@BUILD_STATIC_TRUE@am__DEPENDENCIES_1 = ../libdw/libdw.a $(libelf) \ +@BUILD_STATIC_TRUE@ $(libebl) +am__DEPENDENCIES_2 = +addr2line_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) +ar_SOURCES = ar.c +ar_OBJECTS = ar.$(OBJEXT) +ar_DEPENDENCIES = libar.a $(libelf) $(libeu) $(am__DEPENDENCIES_2) +elfcmp_SOURCES = elfcmp.c +elfcmp_OBJECTS = elfcmp.$(OBJEXT) +elfcmp_DEPENDENCIES = $(libebl) $(libelf) $(am__DEPENDENCIES_2) elflint_SOURCES = elflint.c elflint_OBJECTS = elflint.$(OBJEXT) -am__DEPENDENCIES_1 = ../libebl/libebl.a -@MUDFLAP_FALSE@am__DEPENDENCIES_2 = ../libelf/libelf.so -@MUDFLAP_TRUE@am__DEPENDENCIES_2 = ../libelf/libelf.a -am__DEPENDENCIES_3 = ../lib/libeu.a -elflint_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) \ - $(am__DEPENDENCIES_3) +elflint_DEPENDENCIES = $(libebl) $(libelf) $(libeu) \ + $(am__DEPENDENCIES_2) +findtextrel_SOURCES = findtextrel.c +findtextrel_OBJECTS = findtextrel.$(OBJEXT) +findtextrel_DEPENDENCIES = $(am__DEPENDENCIES_1) $(libelf) \ + $(am__DEPENDENCIES_2) am_ld_OBJECTS = ld.$(OBJEXT) ldgeneric.$(OBJEXT) ldlex.$(OBJEXT) \ ldscript.$(OBJEXT) symbolhash.$(OBJEXT) sectionhash.$(OBJEXT) \ versionhash.$(OBJEXT) ld_OBJECTS = $(am_ld_OBJECTS) -@NATIVE_LD_TRUE@am__DEPENDENCIES_4 = libld_elf.a -ld_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) \ - $(am__DEPENDENCIES_3) $(am__DEPENDENCIES_4) +ld_DEPENDENCIES = $(libebl) $(libelf) $(libeu) $(am__DEPENDENCIES_2) \ + $(am__append_2) +ld_LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(ld_LDFLAGS) $(LDFLAGS) -o \ + $@ am_libld_elf_i386_so_OBJECTS = libld_elf_i386_so_OBJECTS = $(am_libld_elf_i386_so_OBJECTS) libld_elf_i386_so_LDADD = $(LDADD) nm_SOURCES = nm.c nm_OBJECTS = nm.$(OBJEXT) -@MUDFLAP_FALSE@am__DEPENDENCIES_5 = ../libdw/libdw.so -@MUDFLAP_TRUE@am__DEPENDENCIES_5 = ../libdw/libdw.a -nm_DEPENDENCIES = $(am__DEPENDENCIES_5) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_3) +nm_DEPENDENCIES = $(am__DEPENDENCIES_1) $(libebl) $(libelf) $(libeu) \ + $(am__DEPENDENCIES_2) +objdump_SOURCES = objdump.c +objdump_OBJECTS = objdump.$(OBJEXT) +objdump_DEPENDENCIES = $(libasm) $(libebl) $(libelf) $(libeu) \ + $(am__DEPENDENCIES_2) +ranlib_SOURCES = ranlib.c +ranlib_OBJECTS = ranlib.$(OBJEXT) +ranlib_DEPENDENCIES = libar.a $(libelf) $(libeu) $(am__DEPENDENCIES_2) readelf_SOURCES = readelf.c readelf_OBJECTS = readelf.$(OBJEXT) -readelf_DEPENDENCIES = $(am__DEPENDENCIES_5) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_3) +readelf_DEPENDENCIES = $(am__DEPENDENCIES_1) $(libebl) $(libelf) \ + $(libeu) $(am__DEPENDENCIES_2) size_SOURCES = size.c size_OBJECTS = size.$(OBJEXT) -size_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_3) +size_DEPENDENCIES = $(libelf) $(libeu) $(am__DEPENDENCIES_2) +strings_SOURCES = strings.c +strings_OBJECTS = strings.$(OBJEXT) +strings_DEPENDENCIES = $(libelf) $(libeu) $(am__DEPENDENCIES_2) strip_SOURCES = strip.c strip_OBJECTS = strip.$(OBJEXT) -strip_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) \ - $(am__DEPENDENCIES_3) -DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +strip_DEPENDENCIES = $(libebl) $(libelf) $(libeu) \ + $(am__DEPENDENCIES_2) +unstrip_SOURCES = unstrip.c +unstrip_OBJECTS = unstrip.$(OBJEXT) +unstrip_DEPENDENCIES = $(libebl) $(libelf) $(am__DEPENDENCIES_1) \ + $(libeu) $(am__DEPENDENCIES_2) +binSCRIPT_INSTALL = $(INSTALL_SCRIPT) +SCRIPTS = $(bin_SCRIPTS) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/config/depcomp am__depfiles_maybe = depfiles COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) CCLD = $(CC) LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +@MAINTAINER_MODE_FALSE@am__skiplex = test -f $@ || LEXCOMPILE = $(LEX) $(LFLAGS) $(AM_LFLAGS) +YLWRAP = $(top_srcdir)/config/ylwrap +@MAINTAINER_MODE_FALSE@am__skipyacc = test -f $@ || YACCCOMPILE = $(YACC) $(YFLAGS) $(AM_YFLAGS) -SOURCES = $(libld_elf_a_SOURCES) $(libld_elf_i386_pic_a_SOURCES) \ - elflint.c $(ld_SOURCES) $(libld_elf_i386_so_SOURCES) nm.c \ - readelf.c size.c strip.c -DIST_SOURCES = $(libld_elf_a_SOURCES) $(libld_elf_i386_pic_a_SOURCES) \ - elflint.c $(ld_SOURCES) $(libld_elf_i386_so_SOURCES) nm.c \ - readelf.c size.c strip.c +SOURCES = $(libar_a_SOURCES) $(libdummy_a_SOURCES) \ + $(libld_elf_a_SOURCES) $(libld_elf_i386_pic_a_SOURCES) \ + addr2line.c ar.c elfcmp.c elflint.c findtextrel.c \ + $(ld_SOURCES) $(libld_elf_i386_so_SOURCES) nm.c objdump.c \ + ranlib.c readelf.c size.c strings.c strip.c unstrip.c +DIST_SOURCES = $(libar_a_SOURCES) $(am__libdummy_a_SOURCES_DIST) \ + $(am__libld_elf_a_SOURCES_DIST) \ + $(libld_elf_i386_pic_a_SOURCES) addr2line.c ar.c elfcmp.c \ + elflint.c findtextrel.c $(ld_SOURCES) \ + $(libld_elf_i386_so_SOURCES) nm.c objdump.c ranlib.c readelf.c \ + size.c strings.c strip.c unstrip.c HEADERS = $(noinst_HEADERS) ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ -AMDEP_FALSE = @AMDEP_FALSE@ -AMDEP_TRUE = @AMDEP_TRUE@ AMTAR = @AMTAR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -136,45 +181,41 @@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ -CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DATADIRNAME = @DATADIRNAME@ -DEFS = -D_GNU_SOURCE -DHAVE_CONFIG_H $(YYDEBUG) \ +DEBUGPRED = @DEBUGPRED@ +DEFS = -D_GNU_SOURCE -DHAVE_CONFIG_H $(YYDEBUG) -DDEBUGPRED=@DEBUGPRED@ \ -DSRCDIR=\"$(shell cd $(srcdir);pwd)\" -DOBJDIR=\"$(shell pwd)\" DEPDIR = @DEPDIR@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ -EGREP = @EGREP@ EXEEXT = @EXEEXT@ GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ -INTLLIBS = @INTLLIBS@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LEXLIB = @LEXLIB@ LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ -LIBICONV = @LIBICONV@ -LIBINTL = @LIBINTL@ +LIBEBL_SUBDIR = @LIBEBL_SUBDIR@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LOCALEDIR = @LOCALEDIR@ -LTLIBICONV = @LTLIBICONV@ -LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ -MKINSTALLDIRS = @MKINSTALLDIRS@ +MKDIR_P = @MKDIR_P@ +MODVERSION = @MODVERSION@ MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ MSGMERGE = @MSGMERGE@ -MUDFLAP_FALSE = @MUDFLAP_FALSE@ -MUDFLAP_TRUE = @MUDFLAP_TRUE@ -NATIVE_LD_FALSE = @NATIVE_LD_FALSE@ -NATIVE_LD_TRUE = @NATIVE_LD_TRUE@ OBJEXT = @OBJEXT@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ @@ -183,7 +224,6 @@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ -POSUB = @POSUB@ RANLIB = @RANLIB@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ @@ -191,12 +231,14 @@ STRIP = @STRIP@ USE_NLS = @USE_NLS@ VERSION = @VERSION@ XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ YACC = @YACC@ -d +YFLAGS = @YFLAGS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ -ac_ct_RANLIB = @ac_ct_RANLIB@ -ac_ct_STRIP = @ac_ct_STRIP@ -am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ -am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ @@ -209,77 +251,124 @@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ +builddir = @builddir@ datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +eu_version = @eu_version@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ +htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ +localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ +psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ @MUDFLAP_FALSE@AM_CFLAGS = -Wall -Wshadow -std=gnu99 \ -@MUDFLAP_FALSE@ $(if $($(*F)_no_Werror),,-Werror) $(native_ld_cflags) - -@MUDFLAP_TRUE@AM_CFLAGS = -Wall -Wshadow -std=gnu99 \ -@MUDFLAP_TRUE@ $(native_ld_cflags) - -@MUDFLAP_TRUE@AM_LDFLAGS = -fmudflap -INCLUDES = -I$(srcdir) -I$(srcdir)/../libelf -I$(srcdir)/../libebl -I$(srcdir)/../lib -I$(srcdir)/../libdw -I.. +@MUDFLAP_FALSE@ $(native_ld_cflags) $(if \ +@MUDFLAP_FALSE@ $($(*F)_no_Werror),,-Werror) $(if \ +@MUDFLAP_FALSE@ $($(*F)_no_Wunused),,-Wunused -Wextra) $(if \ +@MUDFLAP_FALSE@ $($(*F)_no_Wformat),,-Wformat=2) \ +@MUDFLAP_FALSE@ $(CFLAGS_$(*F)) +@MUDFLAP_TRUE@AM_CFLAGS = -fmudflap -Wall -Wshadow -std=gnu99 \ +@MUDFLAP_TRUE@ $(native_ld_cflags) $(if \ +@MUDFLAP_TRUE@ $($(*F)_no_Werror),,-Werror) $(if \ +@MUDFLAP_TRUE@ $($(*F)_no_Wunused),,-Wunused -Wextra) $(if \ +@MUDFLAP_TRUE@ $($(*F)_no_Wformat),,-Wformat=2) $(CFLAGS_$(*F)) +INCLUDES = -I$(srcdir) -I$(srcdir)/../libelf -I$(srcdir)/../libebl \ + -I$(srcdir)/../libdw -I$(srcdir)/../libdwfl \ + -I$(srcdir)/../libasm -I$(srcdir)/../lib -I.. + +AM_LDFLAGS = -Wl,-rpath-link,../libelf:../libdw AM_YFLAGS = -pld AM_LFLAGS = -Pld -olex.yy.c native_ld = @native_ld@ ld_dsos = libld_elf_i386_pic.a -@NATIVE_LD_FALSE@noinst_LIBRARIES = libld_elf.a $(ld_dsos) -@NATIVE_LD_TRUE@noinst_LIBRARIES = libld_elf.a +@NATIVE_LD_FALSE@noinst_LIBRARIES = libld_elf.a libar.a $(ld_dsos) \ +@NATIVE_LD_FALSE@ $(am__append_1) +@NATIVE_LD_TRUE@noinst_LIBRARIES = libld_elf.a libar.a $(am__append_1) @NATIVE_LD_TRUE@native_ld_cflags = -DBASE_ELF_NAME=elf_$(base_cpu) +@NEVER_TRUE@libdummy_a_SOURCES = i386_ld.c +textrel_check = if readelf -d $@ | fgrep -q TEXTREL; then exit 1; fi ld_SOURCES = ld.c ldgeneric.c ldlex.l ldscript.y symbolhash.c sectionhash.c \ versionhash.c +libar_a_SOURCES = arlib.c arlib2.c noinst_HEADERS = ld.h symbolhash.h sectionhash.h versionhash.h \ ldscript.h xelf.h unaligned.h -EXTRA_DIST = elf32-i386.script libld_elf_i386.map $(ld_modules) +EXTRA_DIST = elf32-i386.script libld_elf_i386.map $(ld_modules) \ + arlib.h debugpred.h make-debug-archive.in ld_modules = i386_ld.c -@MUDFLAP_FALSE@libdw = ../libdw/libdw.so -@MUDFLAP_TRUE@libdw = ../libdw/libdw.a -@MUDFLAP_FALSE@libelf = ../libelf/libelf.so -@MUDFLAP_TRUE@libelf = ../libelf/libelf.a +bin_SCRIPTS = make-debug-archive +CLEANFILES = make-debug-archive none_ld.os $(ld_modules:.c=.os) *.gcno \ + *.gcda *.gconv +@MUDFLAP_TRUE@libmudflap = -lmudflap +@BUILD_STATIC_FALSE@libasm = ../libasm/libasm.so +@BUILD_STATIC_TRUE@libasm = ../libasm/libasm.a +@BUILD_STATIC_FALSE@libdw = ../libdw/libdw.so +@BUILD_STATIC_TRUE@libdw = ../libdw/libdw.a $(libelf) $(libebl) -ldl +@BUILD_STATIC_FALSE@libelf = ../libelf/libelf.so +@BUILD_STATIC_TRUE@libelf = ../libelf/libelf.a libebl = ../libebl/libebl.a libeu = ../lib/libeu.a -readelf_LDADD = $(libdw) $(libebl) $(libelf) $(libeu) -ldl -nm_LDADD = $(libdw) $(libebl) $(libelf) $(libeu) -ldl -size_LDADD = $(libelf) $(libeu) -strip_LDADD = $(libebl) $(libelf) $(libeu) -ldl -ld_LDADD = $(libebl) $(libelf) $(libeu) -ldl $(am__append_1) +nm_no_Wformat = yes +size_no_Wformat = yes +strings_no_Wformat = yes +# XXX While the file is not finished, don't warn about this +ldgeneric_no_Wunused = yes +readelf_LDADD = $(libdw) $(libebl) $(libelf) $(libeu) $(libmudflap) -ldl +nm_LDADD = $(libdw) $(libebl) $(libelf) $(libeu) $(libmudflap) -ldl +size_LDADD = $(libelf) $(libeu) $(libmudflap) +strip_LDADD = $(libebl) $(libelf) $(libeu) $(libmudflap) -ldl +ld_LDADD = $(libebl) $(libelf) $(libeu) $(libmudflap) -ldl \ + $(am__append_2) ld_LDFLAGS = -rdynamic -elflint_LDADD = $(libebl) $(libelf) $(libeu) -ldl +elflint_LDADD = $(libebl) $(libelf) $(libeu) $(libmudflap) -ldl +findtextrel_LDADD = $(libdw) $(libelf) $(libmudflap) +addr2line_LDADD = $(libdw) $(libmudflap) +elfcmp_LDADD = $(libebl) $(libelf) $(libmudflap) -ldl +objdump_LDADD = $(libasm) $(libebl) $(libelf) $(libeu) $(libmudflap) -ldl +ranlib_LDADD = libar.a $(libelf) $(libeu) $(libmudflap) +strings_LDADD = $(libelf) $(libeu) $(libmudflap) +ar_LDADD = libar.a $(libelf) $(libeu) $(libmudflap) +CFLAGS_ar = -DAR=\"$(shell echo ar|sed '$(transform)')\" +unstrip_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(libmudflap) -ldl ldlex_no_Werror = yes # Machine-specific linker code. -libld_elf_a_SOURCES = $(base_cpu)_ld.c -libld_elf_i386_pic_a_SOURCES = -am_libld_elf_i386_pic_a_OBJECTS = i386_ld.os -libld_elf_i386_so_SOURCES = -CLEANFILES = none_ld.os $(ld_modules:.c=.os) +@NATIVE_LD_TRUE@libld_elf_a_SOURCES := $(base_cpu)_ld.c +@NATIVE_LD_FALSE@libld_elf_i386_pic_a_SOURCES = +@NATIVE_LD_FALSE@am_libld_elf_i386_pic_a_OBJECTS = i386_ld.os +@NATIVE_LD_FALSE@libld_elf_i386_so_SOURCES = +MAINTAINERCLEANFILES = ldlex.c ldscript.c ldscript.h all: all-am .SUFFIXES: .SUFFIXES: .c .l .o .obj .y -$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ @@ -304,13 +393,21 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(top_srcdir)/configure: $(am__configure_deps) +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(ACLOCAL_M4): $(am__aclocal_m4_deps) +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh clean-noinstLIBRARIES: -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libar.a: $(libar_a_OBJECTS) $(libar_a_DEPENDENCIES) + -rm -f libar.a + $(libar_a_AR) libar.a $(libar_a_OBJECTS) $(libar_a_LIBADD) + $(RANLIB) libar.a +libdummy.a: $(libdummy_a_OBJECTS) $(libdummy_a_DEPENDENCIES) + -rm -f libdummy.a + $(libdummy_a_AR) libdummy.a $(libdummy_a_OBJECTS) $(libdummy_a_LIBADD) + $(RANLIB) libdummy.a libld_elf.a: $(libld_elf_a_OBJECTS) $(libld_elf_a_DEPENDENCIES) -rm -f libld_elf.a $(libld_elf_a_AR) libld_elf.a $(libld_elf_a_OBJECTS) $(libld_elf_a_LIBADD) @@ -321,7 +418,7 @@ libld_elf_i386_pic.a: $(libld_elf_i386_pic_a_OBJECTS) $(libld_elf_i386_pic_a_DEP $(RANLIB) libld_elf_i386_pic.a install-binPROGRAMS: $(bin_PROGRAMS) @$(NORMAL_INSTALL) - test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)" + test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)" @list='$(bin_PROGRAMS)'; for p in $$list; do \ p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ if test -f $$p \ @@ -345,24 +442,85 @@ clean-binPROGRAMS: clean-noinstPROGRAMS: -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS) +addr2line$(EXEEXT): $(addr2line_OBJECTS) $(addr2line_DEPENDENCIES) + @rm -f addr2line$(EXEEXT) + $(LINK) $(addr2line_OBJECTS) $(addr2line_LDADD) $(LIBS) +ar$(EXEEXT): $(ar_OBJECTS) $(ar_DEPENDENCIES) + @rm -f ar$(EXEEXT) + $(LINK) $(ar_OBJECTS) $(ar_LDADD) $(LIBS) +elfcmp$(EXEEXT): $(elfcmp_OBJECTS) $(elfcmp_DEPENDENCIES) + @rm -f elfcmp$(EXEEXT) + $(LINK) $(elfcmp_OBJECTS) $(elfcmp_LDADD) $(LIBS) elflint$(EXEEXT): $(elflint_OBJECTS) $(elflint_DEPENDENCIES) @rm -f elflint$(EXEEXT) - $(LINK) $(elflint_LDFLAGS) $(elflint_OBJECTS) $(elflint_LDADD) $(LIBS) + $(LINK) $(elflint_OBJECTS) $(elflint_LDADD) $(LIBS) +findtextrel$(EXEEXT): $(findtextrel_OBJECTS) $(findtextrel_DEPENDENCIES) + @rm -f findtextrel$(EXEEXT) + $(LINK) $(findtextrel_OBJECTS) $(findtextrel_LDADD) $(LIBS) ld$(EXEEXT): $(ld_OBJECTS) $(ld_DEPENDENCIES) @rm -f ld$(EXEEXT) - $(LINK) $(ld_LDFLAGS) $(ld_OBJECTS) $(ld_LDADD) $(LIBS) + $(ld_LINK) $(ld_OBJECTS) $(ld_LDADD) $(LIBS) +@NATIVE_LD_TRUE@libld_elf_i386.so$(EXEEXT): $(libld_elf_i386_so_OBJECTS) $(libld_elf_i386_so_DEPENDENCIES) +@NATIVE_LD_TRUE@ @rm -f libld_elf_i386.so$(EXEEXT) +@NATIVE_LD_TRUE@ $(LINK) $(libld_elf_i386_so_OBJECTS) $(libld_elf_i386_so_LDADD) $(LIBS) nm$(EXEEXT): $(nm_OBJECTS) $(nm_DEPENDENCIES) @rm -f nm$(EXEEXT) - $(LINK) $(nm_LDFLAGS) $(nm_OBJECTS) $(nm_LDADD) $(LIBS) + $(LINK) $(nm_OBJECTS) $(nm_LDADD) $(LIBS) +objdump$(EXEEXT): $(objdump_OBJECTS) $(objdump_DEPENDENCIES) + @rm -f objdump$(EXEEXT) + $(LINK) $(objdump_OBJECTS) $(objdump_LDADD) $(LIBS) +ranlib$(EXEEXT): $(ranlib_OBJECTS) $(ranlib_DEPENDENCIES) + @rm -f ranlib$(EXEEXT) + $(LINK) $(ranlib_OBJECTS) $(ranlib_LDADD) $(LIBS) readelf$(EXEEXT): $(readelf_OBJECTS) $(readelf_DEPENDENCIES) @rm -f readelf$(EXEEXT) - $(LINK) $(readelf_LDFLAGS) $(readelf_OBJECTS) $(readelf_LDADD) $(LIBS) + $(LINK) $(readelf_OBJECTS) $(readelf_LDADD) $(LIBS) size$(EXEEXT): $(size_OBJECTS) $(size_DEPENDENCIES) @rm -f size$(EXEEXT) - $(LINK) $(size_LDFLAGS) $(size_OBJECTS) $(size_LDADD) $(LIBS) + $(LINK) $(size_OBJECTS) $(size_LDADD) $(LIBS) +strings$(EXEEXT): $(strings_OBJECTS) $(strings_DEPENDENCIES) + @rm -f strings$(EXEEXT) + $(LINK) $(strings_OBJECTS) $(strings_LDADD) $(LIBS) strip$(EXEEXT): $(strip_OBJECTS) $(strip_DEPENDENCIES) @rm -f strip$(EXEEXT) - $(LINK) $(strip_LDFLAGS) $(strip_OBJECTS) $(strip_LDADD) $(LIBS) + $(LINK) $(strip_OBJECTS) $(strip_LDADD) $(LIBS) +unstrip$(EXEEXT): $(unstrip_OBJECTS) $(unstrip_DEPENDENCIES) + @rm -f unstrip$(EXEEXT) + $(LINK) $(unstrip_OBJECTS) $(unstrip_LDADD) $(LIBS) +install-binSCRIPTS: $(bin_SCRIPTS) + @$(NORMAL_INSTALL) + test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)" + @list='$(bin_SCRIPTS)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f $$d$$p; then \ + f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \ + echo " $(binSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(bindir)/$$f'"; \ + $(binSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(bindir)/$$f"; \ + else :; fi; \ + done + +uninstall-binSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(bin_SCRIPTS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \ + echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \ + rm -f "$(DESTDIR)$(bindir)/$$f"; \ + done + +installcheck-binSCRIPTS: $(bin_SCRIPTS) + bad=0; pid=$$$$; list="$(bin_SCRIPTS)"; for p in $$list; do \ + case ' $(AM_INSTALLCHECK_STD_OPTIONS_EXEMPT) ' in \ + *" $$p "* | *" $(srcdir)/$$p "*) continue;; \ + esac; \ + f=`echo "$$p" | sed 's,^.*/,,;$(transform)'`; \ + for opt in --help --version; do \ + if "$(DESTDIR)$(bindir)/$$f" $$opt >c$${pid}_.out \ + 2>c$${pid}_.err </dev/null \ + && test -n "`cat c$${pid}_.out`" \ + && test -z "`cat c$${pid}_.err`"; then :; \ + else echo "$$f does not support $$opt" 1>&2; bad=1; fi; \ + done; \ + done; rm -f c$${pid}_.???; exit $$bad mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -371,67 +529,57 @@ distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/$(base_cpu)_ld.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/addr2line.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ar.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/arlib.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/arlib2.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elfcmp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elflint.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/findtextrel.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/i386_ld.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ld.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldgeneric.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldlex.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldscript.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/objdump.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ranlib.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/readelf.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sectionhash.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/size.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strings.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strip.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/symbolhash.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unstrip.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/versionhash.Po@am__quote@ .c.o: -@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(COMPILE) -c $< .c.obj: -@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ -@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` .l.c: - $(LEXCOMPILE) $< - sed '/^#/ s|$(LEX_OUTPUT_ROOT)\.c|$@|' $(LEX_OUTPUT_ROOT).c >$@ - rm -f $(LEX_OUTPUT_ROOT).c + $(am__skiplex) $(SHELL) $(YLWRAP) $< $(LEX_OUTPUT_ROOT).c $@ -- $(LEXCOMPILE) .y.c: - $(YACCCOMPILE) $< - if test -f y.tab.h; then \ - to=`echo "$*_H" | sed \ - -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \ - -e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g'`; \ - sed -e "/^#/!b" -e "s/Y_TAB_H/$$to/g" -e "s|y\.tab\.h|$*.h|" \ - y.tab.h >$*.ht; \ - rm -f y.tab.h; \ - if cmp -s $*.ht $*.h; then \ - rm -f $*.ht ;\ - else \ - mv $*.ht $*.h; \ - fi; \ - fi - if test -f y.output; then \ - mv y.output $*.output; \ - fi - sed '/^#/ s|y\.tab\.c|$@|' y.tab.c >$@t && mv $@t $@ - rm -f y.tab.c -uninstall-info-am: + $(am__skipyacc) $(SHELL) $(YLWRAP) $< y.tab.c $@ y.tab.h $*.h y.output $*.output -- $(YACCCOMPILE) ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ - $(AWK) ' { files[$$0] = 1; } \ - END { for (i in files) print i; }'`; \ + $(AWK) '{ files[$$0] = 1; nonemtpy = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ mkid -fID $$unique tags: TAGS @@ -443,8 +591,8 @@ TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ - $(AWK) ' { files[$$0] = 1; } \ - END { for (i in files) print i; }'`; \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ @@ -454,13 +602,12 @@ ctags: CTAGS CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) tags=; \ - here=`pwd`; \ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ - $(AWK) ' { files[$$0] = 1; } \ - END { for (i in files) print i; }'`; \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ test -z "$(CTAGS_ARGS)$$tags$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$tags $$unique @@ -474,22 +621,21 @@ distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) - @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ - topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ - list='$(DISTFILES)'; for file in $$list; do \ - case $$file in \ - $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ - $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ - esac; \ + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ - dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ - if test "$$dir" != "$$file" && test "$$dir" != "."; then \ - dir="/$$dir"; \ - $(mkdir_p) "$(distdir)$$dir"; \ - else \ - dir=''; \ - fi; \ if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ fi; \ @@ -502,10 +648,10 @@ distdir: $(DISTFILES) done check-am: all-am check: check-am -all-am: Makefile $(LIBRARIES) $(PROGRAMS) $(HEADERS) +all-am: Makefile $(LIBRARIES) $(PROGRAMS) $(SCRIPTS) $(HEADERS) installdirs: - for dir in "$(DESTDIR)$(bindir)"; do \ - test -z "$$dir" || $(mkdir_p) "$$dir"; \ + for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am @@ -534,6 +680,7 @@ maintainer-clean-generic: @echo "it deletes files that may require special tools to rebuild." -rm -f ldlex.c -rm -f ldscript.c + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-binPROGRAMS clean-generic clean-noinstLIBRARIES \ @@ -557,13 +704,21 @@ info-am: install-data-am: -install-exec-am: install-binPROGRAMS +install-dvi: install-dvi-am + +install-exec-am: install-binPROGRAMS install-binSCRIPTS + +install-html: install-html-am install-info: install-info-am install-man: -installcheck-am: installcheck-binPROGRAMS +install-pdf: install-pdf-am + +install-ps: install-ps-am + +installcheck-am: installcheck-binPROGRAMS installcheck-binSCRIPTS maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) @@ -582,27 +737,33 @@ ps: ps-am ps-am: -uninstall-am: uninstall-binPROGRAMS uninstall-info-am +uninstall-am: uninstall-binPROGRAMS uninstall-binSCRIPTS + +.MAKE: install-am install-strip .PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ clean-generic clean-noinstLIBRARIES clean-noinstPROGRAMS ctags \ distclean distclean-compile distclean-generic distclean-tags \ distdir dvi dvi-am html html-am info info-am install \ - install-am install-binPROGRAMS install-data install-data-am \ - install-exec install-exec-am install-info install-info-am \ - install-man install-strip installcheck installcheck-am \ - installcheck-binPROGRAMS installdirs maintainer-clean \ + install-am install-binPROGRAMS install-binSCRIPTS install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installcheck-binPROGRAMS \ + installcheck-binSCRIPTS installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \ - uninstall-am uninstall-binPROGRAMS uninstall-info-am + uninstall-am uninstall-binPROGRAMS uninstall-binSCRIPTS ldlex.o: ldscript.c ldscript.h: ldscript.c -libld_elf_i386.so: libld_elf_i386_pic.a libld_elf_i386.map - $(CC) -shared -o $@ -Wl,--whole-archive,$<,--no-whole-archive \ - $(libelf) $(libeu) \ - -Wl,--version-script,$(srcdir)/libld_elf_i386.map +@NATIVE_LD_FALSE@libld_elf_i386.so: libld_elf_i386_pic.a libld_elf_i386.map +@NATIVE_LD_FALSE@ $(LINK) -shared -o $@ -Wl,--whole-archive,$<,--no-whole-archive \ +@NATIVE_LD_FALSE@ $(libelf) $(libeu) \ +@NATIVE_LD_FALSE@ -Wl,--version-script,$(srcdir)/libld_elf_i386.map +@NATIVE_LD_FALSE@ $(textrel_check) %.os: %.c %.o if $(filter-out -fmudflap,$(COMPILE)) -c -o $@ -fpic -DPIC -DSHARED \ @@ -633,6 +794,16 @@ installcheck-binPROGRAMS: $(bin_PROGRAMS) else echo "$$f does not support $$opt" 1>&2; bad=1; fi; \ done; \ done; rm -f c$${pid}_.???; exit $$bad + +make-debug-archive: $(srcdir)/make-debug-archive.in + UNSTRIP=$(bindir)/`echo unstrip | sed '$(transform)'`; \ + AR=$(bindir)/`echo ar | sed '$(transform)'`; \ + sed -e "s,@UNSTRIP@,$$UNSTRIP,g" -e "s,@AR@,$$AR,g" \ + -e "s%[@]PACKAGE_NAME[@]%$(PACKAGE_NAME)%g" \ + -e "s%[@]PACKAGE_VERSION[@]%$(PACKAGE_VERSION)%g" \ + $(srcdir)/make-debug-archive.in > $@.new + chmod +x $@.new + mv -f $@.new $@ # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: diff --git a/src/addr2line.c b/src/addr2line.c new file mode 100644 index 00000000..0d11e188 --- /dev/null +++ b/src/addr2line.c @@ -0,0 +1,490 @@ +/* Locate source files and line information for given addresses + Copyright (C) 2005, 2006, 2007, 2008 Red Hat, Inc. + This file is part of Red Hat elfutils. + Written by Ulrich Drepper <drepper@redhat.com>, 2005. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <argp.h> +#include <assert.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <inttypes.h> +#include <libdwfl.h> +#include <dwarf.h> +#include <libintl.h> +#include <locale.h> +#include <mcheck.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdio_ext.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + + +/* Name and version of program. */ +static void print_version (FILE *stream, struct argp_state *state); +void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; + +/* Bug report address. */ +const char *argp_program_bug_address = PACKAGE_BUGREPORT; + + +/* Values for the parameters which have no short form. */ +#define OPT_DEMANGLER 0x100 + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + { NULL, 0, NULL, 0, N_("Output selection options:"), 2 }, + { "basenames", 's', NULL, 0, N_("Show only base names of source files"), 0 }, + { "absolute", 'A', NULL, 0, + N_("Show absolute file names using compilation directory"), 0 }, + { "functions", 'f', NULL, 0, N_("Also show function names"), 0 }, + { "symbols", 'S', NULL, 0, N_("Also show symbol or section names"), 0 }, + + { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 }, + /* Unsupported options. */ + { "target", 'b', "ARG", OPTION_HIDDEN, NULL, 0 }, + { "demangle", 'C', "ARG", OPTION_HIDDEN | OPTION_ARG_OPTIONAL, NULL, 0 }, + { "demangler", OPT_DEMANGLER, "ARG", OPTION_HIDDEN, NULL, 0 }, + { NULL, 0, NULL, 0, NULL, 0 } +}; + +/* Short description of program. */ +static const char doc[] = N_("\ +Locate source files and line information for ADDRs (in a.out by default)."); + +/* Strings for arguments in help texts. */ +static const char args_doc[] = N_("[ADDR...]"); + +/* Prototype for option handler. */ +static error_t parse_opt (int key, char *arg, struct argp_state *state); + +static struct argp_child argp_children[2]; /* [0] is set in main. */ + +/* Data structure to communicate with argp functions. */ +static const struct argp argp = +{ + options, parse_opt, args_doc, doc, argp_children, NULL, NULL +}; + + +/* Handle ADDR. */ +static int handle_address (const char *addr, Dwfl *dwfl); + + +/* True if only base names of files should be shown. */ +static bool only_basenames; + +/* True if absolute file names based on DW_AT_comp_dir should be shown. */ +static bool use_comp_dir; + +/* True if function names should be shown. */ +static bool show_functions; + +/* True if ELF symbol or section info should be shown. */ +static bool show_symbols; + + +int +main (int argc, char *argv[]) +{ + int remaining; + int result = 0; + + /* Make memory leak detection possible. */ + mtrace (); + + /* We use no threads here which can interfere with handling a stream. */ + (void) __fsetlocking (stdout, FSETLOCKING_BYCALLER); + + /* Set locale. */ + (void) setlocale (LC_ALL, ""); + + /* Make sure the message catalog can be found. */ + (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR); + + /* Initialize the message catalog. */ + (void) textdomain (PACKAGE_TARNAME); + + /* Parse and process arguments. This includes opening the modules. */ + argp_children[0].argp = dwfl_standard_argp (); + argp_children[0].group = 1; + Dwfl *dwfl = NULL; + (void) argp_parse (&argp, argc, argv, 0, &remaining, &dwfl); + assert (dwfl != NULL); + + /* Now handle the addresses. In case none are given on the command + line, read from stdin. */ + if (remaining == argc) + { + /* We use no threads here which can interfere with handling a stream. */ + (void) __fsetlocking (stdin, FSETLOCKING_BYCALLER); + + char *buf = NULL; + size_t len = 0; + while (!feof_unlocked (stdin)) + { + if (getline (&buf, &len, stdin) < 0) + break; + + result = handle_address (buf, dwfl); + } + + free (buf); + } + else + { + do + result = handle_address (argv[remaining], dwfl); + while (++remaining < argc); + } + + return result; +} + + +/* Print the version information. */ +static void +print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) +{ + fprintf (stream, "addr2line (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION); + fprintf (stream, gettext ("\ +Copyright (C) %s Red Hat, Inc.\n\ +This is free software; see the source for copying conditions. There is NO\n\ +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ +"), "2008"); + fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); +} + + +/* Handle program arguments. */ +static error_t +parse_opt (int key, char *arg __attribute__ ((unused)), + struct argp_state *state) +{ + switch (key) + { + case ARGP_KEY_INIT: + state->child_inputs[0] = state->input; + break; + + case 'b': + case 'C': + case OPT_DEMANGLER: + /* Ignored for compatibility. */ + break; + + case 's': + only_basenames = true; + break; + + case 'A': + use_comp_dir = true; + break; + + case 'f': + show_functions = true; + break; + + case 'S': + show_symbols = true; + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + + +static bool +print_dwarf_function (Dwfl_Module *mod, Dwarf_Addr addr) +{ + Dwarf_Addr bias = 0; + Dwarf_Die *cudie = dwfl_module_addrdie (mod, addr, &bias); + + Dwarf_Die *scopes; + int nscopes = dwarf_getscopes (cudie, addr - bias, &scopes); + if (nscopes <= 0) + return false; + + for (int i = 0; i < nscopes; ++i) + switch (dwarf_tag (&scopes[i])) + { + case DW_TAG_subprogram: + { + const char *name = dwarf_diename (&scopes[i]); + if (name == NULL) + return false; + puts (name); + return true; + } + + case DW_TAG_inlined_subroutine: + { + const char *name = dwarf_diename (&scopes[i]); + if (name == NULL) + return false; + printf ("%s inlined", name); + + Dwarf_Files *files; + if (dwarf_getsrcfiles (cudie, &files, NULL) == 0) + { + Dwarf_Attribute attr_mem; + Dwarf_Word val; + if (dwarf_formudata (dwarf_attr (&scopes[i], + DW_AT_call_file, + &attr_mem), &val) == 0) + { + const char *file = dwarf_filesrc (files, val, NULL, NULL); + unsigned int lineno = 0; + unsigned int colno = 0; + if (dwarf_formudata (dwarf_attr (&scopes[i], + DW_AT_call_line, + &attr_mem), &val) == 0) + lineno = val; + if (dwarf_formudata (dwarf_attr (&scopes[i], + DW_AT_call_column, + &attr_mem), &val) == 0) + colno = val; + if (lineno == 0) + { + if (file != NULL) + printf (" from %s", file); + } + else if (colno == 0) + printf (" at %s:%u", file, lineno); + else + printf (" at %s:%u:%u", file, lineno, colno); + } + } + printf (" in "); + continue; + } + } + + return false; +} + +static void +print_addrsym (Dwfl_Module *mod, GElf_Addr addr) +{ + GElf_Sym s; + GElf_Word shndx; + const char *name = dwfl_module_addrsym (mod, addr, &s, &shndx); + if (name == NULL) + { + /* No symbol name. Get a section name instead. */ + int i = dwfl_module_relocate_address (mod, &addr); + if (i >= 0) + name = dwfl_module_relocation_info (mod, i, NULL); + if (name == NULL) + puts ("??"); + else + printf ("(%s)+%#" PRIx64 "\n", name, addr); + } + else if (addr == s.st_value) + puts (name); + else + printf ("%s+%#" PRIx64 "\n", name, addr - s.st_value); +} + +static int +see_one_module (Dwfl_Module *mod, + void **userdata __attribute__ ((unused)), + const char *name __attribute__ ((unused)), + Dwarf_Addr start __attribute__ ((unused)), + void *arg) +{ + Dwfl_Module **result = arg; + if (*result != NULL) + return DWARF_CB_ABORT; + *result = mod; + return DWARF_CB_OK; +} + +static int +find_symbol (Dwfl_Module *mod, + void **userdata __attribute__ ((unused)), + const char *name __attribute__ ((unused)), + Dwarf_Addr start __attribute__ ((unused)), + void *arg) +{ + const char *looking_for = ((void **) arg)[0]; + GElf_Sym *symbol = ((void **) arg)[1]; + + int n = dwfl_module_getsymtab (mod); + for (int i = 1; i < n; ++i) + { + const char *symbol_name = dwfl_module_getsym (mod, i, symbol, NULL); + if (symbol_name == NULL) + continue; + switch (GELF_ST_TYPE (symbol->st_info)) + { + case STT_SECTION: + case STT_FILE: + case STT_TLS: + break; + default: + if (!strcmp (symbol_name, looking_for)) + { + ((void **) arg)[0] = NULL; + return DWARF_CB_ABORT; + } + } + } + + return DWARF_CB_OK; +} + +static int +handle_address (const char *string, Dwfl *dwfl) +{ + char *endp; + uintmax_t addr = strtoumax (string, &endp, 0); + if (endp == string) + { + bool parsed = false; + int n; + char *name = NULL; + if (sscanf (string, "(%m[^)])%" PRIiMAX "%n", &name, &addr, &n) == 2 + && string[n] == '\0') + { + /* It was (section)+offset. This makes sense if there is + only one module to look in for a section. */ + Dwfl_Module *mod = NULL; + if (dwfl_getmodules (dwfl, &see_one_module, &mod, 0) != 0 + || mod == NULL) + error (EXIT_FAILURE, 0, gettext ("Section syntax requires" + " exactly one module")); + + int nscn = dwfl_module_relocations (mod); + for (int i = 0; i < nscn; ++i) + { + GElf_Word shndx; + const char *scn = dwfl_module_relocation_info (mod, i, &shndx); + if (unlikely (scn == NULL)) + break; + if (!strcmp (scn, name)) + { + /* Found the section. */ + GElf_Shdr shdr_mem; + GElf_Addr shdr_bias; + GElf_Shdr *shdr = gelf_getshdr + (elf_getscn (dwfl_module_getelf (mod, &shdr_bias), shndx), + &shdr_mem); + if (unlikely (shdr == NULL)) + break; + + if (addr >= shdr->sh_size) + error (0, 0, + gettext ("offset %#" PRIxMAX " lies outside" + " section '%s'"), + addr, scn); + + addr += shdr->sh_addr + shdr_bias; + parsed = true; + break; + } + } + } + else if (sscanf (string, "%m[^-+]%" PRIiMAX "%n", &name, &addr, &n) == 2 + && string[n] == '\0') + { + /* It was symbol+offset. */ + GElf_Sym sym; + void *arg[2] = { name, &sym }; + (void) dwfl_getmodules (dwfl, &find_symbol, arg, 0); + if (arg[0] != NULL) + error (0, 0, gettext ("cannot find symbol '%s'"), name); + else + { + if (sym.st_size != 0 && addr >= sym.st_size) + error (0, 0, + gettext ("offset %#" PRIxMAX " lies outside" + " contents of '%s'"), + addr, name); + addr += sym.st_value; + parsed = true; + } + } + + free (name); + if (!parsed) + return 1; + } + + Dwfl_Module *mod = dwfl_addrmodule (dwfl, addr); + + if (show_functions) + { + /* First determine the function name. Use the DWARF information if + possible. */ + if (! print_dwarf_function (mod, addr) && !show_symbols) + puts (dwfl_module_addrname (mod, addr) ?: "??"); + } + + if (show_symbols) + print_addrsym (mod, addr); + + Dwfl_Line *line = dwfl_module_getsrc (mod, addr); + + const char *src; + int lineno, linecol; + if (line != NULL && (src = dwfl_lineinfo (line, &addr, &lineno, &linecol, + NULL, NULL)) != NULL) + { + const char *comp_dir = ""; + const char *comp_dir_sep = ""; + + if (only_basenames) + src = basename (src); + else if (use_comp_dir && src[0] != '/') + { + comp_dir = dwfl_line_comp_dir (line); + if (comp_dir != NULL) + comp_dir_sep = "/"; + } + + if (linecol != 0) + printf ("%s%s%s:%d:%d\n", + comp_dir, comp_dir_sep, src, lineno, linecol); + else + printf ("%s%s%s:%d\n", + comp_dir, comp_dir_sep, src, lineno); + } + else + puts ("??:0"); + + return 0; +} + + +#include "debugpred.h" diff --git a/src/ar.c b/src/ar.c new file mode 100644 index 00000000..aade3511 --- /dev/null +++ b/src/ar.c @@ -0,0 +1,1518 @@ +/* Create, modify, and extract from archives. + Copyright (C) 2005, 2007 Red Hat, Inc. + Written by Ulrich Drepper <drepper@redhat.com>, 2005. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <argp.h> +#include <assert.h> +#include <error.h> +#include <fcntl.h> +#include <gelf.h> +#include <libintl.h> +#include <limits.h> +#include <locale.h> +#include <mcheck.h> +#include <search.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdio_ext.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <sys/mman.h> +#include <sys/statfs.h> +#include <sys/time.h> + +#include <system.h> + +#include "arlib.h" + + +/* Name and version of program. */ +static void print_version (FILE *stream, struct argp_state *state); +void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; +/* Prototypes for local functions. */ +static int do_oper_extract (int oper, const char *arfname, char **argv, + int argc, long int instance); +static int do_oper_delete (const char *arfname, char **argv, int argc, + long int instance); +static int do_oper_insert (int oper, const char *arfname, char **argv, + int argc, const char *member); + + +/* Bug report address. */ +const char *argp_program_bug_address = PACKAGE_BUGREPORT; + + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + { NULL, 0, NULL, 0, N_("Commands:"), 0 }, + { NULL, 'd', NULL, 0, N_("Delete files from archive."), 0 }, + { NULL, 'm', NULL, 0, N_("Move files in archive."), 0 }, + { NULL, 'p', NULL, 0, N_("Print files in archive."), 0 }, + { NULL, 'q', NULL, 0, N_("Quick append files to archive."), 0 }, + { NULL, 'r', NULL, 0, + N_("Replace existing or insert new file into archive."), 0 }, + { NULL, 't', NULL, 0, N_("Display content of archive."), 0 }, + { NULL, 'x', NULL, 0, N_("Extract files from archive."), 0 }, + + { NULL, 0, NULL, 0, N_("Command Modifiers:"), 0 }, + { NULL, 'o', NULL, 0, N_("Preserve original dates."), 0 }, + { NULL, 'N', NULL, 0, N_("Use instance [COUNT] of name."), 0 }, + { NULL, 'C', NULL, 0, + N_("Do not replace existing files with extracted files."), 0 }, + { NULL, 'T', NULL, 0, N_("Allow filename to be truncated if necessary."), + 0 }, + { NULL, 'v', NULL, 0, N_("Provide verbose output."), 0 }, + { NULL, 's', NULL, 0, N_("Force regeneration of symbol table."), 0 }, + { NULL, 'a', NULL, 0, N_("Insert file after [MEMBER]."), 0 }, + { NULL, 'b', NULL, 0, N_("Insert file before [MEMBER]."), 0 }, + { NULL, 'i', NULL, 0, N_("Same as -b."), 0 }, + { NULL, 'c', NULL, 0, N_("Suppress message when library has to be created."), + 0 }, + { NULL, 'P', NULL, 0, N_("Use full path for file matching."), 0 }, + { NULL, 'u', NULL, 0, N_("Update only older files in archive."), 0 }, + + { NULL, 0, NULL, 0, NULL, 0 } +}; + +/* Short description of program. */ +static const char doc[] = N_("Create, modify, and extract from archives."); + +/* Strings for arguments in help texts. */ +static const char args_doc[] = N_("[MEMBER] [COUNT] ARCHIVE [FILE...]"); + +/* Prototype for option handler. */ +static error_t parse_opt (int key, char *arg, struct argp_state *state); + +/* Data structure to communicate with argp functions. */ +static struct argp argp = +{ + options, parse_opt, args_doc, doc, NULL, NULL, NULL +}; + + +/* What operation to perform. */ +static enum + { + oper_none, + oper_delete, + oper_move, + oper_print, + oper_qappend, + oper_replace, + oper_list, + oper_extract + } operation; + +/* Modifiers. */ +static bool verbose; +static bool preserve_dates; +static bool instance_specifed; +static bool dont_replace_existing; +static bool allow_truncate_fname; +static bool force_symtab; +static bool suppress_create_msg; +static bool full_path; +static bool update_newer; +static enum { ipos_none, ipos_before, ipos_after } ipos; + + +int +main (int argc, char *argv[]) +{ + /* Make memory leak detection possible. */ + mtrace (); + + /* We use no threads here which can interfere with handling a stream. */ + (void) __fsetlocking (stdin, FSETLOCKING_BYCALLER); + (void) __fsetlocking (stdout, FSETLOCKING_BYCALLER); + (void) __fsetlocking (stderr, FSETLOCKING_BYCALLER); + + /* Set locale. */ + (void) setlocale (LC_ALL, ""); + + /* Make sure the message catalog can be found. */ + (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR); + + /* Initialize the message catalog. */ + (void) textdomain (PACKAGE_TARNAME); + + /* For historical reasons the options in the first parameter need + not be preceded by a dash. Add it now if necessary. */ + if (argc > 1 && argv[1][0] != '-') + { + size_t len = strlen (argv[1]) + 1; + char *newp = alloca (len + 1); + newp[0] = '-'; + memcpy (&newp[1], argv[1], len); + argv[1] = newp; + } + + /* Parse and process arguments. */ + int remaining; + (void) argp_parse (&argp, argc, argv, ARGP_IN_ORDER, &remaining, NULL); + + /* Tell the library which version we are expecting. */ + (void) elf_version (EV_CURRENT); + + /* Handle the [MEMBER] parameter. */ + const char *member = NULL; + if (ipos != ipos_none) + { + /* Only valid for certain operations. */ + if (operation == oper_extract && operation == oper_delete) + error (1, 0, gettext ("\ +'a', 'b', and 'i' are only allowed with the 'm' and 'r' options")); + + if (remaining == argc) + { + error (0, 0, gettext ("MEMBER parameter required")); + argp_help (&argp, stderr, ARGP_HELP_SEE, AR); + exit (EXIT_FAILURE); + } + + member = argv[remaining++]; + } + + /* Handle the [COUNT] parameter. */ + long int instance = -1; + if (instance_specifed) + { + /* Only valid for certain operations. */ + if (operation == oper_extract && operation == oper_delete) + error (1, 0, gettext ("\ +'N' is only meaningful with the 'x' and 'd' options")); + + if (remaining == argc) + { + error (0, 0, gettext ("COUNT parameter required")); + argp_help (&argp, stderr, ARGP_HELP_SEE, AR); + exit (EXIT_FAILURE); + } + + char *endp; + errno = 0; + if (((instance = strtol (argv[remaining], &endp, 10)) == LONG_MAX + && errno == ERANGE) + || instance <= 0 + || *endp != '\0') + error (1, 0, gettext ("invalid COUNT parameter %s"), argv[remaining]); + + ++remaining; + } + + if ((dont_replace_existing || allow_truncate_fname) + && unlikely (operation != oper_extract)) + error (1, 0, gettext ("'%' is only meaningful with the 'x' option"), + dont_replace_existing ? 'C' : 'T'); + + /* There must at least be one more parameter specifying the archive. */ + if (remaining == argc) + { + error (0, 0, gettext ("Archive name required")); + argp_help (&argp, stderr, ARGP_HELP_SEE, AR); + exit (EXIT_FAILURE); + } + + const char *arfname = argv[remaining++]; + argv += remaining; + argc -= remaining; + + int status; + switch (operation) + { + case oper_list: + case oper_print: + status = do_oper_extract (operation, arfname, argv, argc, -1); + break; + + case oper_extract: + status = do_oper_extract (operation, arfname, argv, argc, instance); + break; + + case oper_delete: + status = do_oper_delete (arfname, argv, argc, instance); + break; + + case oper_move: + case oper_qappend: + case oper_replace: + status = do_oper_insert (operation, arfname, argv, argc, member); + break; + + default: + assert (! "should not happen"); + status = 1; + break; + } + + return status; +} + + +/* Print the version information. */ +static void +print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) +{ + fprintf (stream, "ar (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION); + fprintf (stream, gettext ("\ +Copyright (C) %s Red Hat, Inc.\n\ +This is free software; see the source for copying conditions. There is NO\n\ +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ +"), "2008"); + fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); +} + + +/* Handle program arguments. */ +static error_t +parse_opt (int key, char *arg __attribute__ ((unused)), + struct argp_state *state __attribute__ ((unused))) +{ + switch (key) + { + case 'd': + case 'm': + case 'p': + case 'q': + case 'r': + case 't': + case 'x': + if (operation != oper_none) + { + error (0, 0, gettext ("More than one operation specified")); + argp_help (&argp, stderr, ARGP_HELP_SEE, AR); + exit (EXIT_FAILURE); + } + + switch (key) + { + case 'd': + operation = oper_delete; + break; + case 'm': + operation = oper_move; + break; + case 'p': + operation = oper_print; + break; + case 'q': + operation = oper_qappend; + break; + case 'r': + operation = oper_replace; + break; + case 't': + operation = oper_list; + break; + case 'x': + operation = oper_extract; + break; + } + break; + + case 'a': + ipos = ipos_after; + break; + + case 'b': + case 'i': + ipos = ipos_before; + break; + + case 'c': + suppress_create_msg = true; + break; + + case 'C': + dont_replace_existing = true; + break; + + case 'N': + instance_specifed = true; + break; + + case 'o': + preserve_dates = true; + break; + + case 'P': + full_path = true; + break; + + case 's': + force_symtab = true; + break; + + case 'T': + allow_truncate_fname = true; + break; + + case 'v': + verbose = true; + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + + +static int +open_archive (const char *arfname, int flags, int mode, Elf **elf, + struct stat *st, bool miss_allowed) +{ + int fd = open (arfname, flags, mode); + if (fd == -1) + { + if (miss_allowed) + return -1; + + error (EXIT_FAILURE, errno, gettext ("cannot open archive '%s'"), + arfname); + } + + if (elf != NULL) + { + Elf_Cmd cmd = flags == O_RDONLY ? ELF_C_READ_MMAP : ELF_C_RDWR_MMAP; + + *elf = elf_begin (fd, cmd, NULL); + if (*elf == NULL) + error (EXIT_FAILURE, 0, gettext ("cannot open archive '%s': %s"), + arfname, elf_errmsg (-1)); + + if (flags == O_RDONLY && elf_kind (*elf) != ELF_K_AR) + error (EXIT_FAILURE, 0, gettext ("%s: not an archive file"), arfname); + } + + if (st != NULL && fstat (fd, st) != 0) + error (EXIT_FAILURE, errno, gettext ("cannot stat archive '%s'"), + arfname); + + return fd; +} + + +static void +not_found (int argc, char *argv[argc], bool found[argc]) +{ + for (int i = 0; i < argc; ++i) + if (!found[i]) + printf (gettext ("no entry %s in archive\n"), argv[i]); +} + + +static int +copy_content (Elf *elf, int newfd, off_t off, size_t n) +{ + size_t len; + char *rawfile = elf_rawfile (elf, &len); + + assert (off + n <= len); + + /* Tell the kernel we will read all the pages sequentially. */ + size_t ps = sysconf (_SC_PAGESIZE); + if (n > 2 * ps) + posix_madvise (rawfile + (off & ~(ps - 1)), n, POSIX_MADV_SEQUENTIAL); + + return write_retry (newfd, rawfile + off, n) != (ssize_t) n; +} + + +static int +do_oper_extract (int oper, const char *arfname, char **argv, int argc, + long int instance) +{ + bool found[argc]; + memset (found, '\0', sizeof (found)); + + struct statfs f; + f.f_namelen = 0; + + off_t index_off = -1; + size_t index_size = 0; + off_t cur_off = SARMAG; + + int status = 0; + Elf *elf; + int fd = open_archive (arfname, O_RDONLY, 0, &elf, NULL, false); + + if (hcreate (2 * argc) == 0) + error (EXIT_FAILURE, errno, gettext ("cannot create hash table")); + + for (int cnt = 0; cnt < argc; ++cnt) + { + ENTRY entry = { .key = argv[cnt], .data = &argv[cnt] }; + if (hsearch (entry, ENTER) == NULL) + error (EXIT_FAILURE, errno, + gettext ("cannot insert into hash table")); + } + + struct stat st; + if (force_symtab) + { + if (fstat (fd, &st) != 0) + { + error (0, errno, gettext ("cannot stat '%s'"), arfname); + close (fd); + return 1; + } + arlib_init (); + } + + Elf_Cmd cmd = ELF_C_READ_MMAP; + Elf *subelf; + while ((subelf = elf_begin (fd, cmd, elf)) != NULL) + { + Elf_Arhdr *arhdr = elf_getarhdr (subelf); + + if (strcmp (arhdr->ar_name, "/") == 0) + { + index_off = elf_getaroff (subelf); + index_size = arhdr->ar_size; + goto next; + } + if (strcmp (arhdr->ar_name, "//") == 0) + goto next; + + if (force_symtab) + { + arlib_add_symbols (elf, arfname, arhdr->ar_name, cur_off); + cur_off += (((arhdr->ar_size + 1) & ~((off_t) 1)) + + sizeof (struct ar_hdr)); + } + + bool do_extract = argc <= 0; + if (!do_extract) + { + ENTRY entry; + entry.key = arhdr->ar_name; + ENTRY *res = hsearch (entry, FIND); + if (res != NULL && (instance < 0 || instance-- == 0) + && !found[(char **) res->data - argv]) + found[(char **) res->data - argv] = do_extract = true; + } + + if (do_extract) + { + if (verbose) + { + if (oper == oper_print) + { + printf ("\n<%s>\n\n", arhdr->ar_name); + + /* We have to flush now because now we use the descriptor + directly. */ + fflush (stdout); + } + else if (oper == oper_list) + { + char datestr[100]; + strftime (datestr, sizeof (datestr), "%b %e %H:%M %Y", + localtime (&arhdr->ar_date)); + + printf ("%c%c%c%c%c%c%c%c%c %u/%u %6ju %s %s\n", + (arhdr->ar_mode & S_IRUSR) ? 'r' : '-', + (arhdr->ar_mode & S_IWUSR) ? 'w' : '-', + (arhdr->ar_mode & S_IXUSR) + ? ((arhdr->ar_mode & S_ISUID) ? 's' : 'x') + : ((arhdr->ar_mode & S_ISUID) ? 'S' : '-'), + (arhdr->ar_mode & S_IRGRP) ? 'r' : '-', + (arhdr->ar_mode & S_IWGRP) ? 'w' : '-', + (arhdr->ar_mode & S_IXGRP) + ? ((arhdr->ar_mode & S_ISGID) ? 's' : 'x') + : ((arhdr->ar_mode & S_ISGID) ? 'S' : '-'), + (arhdr->ar_mode & S_IROTH) ? 'r' : '-', + (arhdr->ar_mode & S_IWOTH) ? 'w' : '-', + (arhdr->ar_mode & S_IXOTH) + ? ((arhdr->ar_mode & S_ISVTX) ? 't' : 'x') + : ((arhdr->ar_mode & S_ISVTX) ? 'T' : '-'), + arhdr->ar_uid, + arhdr->ar_gid, + (uintmax_t) arhdr->ar_size, + datestr, + arhdr->ar_name); + } + else + printf ("x - %s\n", arhdr->ar_name); + } + + if (oper == oper_list) + { + if (!verbose) + puts (arhdr->ar_name); + + goto next; + } + + size_t nleft; + char *data = elf_rawfile (subelf, &nleft); + if (data == NULL) + { + error (0, 0, gettext ("cannot read content of %s: %s"), + arhdr->ar_name, elf_errmsg (-1)); + status = 1; + goto next; + } + + int xfd; + char tempfname[] = "XXXXXX"; + bool use_mkstemp = true; + + if (oper == oper_print) + xfd = STDOUT_FILENO; + else + { + xfd = mkstemp (tempfname); + if (unlikely (xfd == -1)) + { + /* We cannot create a temporary file. Try to overwrite + the file or create it if it does not exist. */ + int flags = O_WRONLY | O_CREAT; + if (dont_replace_existing) + flags |= O_EXCL; + else + flags |= O_TRUNC; + xfd = open (arhdr->ar_name, flags, 0600); + if (unlikely (xfd == -1)) + { + int printlen = INT_MAX; + + if (errno == ENAMETOOLONG && allow_truncate_fname + && (f.f_namelen != 0 || statfs (".", &f) == 0)) + { + /* Try to truncate the name. First find out by how + much. */ + printlen = f.f_namelen; + char truncfname[f.f_namelen + 1]; + *((char *) mempcpy (truncfname, arhdr->ar_name, + f.f_namelen)) = '\0'; + + xfd = open (truncfname, flags, 0600); + } + + if (xfd == -1) + { + error (0, errno, gettext ("cannot open %.*s"), + (int) printlen, arhdr->ar_name); + status = 1; + goto next; + } + } + + use_mkstemp = false; + } + } + + ssize_t n; + while ((n = TEMP_FAILURE_RETRY (write (xfd, data, nleft))) != -1) + { + nleft -= n; + if (nleft == 0) + break; + data += n; + } + + if (unlikely (n == -1)) + { + error (0, errno, gettext ("failed to write %s"), arhdr->ar_name); + status = 1; + unlink (tempfname); + close (xfd); + goto next; + } + + if (oper != oper_print) + { + /* Fix up the mode. */ + if (unlikely (fchmod (xfd, arhdr->ar_mode) != 0)) + { + error (0, errno, gettext ("cannot change mode of %s"), + arhdr->ar_name); + status = 0; + } + + if (preserve_dates) + { + struct timeval tv[2]; + tv[0].tv_sec = arhdr->ar_date; + tv[0].tv_usec = 0; + tv[1].tv_sec = arhdr->ar_date; + tv[1].tv_usec = 0; + + if (unlikely (futimes (xfd, tv) != 0)) + { + error (0, errno, + gettext ("cannot change modification time of %s"), + arhdr->ar_name); + status = 1; + } + } + + /* If we used a temporary file, move it do the right + name now. */ + if (use_mkstemp) + { + int r; + + if (dont_replace_existing) + { + r = link (tempfname, arhdr->ar_name); + if (likely (r == 0)) + unlink (tempfname); + } + else + r = rename (tempfname, arhdr->ar_name); + + if (unlikely (r) != 0) + { + int printlen = INT_MAX; + + if (errno == ENAMETOOLONG && allow_truncate_fname + && (f.f_namelen != 0 || statfs (".", &f) == 0)) + { + /* Try to truncate the name. First find out by how + much. */ + printlen = f.f_namelen; + char truncfname[f.f_namelen + 1]; + *((char *) mempcpy (truncfname, arhdr->ar_name, + f.f_namelen)) = '\0'; + + if (dont_replace_existing) + { + r = link (tempfname, truncfname); + if (likely (r == 0)) + unlink (tempfname); + } + else + r = rename (tempfname, truncfname); + } + + if (r != 0) + { + error (0, errno, gettext ("\ +cannot rename temporary file to %.*s"), + printlen, arhdr->ar_name); + unlink (tempfname); + status = 1; + } + } + } + + close (xfd); + } + } + + next: + cmd = elf_next (subelf); + if (elf_end (subelf) != 0) + error (1, 0, "%s: %s", arfname, elf_errmsg (-1)); + } + + hdestroy (); + + if (force_symtab) + { + arlib_finalize (); + + if (symtab.symsnamelen != 0 + /* We have to rewrite the file also if it initially had an index + but now does not need one anymore. */ + || (symtab.symsnamelen == 0 && index_size != 0)) + { + char tmpfname[strlen (arfname) + 7]; + strcpy (stpcpy (tmpfname, arfname), "XXXXXX"); + int newfd = mkstemp (tmpfname); + if (unlikely (newfd == -1)) + { + nonew: + error (0, errno, gettext ("cannot create new file")); + status = 1; + } + else + { + /* Create the header. */ + if (unlikely (write_retry (newfd, ARMAG, SARMAG) != SARMAG)) + { + // XXX Use /prof/self/fd/%d ??? + nonew_unlink: + unlink (tmpfname); + if (newfd != -1) + close (newfd); + goto nonew; + } + + /* Create the new file. There are three parts as far we are + concerned: 1. original context before the index, 2. the + new index, 3. everything after the new index. */ + off_t rest_off; + if (index_off != -1) + rest_off = (index_off + sizeof (struct ar_hdr) + + ((index_size + 1) & ~1ul)); + else + rest_off = SARMAG; + + if ((symtab.symsnamelen != 0 + && ((write_retry (newfd, symtab.symsoff, + symtab.symsofflen) + != (ssize_t) symtab.symsofflen) + || (write_retry (newfd, symtab.symsname, + symtab.symsnamelen) + != (ssize_t) symtab.symsnamelen))) + /* Even if the original file had content before the + symbol table, we write it in the correct order. */ + || (index_off != SARMAG + && copy_content (elf, newfd, SARMAG, index_off - SARMAG)) + || copy_content (elf, newfd, rest_off, st.st_size - rest_off) + /* Set the mode of the new file to the same values the + original file has. */ + || fchmod (newfd, st.st_mode & ALLPERMS) != 0 + /* Never complain about fchown failing. */ + || (({asm ("" :: "r" (fchown (newfd, st.st_uid, + st.st_gid))); }), + close (newfd) != 0) + || (newfd = -1, rename (tmpfname, arfname) != 0)) + goto nonew_unlink; + } + } + } + + elf_end (elf); + + close (fd); + + not_found (argc, argv, found); + + return status; +} + + +struct armem +{ + off_t off; + off_t old_off; + size_t size; + long int long_name_off; + struct armem *next; + void *mem; + time_t sec; + uid_t uid; + gid_t gid; + mode_t mode; + const char *name; + Elf *elf; +}; + + +static int +write_member (struct armem *memb, off_t *startp, off_t *lenp, Elf *elf, + off_t end_off, int newfd) +{ + struct ar_hdr arhdr; + char tmpbuf[sizeof (arhdr.ar_name) + 1]; + + bool changed_header = memb->long_name_off != -1; + if (changed_header) + { + /* In case of a long file name we assume the archive header + changed and we write it here. */ + memcpy (&arhdr, elf_rawfile (elf, NULL) + *startp, sizeof (arhdr)); + + snprintf (tmpbuf, sizeof (tmpbuf), "/%-*ld", + (int) sizeof (arhdr.ar_name), memb->long_name_off); + changed_header = memcmp (arhdr.ar_name, tmpbuf, + sizeof (arhdr.ar_name)) != 0; + } + + /* If the files are adjacent in the old file extend the range. */ + if (*startp != -1 && !changed_header && *startp + *lenp == memb->old_off) + { + /* Extend the current range. */ + *lenp += (memb->next != NULL + ? memb->next->off : end_off) - memb->off; + return 0; + } + + /* Write out the old range. */ + if (*startp != -1 && copy_content (elf, newfd, *startp, *lenp)) + return -1; + + *startp = memb->old_off; + *lenp = (memb->next != NULL ? memb->next->off : end_off) - memb->off; + + if (changed_header) + { + memcpy (arhdr.ar_name, tmpbuf, sizeof (arhdr.ar_name)); + + if (unlikely (write_retry (newfd, &arhdr, sizeof (arhdr)) + != sizeof (arhdr))) + return -1; + + *startp += sizeof (struct ar_hdr); + assert ((size_t) *lenp >= sizeof (struct ar_hdr)); + *lenp -= sizeof (struct ar_hdr); + } + + return 0; +} + +/* Store the name in the long name table if necessary. + Record its offset or -1 if we did not need to use the table. */ +static void +remember_long_name (struct armem *mem, const char *name, size_t namelen) +{ + mem->long_name_off = (namelen > MAX_AR_NAME_LEN + ? arlib_add_long_name (name, namelen) + : -1l); +} + +static int +do_oper_delete (const char *arfname, char **argv, int argc, + long int instance) +{ + bool *found = alloca (sizeof (bool) * argc); + memset (found, '\0', sizeof (found)); + + /* List of the files we keep. */ + struct armem *to_copy = NULL; + + int status = 0; + Elf *elf; + struct stat st; + int fd = open_archive (arfname, O_RDONLY, 0, &elf, &st, false); + + if (hcreate (2 * argc) == 0) + error (EXIT_FAILURE, errno, gettext ("cannot create hash table")); + + for (int cnt = 0; cnt < argc; ++cnt) + { + ENTRY entry = { .key = argv[cnt], .data = &argv[cnt] }; + if (hsearch (entry, ENTER) == NULL) + error (EXIT_FAILURE, errno, + gettext ("cannot insert into hash table")); + } + + arlib_init (); + + off_t cur_off = SARMAG; + Elf_Cmd cmd = ELF_C_READ_MMAP; + Elf *subelf; + while ((subelf = elf_begin (fd, cmd, elf)) != NULL) + { + Elf_Arhdr *arhdr = elf_getarhdr (subelf); + + /* Ignore the symbol table and the long file name table here. */ + if (strcmp (arhdr->ar_name, "/") == 0 + || strcmp (arhdr->ar_name, "//") == 0) + goto next; + + bool do_delete = argc <= 0; + if (!do_delete) + { + ENTRY entry; + entry.key = arhdr->ar_name; + ENTRY *res = hsearch (entry, FIND); + if (res != NULL && (instance < 0 || instance-- == 0) + && !found[(char **) res->data - argv]) + found[(char **) res->data - argv] = do_delete = true; + } + + if (do_delete) + { + if (verbose) + printf ("d - %s\n", arhdr->ar_name); + } + else + { + struct armem *newp = alloca (sizeof (struct armem)); + newp->old_off = elf_getaroff (subelf); + newp->off = cur_off; + + cur_off += (((arhdr->ar_size + 1) & ~((off_t) 1)) + + sizeof (struct ar_hdr)); + + if (to_copy == NULL) + to_copy = newp->next = newp; + else + { + newp->next = to_copy->next; + to_copy = to_copy->next = newp; + } + + /* If we recreate the symbol table read the file's symbol + table now. */ + arlib_add_symbols (subelf, arfname, arhdr->ar_name, newp->off); + + /* Remember long file names. */ + remember_long_name (newp, arhdr->ar_name, strlen (arhdr->ar_name)); + } + + next: + cmd = elf_next (subelf); + if (elf_end (subelf) != 0) + error (1, 0, "%s: %s", arfname, elf_errmsg (-1)); + } + + arlib_finalize (); + + hdestroy (); + + /* Create a new, temporary file in the same directory as the + original file. */ + char tmpfname[strlen (arfname) + 7]; + strcpy (stpcpy (tmpfname, arfname), "XXXXXX"); + int newfd = mkstemp (tmpfname); + if (unlikely (newfd == -1)) + goto nonew; + + /* Create the header. */ + if (unlikely (write_retry (newfd, ARMAG, SARMAG) != SARMAG)) + { + // XXX Use /prof/self/fd/%d ??? + nonew_unlink: + unlink (tmpfname); + if (newfd != -1) + close (newfd); + nonew: + error (0, errno, gettext ("cannot create new file")); + status = 1; + goto errout; + } + + /* If the archive is empty that is all we have to do. */ + if (likely (to_copy != NULL)) + { + /* Write the symbol table or the long file name table or both. */ + if (symtab.symsnamelen != 0 + && ((write_retry (newfd, symtab.symsoff, symtab.symsofflen) + != (ssize_t) symtab.symsofflen) + || (write_retry (newfd, symtab.symsname, symtab.symsnamelen) + != (ssize_t) symtab.symsnamelen))) + goto nonew_unlink; + + if (symtab.longnameslen > sizeof (struct ar_hdr) + && (write_retry (newfd, symtab.longnames, symtab.longnameslen) + != (ssize_t) symtab.longnameslen)) + goto nonew_unlink; + + /* NULL-terminate the list of files to copy. */ + struct armem *last = to_copy; + to_copy = to_copy->next; + last->next = NULL; + + off_t start = -1; + off_t len = -1; + + do + if (write_member (to_copy, &start, &len, elf, cur_off, newfd) != 0) + goto nonew_unlink; + while ((to_copy = to_copy->next) != NULL); + + /* Write the last part. */ + if (copy_content (elf, newfd, start, len)) + goto nonew_unlink; + } + + /* Set the mode of the new file to the same values the original file + has. */ + if (fchmod (newfd, st.st_mode & ALLPERMS) != 0 + /* Never complain about fchown failing. */ + || (({asm ("" :: "r" (fchown (newfd, st.st_uid, st.st_gid))); }), + close (newfd) != 0) + || (newfd = -1, rename (tmpfname, arfname) != 0)) + goto nonew_unlink; + + errout: +#ifdef DEBUG + elf_end (elf); + + arlib_fini (); + + close (fd); +#endif + + not_found (argc, argv, found); + + return status; +} + + +static void +no0print (bool ofmt, char *buf, int bufsize, long int val) +{ + char tmpbuf[bufsize + 1]; + snprintf (tmpbuf, sizeof (tmpbuf), ofmt ? "%-*lo" : "%-*ld", bufsize, val); + memcpy (buf, tmpbuf, bufsize); +} + + +static int +do_oper_insert (int oper, const char *arfname, char **argv, int argc, + const char *member) +{ + int status = 0; + Elf *elf; + struct stat st; + int fd = open_archive (arfname, O_RDONLY, 0, &elf, &st, oper != oper_move); + + /* List of the files we keep. */ + struct armem *all = NULL; + struct armem *after_memberelem = NULL; + struct armem **found = alloca (sizeof (*found) * argc); + memset (found, '\0', sizeof (*found) * argc); + + arlib_init (); + + /* Initialize early for no_old case. */ + off_t cur_off = SARMAG; + + if (fd == -1) + { + if (!suppress_create_msg) + fprintf (stderr, "%s: creating %s\n", AR, arfname); + + goto no_old; + } + + /* Store the names of all files from the command line in a hash + table so that we can match it. Note that when no file name is + given we are basically doing nothing except recreating the + index. */ + if (oper != oper_qappend) + { + if (hcreate (2 * argc) == 0) + error (EXIT_FAILURE, errno, gettext ("cannot create hash table")); + + for (int cnt = 0; cnt < argc; ++cnt) + { + ENTRY entry; + entry.key = full_path ? argv[cnt] : basename (argv[cnt]); + entry.data = &argv[cnt]; + if (hsearch (entry, ENTER) == NULL) + error (EXIT_FAILURE, errno, + gettext ("cannot insert into hash table")); + } + } + + /* While iterating over the current content of the archive we must + determine a number of things: which archive members to keep, + which are replaced, and where to insert the new members. */ + Elf_Cmd cmd = ELF_C_READ_MMAP; + Elf *subelf; + while ((subelf = elf_begin (fd, cmd, elf)) != NULL) + { + Elf_Arhdr *arhdr = elf_getarhdr (subelf); + + /* Ignore the symbol table and the long file name table here. */ + if (strcmp (arhdr->ar_name, "/") == 0 + || strcmp (arhdr->ar_name, "//") == 0) + goto next; + + struct armem *newp = alloca (sizeof (struct armem)); + newp->old_off = elf_getaroff (subelf); + newp->size = arhdr->ar_size; + newp->sec = arhdr->ar_date; + newp->mem = NULL; + + /* Remember long file names. */ + remember_long_name (newp, arhdr->ar_name, strlen (arhdr->ar_name)); + + /* Check whether this is a file we are looking for. */ + if (oper != oper_qappend) + { + /* Check whether this is the member used as the insert point. */ + if (member != NULL && strcmp (arhdr->ar_name, member) == 0) + { + /* Note that all == NULL means insert at the beginning. */ + if (ipos == ipos_before) + after_memberelem = all; + else + after_memberelem = newp; + member = NULL; + } + + ENTRY entry; + entry.key = arhdr->ar_name; + ENTRY *res = hsearch (entry, FIND); + if (res != NULL && found[(char **) res->data - argv] == NULL) + { + found[(char **) res->data - argv] = newp; + + /* If we insert before or after a certain element move + all files to a special list. */ + if (unlikely (ipos != ipos_none || oper == oper_move)) + { + if (after_memberelem == newp) + /* Since we remove this element even though we should + insert everything after it, we in fact insert + everything after the previous element. */ + after_memberelem = all; + + goto next; + } + } + } + + if (all == NULL) + all = newp->next = newp; + else + { + newp->next = all->next; + all = all->next = newp; + } + + next: + cmd = elf_next (subelf); + if (elf_end (subelf) != 0) + error (EXIT_FAILURE, 0, "%s: %s", arfname, elf_errmsg (-1)); + } + + if (oper != oper_qappend) + hdestroy (); + + no_old: + if (member != NULL) + error (EXIT_FAILURE, 0, gettext ("position member %s not found"), + member); + + if (oper == oper_move) + { + /* Make sure all requested elements are found in the archive. */ + for (int cnt = 0; cnt < argc; ++cnt) + { + if (found[cnt] == NULL) + { + fprintf (stderr, gettext ("%s: no entry %s in archive!\n"), + AR, argv[cnt]); + status = 1; + } + + if (verbose) + printf ("m - %s\n", argv[cnt]); + } + } + else + { + /* Open all the new files, get their sizes and add all symbols. */ + for (int cnt = 0; cnt < argc; ++cnt) + { + const char *bname = basename (argv[cnt]); + size_t bnamelen = strlen (bname); + if (found[cnt] == NULL) + { + found[cnt] = alloca (sizeof (struct armem)); + found[cnt]->old_off = -1; + + remember_long_name (found[cnt], bname, bnamelen); + } + + struct stat newst; + Elf *newelf; + int newfd = open (argv[cnt], O_RDONLY); + if (newfd == -1) + { + error (0, errno, gettext ("cannot open %s"), argv[cnt]); + status = 1; + } + else if (fstat (newfd, &newst) == -1) + { + error (0, errno, gettext ("cannot stat %s"), argv[cnt]); + close (newfd); + status = 1; + } + else if (!S_ISREG (newst.st_mode)) + { + error (0, errno, gettext ("%s is no regular file"), argv[cnt]); + close (newfd); + status = 1; + } + else if (update_newer + && found[cnt]->old_off != -1l + && found[cnt]->sec > st.st_mtime) + /* Do nothing, the file in the archive is younger. */ + close (newfd); + else if ((newelf = elf_begin (newfd, ELF_C_READ_MMAP, NULL)) + == NULL) + { + fprintf (stderr, + gettext ("cannot get ELF descriptor for %s: %s\n"), + argv[cnt], elf_errmsg (-1)); + status = 1; + } + else + { + if (verbose) + printf ("%c - %s\n", + found[cnt]->old_off == -1l ? 'a' : 'r', argv[cnt]); + + found[cnt]->elf = newelf; + found[cnt]->sec = newst.st_mtime; + found[cnt]->uid = newst.st_uid; + found[cnt]->gid = newst.st_gid; + found[cnt]->mode = newst.st_mode; + found[cnt]->name = bname; + + found[cnt]->mem = elf_rawfile (newelf, &found[cnt]->size); + if (found[cnt] == NULL || elf_cntl (newelf, ELF_C_FDDONE) != 0) + error (EXIT_FAILURE, 0, gettext ("cannot read %s: %s"), + argv[cnt], elf_errmsg (-1)); + + close (newfd); + + if (found[cnt]->old_off != -1l) + /* Remember long file names. */ + remember_long_name (found[cnt], bname, bnamelen); + } + } + } + + if (status != 0) + { +#ifdef DEBUG + elf_end (elf); + + arlib_fini (); + + close (fd); +#endif + + return status; + } + + /* If we have no entry point so far add at the end. AFTER_MEMBERELEM + being NULL when adding before an entry means add at the beginning. */ + if (ipos != ipos_before && after_memberelem == NULL) + after_memberelem = all; + + /* Convert the circular list into a normal list first. */ + if (all != NULL) + { + struct armem *tmp = all; + all = all->next; + tmp->next = NULL; + } + + struct armem *last_added = after_memberelem; + for (int cnt = 0; cnt < argc; ++cnt) + if (oper != oper_replace || found[cnt]->old_off == -1) + { + if (last_added == NULL) + { + found[cnt]->next = all; + last_added = all = found[cnt]; + } + else + { + found[cnt]->next = last_added->next; + last_added = last_added->next = found[cnt]; + } + } + + /* Finally compute the offset and add the symbols for the files + after the insert point. */ + if (likely (all != NULL)) + for (struct armem *memp = all; memp != NULL; memp = memp->next) + { + memp->off = cur_off; + + if (memp->mem == NULL) + { + Elf_Arhdr *arhdr; + /* Fake initializing arhdr and subelf to keep gcc calm. */ + asm ("" : "=m" (arhdr), "=m" (subelf)); + if (elf_rand (elf, memp->old_off) == 0 + || (subelf = elf_begin (fd, ELF_C_READ_MMAP, elf)) == NULL + || (arhdr = elf_getarhdr (subelf)) == NULL) + /* This should never happen since we already looked at the + archive content. But who knows... */ + error (EXIT_FAILURE, 0, "%s: %s", arfname, elf_errmsg (-1)); + + arlib_add_symbols (subelf, arfname, arhdr->ar_name, cur_off); + + elf_end (subelf); + } + else + arlib_add_symbols (memp->elf, arfname, memp->name, cur_off); + + cur_off += (((memp->size + 1) & ~((off_t) 1)) + + sizeof (struct ar_hdr)); + } + + /* Now we have all the information for the symbol table and long + file name table. Construct the final layout. */ + arlib_finalize (); + + /* Create a new, temporary file in the same directory as the + original file. */ + char tmpfname[strlen (arfname) + 7]; + strcpy (stpcpy (tmpfname, arfname), "XXXXXX"); + int newfd; + if (fd != -1) + newfd = mkstemp (tmpfname); + else + { + newfd = open (arfname, O_RDWR | O_CREAT | O_EXCL, DEFFILEMODE); + if (newfd == -1 && errno == EEXIST) + /* Bah, first the file did not exist, now it does. Restart. */ + return do_oper_insert (oper, arfname, argv, argc, member); + } + if (unlikely (newfd == -1)) + goto nonew; + + /* Create the header. */ + if (unlikely (write_retry (newfd, ARMAG, SARMAG) != SARMAG)) + { + nonew_unlink: + if (fd != -1) + { + // XXX Use /prof/self/fd/%d ??? + unlink (tmpfname); + if (newfd != -1) + close (newfd); + } + nonew: + error (0, errno, gettext ("cannot create new file")); + status = 1; + goto errout; + } + + /* If the new archive is not empty we actually have something to do. */ + if (likely (all != NULL)) + { + /* Write the symbol table or the long file name table or both. */ + if (symtab.symsnamelen != 0 + && ((write_retry (newfd, symtab.symsoff, symtab.symsofflen) + != (ssize_t) symtab.symsofflen) + || (write_retry (newfd, symtab.symsname, symtab.symsnamelen) + != (ssize_t) symtab.symsnamelen))) + goto nonew_unlink; + + if (symtab.longnameslen > sizeof (struct ar_hdr) + && (write_retry (newfd, symtab.longnames, symtab.longnameslen) + != (ssize_t) symtab.longnameslen)) + goto nonew_unlink; + + off_t start = -1; + off_t len = -1; + + while (all != NULL) + { + if (all->mem != NULL) + { + /* This is a new file. If there is anything from the + archive left to be written do it now. */ + if (start != -1 && copy_content (elf, newfd, start, len)) + goto nonew_unlink; + + start = -1; + len = -1; + + /* Create the header. */ + struct ar_hdr arhdr; + char tmpbuf[sizeof (arhdr.ar_name) + 1]; + if (all->long_name_off == -1) + { + size_t namelen = strlen (all->name); + char *p = mempcpy (arhdr.ar_name, all->name, namelen); + *p++ = '/'; + memset (p, ' ', sizeof (arhdr.ar_name) - namelen - 1); + } + else + { + snprintf (tmpbuf, sizeof (arhdr.ar_name) + 1, "/%-*ld", + (int) sizeof (arhdr.ar_name), all->long_name_off); + memcpy (arhdr.ar_name, tmpbuf, sizeof (arhdr.ar_name)); + } + + no0print (false, arhdr.ar_date, sizeof (arhdr.ar_date), + all->sec); + no0print (false, arhdr.ar_uid, sizeof (arhdr.ar_uid), all->uid); + no0print (false, arhdr.ar_gid, sizeof (arhdr.ar_gid), all->gid); + no0print (true, arhdr.ar_mode, sizeof (arhdr.ar_mode), + all->mode); + no0print (false, arhdr.ar_size, sizeof (arhdr.ar_size), + all->size); + memcpy (arhdr.ar_fmag, ARFMAG, sizeof (arhdr.ar_fmag)); + + if (unlikely (write_retry (newfd, &arhdr, sizeof (arhdr)) + != sizeof (arhdr))) + goto nonew_unlink; + + /* Now the file itself. */ + if (unlikely (write_retry (newfd, all->mem, all->size) + != (off_t) all->size)) + goto nonew_unlink; + + /* Pad the file if its size is odd. */ + if ((all->size & 1) != 0) + if (unlikely (write_retry (newfd, "\n", 1) != 1)) + goto nonew_unlink; + } + else + { + /* This is a member from the archive. */ + if (write_member (all, &start, &len, elf, cur_off, newfd) + != 0) + goto nonew_unlink; + } + + all = all->next; + } + + /* Write the last part. */ + if (start != -1 && copy_content (elf, newfd, start, len)) + goto nonew_unlink; + } + + /* Set the mode of the new file to the same values the original file + has. */ + if (fd != -1 + && (fchmod (newfd, st.st_mode & ALLPERMS) != 0 + /* Never complain about fchown failing. */ + || (({asm ("" :: "r" (fchown (newfd, st.st_uid, st.st_gid))); }), + close (newfd) != 0) + || (newfd = -1, rename (tmpfname, arfname) != 0))) + goto nonew_unlink; + + errout: +#ifdef DEBUG + elf_end (elf); + + arlib_fini (); + + close (fd); +#endif + + return status; +} + + +#include "debugpred.h" diff --git a/src/arlib.c b/src/arlib.c new file mode 100644 index 00000000..af98454c --- /dev/null +++ b/src/arlib.c @@ -0,0 +1,279 @@ +/* Functions to handle creation of Linux archives. + Copyright (C) 2007 Red Hat, Inc. + Written by Ulrich Drepper <drepper@redhat.com>, 2007. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <assert.h> +#include <error.h> +#include <gelf.h> +#include <libintl.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> + +#include <system.h> + +#include "arlib.h" + + +/* The one symbol table we hanble. */ +struct arlib_symtab symtab; + + +/* Initialize ARLIB_SYMTAB structure. */ +void +arlib_init (void) +{ +#define obstack_chunk_alloc xmalloc +#define obstack_chunk_free free + obstack_init (&symtab.symsoffob); + obstack_init (&symtab.symsnameob); + obstack_init (&symtab.longnamesob); + + /* We add the archive header here as well, that avoids allocating + another memory block. */ + struct ar_hdr ar_hdr; + memcpy (ar_hdr.ar_name, "/ ", sizeof (ar_hdr.ar_name)); + /* Using snprintf here has a problem: the call always wants to add a + NUL byte. We could use a trick whereby we specify the target + buffer size longer than it is and this would not actually fail, + since all the fields are consecutive and we fill them in in + sequence (i.e., the NUL byte gets overwritten). But + _FORTIFY_SOURCE=2 would not let us play these games. Therefore + we play it safe. */ + char tmpbuf[sizeof (ar_hdr.ar_date) + 1]; + memcpy (ar_hdr.ar_date, tmpbuf, + snprintf (tmpbuf, sizeof (tmpbuf), "%-*lld", + (int) sizeof (ar_hdr.ar_date), + (long long int) time (NULL))); + assert ((sizeof (struct ar_hdr) % sizeof (uint32_t)) == 0); + + /* Note the string for the ar_uid and ar_gid cases is longer than + necessary. This does not matter since we copy only as much as + necessary but it helps the compiler to use the same string for + the ar_mode case. */ + memcpy (ar_hdr.ar_uid, "0 ", sizeof (ar_hdr.ar_uid)); + memcpy (ar_hdr.ar_gid, "0 ", sizeof (ar_hdr.ar_gid)); + memcpy (ar_hdr.ar_mode, "0 ", sizeof (ar_hdr.ar_mode)); + memcpy (ar_hdr.ar_fmag, ARFMAG, sizeof (ar_hdr.ar_fmag)); + + /* Add the archive header to the file content. */ + obstack_grow (&symtab.symsoffob, &ar_hdr, sizeof (ar_hdr)); + + /* The first word in the offset table specifies the size. Create + such an entry now. The real value will be filled-in later. For + all supported platforms the following is true. */ + assert (sizeof (uint32_t) == sizeof (int)); + obstack_int_grow (&symtab.symsoffob, 0); + + /* The long name obstack also gets its archive header. As above, + some of the input strings are longer than required but we only + copy the necessary part. */ + memcpy (ar_hdr.ar_name, "// ", sizeof (ar_hdr.ar_name)); + memcpy (ar_hdr.ar_date, " ", sizeof (ar_hdr.ar_date)); + memcpy (ar_hdr.ar_uid, " ", sizeof (ar_hdr.ar_uid)); + memcpy (ar_hdr.ar_gid, " ", sizeof (ar_hdr.ar_gid)); + memcpy (ar_hdr.ar_mode, " ", sizeof (ar_hdr.ar_mode)); + /* The ar_size field will be filled in later and ar_fmag is already OK. */ + obstack_grow (&symtab.longnamesob, &ar_hdr, sizeof (ar_hdr)); + + /* All other members are zero. */ + symtab.symsofflen = 0; + symtab.symsoff = NULL; + symtab.symsnamelen = 0; + symtab.symsname = NULL; +} + + +/* Finalize ARLIB_SYMTAB content. */ +void +arlib_finalize (void) +{ + char tmpbuf[sizeof (((struct ar_hdr *) NULL)->ar_size) + 1]; + + symtab.longnameslen = obstack_object_size (&symtab.longnamesob); + if (symtab.longnameslen != sizeof (struct ar_hdr)) + { + if ((symtab.longnameslen & 1) != 0) + { + /* Add one more byte to make length even. */ + obstack_grow (&symtab.longnamesob, "\n", 1); + ++symtab.longnameslen; + } + + symtab.longnames = obstack_finish (&symtab.longnamesob); + + memcpy (&((struct ar_hdr *) symtab.longnames)->ar_size, tmpbuf, + snprintf (tmpbuf, sizeof (tmpbuf), "%-*zu", + (int) sizeof (((struct ar_hdr *) NULL)->ar_size), + symtab.longnameslen - sizeof (struct ar_hdr))); + } + + symtab.symsofflen = obstack_object_size (&symtab.symsoffob); + assert (symtab.symsofflen % sizeof (uint32_t) == 0); + if (symtab.symsofflen != 0) + { + symtab.symsoff = (uint32_t *) obstack_finish (&symtab.symsoffob); + + /* Fill in the number of offsets now. */ + symtab.symsoff[AR_HDR_WORDS] = le_bswap_32 ((symtab.symsofflen + - sizeof (struct ar_hdr)) + / sizeof (uint32_t) - 1); + } + + symtab.symsnamelen = obstack_object_size (&symtab.symsnameob); + if ((symtab.symsnamelen & 1) != 0) + { + /* Add one more NUL byte to make length even. */ + obstack_grow (&symtab.symsnameob, "", 1); + ++symtab.symsnamelen; + } + symtab.symsname = obstack_finish (&symtab.symsnameob); + + /* Determine correction for the offsets in the symbol table. */ + off_t disp = 0; + if (symtab.symsnamelen > 0) + disp = symtab.symsofflen + symtab.symsnamelen; + if (symtab.longnameslen > sizeof (struct ar_hdr)) + disp += symtab.longnameslen; + + if (disp != 0 && symtab.symsoff != NULL) + { + uint32_t nsyms = le_bswap_32 (symtab.symsoff[AR_HDR_WORDS]); + + for (uint32_t cnt = 1; cnt <= nsyms; ++cnt) + { + uint32_t val = le_bswap_32 (symtab.symsoff[AR_HDR_WORDS + cnt]); + val += disp; + symtab.symsoff[AR_HDR_WORDS + cnt] = le_bswap_32 (val); + } + } + + /* See comment for ar_date above. */ + memcpy (&((struct ar_hdr *) symtab.symsoff)->ar_size, tmpbuf, + snprintf (tmpbuf, sizeof (tmpbuf), "%-*zu", + (int) sizeof (((struct ar_hdr *) NULL)->ar_size), + symtab.symsofflen + symtab.symsnamelen + - sizeof (struct ar_hdr))); +} + + +/* Free resources for ARLIB_SYMTAB. */ +void +arlib_fini (void) +{ + obstack_free (&symtab.symsoffob, NULL); + obstack_free (&symtab.symsnameob, NULL); + obstack_free (&symtab.longnamesob, NULL); +} + + +/* Add name a file offset of a symbol. */ +void +arlib_add_symref (const char *symname, off_t symoff) +{ + /* For all supported platforms the following is true. */ + assert (sizeof (uint32_t) == sizeof (int)); + obstack_int_grow (&symtab.symsoffob, (int) le_bswap_32 (symoff)); + + size_t symname_len = strlen (symname) + 1; + obstack_grow (&symtab.symsnameob, symname, symname_len); +} + + +/* Add symbols from ELF with value OFFSET to the symbol table SYMTAB. */ +void +arlib_add_symbols (Elf *elf, const char *arfname, const char *membername, + off_t off) +{ + if (sizeof (off) > sizeof (uint32_t) && off > ~((uint32_t) 0)) + /* The archive is too big. */ + error (EXIT_FAILURE, 0, gettext ("the archive '%s' is too large"), + arfname); + + /* We only add symbol tables for ELF files. It makes not much sense + to add symbols from executables but we do so for compatibility. + For DSOs and executables we use the dynamic symbol table, for + relocatable files all the DT_SYMTAB tables. */ + if (elf_kind (elf) != ELF_K_ELF) + return; + + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); + if (ehdr == NULL) + error (EXIT_FAILURE, 0, gettext ("cannot read ELF header of %s(%s): %s"), + arfname, membername, elf_errmsg (-1)); + + GElf_Word symtype; + if (ehdr->e_type == ET_REL) + symtype = SHT_SYMTAB; + else if (ehdr->e_type == ET_EXEC || ehdr->e_type == ET_DYN) + symtype = SHT_DYNSYM; + else + /* We do not handle that type. */ + return; + + /* Iterate over all sections. */ + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (elf, scn)) != NULL) + { + /* Get the section header. */ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr == NULL) + continue; + + if (shdr->sh_type != symtype) + continue; + + Elf_Data *data = elf_getdata (scn, NULL); + if (data == NULL) + continue; + + int nsyms = shdr->sh_size / shdr->sh_entsize; + for (int ndx = shdr->sh_info; ndx < nsyms; ++ndx) + { + GElf_Sym sym_mem; + GElf_Sym *sym = gelf_getsym (data, ndx, &sym_mem); + if (sym == NULL) + continue; + + /* Ignore undefined symbols. */ + if (sym->st_shndx == SHN_UNDEF) + continue; + + /* Use this symbol. */ + const char *symname = elf_strptr (elf, shdr->sh_link, sym->st_name); + if (symname != NULL) + arlib_add_symref (symname, off); + } + + /* Only relocatable files can have more than one symbol table. */ + if (ehdr->e_type != ET_REL) + break; + } +} diff --git a/src/arlib.h b/src/arlib.h new file mode 100644 index 00000000..fd26d248 --- /dev/null +++ b/src/arlib.h @@ -0,0 +1,96 @@ +/* Copyright (C) 2007 Red Hat, Inc. + Written by Ulrich Drepper <drepper@redhat.com>, 2007. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#ifndef _ARLIB_H +#define _ARLIB_H 1 + +#include <ar.h> +#include <byteswap.h> +#include <endian.h> +#include <libelf.h> +#include <obstack.h> +#include <stddef.h> +#include <stdint.h> +#include <sys/types.h> + + +/* Maximum length of a file name that fits directly into the ar header. + We cannot use the final byte since a / goes there. */ +#define MAX_AR_NAME_LEN (sizeof (((struct ar_hdr *) NULL)->ar_name) - 1) + + +/* Words matching in size to archive header. */ +#define AR_HDR_WORDS (sizeof (struct ar_hdr) / sizeof (uint32_t)) + + +#if __BYTE_ORDER == __LITTLE_ENDIAN +# define le_bswap_32(val) bswap_32 (val) +#else +# define le_bswap_32(val) (val) +#endif + + +/* Symbol table type. */ +struct arlib_symtab +{ + /* Symbol table handling. */ + struct obstack symsoffob; + struct obstack symsnameob; + size_t symsofflen; + uint32_t *symsoff; + size_t symsnamelen; + char *symsname; + + /* Long filename handling. */ + struct obstack longnamesob; + size_t longnameslen; + char *longnames; +}; + + +/* Global variable with symbol table. */ +extern struct arlib_symtab symtab; + + +/* Initialize ARLIB_SYMTAB structure. */ +extern void arlib_init (void); + +/* Finalize ARLIB_SYMTAB content. */ +extern void arlib_finalize (void); + +/* Free resources for ARLIB_SYMTAB. */ +extern void arlib_fini (void); + +/* Add symbols from ELF with value OFFSET to the symbol table SYMTAB. */ +extern void arlib_add_symbols (Elf *elf, const char *arfname, + const char *membername, off_t off); + +/* Add name a file offset of a symbol. */ +extern void arlib_add_symref (const char *symname, off_t symoff); + +/* Add long file name FILENAME of length FILENAMELEN to the symbol table + SYMTAB. Return the offset into the long file name table. */ +extern long int arlib_add_long_name (const char *filename, size_t filenamelen); + +#endif /* arlib.h */ diff --git a/src/arlib2.c b/src/arlib2.c new file mode 100644 index 00000000..7098fec1 --- /dev/null +++ b/src/arlib2.c @@ -0,0 +1,50 @@ +/* Functions to handle creation of Linux archives. + Copyright (C) 2007 Red Hat, Inc. + Written by Ulrich Drepper <drepper@redhat.com>, 2007. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <error.h> +#include <libintl.h> +#include <limits.h> +#include <string.h> +#include <sys/param.h> + +#include "arlib.h" + + +/* Add long file name FILENAME of length FILENAMELEN to the symbol table + SYMTAB. Return the offset into the long file name table. */ +long int +arlib_add_long_name (const char *filename, size_t filenamelen) +{ + size_t size = obstack_object_size (&symtab.longnamesob); + + obstack_grow (&symtab.longnamesob, filename, filenamelen); + obstack_grow (&symtab.longnamesob, "/\n", 2); + + return size - sizeof (struct ar_hdr); +} diff --git a/src/debugpred.h b/src/debugpred.h new file mode 100644 index 00000000..867f4ace --- /dev/null +++ b/src/debugpred.h @@ -0,0 +1,53 @@ +/* Support to debug branch prediction. + Copyright (C) 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + Written by Ulrich Drepper <drepper@redhat.com>, 2007. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#include <stdio.h> + +#if DEBUGPRED +extern const unsigned long int __start_predict_data; +extern const unsigned long int __stop_predict_data; +extern const unsigned long int __start_predict_line; +extern const char *__start_predict_file; + +static void +__attribute__ ((destructor)) +predprint (void) +{ + const unsigned long int *s = &__start_predict_data; + const unsigned long int *e = &__stop_predict_data; + const unsigned long int *sl = &__start_predict_line; + const char **sf = &__start_predict_file; + while (s < e) + { + if (s[0] != 0 || s[1] != 0) + printf ("%s:%lu: wrong=%lu, correct=%lu%s\n", *sf, *sl, s[0], s[1], + s[0] > s[1] ? " <==== WARNING" : ""); + ++sl; + ++sf; + s += 2; + } +} +#endif diff --git a/src/elf32-i386.script b/src/elf32-i386.script index d62333ac..a6cfffa1 100644 --- a/src/elf32-i386.script +++ b/src/elf32-i386.script @@ -18,7 +18,10 @@ SEGMENT [RX] #endif .interp; + .note.ABI-tag; + .note.gnu.build-id; .hash; + .gnu.hash; .dynsym; .dynstr; .gnu.version; @@ -47,6 +50,7 @@ SEGMENT [RX] *(.gnu.linkonce.r.*) } .rodata1; + .eh_frame_hdr; . = ALIGN(32 / 8); PROVIDE (__preinit_array_start = .); .preinit_array @@ -85,18 +89,24 @@ SEGMENT [RW] /* Adjust the address for the data segment. We want to adjust up to the same address within the page on the next page up. */ . = ALIGN(PAGESIZE) + (. & (PAGESIZE - 1)); - .data - { - *(.data) - *(.data.*) - *(.gnu.linkonce.d.*) - } - .data1; .eh_frame { KEEP (*(.eh_frame)) } .gcc_except_table; + .tdata + { + *(.tdata) + *(.tdata.*) + *(.gnu.linkone.td.*) + } + .tbss + { + *(.tbss) + *(.tbss.*) + *(.gnu.linkone.tb.*) + *(.tcommon) + } .ctors { /* gcc uses crtbegin.o to find the start of @@ -125,15 +135,19 @@ SEGMENT [RW] KEEP (*(.dtors)) } .jcr; - .got + .dynamic; + .got; + .got.plt; + .data { - *(.got.plt) - *(.got) + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) } - .dynamic; /* We want the small data sections together, so single-instruction offsets can access them all, and initialized data all before uninitialized, so we can shorten the on-disk segment size. */ + .data1; .sdata { *(.sdata) diff --git a/src/elfcmp.c b/src/elfcmp.c new file mode 100644 index 00000000..0e134df8 --- /dev/null +++ b/src/elfcmp.c @@ -0,0 +1,751 @@ +/* Compare relevant content of two ELF files. + Copyright (C) 2005, 2006, 2007, 2008 Red Hat, Inc. + This file is part of Red Hat elfutils. + Written by Ulrich Drepper <drepper@redhat.com>, 2005. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <argp.h> +#include <assert.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <locale.h> +#include <libintl.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "../libelf/elf-knowledge.h" +#include "../libebl/libeblP.h" + + +/* Prototypes of local functions. */ +static Elf *open_file (const char *fname, int *fdp, Ebl **eblp); +static bool search_for_copy_reloc (Ebl *ebl, size_t scnndx, int symndx); +static int regioncompare (const void *p1, const void *p2); + + +/* Name and version of program. */ +static void print_version (FILE *stream, struct argp_state *state); +void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; + +/* Bug report address. */ +const char *argp_program_bug_address = PACKAGE_BUGREPORT; + +/* Values for the parameters which have no short form. */ +#define OPT_GAPS 0x100 +#define OPT_HASH_INEXACT 0x101 + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + { NULL, 0, NULL, 0, N_("Control options:"), 0 }, + { "gaps", OPT_GAPS, "ACTION", 0, N_("Control treatment of gaps in loadable segments [ignore|match] (default: ignore)"), 0 }, + { "hash-inexact", OPT_HASH_INEXACT, NULL, 0, + N_("Ignore permutation of buckets in SHT_HASH section"), 0 }, + { "quiet", 'q', NULL, 0, N_("Output nothing; yield exit status only"), 0 }, + + { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 }, + { NULL, 0, NULL, 0, NULL, 0 } +}; + +/* Short description of program. */ +static const char doc[] = N_("\ +Compare relevant parts of two ELF files for equality."); + +/* Strings for arguments in help texts. */ +static const char args_doc[] = N_("FILE1 FILE2"); + +/* Prototype for option handler. */ +static error_t parse_opt (int key, char *arg, struct argp_state *state); + +/* Data structure to communicate with argp functions. */ +static struct argp argp = +{ + options, parse_opt, args_doc, doc, NULL, NULL, NULL +}; + + +/* How to treat gaps in loadable segments. */ +static enum + { + gaps_ignore = 0, + gaps_match + } + gaps; + +/* Structure to hold information about used regions. */ +struct region +{ + GElf_Addr from; + GElf_Addr to; + struct region *next; +}; + +/* Nonzero if only exit status is wanted. */ +static bool quiet; + +/* True iff SHT_HASH treatment should be generous. */ +static bool hash_inexact; + +static bool hash_content_equivalent (size_t entsize, Elf_Data *, Elf_Data *); + + +int +main (int argc, char *argv[]) +{ + /* Set locale. */ + (void) setlocale (LC_ALL, ""); + + /* Make sure the message catalog can be found. */ + (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR); + + /* Initialize the message catalog. */ + (void) textdomain (PACKAGE_TARNAME); + + /* Parse and process arguments. */ + int remaining; + (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL); + + /* We expect exactly two non-option parameters. */ + if (unlikely (remaining + 2 != argc)) + { + fputs (gettext ("Invalid number of parameters.\n"), stderr); + argp_help (&argp, stderr, ARGP_HELP_SEE, program_invocation_short_name); + exit (1); + } + + /* Comparing the files is done in two phases: + 1. compare all sections. Sections which are irrelevant (i.e., if + strip would remove them) are ignored. Some section types are + handled special. + 2. all parts of the loadable segments which are not parts of any + section is compared according to the rules of the --gaps option. + */ + int result = 0; + elf_version (EV_CURRENT); + + const char *const fname1 = argv[remaining]; + int fd1; + Ebl *ebl1; + Elf *elf1 = open_file (fname1, &fd1, &ebl1); + + const char *const fname2 = argv[remaining + 1]; + int fd2; + Ebl *ebl2; + Elf *elf2 = open_file (fname2, &fd2, &ebl2); + + GElf_Ehdr ehdr1_mem; + GElf_Ehdr *ehdr1 = gelf_getehdr (elf1, &ehdr1_mem); + if (ehdr1 == NULL) + error (EXIT_FAILURE, 0, gettext ("cannot get ELF header of '%s': %s"), + fname1, elf_errmsg (-1)); + GElf_Ehdr ehdr2_mem; + GElf_Ehdr *ehdr2 = gelf_getehdr (elf2, &ehdr2_mem); + if (ehdr2 == NULL) + error (EXIT_FAILURE, 0, gettext ("cannot get ELF header of '%s': %s"), + fname2, elf_errmsg (-1)); + + /* Compare the ELF headers. */ + if (unlikely (memcmp (ehdr1->e_ident, ehdr2->e_ident, EI_NIDENT) != 0 + || ehdr1->e_type != ehdr2->e_type + || ehdr1->e_machine != ehdr2->e_machine + || ehdr1->e_version != ehdr2->e_version + || ehdr1->e_entry != ehdr2->e_entry + || ehdr1->e_phoff != ehdr2->e_phoff + || ehdr1->e_flags != ehdr2->e_flags + || ehdr1->e_ehsize != ehdr2->e_ehsize + || ehdr1->e_phentsize != ehdr2->e_phentsize + || ehdr1->e_phnum != ehdr2->e_phnum + || ehdr1->e_shentsize != ehdr2->e_shentsize)) + { + if (! quiet) + error (0, 0, gettext ("%s %s diff: ELF header"), fname1, fname2); + result = 1; + goto out; + } + + /* Iterate over all sections. We expect the sections in the two + files to match exactly. */ + Elf_Scn *scn1 = NULL; + Elf_Scn *scn2 = NULL; + struct region *regions = NULL; + size_t nregions = 0; + while (1) + { + GElf_Shdr shdr1_mem; + GElf_Shdr *shdr1; + const char *sname1 = NULL; + do + { + scn1 = elf_nextscn (elf1, scn1); + shdr1 = gelf_getshdr (scn1, &shdr1_mem); + if (shdr1 != NULL) + sname1 = elf_strptr (elf1, ehdr1->e_shstrndx, shdr1->sh_name); + } + while (scn1 != NULL + && ebl_section_strip_p (ebl1, ehdr1, shdr1, sname1, true, false)); + + GElf_Shdr shdr2_mem; + GElf_Shdr *shdr2; + const char *sname2 = NULL; + do + { + scn2 = elf_nextscn (elf2, scn2); + shdr2 = gelf_getshdr (scn2, &shdr2_mem); + if (shdr2 != NULL) + sname2 = elf_strptr (elf2, ehdr2->e_shstrndx, shdr2->sh_name); + } + while (scn2 != NULL + && ebl_section_strip_p (ebl2, ehdr2, shdr2, sname2, true, false)); + + if (scn1 == NULL || scn2 == NULL) + break; + + if (gaps != gaps_ignore && (shdr1->sh_flags & SHF_ALLOC) != 0) + { + struct region *newp = (struct region *) alloca (sizeof (*newp)); + newp->from = shdr1->sh_offset; + newp->to = shdr1->sh_offset + shdr1->sh_size; + newp->next = regions; + regions = newp; + + ++nregions; + } + + /* Compare the headers. We allow the name to be at a different + location. */ + if (unlikely (strcmp (sname1, sname2) != 0)) + { + header_mismatch: + error (0, 0, gettext ("%s %s differ: section header"), + fname1, fname2); + result = 1; + goto out; + } + + /* We ignore certain sections. */ + if (strcmp (sname1, ".gnu_debuglink") == 0 + || strcmp (sname1, ".gnu.prelink_undo") == 0) + continue; + + if (shdr1->sh_type != shdr2->sh_type + // XXX Any flags which should be ignored? + || shdr1->sh_flags != shdr2->sh_flags + || shdr1->sh_addr != shdr2->sh_addr + || (shdr1->sh_offset != shdr2->sh_offset + && (shdr1->sh_flags & SHF_ALLOC) + && ehdr1->e_type != ET_REL) + || shdr1->sh_size != shdr2->sh_size + || shdr1->sh_link != shdr2->sh_link + || shdr1->sh_info != shdr2->sh_info + || shdr1->sh_addralign != shdr2->sh_addralign + || shdr1->sh_entsize != shdr2->sh_entsize) + goto header_mismatch; + + Elf_Data *data1 = elf_getdata (scn1, NULL); + if (data1 == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot get content of section %zu in '%s': %s"), + elf_ndxscn (scn1), fname1, elf_errmsg (-1)); + + Elf_Data *data2 = elf_getdata (scn2, NULL); + if (data2 == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot get content of section %zu in '%s': %s"), + elf_ndxscn (scn2), fname2, elf_errmsg (-1)); + + switch (shdr1->sh_type) + { + case SHT_DYNSYM: + case SHT_SYMTAB: + /* Iterate over the symbol table. We ignore the st_size + value of undefined symbols. */ + for (int ndx = 0; ndx < (int) (shdr1->sh_size / shdr1->sh_entsize); + ++ndx) + { + GElf_Sym sym1_mem; + GElf_Sym *sym1 = gelf_getsym (data1, ndx, &sym1_mem); + if (sym1 == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot get symbol in '%s': %s"), + fname1, elf_errmsg (-1)); + GElf_Sym sym2_mem; + GElf_Sym *sym2 = gelf_getsym (data2, ndx, &sym2_mem); + if (sym2 == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot get symbol in '%s': %s"), + fname2, elf_errmsg (-1)); + + const char *name1 = elf_strptr (elf1, shdr1->sh_link, + sym1->st_name); + const char *name2 = elf_strptr (elf2, shdr2->sh_link, + sym2->st_name); + if (unlikely (strcmp (name1, name2) != 0 + || sym1->st_value != sym2->st_value + || (sym1->st_size != sym2->st_size + && sym1->st_shndx != SHN_UNDEF) + || sym1->st_info != sym2->st_info + || sym1->st_other != sym2->st_other + || sym1->st_shndx != sym1->st_shndx)) + { + // XXX Do we want to allow reordered symbol tables? + symtab_mismatch: + if (! quiet) + { + if (elf_ndxscn (scn1) == elf_ndxscn (scn2)) + error (0, 0, + gettext ("%s %s differ: symbol table [%zu]"), + fname1, fname2, elf_ndxscn (scn1)); + else + error (0, 0, gettext ("\ +%s %s differ: symbol table [%zu,%zu]"), + fname1, fname2, elf_ndxscn (scn1), + elf_ndxscn (scn2)); + } + result = 1; + goto out; + } + + if (sym1->st_shndx == SHN_UNDEF + && sym1->st_size != sym2->st_size) + { + /* The size of the symbol in the object defining it + might have changed. That is OK unless the symbol + is used in a copy relocation. Look over the + sections in both files and determine which + relocation section uses this symbol table + section. Then look through the relocations to + see whether any copy relocation references this + symbol. */ + if (search_for_copy_reloc (ebl1, elf_ndxscn (scn1), ndx) + || search_for_copy_reloc (ebl2, elf_ndxscn (scn2), ndx)) + goto symtab_mismatch; + } + } + break; + + default: + /* Compare the section content byte for byte. */ + assert (shdr1->sh_type == SHT_NOBITS + || (data1->d_buf != NULL || data1->d_size == 0)); + assert (shdr2->sh_type == SHT_NOBITS + || (data2->d_buf != NULL || data1->d_size == 0)); + + if (unlikely (data1->d_size != data2->d_size + || (shdr1->sh_type != SHT_NOBITS + && memcmp (data1->d_buf, data2->d_buf, + data1->d_size) != 0))) + { + if (hash_inexact + && shdr1->sh_type == SHT_HASH + && data1->d_size == data2->d_size + && hash_content_equivalent (shdr1->sh_entsize, data1, data2)) + break; + + if (! quiet) + { + if (elf_ndxscn (scn1) == elf_ndxscn (scn2)) + error (0, 0, gettext ("\ +%s %s differ: section [%zu] '%s' content"), + fname1, fname2, elf_ndxscn (scn1), sname1); + else + error (0, 0, gettext ("\ +%s %s differ: section [%zu,%zu] '%s' content"), + fname1, fname2, elf_ndxscn (scn1), + elf_ndxscn (scn2), sname1); + } + result = 1; + goto out; + } + break; + } + } + + if (unlikely (scn1 != scn2)) + { + if (! quiet) + error (0, 0, + gettext ("%s %s differ: unequal amount of important sections"), + fname1, fname2); + result = 1; + goto out; + } + + /* We we look at gaps, create artificial ones for the parts of the + program which we are not in sections. */ + struct region ehdr_region; + struct region phdr_region; + if (gaps != gaps_ignore) + { + ehdr_region.from = 0; + ehdr_region.to = ehdr1->e_ehsize; + ehdr_region.next = &phdr_region; + + phdr_region.from = ehdr1->e_phoff; + phdr_region.to = ehdr1->e_phoff + ehdr1->e_phnum * ehdr1->e_phentsize; + phdr_region.next = regions; + + regions = &ehdr_region; + nregions += 2; + } + + /* If we need to look at the gaps we need access to the file data. */ + char *raw1 = NULL; + size_t size1 = 0; + char *raw2 = NULL; + size_t size2 = 0; + struct region *regionsarr = alloca (nregions * sizeof (struct region)); + if (gaps != gaps_ignore) + { + raw1 = elf_rawfile (elf1, &size1); + if (raw1 == NULL ) + error (EXIT_FAILURE, 0, gettext ("cannot load data of '%s': %s"), + fname1, elf_errmsg (-1)); + + raw2 = elf_rawfile (elf2, &size2); + if (raw2 == NULL ) + error (EXIT_FAILURE, 0, gettext ("cannot load data of '%s': %s"), + fname2, elf_errmsg (-1)); + + for (size_t cnt = 0; cnt < nregions; ++cnt) + { + regionsarr[cnt] = *regions; + regions = regions->next; + } + + qsort (regionsarr, nregions, sizeof (regionsarr[0]), regioncompare); + } + + /* Compare the program header tables. */ + for (int ndx = 0; ndx < ehdr1->e_phnum; ++ndx) + { + GElf_Phdr phdr1_mem; + GElf_Phdr *phdr1 = gelf_getphdr (elf1, ndx, &phdr1_mem); + if (ehdr1 == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot get program header entry %d of '%s': %s"), + ndx, fname1, elf_errmsg (-1)); + GElf_Phdr phdr2_mem; + GElf_Phdr *phdr2 = gelf_getphdr (elf2, ndx, &phdr2_mem); + if (ehdr2 == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot get program header entry %d of '%s': %s"), + ndx, fname2, elf_errmsg (-1)); + + if (unlikely (memcmp (phdr1, phdr2, sizeof (GElf_Phdr)) != 0)) + { + if (! quiet) + error (0, 0, gettext ("%s %s differ: program header %d"), + fname1, fname2, ndx); + result = 1; + goto out; + } + + if (gaps != gaps_ignore && phdr1->p_type == PT_LOAD) + { + size_t cnt = 0; + while (cnt < nregions && regionsarr[cnt].to < phdr1->p_offset) + ++cnt; + + GElf_Off last = phdr1->p_offset; + GElf_Off end = phdr1->p_offset + phdr1->p_filesz; + while (cnt < nregions && regionsarr[cnt].from < end) + { + if (last < regionsarr[cnt].from) + { + /* Compare the [LAST,FROM) region. */ + assert (gaps == gaps_match); + if (unlikely (memcmp (raw1 + last, raw2 + last, + regionsarr[cnt].from - last) != 0)) + { + gapmismatch: + if (!quiet) + error (0, 0, gettext ("%s %s differ: gap"), + fname1, fname2); + result = 1; + goto out; + } + + } + last = regionsarr[cnt].to; + ++cnt; + } + + if (cnt == nregions && last < end) + goto gapmismatch; + } + } + + out: + elf_end (elf1); + elf_end (elf2); + close (fd1); + close (fd2); + + return result; +} + + +/* Print the version information. */ +static void +print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) +{ + fprintf (stream, "elfcmp (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION); + fprintf (stream, gettext ("\ +Copyright (C) %s Red Hat, Inc.\n\ +This is free software; see the source for copying conditions. There is NO\n\ +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ +"), "2008"); + fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); +} + + +/* Handle program arguments. */ +static error_t +parse_opt (int key, char *arg, + struct argp_state *state __attribute__ ((unused))) +{ + switch (key) + { + case 'q': + quiet = true; + break; + + case OPT_GAPS: + if (strcasecmp (arg, "ignore") == 0) + gaps = gaps_ignore; + else if (likely (strcasecmp (arg, "match") == 0)) + gaps = gaps_match; + else + { + fprintf (stderr, + gettext ("Invalid value '%s' for --gaps parameter."), + arg); + argp_help (&argp, stderr, ARGP_HELP_SEE, + program_invocation_short_name); + exit (1); + } + break; + + case OPT_HASH_INEXACT: + hash_inexact = true; + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + + +static Elf * +open_file (const char *fname, int *fdp, Ebl **eblp) +{ + int fd = open (fname, O_RDONLY); + if (unlikely (fd == -1)) + error (EXIT_FAILURE, errno, gettext ("cannot open '%s'"), fname); + Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL); + if (elf == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create ELF descriptor for '%s': %s"), + fname, elf_errmsg (-1)); + Ebl *ebl = ebl_openbackend (elf); + if (ebl == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create EBL descriptor for '%s'"), fname); + + *fdp = fd; + *eblp = ebl; + return elf; +} + + +static bool +search_for_copy_reloc (Ebl *ebl, size_t scnndx, int symndx) +{ + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header of section %zu: %s"), + elf_ndxscn (scn), elf_errmsg (-1)); + + if ((shdr->sh_type != SHT_REL && shdr->sh_type != SHT_RELA) + || shdr->sh_link != scnndx) + continue; + + Elf_Data *data = elf_getdata (scn, NULL); + if (data == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot get content of section %zu: %s"), + elf_ndxscn (scn), elf_errmsg (-1)); + + if (shdr->sh_type == SHT_REL) + for (int ndx = 0; ndx < (int) (shdr->sh_size / shdr->sh_entsize); + ++ndx) + { + GElf_Rel rel_mem; + GElf_Rel *rel = gelf_getrel (data, ndx, &rel_mem); + if (rel == NULL) + error (EXIT_FAILURE, 0, gettext ("cannot get relocation: %s"), + elf_errmsg (-1)); + + if ((int) GELF_R_SYM (rel->r_info) == symndx + && ebl_copy_reloc_p (ebl, GELF_R_TYPE (rel->r_info))) + return true; + } + else + for (int ndx = 0; ndx < (int) (shdr->sh_size / shdr->sh_entsize); + ++ndx) + { + GElf_Rela rela_mem; + GElf_Rela *rela = gelf_getrela (data, ndx, &rela_mem); + if (rela == NULL) + error (EXIT_FAILURE, 0, gettext ("cannot get relocation: %s"), + elf_errmsg (-1)); + + if ((int) GELF_R_SYM (rela->r_info) == symndx + && ebl_copy_reloc_p (ebl, GELF_R_TYPE (rela->r_info))) + return true; + } + } + + return false; +} + + +static int +regioncompare (const void *p1, const void *p2) +{ + const struct region *r1 = (const struct region *) p1; + const struct region *r2 = (const struct region *) p2; + + if (r1->from < r2->from) + return -1; + return 1; +} + + +static int +compare_Elf32_Word (const void *p1, const void *p2) +{ + const Elf32_Word *w1 = p1; + const Elf32_Word *w2 = p2; + assert (sizeof (int) >= sizeof (*w1)); + return (int) *w1 - (int) *w2; +} + +static int +compare_Elf64_Xword (const void *p1, const void *p2) +{ + const Elf64_Xword *w1 = p1; + const Elf64_Xword *w2 = p2; + return *w1 < *w2 ? -1 : *w1 > *w2 ? 1 : 0; +} + +static bool +hash_content_equivalent (size_t entsize, Elf_Data *data1, Elf_Data *data2) +{ +#define CHECK_HASH(Hash_Word) \ + { \ + const Hash_Word *const hash1 = data1->d_buf; \ + const Hash_Word *const hash2 = data2->d_buf; \ + const size_t nbucket = hash1[0]; \ + const size_t nchain = hash1[1]; \ + if (data1->d_size != (2 + nbucket + nchain) * sizeof hash1[0] \ + || hash2[0] != nbucket || hash2[1] != nchain) \ + return false; \ + \ + const Hash_Word *const bucket1 = &hash1[2]; \ + const Hash_Word *const chain1 = &bucket1[nbucket]; \ + const Hash_Word *const bucket2 = &hash2[2]; \ + const Hash_Word *const chain2 = &bucket2[nbucket]; \ + \ + bool chain_ok[nchain]; \ + Hash_Word temp1[nchain - 1]; \ + Hash_Word temp2[nchain - 1]; \ + memset (chain_ok, 0, sizeof chain_ok); \ + for (size_t i = 0; i < nbucket; ++i) \ + { \ + if (bucket1[i] >= nchain || bucket2[i] >= nchain) \ + return false; \ + \ + size_t b1 = 0; \ + for (size_t p = bucket1[i]; p != STN_UNDEF; p = chain1[p]) \ + if (p >= nchain || b1 >= nchain - 1) \ + return false; \ + else \ + temp1[b1++] = p; \ + \ + size_t b2 = 0; \ + for (size_t p = bucket2[i]; p != STN_UNDEF; p = chain2[p]) \ + if (p >= nchain || b2 >= nchain - 1) \ + return false; \ + else \ + temp2[b2++] = p; \ + \ + if (b1 != b2) \ + return false; \ + \ + qsort (temp1, b1, sizeof temp1[0], compare_##Hash_Word); \ + qsort (temp2, b2, sizeof temp2[0], compare_##Hash_Word); \ + \ + for (b1 = 0; b1 < b2; ++b1) \ + if (temp1[b1] != temp2[b1]) \ + return false; \ + else \ + chain_ok[temp1[b1]] = true; \ + } \ + \ + for (size_t i = 0; i < nchain; ++i) \ + if (!chain_ok[i] && chain1[i] != chain2[i]) \ + return false; \ + \ + return true; \ + } + + switch (entsize) + { + case 4: + CHECK_HASH (Elf32_Word); + break; + case 8: + CHECK_HASH (Elf64_Xword); + break; + } + + return false; +} + + +#include "debugpred.h" diff --git a/src/elflint.c b/src/elflint.c index d66cdd13..bc4219b3 100644 --- a/src/elflint.c +++ b/src/elflint.c @@ -1,16 +1,28 @@ /* Pedantic checking of ELF files compliance with gABI/psABI spec. - Copyright (C) 2001, 2002, 2003, 2004 Red Hat, Inc. + Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Red Hat, Inc. + This file is part of Red Hat elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 2001. - This program is Open Source software; you can redistribute it and/or - modify it under the terms of the Open Software License version 1.0 as - published by the Open Source Initiative. + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. - You should have received a copy of the Open Software License along - with this program; if not, you may obtain a copy of the Open Software - License version 1.0 from http://www.opensource.org/licenses/osl.php or - by writing the Open Source Initiative c/o Lawrence Rosen, Esq., - 3001 King Ranch Road, Ukiah, CA 95482. */ + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ #ifdef HAVE_CONFIG_H # include <config.h> @@ -24,7 +36,6 @@ #include <fcntl.h> #include <gelf.h> #include <inttypes.h> -#include <libebl.h> #include <libintl.h> #include <locale.h> #include <stdbool.h> @@ -35,12 +46,20 @@ #include <elf-knowledge.h> #include <system.h> +#include "../libelf/libelfP.h" +#include "../libelf/common.h" +#include "../libebl/libeblP.h" +#include "../libdw/libdwP.h" +#include "../libdwfl/libdwflP.h" +#include "../libdw/memory-access.h" /* Name and version of program. */ static void print_version (FILE *stream, struct argp_state *state); void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; +/* Bug report address. */ +const char *argp_program_bug_address = PACKAGE_BUGREPORT; #define ARGP_strict 300 #define ARGP_gnuld 301 @@ -50,12 +69,13 @@ static const struct argp_option options[] = { { "strict", ARGP_strict, NULL, 0, - N_("Be extremely strict, flag level 2 features.") }, - { "quiet", 'q', NULL, 0, N_("Do not print anything if successful") }, + N_("Be extremely strict, flag level 2 features."), 0 }, + { "quiet", 'q', NULL, 0, N_("Do not print anything if successful"), 0 }, + { "debuginfo", 'd', NULL, 0, N_("Binary is a separate debuginfo file"), 0 }, { "gnu-ld", ARGP_gnuld, NULL, 0, N_("Binary has been created with GNU ld and is therefore known to be \ -broken in certain ways") }, - { NULL, 0, NULL, 0, NULL } +broken in certain ways"), 0 }, + { NULL, 0, NULL, 0, NULL, 0 } }; /* Short description of program. */ @@ -68,13 +88,10 @@ static const char args_doc[] = N_("FILE..."); /* Prototype for option handler. */ static error_t parse_opt (int key, char *arg, struct argp_state *state); -/* Function to print some extra text in the help message. */ -static char *more_help (int key, const char *text, void *input); - /* Data structure to communicate with argp functions. */ static struct argp argp = { - options, parse_opt, args_doc, doc, NULL, more_help + options, parse_opt, args_doc, doc, NULL, NULL, NULL }; @@ -84,6 +101,9 @@ static void process_file (int fd, Elf *elf, const char *prefix, bool only_one); static void process_elf_file (Elf *elf, const char *prefix, const char *suffix, const char *fname, size_t size, bool only_one); +static void check_note_section (Ebl *ebl, GElf_Ehdr *ehdr, + GElf_Shdr *shdr, int idx); + /* Report an error. */ #define ERROR(str, args...) \ @@ -91,7 +111,7 @@ static void process_elf_file (Elf *elf, const char *prefix, const char *suffix, printf (str, ##args); \ ++error_count; \ } while (0) -static int error_count; +static unsigned int error_count; /* True if we should perform very strict testing. */ static bool be_strict; @@ -99,6 +119,9 @@ static bool be_strict; /* True if no message is to be printed if the run is succesful. */ static bool be_quiet; +/* True if binary is from strip -f, not a normal ELF file. */ +static bool is_debuginfo; + /* True if binary is assumed to be generated with GNU ld. */ static bool gnuld; @@ -112,38 +135,25 @@ static int *scnref; int main (int argc, char *argv[]) { - int remaining; - bool only_one; - /* Set locale. */ setlocale (LC_ALL, ""); /* Initialize the message catalog. */ - textdomain (PACKAGE); + textdomain (PACKAGE_TARNAME); /* Parse and process arguments. */ + int remaining; argp_parse (&argp, argc, argv, 0, &remaining, NULL); - /* If no ELF file is given punt. */ - if (remaining >= argc) - { - argp_help (&argp, stdout, ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR, - program_invocation_short_name); - exit (1); - } - /* Before we start tell the ELF library which version we are using. */ elf_version (EV_CURRENT); /* Now process all the files given at the command line. */ - only_one = remaining + 1 == argc; + bool only_one = remaining + 1 == argc; do { - int fd; - Elf *elf; - /* Open the file. */ - fd = open (argv[remaining], O_RDONLY); + int fd = open (argv[remaining], O_RDONLY); if (fd == -1) { error (0, errno, gettext ("cannot open input file")); @@ -151,7 +161,7 @@ main (int argc, char *argv[]) } /* Create an `Elf' descriptor. */ - elf = elf_begin (fd, ELF_C_READ_MMAP, NULL); + Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL); if (elf == NULL) ERROR (gettext ("cannot generate Elf descriptor: %s\n"), elf_errmsg (-1)); @@ -189,7 +199,8 @@ main (int argc, char *argv[]) /* Handle program arguments. */ static error_t -parse_opt (int key, char *arg, struct argp_state *state) +parse_opt (int key, char *arg __attribute__ ((unused)), + struct argp_state *state __attribute__ ((unused))) { switch (key) { @@ -201,10 +212,19 @@ parse_opt (int key, char *arg, struct argp_state *state) be_quiet = true; break; + case 'd': + is_debuginfo = true; + case ARGP_gnuld: gnuld = true; break; + case ARGP_KEY_NO_ARGS: + fputs (gettext ("Missing file name.\n"), stderr); + argp_help (&argp, stderr, ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR, + program_invocation_short_name); + exit (1); + default: return ARGP_ERR_UNKNOWN; } @@ -212,37 +232,16 @@ parse_opt (int key, char *arg, struct argp_state *state) } -static char * -more_help (int key, const char *text, void *input) -{ - char *buf; - - switch (key) - { - case ARGP_KEY_HELP_EXTRA: - /* We print some extra information. */ - if (asprintf (&buf, gettext ("Please report bugs to %s.\n"), - PACKAGE_BUGREPORT) < 0) - buf = NULL; - return buf; - - default: - break; - } - return (char *) text; -} - - /* Print the version information. */ static void -print_version (FILE *stream, struct argp_state *state) +print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) { - fprintf (stream, "elflint (%s) %s\n", PACKAGE_NAME, VERSION); + fprintf (stream, "elflint (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION); fprintf (stream, gettext ("\ Copyright (C) %s Red Hat, Inc.\n\ This is free software; see the source for copying conditions. There is NO\n\ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ -"), "2004"); +"), "2008"); fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); } @@ -310,14 +309,14 @@ process_file (int fd, Elf *elf, const char *prefix, const char *suffix, default: /* We cannot do anything. */ ERROR (gettext ("\ -Not an ELF file - it has the wrong magic bytes at the start")); +Not an ELF file - it has the wrong magic bytes at the start\n")); break; } } static const char * -section_name (Ebl *ebl, GElf_Ehdr *ehdr, int idx) +section_name (Ebl *ebl, int idx) { GElf_Shdr shdr_mem; GElf_Shdr *shdr; @@ -340,7 +339,7 @@ static const int valid_e_machine[] = EM_68HC16, EM_68HC11, EM_68HC08, EM_68HC05, EM_SVX, EM_ST19, EM_VAX, EM_CRIS, EM_JAVELIN, EM_FIREPATH, EM_ZSP, EM_MMIX, EM_HUANY, EM_PRISM, EM_AVR, EM_FR30, EM_D10V, EM_D30V, EM_V850, EM_M32R, EM_MN10300, - EM_MN10200, EM_PJ, EM_OPENRISC, EM_ARC_A5, EM_XTENSA + EM_MN10200, EM_PJ, EM_OPENRISC, EM_ARC_A5, EM_XTENSA, EM_ALPHA }; #define nvalid_e_machine \ (sizeof (valid_e_machine) / sizeof (valid_e_machine[0])) @@ -382,7 +381,7 @@ check_elf_header (Ebl *ebl, GElf_Ehdr *ehdr, size_t size) /* We currently don't handle any OS ABIs. */ if (ehdr->e_ident[EI_OSABI] != ELFOSABI_NONE) - ERROR (gettext ("unsupported OS ABI e_ident[%d] == \"%s\"\n"), + ERROR (gettext ("unsupported OS ABI e_ident[%d] == '%s'\n"), EI_OSABI, ebl_osabi_name (ebl, ehdr->e_ident[EI_OSABI], buf, sizeof (buf))); @@ -441,9 +440,7 @@ executables and DSOs cannot have zero program header offset\n")); /* Get the header of the zeroth section. The sh_size field might contain the section number. */ GElf_Shdr shdr_mem; - GElf_Shdr *shdr; - - shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem); + GElf_Shdr *shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem); if (shdr != NULL) { /* The error will be reported later. */ @@ -460,17 +457,9 @@ invalid number of section header table entries\n")); /* Get the header of the zeroth section. The sh_size field might contain the section number. */ GElf_Shdr shdr_mem; - GElf_Shdr *shdr; - - shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem); - if (shdr != NULL) - { - /* The error will be reported later. */ - if (shdr->sh_link >= shnum) - ERROR (gettext ("invalid section header index\n")); - else - shstrndx = shdr->sh_link; - } + GElf_Shdr *shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem); + if (shdr != NULL && shdr->sh_link < shnum) + shstrndx = shdr->sh_link; } else if (shstrndx >= shnum) ERROR (gettext ("invalid section header index\n")); @@ -522,7 +511,7 @@ invalid number of section header table entries\n")); /* Check that there is a section group section with index < IDX which contains section IDX and that there is exactly one. */ static void -check_scn_group (Ebl *ebl, GElf_Ehdr *ehdr, int idx) +check_scn_group (Ebl *ebl, int idx) { if (scnref[idx] == 0) { @@ -532,15 +521,9 @@ check_scn_group (Ebl *ebl, GElf_Ehdr *ehdr, int idx) for (cnt = idx + 1; cnt < shnum; ++cnt) { - Elf_Scn *scn; + Elf_Scn *scn = elf_getscn (ebl->elf, cnt); GElf_Shdr shdr_mem; - GElf_Shdr *shdr; - Elf_Data *data; - Elf32_Word *grpdata; - size_t inner; - - scn = elf_getscn (ebl->elf, cnt); - shdr = gelf_getshdr (scn, &shdr_mem); + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); if (shdr == NULL) /* We cannot get the section header so we cannot check it. The error to get the section header will be shown @@ -550,13 +533,14 @@ check_scn_group (Ebl *ebl, GElf_Ehdr *ehdr, int idx) if (shdr->sh_type != SHT_GROUP) continue; - data = elf_getdata (scn, NULL); + Elf_Data *data = elf_getdata (scn, NULL); if (data == NULL || data->d_size < sizeof (Elf32_Word)) /* Cannot check the section. */ continue; - grpdata = (Elf32_Word *) data->d_buf; - for (inner = 1; inner < data->d_size / sizeof (Elf32_Word); ++inner) + Elf32_Word *grpdata = (Elf32_Word *) data->d_buf; + for (size_t inner = 1; inner < data->d_size / sizeof (Elf32_Word); + ++inner) if (grpdata[inner] == (Elf32_Word) idx) goto out; } @@ -565,74 +549,74 @@ check_scn_group (Ebl *ebl, GElf_Ehdr *ehdr, int idx) if (cnt == shnum) ERROR (gettext ("\ section [%2d] '%s': section with SHF_GROUP flag set not part of a section group\n"), - idx, section_name (ebl, ehdr, idx)); + idx, section_name (ebl, idx)); else ERROR (gettext ("\ section [%2d] '%s': section group [%2zu] '%s' does not preceed group member\n"), - idx, section_name (ebl, ehdr, idx), - cnt, section_name (ebl, ehdr, cnt)); + idx, section_name (ebl, idx), + cnt, section_name (ebl, cnt)); } } static void -check_symtab (Ebl *ebl, GElf_Ehdr *ehdr, int idx) +check_symtab (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) { bool no_xndx_warned = false; int no_pt_tls = 0; - - Elf_Scn *scn = elf_getscn (ebl->elf, idx); - GElf_Shdr shdr_mem; - GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); - GElf_Shdr strshdr_mem; - GElf_Shdr *strshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), - &strshdr_mem); - if (shdr == NULL || strshdr == NULL) - return; - Elf_Data *data = elf_getdata (scn, NULL); + Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL); if (data == NULL) { ERROR (gettext ("section [%2d] '%s': cannot get section data\n"), - idx, section_name (ebl, ehdr, idx)); + idx, section_name (ebl, idx)); return; } + GElf_Shdr strshdr_mem; + GElf_Shdr *strshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), + &strshdr_mem); + if (strshdr == NULL) + return; + if (strshdr->sh_type != SHT_STRTAB) - ERROR (gettext ("section [%2d] '%s': referenced as string table for section [%2d] '%s' but type is not SHT_STRTAB\n"), - shdr->sh_link, section_name (ebl, ehdr, shdr->sh_link), - idx, section_name (ebl, ehdr, idx)); + { + ERROR (gettext ("section [%2d] '%s': referenced as string table for section [%2d] '%s' but type is not SHT_STRTAB\n"), + shdr->sh_link, section_name (ebl, shdr->sh_link), + idx, section_name (ebl, idx)); + strshdr = NULL; + } /* Search for an extended section index table section. */ - size_t cnt; - GElf_Shdr xndxshdr_mem; - GElf_Shdr *xndxshdr = NULL; Elf_Data *xndxdata = NULL; Elf32_Word xndxscnidx = 0; - for (cnt = 1; cnt < shnum; ++cnt) + bool found_xndx = false; + for (size_t cnt = 1; cnt < shnum; ++cnt) if (cnt != (size_t) idx) { Elf_Scn *xndxscn = elf_getscn (ebl->elf, cnt); - xndxshdr = gelf_getshdr (xndxscn, &xndxshdr_mem); - xndxdata = elf_getdata (xndxscn, NULL); - xndxscnidx = elf_ndxscn (xndxscn); - - if (xndxshdr == NULL || xndxdata == NULL) + GElf_Shdr xndxshdr_mem; + GElf_Shdr *xndxshdr = gelf_getshdr (xndxscn, &xndxshdr_mem); + if (xndxshdr == NULL) continue; if (xndxshdr->sh_type == SHT_SYMTAB_SHNDX && xndxshdr->sh_link == (GElf_Word) idx) - break; + { + if (found_xndx) + ERROR (gettext ("\ +section [%2d] '%s': symbol table cannot have more than one extended index section\n"), + idx, section_name (ebl, idx)); + + xndxdata = elf_getdata (xndxscn, NULL); + xndxscnidx = elf_ndxscn (xndxscn); + found_xndx = true; + } } - if (cnt == shnum) - { - xndxshdr = NULL; - xndxdata = NULL; - } if (shdr->sh_entsize != gelf_fsize (ebl->elf, ELF_T_SYM, 1, EV_CURRENT)) ERROR (gettext ("\ -section [%2zu] '%s': entry size is does not match ElfXX_Sym\n"), - cnt, section_name (ebl, ehdr, cnt)); +section [%2u] '%s': entry size is does not match ElfXX_Sym\n"), + idx, section_name (ebl, idx)); /* Test the zeroth entry. */ GElf_Sym sym_mem; @@ -640,48 +624,50 @@ section [%2zu] '%s': entry size is does not match ElfXX_Sym\n"), GElf_Sym *sym = gelf_getsymshndx (data, xndxdata, 0, &sym_mem, &xndx); if (sym == NULL) ERROR (gettext ("section [%2d] '%s': cannot get symbol %d: %s\n"), - idx, section_name (ebl, ehdr, idx), 0, elf_errmsg (-1)); + idx, section_name (ebl, idx), 0, elf_errmsg (-1)); else { if (sym->st_name != 0) ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"), - idx, section_name (ebl, ehdr, idx), "st_name"); + idx, section_name (ebl, idx), "st_name"); if (sym->st_value != 0) ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"), - idx, section_name (ebl, ehdr, idx), "st_value"); + idx, section_name (ebl, idx), "st_value"); if (sym->st_size != 0) ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"), - idx, section_name (ebl, ehdr, idx), "st_size"); + idx, section_name (ebl, idx), "st_size"); if (sym->st_info != 0) ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"), - idx, section_name (ebl, ehdr, idx), "st_info"); + idx, section_name (ebl, idx), "st_info"); if (sym->st_other != 0) ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"), - idx, section_name (ebl, ehdr, idx), "st_other"); + idx, section_name (ebl, idx), "st_other"); if (sym->st_shndx != 0) ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"), - idx, section_name (ebl, ehdr, idx), "st_shndx"); + idx, section_name (ebl, idx), "st_shndx"); if (xndxdata != NULL && xndx != 0) ERROR (gettext ("\ section [%2d] '%s': XINDEX for zeroth entry not zero\n"), - xndxscnidx, section_name (ebl, ehdr, xndxscnidx)); + xndxscnidx, section_name (ebl, xndxscnidx)); } - for (cnt = 1; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt) + for (size_t cnt = 1; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt) { sym = gelf_getsymshndx (data, xndxdata, cnt, &sym_mem, &xndx); if (sym == NULL) { ERROR (gettext ("section [%2d] '%s': cannot get symbol %zu: %s\n"), - idx, section_name (ebl, ehdr, idx), cnt, elf_errmsg (-1)); + idx, section_name (ebl, idx), cnt, elf_errmsg (-1)); continue; } const char *name = NULL; - if (sym->st_name >= strshdr->sh_size) + if (strshdr == NULL) + name = ""; + else if (sym->st_name >= strshdr->sh_size) ERROR (gettext ("\ section [%2d] '%s': symbol %zu: invalid name value\n"), - idx, section_name (ebl, ehdr, idx), cnt); + idx, section_name (ebl, idx), cnt); else { name = elf_strptr (ebl->elf, shdr->sh_link, sym->st_name); @@ -694,13 +680,13 @@ section [%2d] '%s': symbol %zu: invalid name value\n"), { ERROR (gettext ("\ section [%2d] '%s': symbol %zu: too large section index but no extended section index section\n"), - idx, section_name (ebl, ehdr, idx), cnt); + idx, section_name (ebl, idx), cnt); no_xndx_warned = true; } else if (xndx < SHN_LORESERVE) ERROR (gettext ("\ section [%2d] '%s': symbol %zu: XINDEX used for index which would fit in st_shndx (%" PRIu32 ")\n"), - xndxscnidx, section_name (ebl, ehdr, xndxscnidx), cnt, + xndxscnidx, section_name (ebl, xndxscnidx), cnt, xndx); } else if ((sym->st_shndx >= SHN_LORESERVE @@ -712,18 +698,19 @@ section [%2d] '%s': symbol %zu: XINDEX used for index which would fit in st_shnd /* || sym->st_shndx > SHN_HIRESERVE always false */))) ERROR (gettext ("\ section [%2d] '%s': symbol %zu: invalid section index\n"), - idx, section_name (ebl, ehdr, idx), cnt); + idx, section_name (ebl, idx), cnt); else xndx = sym->st_shndx; - if (GELF_ST_TYPE (sym->st_info) >= STT_NUM) + if (GELF_ST_TYPE (sym->st_info) >= STT_NUM + && !ebl_symbol_type_name (ebl, GELF_ST_TYPE (sym->st_info), NULL, 0)) ERROR (gettext ("section [%2d] '%s': symbol %zu: unknown type\n"), - idx, section_name (ebl, ehdr, idx), cnt); + idx, section_name (ebl, idx), cnt); if (GELF_ST_BIND (sym->st_info) >= STB_NUM) ERROR (gettext ("\ section [%2d] '%s': symbol %zu: unknown symbol binding\n"), - idx, section_name (ebl, ehdr, idx), cnt); + idx, section_name (ebl, idx), cnt); if (xndx == SHN_COMMON) { @@ -731,15 +718,15 @@ section [%2d] '%s': symbol %zu: unknown symbol binding\n"), if (ehdr->e_type != ET_REL) ERROR (gettext ("\ section [%2d] '%s': symbol %zu: COMMON only allowed in relocatable files\n"), - idx, section_name (ebl, ehdr, idx), cnt); + idx, section_name (ebl, idx), cnt); if (cnt < shdr->sh_info) ERROR (gettext ("\ section [%2d] '%s': symbol %zu: local COMMON symbols are nonsense\n"), - idx, section_name (ebl, ehdr, idx), cnt); + idx, section_name (ebl, idx), cnt); if (GELF_R_TYPE (sym->st_info) == STT_FUNC) ERROR (gettext ("\ section [%2d] '%s': symbol %zu: function in COMMON section is nonsense\n"), - idx, section_name (ebl, ehdr, idx), cnt); + idx, section_name (ebl, idx), cnt); } else if (xndx > 0 && xndx < shnum) { @@ -749,26 +736,45 @@ section [%2d] '%s': symbol %zu: function in COMMON section is nonsense\n"), destshdr = gelf_getshdr (elf_getscn (ebl->elf, xndx), &destshdr_mem); if (destshdr != NULL) { + GElf_Addr sh_addr = (ehdr->e_type == ET_REL ? 0 + : destshdr->sh_addr); if (GELF_ST_TYPE (sym->st_info) != STT_TLS) { - if ((sym->st_value - destshdr->sh_addr) > destshdr->sh_size) - ERROR (gettext ("\ + if (! ebl_check_special_symbol (ebl, ehdr, sym, name, + destshdr)) + { + if (sym->st_value - sh_addr > destshdr->sh_size) + { + /* GNU ld has severe bugs. When it decides to remove + empty sections it leaves symbols referencing them + behind. These are symbols in .symtab. */ + if (!gnuld + || strcmp (section_name (ebl, idx), ".symtab") + || (strcmp (name, "__preinit_array_start") != 0 + && strcmp (name, "__preinit_array_end") != 0 + && strcmp (name, "__init_array_start") != 0 + && strcmp (name, "__init_array_end") != 0 + && strcmp (name, "__fini_array_start") != 0 + && strcmp (name, "__fini_array_end") != 0)) + ERROR (gettext ("\ section [%2d] '%s': symbol %zu: st_value out of bounds\n"), - idx, section_name (ebl, ehdr, idx), cnt); - else if ((sym->st_value - destshdr->sh_addr + sym->st_size) - > destshdr->sh_size) - ERROR (gettext ("\ + idx, section_name (ebl, idx), cnt); + } + else if ((sym->st_value - sh_addr + + sym->st_size) > destshdr->sh_size) + ERROR (gettext ("\ section [%2d] '%s': symbol %zu does not fit completely in referenced section [%2d] '%s'\n"), - idx, section_name (ebl, ehdr, idx), cnt, - (int) xndx, section_name (ebl, ehdr, xndx)); + idx, section_name (ebl, idx), cnt, + (int) xndx, section_name (ebl, xndx)); + } } else { if ((destshdr->sh_flags & SHF_TLS) == 0) ERROR (gettext ("\ section [%2d] '%s': symbol %zu: referenced section [%2d] '%s' does not have SHF_TLS flag set\n"), - idx, section_name (ebl, ehdr, idx), cnt, - (int) xndx, section_name (ebl, ehdr, xndx)); + idx, section_name (ebl, idx), cnt, + (int) xndx, section_name (ebl, xndx)); if (ehdr->e_type == ET_REL) { @@ -777,14 +783,14 @@ section [%2d] '%s': symbol %zu: referenced section [%2d] '%s' does not have SHF_ if (sym->st_value > destshdr->sh_size) ERROR (gettext ("\ section [%2d] '%s': symbol %zu: st_value out of bounds of referenced section [%2d] '%s'\n"), - idx, section_name (ebl, ehdr, idx), cnt, - (int) xndx, section_name (ebl, ehdr, xndx)); + idx, section_name (ebl, idx), cnt, + (int) xndx, section_name (ebl, xndx)); else if (sym->st_value + sym->st_size > destshdr->sh_size) ERROR (gettext ("\ section [%2d] '%s': symbol %zu does not fit completely in referenced section [%2d] '%s'\n"), - idx, section_name (ebl, ehdr, idx), cnt, - (int) xndx, section_name (ebl, ehdr, xndx)); + idx, section_name (ebl, idx), cnt, + (int) xndx, section_name (ebl, xndx)); } else { @@ -804,7 +810,7 @@ section [%2d] '%s': symbol %zu does not fit completely in referenced section [%2 if (no_pt_tls++ == 0) ERROR (gettext ("\ section [%2d] '%s': symbol %zu: TLS symbol but no TLS program header entry\n"), - idx, section_name (ebl, ehdr, idx), cnt); + idx, section_name (ebl, idx), cnt); } else { @@ -812,22 +818,22 @@ section [%2d] '%s': symbol %zu: TLS symbol but no TLS program header entry\n"), < destshdr->sh_offset - phdr->p_offset) ERROR (gettext ("\ section [%2d] '%s': symbol %zu: st_value short of referenced section [%2d] '%s'\n"), - idx, section_name (ebl, ehdr, idx), cnt, - (int) xndx, section_name (ebl, ehdr, xndx)); + idx, section_name (ebl, idx), cnt, + (int) xndx, section_name (ebl, xndx)); else if (sym->st_value > (destshdr->sh_offset - phdr->p_offset + destshdr->sh_size)) ERROR (gettext ("\ section [%2d] '%s': symbol %zu: st_value out of bounds of referenced section [%2d] '%s'\n"), - idx, section_name (ebl, ehdr, idx), cnt, - (int) xndx, section_name (ebl, ehdr, xndx)); + idx, section_name (ebl, idx), cnt, + (int) xndx, section_name (ebl, xndx)); else if (sym->st_value + sym->st_size > (destshdr->sh_offset - phdr->p_offset + destshdr->sh_size)) ERROR (gettext ("\ section [%2d] '%s': symbol %zu does not fit completely in referenced section [%2d] '%s'\n"), - idx, section_name (ebl, ehdr, idx), cnt, - (int) xndx, section_name (ebl, ehdr, xndx)); + idx, section_name (ebl, idx), cnt, + (int) xndx, section_name (ebl, xndx)); } } } @@ -839,100 +845,137 @@ section [%2d] '%s': symbol %zu does not fit completely in referenced section [%2 if (cnt >= shdr->sh_info) ERROR (gettext ("\ section [%2d] '%s': symbol %zu: local symbol outside range described in sh_info\n"), - idx, section_name (ebl, ehdr, idx), cnt); + idx, section_name (ebl, idx), cnt); } else { if (cnt < shdr->sh_info) ERROR (gettext ("\ section [%2d] '%s': symbol %zu: non-local symbol outside range described in sh_info\n"), - idx, section_name (ebl, ehdr, idx), cnt); + idx, section_name (ebl, idx), cnt); } if (GELF_ST_TYPE (sym->st_info) == STT_SECTION && GELF_ST_BIND (sym->st_info) != STB_LOCAL) ERROR (gettext ("\ section [%2d] '%s': symbol %zu: non-local section symbol\n"), - idx, section_name (ebl, ehdr, idx), cnt); + idx, section_name (ebl, idx), cnt); if (name != NULL) { if (strcmp (name, "_GLOBAL_OFFSET_TABLE_") == 0) { - /* Check that address and size match the global offset - table. We have to locate the GOT by searching for a - section named ".got". */ - Elf_Scn *gscn = NULL; + /* Check that address and size match the global offset table. */ - while ((gscn = elf_nextscn (ebl->elf, gscn)) != NULL) + GElf_Shdr destshdr_mem; + GElf_Shdr *destshdr = gelf_getshdr (elf_getscn (ebl->elf, xndx), + &destshdr_mem); + + if (destshdr == NULL && xndx == SHN_ABS) + { + /* In a DSO, we have to find the GOT section by name. */ + Elf_Scn *gotscn = NULL; + Elf_Scn *gscn = NULL; + while ((gscn = elf_nextscn (ebl->elf, gscn)) != NULL) + { + destshdr = gelf_getshdr (gscn, &destshdr_mem); + assert (destshdr != NULL); + const char *sname = elf_strptr (ebl->elf, + ehdr->e_shstrndx, + destshdr->sh_name); + if (sname != NULL) + { + if (strcmp (sname, ".got.plt") == 0) + break; + if (strcmp (sname, ".got") == 0) + /* Do not stop looking. + There might be a .got.plt section. */ + gotscn = gscn; + } + + destshdr = NULL; + } + + if (destshdr == NULL && gotscn != NULL) + destshdr = gelf_getshdr (gotscn, &destshdr_mem); + } + + const char *sname = ((destshdr == NULL || xndx == SHN_UNDEF) + ? NULL + : elf_strptr (ebl->elf, ehdr->e_shstrndx, + destshdr->sh_name)); + if (sname == NULL) { - GElf_Shdr gshdr_mem; - GElf_Shdr *gshdr = gelf_getshdr (gscn, &gshdr_mem); - assert (gshdr != NULL); + if (xndx != SHN_UNDEF || ehdr->e_type != ET_REL) + ERROR (gettext ("\ +section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol refers to \ +bad section [%2d]\n"), + idx, section_name (ebl, idx), xndx); + } + else if (strcmp (sname, ".got.plt") != 0 + && strcmp (sname, ".got") != 0) + ERROR (gettext ("\ +section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol refers to \ +section [%2d] '%s'\n"), + idx, section_name (ebl, idx), xndx, sname); - const char *sname = elf_strptr (ebl->elf, ehdr->e_shstrndx, - gshdr->sh_name); - if (sname != NULL && strcmp (sname, ".got") == 0) + if (destshdr != NULL) + { + /* Found it. */ + if (!ebl_check_special_symbol (ebl, ehdr, sym, name, + destshdr)) { - /* Found it. */ - if (sym->st_value != gshdr->sh_addr) - /* This test is more strict than the psABIs - which usually allow the symbol to be in the - middle of the .got section, allowing - negative offsets. */ + if (ehdr->e_type != ET_REL + && sym->st_value != destshdr->sh_addr) + /* This test is more strict than the psABIs which + usually allow the symbol to be in the middle of + the .got section, allowing negative offsets. */ ERROR (gettext ("\ -section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol value %#" PRIx64 " does not match .got section address %#" PRIx64 "\n"), - idx, section_name (ebl, ehdr, idx), +section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol value %#" PRIx64 " does not match %s section address %#" PRIx64 "\n"), + idx, section_name (ebl, idx), (uint64_t) sym->st_value, - (uint64_t) gshdr->sh_addr); + sname, (uint64_t) destshdr->sh_addr); - if (sym->st_size != gshdr->sh_size) + if (!gnuld && sym->st_size != destshdr->sh_size) ERROR (gettext ("\ -section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol size %" PRIu64 " does not match .got section size %" PRIu64 "\n"), - idx, section_name (ebl, ehdr, idx), +section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol size %" PRIu64 " does not match %s section size %" PRIu64 "\n"), + idx, section_name (ebl, idx), (uint64_t) sym->st_size, - (uint64_t) gshdr->sh_size); - - break; + sname, (uint64_t) destshdr->sh_size); } } - - if (gscn == NULL) + else ERROR (gettext ("\ section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol present, but no .got section\n"), - idx, section_name (ebl, ehdr, idx)); + idx, section_name (ebl, idx)); } else if (strcmp (name, "_DYNAMIC") == 0) - { - /* Check that address and size match the dynamic - section. We locate the dynamic section via the - program header entry. */ - int pcnt; - - for (pcnt = 0; pcnt < ehdr->e_phnum; ++pcnt) - { - GElf_Phdr phdr_mem; - GElf_Phdr *phdr = gelf_getphdr (ebl->elf, pcnt, &phdr_mem); + /* Check that address and size match the dynamic section. + We locate the dynamic section via the program header + entry. */ + for (int pcnt = 0; pcnt < ehdr->e_phnum; ++pcnt) + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = gelf_getphdr (ebl->elf, pcnt, &phdr_mem); - if (phdr != NULL && phdr->p_type == PT_DYNAMIC) - { - if (sym->st_value != phdr->p_vaddr) - ERROR (gettext ("\ + if (phdr != NULL && phdr->p_type == PT_DYNAMIC) + { + if (sym->st_value != phdr->p_vaddr) + ERROR (gettext ("\ section [%2d] '%s': _DYNAMIC_ symbol value %#" PRIx64 " does not match dynamic segment address %#" PRIx64 "\n"), - idx, section_name (ebl, ehdr, idx), - (uint64_t) sym->st_value, - (uint64_t) phdr->p_vaddr); + idx, section_name (ebl, idx), + (uint64_t) sym->st_value, + (uint64_t) phdr->p_vaddr); - if (sym->st_size != phdr->p_memsz) - ERROR (gettext ("\ + if (!gnuld && sym->st_size != phdr->p_memsz) + ERROR (gettext ("\ section [%2d] '%s': _DYNAMIC symbol size %" PRIu64 " does not match dynamic segment size %" PRIu64 "\n"), - idx, section_name (ebl, ehdr, idx), - (uint64_t) sym->st_size, - (uint64_t) phdr->p_memsz); + idx, section_name (ebl, idx), + (uint64_t) sym->st_size, + (uint64_t) phdr->p_memsz); - break; - } - } + break; + } } } } @@ -940,14 +983,15 @@ section [%2d] '%s': _DYNAMIC symbol size %" PRIu64 " does not match dynamic segm static bool -is_rel_dyn (Ebl *ebl, GElf_Ehdr *ehdr, int idx, GElf_Shdr *shdr, bool rela) +is_rel_dyn (Ebl *ebl, const GElf_Ehdr *ehdr, int idx, const GElf_Shdr *shdr, + bool is_rela) { /* If this is no executable or DSO it cannot be a .rel.dyn section. */ if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) return false; /* Check the section name. Unfortunately necessary. */ - if (strcmp (section_name (ebl, ehdr, idx), rela ? ".rela.dyn" : ".rel.dyn")) + if (strcmp (section_name (ebl, idx), is_rela ? ".rela.dyn" : ".rel.dyn")) return false; /* When a .rel.dyn section is used a DT_RELCOUNT dynamic section @@ -963,7 +1007,7 @@ is_rel_dyn (Ebl *ebl, GElf_Ehdr *ehdr, int idx, GElf_Shdr *shdr, bool rela) { /* Found the dynamic section. Look through it. */ Elf_Data *d = elf_getdata (scn, NULL); - int cnt; + size_t cnt; for (cnt = 1; cnt < rcshdr->sh_size / rcshdr->sh_entsize; ++cnt) { @@ -973,14 +1017,106 @@ is_rel_dyn (Ebl *ebl, GElf_Ehdr *ehdr, int idx, GElf_Shdr *shdr, bool rela) if (dyn->d_tag == DT_RELCOUNT) { - /* Found it. One last check: does the number - specified number of relative relocations exceed - the total number of relocations? */ - if (dyn->d_un.d_val > shdr->sh_size / shdr->sh_entsize) + /* Found it. Does the type match. */ + if (is_rela) ERROR (gettext ("\ +section [%2d] '%s': DT_RELCOUNT used for this RELA section\n"), + idx, section_name (ebl, idx)); + else + { + /* Does the number specified number of relative + relocations exceed the total number of + relocations? */ + if (dyn->d_un.d_val > shdr->sh_size / shdr->sh_entsize) + ERROR (gettext ("\ +section [%2d] '%s': DT_RELCOUNT value %d too high for this section\n"), + idx, section_name (ebl, idx), + (int) dyn->d_un.d_val); + + /* Make sure the specified number of relocations are + relative. */ + Elf_Data *reldata = elf_getdata (elf_getscn (ebl->elf, + idx), NULL); + if (reldata != NULL) + for (size_t inner = 0; + inner < shdr->sh_size / shdr->sh_entsize; + ++inner) + { + GElf_Rel rel_mem; + GElf_Rel *rel = gelf_getrel (reldata, inner, + &rel_mem); + if (rel == NULL) + /* The problem will be reported elsewhere. */ + break; + + if (ebl_relative_reloc_p (ebl, + GELF_R_TYPE (rel->r_info))) + { + if (inner >= dyn->d_un.d_val) + ERROR (gettext ("\ +section [%2d] '%s': relative relocations after index %d as specified by DT_RELCOUNT\n"), + idx, section_name (ebl, idx), + (int) dyn->d_un.d_val); + } + else if (inner < dyn->d_un.d_val) + ERROR (gettext ("\ +section [%2d] '%s': non-relative relocation at index %zu; DT_RELCOUNT specified %d relative relocations\n"), + idx, section_name (ebl, idx), + inner, (int) dyn->d_un.d_val); + } + } + } + + if (dyn->d_tag == DT_RELACOUNT) + { + /* Found it. Does the type match. */ + if (!is_rela) + ERROR (gettext ("\ +section [%2d] '%s': DT_RELACOUNT used for this REL section\n"), + idx, section_name (ebl, idx)); + else + { + /* Does the number specified number of relative + relocations exceed the total number of + relocations? */ + if (dyn->d_un.d_val > shdr->sh_size / shdr->sh_entsize) + ERROR (gettext ("\ section [%2d] '%s': DT_RELCOUNT value %d too high for this section\n"), - idx, section_name (ebl, ehdr, idx), - (int) dyn->d_un.d_val); + idx, section_name (ebl, idx), + (int) dyn->d_un.d_val); + + /* Make sure the specified number of relocations are + relative. */ + Elf_Data *reldata = elf_getdata (elf_getscn (ebl->elf, + idx), NULL); + if (reldata != NULL) + for (size_t inner = 0; + inner < shdr->sh_size / shdr->sh_entsize; + ++inner) + { + GElf_Rela rela_mem; + GElf_Rela *rela = gelf_getrela (reldata, inner, + &rela_mem); + if (rela == NULL) + /* The problem will be reported elsewhere. */ + break; + + if (ebl_relative_reloc_p (ebl, + GELF_R_TYPE (rela->r_info))) + { + if (inner >= dyn->d_un.d_val) + ERROR (gettext ("\ +section [%2d] '%s': relative relocations after index %d as specified by DT_RELCOUNT\n"), + idx, section_name (ebl, idx), + (int) dyn->d_un.d_val); + } + else if (inner < dyn->d_un.d_val) + ERROR (gettext ("\ +section [%2d] '%s': non-relative relocation at index %zu; DT_RELCOUNT specified %d relative relocations\n"), + idx, section_name (ebl, idx), + inner, (int) dyn->d_un.d_val); + } + } } } @@ -992,273 +1128,360 @@ section [%2d] '%s': DT_RELCOUNT value %d too high for this section\n"), } -static void -check_rela (Ebl *ebl, GElf_Ehdr *ehdr, int idx) +struct loaded_segment { - Elf_Scn *scn; - GElf_Shdr shdr_mem; - GElf_Shdr *shdr; - Elf_Data *data; - GElf_Shdr destshdr_mem; - GElf_Shdr *destshdr = NULL; - size_t cnt; - bool reldyn = false; - bool known_broken = gnuld; + GElf_Addr from; + GElf_Addr to; + bool read_only; + struct loaded_segment *next; +}; - scn = elf_getscn (ebl->elf, idx); - shdr = gelf_getshdr (scn, &shdr_mem); - if (shdr == NULL) - return; - data = elf_getdata (scn, NULL); - if (data == NULL) - { - ERROR (gettext ("section [%2d] '%s': cannot get section data\n"), - idx, section_name (ebl, ehdr, idx)); - return; - } + +/* Check whether binary has text relocation flag set. */ +static bool textrel; + +/* Keep track of whether text relocation flag is needed. */ +static bool needed_textrel; + + +static bool +check_reloc_shdr (Ebl *ebl, const GElf_Ehdr *ehdr, const GElf_Shdr *shdr, + int idx, int reltype, GElf_Shdr **destshdrp, + GElf_Shdr *destshdr_memp, struct loaded_segment **loadedp) +{ + bool reldyn = false; /* Check whether the link to the section we relocate is reasonable. */ if (shdr->sh_info >= shnum) ERROR (gettext ("section [%2d] '%s': invalid destination section index\n"), - idx, section_name (ebl, ehdr, idx)); - else + idx, section_name (ebl, idx)); + else if (shdr->sh_info != 0) { - destshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_info), - &destshdr_mem); - if (destshdr != NULL) + *destshdrp = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_info), + destshdr_memp); + if (*destshdrp != NULL) { - if(destshdr->sh_type != SHT_PROGBITS - && destshdr->sh_type != SHT_NOBITS) + if((*destshdrp)->sh_type != SHT_PROGBITS + && (*destshdrp)->sh_type != SHT_NOBITS) { reldyn = is_rel_dyn (ebl, ehdr, idx, shdr, true); if (!reldyn) ERROR (gettext ("\ section [%2d] '%s': invalid destination section type\n"), - idx, section_name (ebl, ehdr, idx)); + idx, section_name (ebl, idx)); else { - /* There is no standard, but we require that .rela.dyn + /* There is no standard, but we require that .rel{,a}.dyn sections have a sh_info value of zero. */ if (shdr->sh_info != 0) ERROR (gettext ("\ section [%2d] '%s': sh_info should be zero\n"), - idx, section_name (ebl, ehdr, idx)); + idx, section_name (ebl, idx)); } } - if ((destshdr->sh_flags & (SHF_MERGE | SHF_STRINGS)) != 0) + if (((*destshdrp)->sh_flags & (SHF_MERGE | SHF_STRINGS)) != 0) ERROR (gettext ("\ section [%2d] '%s': no relocations for merge-able sections possible\n"), - idx, section_name (ebl, ehdr, idx)); + idx, section_name (ebl, idx)); } } - if (shdr->sh_entsize != gelf_fsize (ebl->elf, ELF_T_RELA, 1, EV_CURRENT)) - ERROR (gettext ("\ -section [%2d] '%s': section entry size does not match ElfXX_Rela\n"), - idx, section_name (ebl, ehdr, idx)); - - Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link); - GElf_Shdr symshdr_mem; - GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem); - Elf_Data *symdata = elf_getdata (symscn, NULL); - - for (cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt) + if (shdr->sh_entsize != gelf_fsize (ebl->elf, reltype, 1, EV_CURRENT)) + ERROR (gettext (reltype == ELF_T_RELA ? "\ +section [%2d] '%s': section entry size does not match ElfXX_Rela\n" : "\ +section [%2d] '%s': section entry size does not match ElfXX_Rel\n"), + idx, section_name (ebl, idx)); + + /* In preparation of checking whether relocations are text + relocations or not we need to determine whether the file is + flagged to have text relocation and we need to determine a) what + the loaded segments are and b) which are read-only. This will + also allow us to determine whether the same reloc section is + modifying loaded and not loaded segments. */ + for (int i = 0; i < ehdr->e_phnum; ++i) { - GElf_Rela rela_mem; - GElf_Rela *rela; + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = gelf_getphdr (ebl->elf, i, &phdr_mem); + if (phdr == NULL) + continue; - rela = gelf_getrela (data, cnt, &rela_mem); - if (rela == NULL) + if (phdr->p_type == PT_LOAD) { - ERROR (gettext ("\ -section [%2d] '%s': cannot get relocation %zu: %s\n"), - idx, section_name (ebl, ehdr, idx), cnt, elf_errmsg (-1)); - continue; + struct loaded_segment *newp = xmalloc (sizeof (*newp)); + newp->from = phdr->p_vaddr; + newp->to = phdr->p_vaddr + phdr->p_memsz; + newp->read_only = (phdr->p_flags & PF_W) == 0; + newp->next = *loadedp; + *loadedp = newp; } + else if (phdr->p_type == PT_DYNAMIC) + { + Elf_Scn *dynscn = gelf_offscn (ebl->elf, phdr->p_offset); + GElf_Shdr dynshdr_mem; + GElf_Shdr *dynshdr = gelf_getshdr (dynscn, &dynshdr_mem); + Elf_Data *dyndata = elf_getdata (dynscn, NULL); + if (dynshdr != NULL && dynshdr->sh_type == SHT_DYNAMIC + && dyndata != NULL) + for (size_t j = 0; j < dynshdr->sh_size / dynshdr->sh_entsize; ++j) + { + GElf_Dyn dyn_mem; + GElf_Dyn *dyn = gelf_getdyn (dyndata, j, &dyn_mem); + if (dyn != NULL + && (dyn->d_tag == DT_TEXTREL + || (dyn->d_tag == DT_FLAGS + && (dyn->d_un.d_val & DF_TEXTREL) != 0))) + { + textrel = true; + break; + } + } + } + } - if (!ebl_reloc_type_check (ebl, GELF_R_TYPE (rela->r_info))) - ERROR (gettext ("section [%2d] '%s': relocation %zu: invalid type\n"), - idx, section_name (ebl, ehdr, idx), cnt); - else if (!ebl_reloc_valid_use (ebl, GELF_R_TYPE (rela->r_info))) + /* A quick test which can be easily done here (although it is a bit + out of place): the text relocation flag makes only sense if there + is a segment which is not writable. */ + if (textrel) + { + struct loaded_segment *seg = *loadedp; + while (seg != NULL && !seg->read_only) + seg = seg->next; + if (seg == NULL) ERROR (gettext ("\ +text relocation flag set but there is no read-only segment\n")); + } + + return reldyn; +} + + +enum load_state + { + state_undecided, + state_loaded, + state_unloaded, + state_error + }; + + +static void +check_one_reloc (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *relshdr, int idx, + size_t cnt, const GElf_Shdr *symshdr, Elf_Data *symdata, + GElf_Addr r_offset, GElf_Xword r_info, + const GElf_Shdr *destshdr, bool reldyn, + struct loaded_segment *loaded, enum load_state *statep) +{ + bool known_broken = gnuld; + + if (!ebl_reloc_type_check (ebl, GELF_R_TYPE (r_info))) + ERROR (gettext ("section [%2d] '%s': relocation %zu: invalid type\n"), + idx, section_name (ebl, idx), cnt); + else if (((ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) + /* The executable/DSO can contain relocation sections with + all the relocations the linker has applied. Those sections + are marked non-loaded, though. */ + || (relshdr->sh_flags & SHF_ALLOC) != 0) + && !ebl_reloc_valid_use (ebl, GELF_R_TYPE (r_info))) + ERROR (gettext ("\ section [%2d] '%s': relocation %zu: relocation type invalid for the file type\n"), - idx, section_name (ebl, ehdr, idx), cnt); + idx, section_name (ebl, idx), cnt); - if (symshdr != NULL - && ((GELF_R_SYM (rela->r_info) + 1) - * gelf_fsize (ebl->elf, ELF_T_SYM, 1, EV_CURRENT) - > symshdr->sh_size)) - ERROR (gettext ("\ + if (symshdr != NULL + && ((GELF_R_SYM (r_info) + 1) + * gelf_fsize (ebl->elf, ELF_T_SYM, 1, EV_CURRENT) + > symshdr->sh_size)) + ERROR (gettext ("\ section [%2d] '%s': relocation %zu: invalid symbol index\n"), - idx, section_name (ebl, ehdr, idx), cnt); + idx, section_name (ebl, idx), cnt); - if (ebl_gotpc_reloc_check (ebl, GELF_R_TYPE (rela->r_info))) - { - const char *name; - char buf[64]; - GElf_Sym sym_mem; - GElf_Sym *sym = gelf_getsym (symdata, GELF_R_SYM (rela->r_info), - &sym_mem); - if (sym != NULL - /* Get the name for the symbol. */ - && (name = elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name)) - && strcmp (name, "_GLOBAL_OFFSET_TABLE_") !=0 ) - ERROR (gettext ("\ + /* No more tests if this is a no-op relocation. */ + if (ebl_none_reloc_p (ebl, GELF_R_TYPE (r_info))) + return; + + if (ebl_gotpc_reloc_check (ebl, GELF_R_TYPE (r_info))) + { + const char *name; + char buf[64]; + GElf_Sym sym_mem; + GElf_Sym *sym = gelf_getsym (symdata, GELF_R_SYM (r_info), &sym_mem); + if (sym != NULL + /* Get the name for the symbol. */ + && (name = elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name)) + && strcmp (name, "_GLOBAL_OFFSET_TABLE_") !=0 ) + ERROR (gettext ("\ section [%2d] '%s': relocation %zu: only symbol '_GLOBAL_OFFSET_TABLE_' can be used with %s\n"), - idx, section_name (ebl, ehdr, idx), cnt, - ebl_reloc_type_name (ebl, GELF_R_SYM (rela->r_info), - buf, sizeof (buf))); - } + idx, section_name (ebl, idx), cnt, + ebl_reloc_type_name (ebl, GELF_R_SYM (r_info), + buf, sizeof (buf))); + } - if (reldyn) + if (reldyn) + { + // XXX TODO Check .rel.dyn section addresses. + } + else if (!known_broken) + { + if (destshdr != NULL + && GELF_R_TYPE (r_info) != 0 + && (r_offset - (ehdr->e_type == ET_REL ? 0 + : destshdr->sh_addr)) >= destshdr->sh_size) + ERROR (gettext ("\ +section [%2d] '%s': relocation %zu: offset out of bounds\n"), + idx, section_name (ebl, idx), cnt); + } + + GElf_Sym sym_mem; + GElf_Sym *sym = gelf_getsym (symdata, GELF_R_SYM (r_info), &sym_mem); + + if (ebl_copy_reloc_p (ebl, GELF_R_TYPE (r_info)) + /* Make sure the referenced symbol is an object or unspecified. */ + && sym != NULL + && GELF_ST_TYPE (sym->st_info) != STT_NOTYPE + && GELF_ST_TYPE (sym->st_info) != STT_OBJECT) + { + char buf[64]; + ERROR (gettext ("section [%2d] '%s': relocation %zu: copy relocation against symbol of type %s\n"), + idx, section_name (ebl, idx), cnt, + ebl_symbol_type_name (ebl, GELF_ST_TYPE (sym->st_info), + buf, sizeof (buf))); + } + + if ((ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) + || (relshdr->sh_flags & SHF_ALLOC) != 0) + { + bool in_loaded_seg = false; + while (loaded != NULL) { - // XXX TODO Check .rel.dyn section addresses. + if (r_offset < loaded->to + && r_offset + (sym == NULL ? 0 : sym->st_size) >= loaded->from) + { + /* The symbol is in this segment. */ + if (loaded->read_only) + { + if (textrel) + needed_textrel = true; + else + ERROR (gettext ("section [%2d] '%s': relocation %zu: read-only section modified but text relocation flag not set\n"), + idx, section_name (ebl, idx), cnt); + } + + in_loaded_seg = true; + } + + loaded = loaded->next; } - else if (!known_broken) + + if (*statep == state_undecided) + *statep = in_loaded_seg ? state_loaded : state_unloaded; + else if ((*statep == state_unloaded && in_loaded_seg) + || (*statep == state_loaded && !in_loaded_seg)) { - if (destshdr != NULL - && (rela->r_offset - destshdr->sh_addr) >= destshdr->sh_size) - ERROR (gettext ("\ -section [%2d] '%s': relocation %zu: offset out of bounds\n"), - idx, section_name (ebl, ehdr, idx), cnt); + ERROR (gettext ("\ +section [%2d] '%s': relocations are against loaded and unloaded data\n"), + idx, section_name (ebl, idx)); + *statep = state_error; } } } static void -check_rel (Ebl *ebl, GElf_Ehdr *ehdr, int idx) +check_rela (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) { - Elf_Scn *scn; - GElf_Shdr shdr_mem; - GElf_Shdr *shdr; - Elf_Data *data; - GElf_Shdr destshdr_mem; - GElf_Shdr *destshdr = NULL; - size_t cnt; - bool reldyn = false; - bool known_broken = gnuld; - - scn = elf_getscn (ebl->elf, idx); - shdr = gelf_getshdr (scn, &shdr_mem); - if (shdr == NULL) - return; - data = elf_getdata (scn, NULL); + Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL); if (data == NULL) { ERROR (gettext ("section [%2d] '%s': cannot get section data\n"), - idx, section_name (ebl, ehdr, idx)); + idx, section_name (ebl, idx)); return; } - /* Check whether the link to the section we relocate is reasonable. */ - if (shdr->sh_info >= shnum) - ERROR (gettext ("section [%2d] '%s': invalid destination section index\n"), - idx, section_name (ebl, ehdr, idx)); - else + /* Check the fields of the section header. */ + GElf_Shdr destshdr_mem; + GElf_Shdr *destshdr = NULL; + struct loaded_segment *loaded = NULL; + bool reldyn = check_reloc_shdr (ebl, ehdr, shdr, idx, ELF_T_RELA, &destshdr, + &destshdr_mem, &loaded); + + Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link); + GElf_Shdr symshdr_mem; + GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem); + Elf_Data *symdata = elf_getdata (symscn, NULL); + enum load_state state = state_undecided; + + for (size_t cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt) { - destshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_info), - &destshdr_mem); - if (destshdr != NULL) + GElf_Rela rela_mem; + GElf_Rela *rela = gelf_getrela (data, cnt, &rela_mem); + if (rela == NULL) { - if (destshdr->sh_type != SHT_PROGBITS - && destshdr->sh_type != SHT_NOBITS) - { - reldyn = is_rel_dyn (ebl, ehdr, idx, shdr, false); - if (!reldyn) - ERROR (gettext ("\ -section [%2d] '%s': invalid destination section type\n"), - idx, section_name (ebl, ehdr, idx)); - else - { - /* There is no standard, but we require that .rela.dyn - sections have a sh_info value of zero. */ - if (shdr->sh_info != 0) - ERROR (gettext ("\ -section [%2d] '%s': sh_info should be zero\n"), - idx, section_name (ebl, ehdr, idx)); - } - } - - if ((destshdr->sh_flags & (SHF_MERGE | SHF_STRINGS)) != 0) - ERROR (gettext ("\ -section [%2d] '%s': no relocations for merge-able sections possible\n"), - idx, section_name (ebl, ehdr, idx)); + ERROR (gettext ("\ +section [%2d] '%s': cannot get relocation %zu: %s\n"), + idx, section_name (ebl, idx), cnt, elf_errmsg (-1)); + continue; } + + check_one_reloc (ebl, ehdr, shdr, idx, cnt, symshdr, symdata, + rela->r_offset, rela->r_info, destshdr, reldyn, loaded, + &state); } - if (shdr->sh_entsize != gelf_fsize (ebl->elf, ELF_T_REL, 1, EV_CURRENT)) - ERROR (gettext ("\ -section [%2d] '%s': section entry size does not match ElfXX_Rel\n"), - idx, section_name (ebl, ehdr, idx)); + while (loaded != NULL) + { + struct loaded_segment *old = loaded; + loaded = loaded->next; + free (old); + } +} + + +static void +check_rel (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) +{ + Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL); + if (data == NULL) + { + ERROR (gettext ("section [%2d] '%s': cannot get section data\n"), + idx, section_name (ebl, idx)); + return; + } + + /* Check the fields of the section header. */ + GElf_Shdr destshdr_mem; + GElf_Shdr *destshdr = NULL; + struct loaded_segment *loaded = NULL; + bool reldyn = check_reloc_shdr (ebl, ehdr, shdr, idx, ELF_T_REL, &destshdr, + &destshdr_mem, &loaded); Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link); GElf_Shdr symshdr_mem; GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem); Elf_Data *symdata = elf_getdata (symscn, NULL); + enum load_state state = state_undecided; - for (cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt) + for (size_t cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt) { GElf_Rel rel_mem; - GElf_Rel *rel; - - rel = gelf_getrel (data, cnt, &rel_mem); + GElf_Rel *rel = gelf_getrel (data, cnt, &rel_mem); if (rel == NULL) { ERROR (gettext ("\ section [%2d] '%s': cannot get relocation %zu: %s\n"), - idx, section_name (ebl, ehdr, idx), cnt, elf_errmsg (-1)); + idx, section_name (ebl, idx), cnt, elf_errmsg (-1)); continue; } - if (!ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info))) - ERROR (gettext ("section [%2d] '%s': relocation %zu: invalid type\n"), - idx, section_name (ebl, ehdr, idx), cnt); - else if (!ebl_reloc_valid_use (ebl, GELF_R_TYPE (rel->r_info))) - ERROR (gettext ("\ -section [%2d] '%s': relocation %zu: relocation type invalid for the file type\n"), - idx, section_name (ebl, ehdr, idx), cnt); - - if (symshdr != NULL - && ((GELF_R_SYM (rel->r_info) + 1) - * gelf_fsize (ebl->elf, ELF_T_SYM, 1, EV_CURRENT) - > symshdr->sh_size)) - ERROR (gettext ("\ -section [%2d] '%s': relocation %zu: invalid symbol index\n"), - idx, section_name (ebl, ehdr, idx), cnt); - - if (ebl_gotpc_reloc_check (ebl, GELF_R_TYPE (rel->r_info))) - { - const char *name; - char buf[64]; - GElf_Sym sym_mem; - GElf_Sym *sym = gelf_getsym (symdata, GELF_R_SYM (rel->r_info), - &sym_mem); - if (sym != NULL - /* Get the name for the symbol. */ - && (name = elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name)) - && strcmp (name, "_GLOBAL_OFFSET_TABLE_") !=0 ) - ERROR (gettext ("\ -section [%2d] '%s': relocation %zu: only symbol '_GLOBAL_OFFSET_TABLE_' can be used with %s\n"), - idx, section_name (ebl, ehdr, idx), cnt, - ebl_reloc_type_name (ebl, GELF_R_SYM (rel->r_info), - buf, sizeof (buf))); - } + check_one_reloc (ebl, ehdr, shdr, idx, cnt, symshdr, symdata, + rel->r_offset, rel->r_info, destshdr, reldyn, loaded, + &state); + } - if (reldyn) - { - // XXX TODO Check .rel.dyn section addresses. - } - else if (!known_broken) - { - if (destshdr != NULL - && GELF_R_TYPE (rel->r_info) != 0 - && (rel->r_offset - destshdr->sh_addr) >= destshdr->sh_size) - ERROR (gettext ("\ -section [%2d] '%s': relocation %zu: offset out of bounds\n"), - idx, section_name (ebl, ehdr, idx), cnt); - } + while (loaded != NULL) + { + struct loaded_segment *old = loaded; + loaded = loaded->next; + free (old); } } @@ -1268,11 +1491,8 @@ static int ndynamic; static void -check_dynamic (Ebl *ebl, GElf_Ehdr *ehdr, int idx) +check_dynamic (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) { - Elf_Scn *scn; - GElf_Shdr shdr_mem; - GElf_Shdr *shdr; Elf_Data *data; GElf_Shdr strshdr_mem; GElf_Shdr *strshdr; @@ -1283,8 +1503,7 @@ check_dynamic (Ebl *ebl, GElf_Ehdr *ehdr, int idx) [DT_PLTRELSZ] = { [DT_JMPREL] = true }, [DT_HASH] = { [DT_SYMTAB] = true }, [DT_STRTAB] = { [DT_STRSZ] = true }, - [DT_SYMTAB] = { [DT_STRTAB] = true, [DT_HASH] = true, - [DT_SYMENT] = true }, + [DT_SYMTAB] = { [DT_STRTAB] = true, [DT_SYMENT] = true }, [DT_RELA] = { [DT_RELASZ] = true, [DT_RELAENT] = true }, [DT_RELASZ] = { [DT_RELA] = true }, [DT_RELAENT] = { [DT_RELA] = true }, @@ -1298,9 +1517,10 @@ check_dynamic (Ebl *ebl, GElf_Ehdr *ehdr, int idx) [DT_JMPREL] = { [DT_PLTRELSZ] = true, [DT_PLTREL] = true }, [DT_RUNPATH] = { [DT_STRTAB] = true }, [DT_PLTREL] = { [DT_JMPREL] = true }, - [DT_PLTRELSZ] = { [DT_JMPREL] = true } }; bool has_dt[DT_NUM]; + bool has_val_dt[DT_VALNUM]; + bool has_addr_dt[DT_ADDRNUM]; static const bool level2[DT_NUM] = { [DT_RPATH] = true, @@ -1311,7 +1531,6 @@ check_dynamic (Ebl *ebl, GElf_Ehdr *ehdr, int idx) static const bool mandatory[DT_NUM] = { [DT_NULL] = true, - [DT_HASH] = true, [DT_STRTAB] = true, [DT_SYMTAB] = true, [DT_STRSZ] = true, @@ -1323,19 +1542,17 @@ check_dynamic (Ebl *ebl, GElf_Ehdr *ehdr, int idx) GElf_Word pltrelsz = 0; memset (has_dt, '\0', sizeof (has_dt)); + memset (has_val_dt, '\0', sizeof (has_val_dt)); + memset (has_addr_dt, '\0', sizeof (has_addr_dt)); if (++ndynamic == 2) ERROR (gettext ("more than one dynamic section present\n")); - scn = elf_getscn (ebl->elf, idx); - shdr = gelf_getshdr (scn, &shdr_mem); - if (shdr == NULL) - return; - data = elf_getdata (scn, NULL); + data = elf_getdata (elf_getscn (ebl->elf, idx), NULL); if (data == NULL) { ERROR (gettext ("section [%2d] '%s': cannot get section data\n"), - idx, section_name (ebl, ehdr, idx)); + idx, section_name (ebl, idx)); return; } @@ -1343,30 +1560,28 @@ check_dynamic (Ebl *ebl, GElf_Ehdr *ehdr, int idx) if (strshdr != NULL && strshdr->sh_type != SHT_STRTAB) ERROR (gettext ("\ section [%2d] '%s': referenced as string table for section [%2d] '%s' but type is not SHT_STRTAB\n"), - shdr->sh_link, section_name (ebl, ehdr, shdr->sh_link), - idx, section_name (ebl, ehdr, idx)); + shdr->sh_link, section_name (ebl, shdr->sh_link), + idx, section_name (ebl, idx)); if (shdr->sh_entsize != gelf_fsize (ebl->elf, ELF_T_DYN, 1, EV_CURRENT)) ERROR (gettext ("\ section [%2d] '%s': section entry size does not match ElfXX_Dyn\n"), - idx, section_name (ebl, ehdr, idx)); + idx, section_name (ebl, idx)); if (shdr->sh_info != 0) ERROR (gettext ("section [%2d] '%s': sh_info not zero\n"), - idx, section_name (ebl, ehdr, idx)); + idx, section_name (ebl, idx)); bool non_null_warned = false; for (cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt) { GElf_Dyn dyn_mem; - GElf_Dyn *dyn; - - dyn = gelf_getdyn (data, cnt, &dyn_mem); + GElf_Dyn *dyn = gelf_getdyn (data, cnt, &dyn_mem); if (dyn == NULL) { ERROR (gettext ("\ section [%2d] '%s': cannot get dynamic section entry %zu: %s\n"), - idx, section_name (ebl, ehdr, idx), cnt, elf_errmsg (-1)); + idx, section_name (ebl, idx), cnt, elf_errmsg (-1)); continue; } @@ -1374,15 +1589,15 @@ section [%2d] '%s': cannot get dynamic section entry %zu: %s\n"), { ERROR (gettext ("\ section [%2d] '%s': non-DT_NULL entries follow DT_NULL entry\n"), - idx, section_name (ebl, ehdr, idx)); + idx, section_name (ebl, idx)); non_null_warned = true; } if (!ebl_dynamic_tag_check (ebl, dyn->d_tag)) ERROR (gettext ("section [%2d] '%s': entry %zu: unknown tag\n"), - idx, section_name (ebl, ehdr, idx), cnt); + idx, section_name (ebl, idx), cnt); - if (dyn->d_tag < DT_NUM) + if (dyn->d_tag >= 0 && dyn->d_tag < DT_NUM) { if (has_dt[dyn->d_tag] && dyn->d_tag != DT_NEEDED @@ -1392,7 +1607,7 @@ section [%2d] '%s': non-DT_NULL entries follow DT_NULL entry\n"), char buf[50]; ERROR (gettext ("\ section [%2d] '%s': entry %zu: more than one entry with tag %s\n"), - idx, section_name (ebl, ehdr, idx), cnt, + idx, section_name (ebl, idx), cnt, ebl_dynamic_tag_name (ebl, dyn->d_tag, buf, sizeof (buf))); } @@ -1402,19 +1617,25 @@ section [%2d] '%s': entry %zu: more than one entry with tag %s\n"), char buf[50]; ERROR (gettext ("\ section [%2d] '%s': entry %zu: level 2 tag %s used\n"), - idx, section_name (ebl, ehdr, idx), cnt, + idx, section_name (ebl, idx), cnt, ebl_dynamic_tag_name (ebl, dyn->d_tag, buf, sizeof (buf))); } has_dt[dyn->d_tag] = true; } + else if (dyn->d_tag <= DT_VALRNGHI + && DT_VALTAGIDX (dyn->d_tag) < DT_VALNUM) + has_val_dt[DT_VALTAGIDX (dyn->d_tag)] = true; + else if (dyn->d_tag <= DT_ADDRRNGHI + && DT_ADDRTAGIDX (dyn->d_tag) < DT_ADDRNUM) + has_addr_dt[DT_ADDRTAGIDX (dyn->d_tag)] = true; if (dyn->d_tag == DT_PLTREL && dyn->d_un.d_val != DT_REL && dyn->d_un.d_val != DT_RELA) ERROR (gettext ("\ section [%2d] '%s': entry %zu: DT_PLTREL value must be DT_REL or DT_RELA\n"), - idx, section_name (ebl, ehdr, idx), cnt); + idx, section_name (ebl, idx), cnt); if (dyn->d_tag == DT_REL) reladdr = dyn->d_un.d_ptr; @@ -1424,14 +1645,89 @@ section [%2d] '%s': entry %zu: DT_PLTREL value must be DT_REL or DT_RELA\n"), pltreladdr = dyn->d_un.d_ptr; if (dyn->d_tag == DT_PLTRELSZ) pltrelsz = dyn->d_un.d_val; + + /* Check that addresses for entries are in loaded segments. */ + switch (dyn->d_tag) + { + size_t n; + case DT_STRTAB: + /* We require the referenced section is the same as the one + specified in sh_link. */ + if (strshdr->sh_addr != dyn->d_un.d_val) + { + ERROR (gettext ("\ +section [%2d] '%s': entry %zu: pointer does not match address of section [%2d] '%s' referenced by sh_link\n"), + idx, section_name (ebl, idx), cnt, + shdr->sh_link, section_name (ebl, shdr->sh_link)); + break; + } + goto check_addr; + + default: + if (dyn->d_tag < DT_ADDRRNGLO || dyn->d_tag > DT_ADDRRNGHI) + /* Value is no pointer. */ + break; + /* FALLTHROUGH */ + + case DT_AUXILIARY: + case DT_FILTER: + case DT_FINI: + case DT_FINI_ARRAY: + case DT_HASH: + case DT_INIT: + case DT_INIT_ARRAY: + case DT_JMPREL: + case DT_PLTGOT: + case DT_REL: + case DT_RELA: + case DT_SYMBOLIC: + case DT_SYMTAB: + case DT_VERDEF: + case DT_VERNEED: + case DT_VERSYM: + check_addr: + for (n = 0; n < ehdr->e_phnum; ++n) + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = gelf_getphdr (ebl->elf, n, &phdr_mem); + if (phdr != NULL && phdr->p_type == PT_LOAD + && phdr->p_vaddr <= dyn->d_un.d_ptr + && phdr->p_vaddr + phdr->p_memsz > dyn->d_un.d_ptr) + break; + } + if (unlikely (n >= ehdr->e_phnum)) + { + char buf[50]; + ERROR (gettext ("\ +section [%2d] '%s': entry %zu: %s value must point into loaded segment\n"), + idx, section_name (ebl, idx), cnt, + ebl_dynamic_tag_name (ebl, dyn->d_tag, buf, + sizeof (buf))); + } + break; + + case DT_NEEDED: + case DT_RPATH: + case DT_RUNPATH: + case DT_SONAME: + if (dyn->d_un.d_ptr >= strshdr->sh_size) + { + char buf[50]; + ERROR (gettext ("\ +section [%2d] '%s': entry %zu: %s value must be valid offset in section [%2d] '%s'\n"), + idx, section_name (ebl, idx), cnt, + ebl_dynamic_tag_name (ebl, dyn->d_tag, buf, + sizeof (buf)), + shdr->sh_link, section_name (ebl, shdr->sh_link)); + } + break; + } } for (cnt = 1; cnt < DT_NUM; ++cnt) if (has_dt[cnt]) { - int inner; - - for (inner = 0; inner < DT_NUM; ++inner) + for (int inner = 0; inner < DT_NUM; ++inner) if (dependencies[cnt][inner] && ! has_dt[inner]) { char buf1[50]; @@ -1439,7 +1735,7 @@ section [%2d] '%s': entry %zu: DT_PLTREL value must be DT_REL or DT_RELA\n"), ERROR (gettext ("\ section [%2d] '%s': contains %s entry but not %s\n"), - idx, section_name (ebl, ehdr, idx), + idx, section_name (ebl, idx), ebl_dynamic_tag_name (ebl, cnt, buf1, sizeof (buf1)), ebl_dynamic_tag_name (ebl, inner, buf2, sizeof (buf2))); } @@ -1451,95 +1747,144 @@ section [%2d] '%s': contains %s entry but not %s\n"), char buf[50]; ERROR (gettext ("\ section [%2d] '%s': mandatory tag %s not present\n"), - idx, section_name (ebl, ehdr, idx), + idx, section_name (ebl, idx), ebl_dynamic_tag_name (ebl, cnt, buf, sizeof (buf))); } } + /* Make sure we have an hash table. */ + if (!has_dt[DT_HASH] && !has_addr_dt[DT_ADDRTAGIDX (DT_GNU_HASH)]) + ERROR (gettext ("\ +section [%2d] '%s': no hash section present\n"), + idx, section_name (ebl, idx)); + + /* The GNU-style hash table also needs a symbol table. */ + if (!has_dt[DT_HASH] && has_addr_dt[DT_ADDRTAGIDX (DT_GNU_HASH)] + && !has_dt[DT_SYMTAB]) + ERROR (gettext ("\ +section [%2d] '%s': contains %s entry but not %s\n"), + idx, section_name (ebl, idx), + "DT_GNU_HASH", "DT_SYMTAB"); + /* Check the rel/rela tags. At least one group must be available. */ if ((has_dt[DT_RELA] || has_dt[DT_RELASZ] || has_dt[DT_RELAENT]) && (!has_dt[DT_RELA] || !has_dt[DT_RELASZ] || !has_dt[DT_RELAENT])) ERROR (gettext ("\ section [%2d] '%s': not all of %s, %s, and %s are present\n"), - idx, section_name (ebl, ehdr, idx), + idx, section_name (ebl, idx), "DT_RELA", "DT_RELASZ", "DT_RELAENT"); if ((has_dt[DT_REL] || has_dt[DT_RELSZ] || has_dt[DT_RELENT]) && (!has_dt[DT_REL] || !has_dt[DT_RELSZ] || !has_dt[DT_RELENT])) ERROR (gettext ("\ section [%2d] '%s': not all of %s, %s, and %s are present\n"), - idx, section_name (ebl, ehdr, idx), + idx, section_name (ebl, idx), "DT_REL", "DT_RELSZ", "DT_RELENT"); + + /* Check that all prelink sections are present if any of them is. */ + if (has_val_dt[DT_VALTAGIDX (DT_GNU_PRELINKED)] + || has_val_dt[DT_VALTAGIDX (DT_CHECKSUM)]) + { + if (!has_val_dt[DT_VALTAGIDX (DT_GNU_PRELINKED)]) + ERROR (gettext ("\ +section [%2d] '%s': %s tag missing in DSO marked during prelinking\n"), + idx, section_name (ebl, idx), "DT_GNU_PRELINKED"); + if (!has_val_dt[DT_VALTAGIDX (DT_CHECKSUM)]) + ERROR (gettext ("\ +section [%2d] '%s': %s tag missing in DSO marked during prelinking\n"), + idx, section_name (ebl, idx), "DT_CHECKSUM"); + + /* Only DSOs can be marked like this. */ + if (ehdr->e_type != ET_DYN) + ERROR (gettext ("\ +section [%2d] '%s': non-DSO file marked as dependency during prelink\n"), + idx, section_name (ebl, idx)); + } + + if (has_val_dt[DT_VALTAGIDX (DT_GNU_CONFLICTSZ)] + || has_val_dt[DT_VALTAGIDX (DT_GNU_LIBLISTSZ)] + || has_addr_dt[DT_ADDRTAGIDX (DT_GNU_CONFLICT)] + || has_addr_dt[DT_ADDRTAGIDX (DT_GNU_LIBLIST)]) + { + if (!has_val_dt[DT_VALTAGIDX (DT_GNU_CONFLICTSZ)]) + ERROR (gettext ("\ +section [%2d] '%s': %s tag missing in prelinked executable\n"), + idx, section_name (ebl, idx), "DT_GNU_CONFLICTSZ"); + if (!has_val_dt[DT_VALTAGIDX (DT_GNU_LIBLISTSZ)]) + ERROR (gettext ("\ +section [%2d] '%s': %s tag missing in prelinked executable\n"), + idx, section_name (ebl, idx), "DT_GNU_LIBLISTSZ"); + if (!has_addr_dt[DT_ADDRTAGIDX (DT_GNU_CONFLICT)]) + ERROR (gettext ("\ +section [%2d] '%s': %s tag missing in prelinked executable\n"), + idx, section_name (ebl, idx), "DT_GNU_CONFLICT"); + if (!has_addr_dt[DT_ADDRTAGIDX (DT_GNU_LIBLIST)]) + ERROR (gettext ("\ +section [%2d] '%s': %s tag missing in prelinked executable\n"), + idx, section_name (ebl, idx), "DT_GNU_LIBLIST"); + } } static void -check_symtab_shndx (Ebl *ebl, GElf_Ehdr *ehdr, int idx) +check_symtab_shndx (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) { - Elf_Scn *scn; - GElf_Shdr shdr_mem; - GElf_Shdr *shdr; - GElf_Shdr symshdr_mem; - GElf_Shdr *symshdr; - Elf_Scn *symscn; - size_t cnt; - Elf_Data *data; - Elf_Data *symdata; - - scn = elf_getscn (ebl->elf, idx); - shdr = gelf_getshdr (scn, &shdr_mem); - if (shdr == NULL) - return; + if (ehdr->e_type != ET_REL) + { + ERROR (gettext ("\ +section [%2d] '%s': only relocatable files can have extended section index\n"), + idx, section_name (ebl, idx)); + return; + } - symscn = elf_getscn (ebl->elf, shdr->sh_link); - symshdr = gelf_getshdr (symscn, &symshdr_mem); + Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link); + GElf_Shdr symshdr_mem; + GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem); if (symshdr != NULL && symshdr->sh_type != SHT_SYMTAB) ERROR (gettext ("\ section [%2d] '%s': extended section index section not for symbol table\n"), - idx, section_name (ebl, ehdr, idx)); - symdata = elf_getdata (symscn, NULL); + idx, section_name (ebl, idx)); + Elf_Data *symdata = elf_getdata (symscn, NULL); if (symdata == NULL) ERROR (gettext ("cannot get data for symbol section\n")); if (shdr->sh_entsize != sizeof (Elf32_Word)) ERROR (gettext ("\ section [%2d] '%s': entry size does not match Elf32_Word\n"), - idx, section_name (ebl, ehdr, idx)); + idx, section_name (ebl, idx)); if (symshdr != NULL && (shdr->sh_size / shdr->sh_entsize < symshdr->sh_size / symshdr->sh_entsize)) ERROR (gettext ("\ section [%2d] '%s': extended index table too small for symbol table\n"), - idx, section_name (ebl, ehdr, idx)); + idx, section_name (ebl, idx)); if (shdr->sh_info != 0) ERROR (gettext ("section [%2d] '%s': sh_info not zero\n"), - idx, section_name (ebl, ehdr, idx)); + idx, section_name (ebl, idx)); - for (cnt = idx + 1; cnt < shnum; ++cnt) + for (size_t cnt = idx + 1; cnt < shnum; ++cnt) { GElf_Shdr rshdr_mem; - GElf_Shdr *rshdr; - - rshdr = gelf_getshdr (elf_getscn (ebl->elf, cnt), &rshdr_mem); + GElf_Shdr *rshdr = gelf_getshdr (elf_getscn (ebl->elf, cnt), &rshdr_mem); if (rshdr != NULL && rshdr->sh_type == SHT_SYMTAB_SHNDX && rshdr->sh_link == shdr->sh_link) { ERROR (gettext ("\ section [%2d] '%s': extended section index in section [%2zu] '%s' refers to same symbol table\n"), - idx, section_name (ebl, ehdr, idx), - cnt, section_name (ebl, ehdr, cnt)); + idx, section_name (ebl, idx), + cnt, section_name (ebl, cnt)); break; } } - data = elf_getdata (scn, NULL); + Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL); if (*((Elf32_Word *) data->d_buf) != 0) ERROR (gettext ("symbol 0 should have zero extended section index\n")); - for (cnt = 1; cnt < data->d_size / sizeof (Elf32_Word); ++cnt) + for (size_t cnt = 1; cnt < data->d_size / sizeof (Elf32_Word); ++cnt) { Elf32_Word xndx = ((Elf32_Word *) data->d_buf)[cnt]; @@ -1563,92 +1908,437 @@ extended section index is %" PRIu32 " but symbol index is not XINDEX\n"), static void -check_hash (Ebl *ebl, GElf_Ehdr *ehdr, int idx) +check_sysv_hash (Ebl *ebl, GElf_Shdr *shdr, Elf_Data *data, int idx, + GElf_Shdr *symshdr) { - Elf_Scn *scn; - GElf_Shdr shdr_mem; - GElf_Shdr *shdr; - Elf_Data *data; - Elf32_Word nbucket; - Elf32_Word nchain; - GElf_Shdr symshdr_mem; - GElf_Shdr *symshdr; + Elf32_Word nbucket = ((Elf32_Word *) data->d_buf)[0]; + Elf32_Word nchain = ((Elf32_Word *) data->d_buf)[1]; - scn = elf_getscn (ebl->elf, idx); - shdr = gelf_getshdr (scn, &shdr_mem); - if (shdr == NULL) - return; - data = elf_getdata (scn, NULL); + if (shdr->sh_size < (2 + nbucket + nchain) * shdr->sh_entsize) + ERROR (gettext ("\ +section [%2d] '%s': hash table section is too small (is %ld, expected %ld)\n"), + idx, section_name (ebl, idx), (long int) shdr->sh_size, + (long int) ((2 + nbucket + nchain) * shdr->sh_entsize)); + + size_t maxidx = nchain; + + if (symshdr != NULL) + { + size_t symsize = symshdr->sh_size / symshdr->sh_entsize; + + if (nchain > symshdr->sh_size / symshdr->sh_entsize) + ERROR (gettext ("section [%2d] '%s': chain array too large\n"), + idx, section_name (ebl, idx)); + + maxidx = symsize; + } + + size_t cnt; + for (cnt = 2; cnt < 2 + nbucket; ++cnt) + if (((Elf32_Word *) data->d_buf)[cnt] >= maxidx) + ERROR (gettext ("\ +section [%2d] '%s': hash bucket reference %zu out of bounds\n"), + idx, section_name (ebl, idx), cnt - 2); + + for (; cnt < 2 + nbucket + nchain; ++cnt) + if (((Elf32_Word *) data->d_buf)[cnt] >= maxidx) + ERROR (gettext ("\ +section [%2d] '%s': hash chain reference %zu out of bounds\n"), + idx, section_name (ebl, idx), cnt - 2 - nbucket); +} + + +static void +check_sysv_hash64 (Ebl *ebl, GElf_Shdr *shdr, Elf_Data *data, int idx, + GElf_Shdr *symshdr) +{ + Elf64_Xword nbucket = ((Elf64_Xword *) data->d_buf)[0]; + Elf64_Xword nchain = ((Elf64_Xword *) data->d_buf)[1]; + + if (shdr->sh_size < (2 + nbucket + nchain) * shdr->sh_entsize) + ERROR (gettext ("\ +section [%2d] '%s': hash table section is too small (is %ld, expected %ld)\n"), + idx, section_name (ebl, idx), (long int) shdr->sh_size, + (long int) ((2 + nbucket + nchain) * shdr->sh_entsize)); + + size_t maxidx = nchain; + + if (symshdr != NULL) + { + size_t symsize = symshdr->sh_size / symshdr->sh_entsize; + + if (nchain > symshdr->sh_size / symshdr->sh_entsize) + ERROR (gettext ("section [%2d] '%s': chain array too large\n"), + idx, section_name (ebl, idx)); + + maxidx = symsize; + } + + size_t cnt; + for (cnt = 2; cnt < 2 + nbucket; ++cnt) + if (((Elf64_Xword *) data->d_buf)[cnt] >= maxidx) + ERROR (gettext ("\ +section [%2d] '%s': hash bucket reference %zu out of bounds\n"), + idx, section_name (ebl, idx), cnt - 2); + + for (; cnt < 2 + nbucket + nchain; ++cnt) + if (((Elf64_Xword *) data->d_buf)[cnt] >= maxidx) + ERROR (gettext ("\ +section [%2d] '%s': hash chain reference %" PRIu64 " out of bounds\n"), + idx, section_name (ebl, idx), (uint64_t) (cnt - 2 - nbucket)); +} + + +static void +check_gnu_hash (Ebl *ebl, GElf_Shdr *shdr, Elf_Data *data, int idx, + GElf_Shdr *symshdr) +{ + Elf32_Word nbuckets = ((Elf32_Word *) data->d_buf)[0]; + Elf32_Word symbias = ((Elf32_Word *) data->d_buf)[1]; + Elf32_Word bitmask_words = ((Elf32_Word *) data->d_buf)[2]; + + if (!powerof2 (bitmask_words)) + ERROR (gettext ("\ +section [%2d] '%s': bitmask size not power of 2: %u\n"), + idx, section_name (ebl, idx), bitmask_words); + + size_t bitmask_idxmask = bitmask_words - 1; + if (gelf_getclass (ebl->elf) == ELFCLASS64) + bitmask_words *= 2; + Elf32_Word shift = ((Elf32_Word *) data->d_buf)[3]; + + if (shdr->sh_size < (4 + bitmask_words + nbuckets) * sizeof (Elf32_Word)) + { + ERROR (gettext ("\ +section [%2d] '%s': hash table section is too small (is %ld, expected at least%ld)\n"), + idx, section_name (ebl, idx), (long int) shdr->sh_size, + (long int) ((4 + bitmask_words + nbuckets) * sizeof (Elf32_Word))); + return; + } + + if (shift > 31) + ERROR (gettext ("\ +section [%2d] '%s': 2nd hash function shift too big: %u\n"), + idx, section_name (ebl, idx), shift); + + size_t maxidx = shdr->sh_size / sizeof (Elf32_Word) - (4 + bitmask_words + + nbuckets); + + if (symshdr != NULL) + maxidx = MIN (maxidx, symshdr->sh_size / symshdr->sh_entsize); + + /* We need the symbol section data. */ + Elf_Data *symdata = elf_getdata (elf_getscn (ebl->elf, shdr->sh_link), NULL); + + union + { + Elf32_Word *p32; + Elf64_Xword *p64; + } bitmask = { .p32 = &((Elf32_Word *) data->d_buf)[4] }, + collected = { .p32 = xcalloc (bitmask_words, sizeof (Elf32_Word)) }; + + size_t classbits = gelf_getclass (ebl->elf) == ELFCLASS32 ? 32 : 64; + + size_t cnt; + for (cnt = 4 + bitmask_words; cnt < 4 + bitmask_words + nbuckets; ++cnt) + { + Elf32_Word symidx = ((Elf32_Word *) data->d_buf)[cnt]; + + if (symidx == 0) + continue; + + if (symidx < symbias) + { + ERROR (gettext ("\ +section [%2d] '%s': hash chain for bucket %zu lower than symbol index bias\n"), + idx, section_name (ebl, idx), cnt - (4 + bitmask_words)); + continue; + } + + while (symidx - symbias < maxidx) + { + Elf32_Word chainhash = ((Elf32_Word *) data->d_buf)[4 + + bitmask_words + + nbuckets + + symidx + - symbias]; + + if (symdata != NULL) + { + /* Check that the referenced symbol is not undefined. */ + GElf_Sym sym_mem; + GElf_Sym *sym = gelf_getsym (symdata, symidx, &sym_mem); + if (sym != NULL && sym->st_shndx == SHN_UNDEF + && GELF_ST_TYPE (sym->st_info) != STT_FUNC) + ERROR (gettext ("\ +section [%2d] '%s': symbol %u referenced in chain for bucket %zu is undefined\n"), + idx, section_name (ebl, idx), symidx, + cnt - (4 + bitmask_words)); + + const char *symname = elf_strptr (ebl->elf, symshdr->sh_link, + sym->st_name); + if (symname != NULL) + { + Elf32_Word hval = elf_gnu_hash (symname); + if ((hval & ~1u) != (chainhash & ~1u)) + ERROR (gettext ("\ +section [%2d] '%s': hash value for symbol %u in chain for bucket %zu wrong\n"), + idx, section_name (ebl, idx), symidx, + cnt - (4 + bitmask_words)); + + /* Set the bits in the bitmask. */ + size_t maskidx = (hval / classbits) & bitmask_idxmask; + if (classbits == 32) + { + collected.p32[maskidx] + |= UINT32_C (1) << (hval & (classbits - 1)); + collected.p32[maskidx] + |= UINT32_C (1) << ((hval >> shift) & (classbits - 1)); + } + else + { + collected.p64[maskidx] + |= UINT64_C (1) << (hval & (classbits - 1)); + collected.p64[maskidx] + |= UINT64_C (1) << ((hval >> shift) & (classbits - 1)); + } + } + } + + if ((chainhash & 1) != 0) + break; + + ++symidx; + } + + if (symidx - symbias >= maxidx) + ERROR (gettext ("\ +section [%2d] '%s': hash chain for bucket %zu out of bounds\n"), + idx, section_name (ebl, idx), cnt - (4 + bitmask_words)); + else if (symshdr != NULL + && symidx > symshdr->sh_size / symshdr->sh_entsize) + ERROR (gettext ("\ +section [%2d] '%s': symbol reference in chain for bucket %zu out of bounds\n"), + idx, section_name (ebl, idx), cnt - (4 + bitmask_words)); + } + + if (memcmp (collected.p32, bitmask.p32, bitmask_words * sizeof (Elf32_Word))) + ERROR (gettext ("\ +section [%2d] '%s': bitmask does not match names in the hash table\n"), + idx, section_name (ebl, idx)); + + free (collected.p32); +} + + +static void +check_hash (int tag, Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) +{ + if (ehdr->e_type == ET_REL) + { + ERROR (gettext ("\ +section [%2d] '%s': relocatable files cannot have hash tables\n"), + idx, section_name (ebl, idx)); + return; + } + + Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL); if (data == NULL) { ERROR (gettext ("section [%2d] '%s': cannot get section data\n"), - idx, section_name (ebl, ehdr, idx)); + idx, section_name (ebl, idx)); return; } - symshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), &symshdr_mem); + GElf_Shdr symshdr_mem; + GElf_Shdr *symshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), + &symshdr_mem); if (symshdr != NULL && symshdr->sh_type != SHT_DYNSYM) ERROR (gettext ("\ section [%2d] '%s': hash table not for dynamic symbol table\n"), - idx, section_name (ebl, ehdr, idx)); + idx, section_name (ebl, idx)); - if (shdr->sh_entsize != sizeof (Elf32_Word)) + if (shdr->sh_entsize != (tag == SHT_GNU_HASH + ? (gelf_getclass (ebl->elf) == ELFCLASS32 + ? sizeof (Elf32_Word) : 0) + : (size_t) ebl_sysvhash_entrysize (ebl))) ERROR (gettext ("\ -section [%2d] '%s': entry size does not match Elf32_Word\n"), - idx, section_name (ebl, ehdr, idx)); +section [%2d] '%s': hash table entry size incorrect\n"), + idx, section_name (ebl, idx)); if ((shdr->sh_flags & SHF_ALLOC) == 0) ERROR (gettext ("section [%2d] '%s': not marked to be allocated\n"), - idx, section_name (ebl, ehdr, idx)); + idx, section_name (ebl, idx)); - if (shdr->sh_size < 2 * shdr->sh_entsize) + if (shdr->sh_size < (tag == SHT_GNU_HASH ? 4 : 2) * (shdr->sh_entsize ?: 4)) { ERROR (gettext ("\ -section [%2d] '%s': hash table has not even room for nbucket and nchain\n"), - idx, section_name (ebl, ehdr, idx)); +section [%2d] '%s': hash table has not even room for initial administrative entries\n"), + idx, section_name (ebl, idx)); return; } - nbucket = ((Elf32_Word *) data->d_buf)[0]; - nchain = ((Elf32_Word *) data->d_buf)[1]; + switch (tag) + { + case SHT_HASH: + if (ebl_sysvhash_entrysize (ebl) == sizeof (Elf64_Xword)) + check_sysv_hash64 (ebl, shdr, data, idx, symshdr); + else + check_sysv_hash (ebl, shdr, data, idx, symshdr); + break; - if (shdr->sh_size < (2 + nbucket + nchain) * shdr->sh_entsize) - ERROR (gettext ("\ -section [%2d] '%s': hash table section is too small (is %ld, expected %ld)\n"), - idx, section_name (ebl, ehdr, idx), (long int) shdr->sh_size, - (long int) ((2 + nbucket + nchain) * shdr->sh_entsize)); + case SHT_GNU_HASH: + check_gnu_hash (ebl, shdr, data, idx, symshdr); + break; - if (symshdr != NULL) + default: + assert (! "should not happen"); + } +} + + +/* Compare content of both hash tables, it must be identical. */ +static void +compare_hash_gnu_hash (Ebl *ebl, GElf_Ehdr *ehdr, size_t hash_idx, + size_t gnu_hash_idx) +{ + Elf_Scn *hash_scn = elf_getscn (ebl->elf, hash_idx); + Elf_Data *hash_data = elf_getdata (hash_scn, NULL); + GElf_Shdr hash_shdr_mem; + GElf_Shdr *hash_shdr = gelf_getshdr (hash_scn, &hash_shdr_mem); + Elf_Scn *gnu_hash_scn = elf_getscn (ebl->elf, gnu_hash_idx); + Elf_Data *gnu_hash_data = elf_getdata (gnu_hash_scn, NULL); + GElf_Shdr gnu_hash_shdr_mem; + GElf_Shdr *gnu_hash_shdr = gelf_getshdr (gnu_hash_scn, &gnu_hash_shdr_mem); + + if (hash_shdr == NULL || gnu_hash_shdr == NULL + || hash_data == NULL || gnu_hash_data == NULL) + /* None of these pointers should be NULL since we used the + sections already. We are careful nonetheless. */ + return; + + /* The link must point to the same symbol table. */ + if (hash_shdr->sh_link != gnu_hash_shdr->sh_link) { - size_t symsize = symshdr->sh_size / symshdr->sh_entsize; - size_t cnt; + ERROR (gettext ("\ +sh_link in hash sections [%2zu] '%s' and [%2zu] '%s' not identical\n"), + hash_idx, elf_strptr (ebl->elf, shstrndx, hash_shdr->sh_name), + gnu_hash_idx, + elf_strptr (ebl->elf, shstrndx, gnu_hash_shdr->sh_name)); + return; + } - if (nchain < symshdr->sh_size / symshdr->sh_entsize) - ERROR (gettext ("section [%2d] '%s': chain array not large enough\n"), - idx, section_name (ebl, ehdr, idx)); + Elf_Scn *sym_scn = elf_getscn (ebl->elf, hash_shdr->sh_link); + Elf_Data *sym_data = elf_getdata (sym_scn, NULL); + GElf_Shdr sym_shdr_mem; + GElf_Shdr *sym_shdr = gelf_getshdr (sym_scn, &sym_shdr_mem); - for (cnt = 2; cnt < 2 + nbucket; ++cnt) - if (((Elf32_Word *) data->d_buf)[cnt] >= symsize) - ERROR (gettext ("\ -section [%2d] '%s': hash bucket reference %zu out of bounds\n"), - idx, section_name (ebl, ehdr, idx), cnt - 2); + if (sym_data == NULL || sym_shdr == NULL) + return; - for (; cnt < 2 + nbucket + nchain; ++cnt) - if (((Elf32_Word *) data->d_buf)[cnt] >= symsize) - ERROR (gettext ("\ -section [%2d] '%s': hash chain reference %zu out of bounds\n"), - idx, section_name (ebl, ehdr, idx), cnt - 2 - nbucket); + int nentries = sym_shdr->sh_size / sym_shdr->sh_entsize; + char *used = alloca (nentries); + memset (used, '\0', nentries); + + /* First go over the GNU_HASH table and mark the entries as used. */ + const Elf32_Word *gnu_hasharr = (Elf32_Word *) gnu_hash_data->d_buf; + Elf32_Word gnu_nbucket = gnu_hasharr[0]; + const int bitmap_factor = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 1 : 2; + const Elf32_Word *gnu_bucket = (gnu_hasharr + + (4 + gnu_hasharr[2] * bitmap_factor)); + const Elf32_Word *gnu_chain = gnu_bucket + gnu_hasharr[0] - gnu_hasharr[1]; + + for (Elf32_Word cnt = 0; cnt < gnu_nbucket; ++cnt) + { + Elf32_Word symidx = gnu_bucket[cnt]; + if (symidx != STN_UNDEF) + do + used[symidx] |= 1; + while ((gnu_chain[symidx++] & 1u) == 0); + } + + /* Now go over the old hash table and check that we cover the same + entries. */ + if (hash_shdr->sh_entsize == sizeof (Elf32_Word)) + { + const Elf32_Word *hasharr = (Elf32_Word *) hash_data->d_buf; + Elf32_Word nbucket = hasharr[0]; + const Elf32_Word *bucket = &hasharr[2]; + const Elf32_Word *chain = &hasharr[2 + nbucket]; + + for (Elf32_Word cnt = 0; cnt < nbucket; ++cnt) + { + Elf32_Word symidx = bucket[cnt]; + while (symidx != STN_UNDEF) + { + used[symidx] |= 2; + symidx = chain[symidx]; + } + } + } + else + { + const Elf64_Xword *hasharr = (Elf64_Xword *) hash_data->d_buf; + Elf64_Xword nbucket = hasharr[0]; + const Elf64_Xword *bucket = &hasharr[2]; + const Elf64_Xword *chain = &hasharr[2 + nbucket]; + + for (Elf64_Xword cnt = 0; cnt < nbucket; ++cnt) + { + Elf64_Xword symidx = bucket[cnt]; + while (symidx != STN_UNDEF) + { + used[symidx] |= 2; + symidx = chain[symidx]; + } + } } + + /* Now see which entries are not set in one or both hash tables + (unless the symbol is undefined in which case it can be omitted + in the new table format). */ + if ((used[0] & 1) != 0) + ERROR (gettext ("section [%2zu] '%s': reference to symbol index 0\n"), + gnu_hash_idx, + elf_strptr (ebl->elf, shstrndx, gnu_hash_shdr->sh_name)); + if ((used[0] & 2) != 0) + ERROR (gettext ("section [%2zu] '%s': reference to symbol index 0\n"), + hash_idx, elf_strptr (ebl->elf, shstrndx, hash_shdr->sh_name)); + + for (int cnt = 1; cnt < nentries; ++cnt) + if (used[cnt] != 0 && used[cnt] != 3) + { + if (used[cnt] == 1) + ERROR (gettext ("\ +symbol %d referenced in new hash table in [%2zu] '%s' but not in old hash table in [%2zu] '%s'\n"), + cnt, gnu_hash_idx, + elf_strptr (ebl->elf, shstrndx, gnu_hash_shdr->sh_name), + hash_idx, + elf_strptr (ebl->elf, shstrndx, hash_shdr->sh_name)); + else + { + GElf_Sym sym_mem; + GElf_Sym *sym = gelf_getsym (sym_data, cnt, &sym_mem); + + if (sym != NULL && sym->st_shndx != STN_UNDEF) + ERROR (gettext ("\ +symbol %d referenced in old hash table in [%2zu] '%s' but not in new hash table in [%2zu] '%s'\n"), + cnt, hash_idx, + elf_strptr (ebl->elf, shstrndx, hash_shdr->sh_name), + gnu_hash_idx, + elf_strptr (ebl->elf, shstrndx, gnu_hash_shdr->sh_name)); + } + } } static void -check_null (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) +check_null (Ebl *ebl, GElf_Shdr *shdr, int idx) { #define TEST(name, extra) \ if (extra && shdr->sh_##name != 0) \ ERROR (gettext ("section [%2d] '%s': nonzero sh_%s for NULL section\n"), \ - idx, section_name (ebl, ehdr, idx), #name) + idx, section_name (ebl, idx), #name) TEST (name, 1); TEST (flags, 1); @@ -1669,44 +2359,57 @@ check_group (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) { ERROR (gettext ("\ section [%2d] '%s': section groups only allowed in relocatable object files\n"), - idx, section_name (ebl, ehdr, idx)); + idx, section_name (ebl, idx)); return; } /* Check that sh_link is an index of a symbol table. */ + Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link); GElf_Shdr symshdr_mem; - GElf_Shdr *symshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), - &symshdr_mem); + GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem); if (symshdr == NULL) ERROR (gettext ("section [%2d] '%s': cannot get symbol table: %s\n"), - idx, section_name (ebl, ehdr, idx), elf_errmsg (-1)); + idx, section_name (ebl, idx), elf_errmsg (-1)); else { if (symshdr->sh_type != SHT_SYMTAB) ERROR (gettext ("\ section [%2d] '%s': section reference in sh_link is no symbol table\n"), - idx, section_name (ebl, ehdr, idx)); + idx, section_name (ebl, idx)); if (shdr->sh_info >= symshdr->sh_size / gelf_fsize (ebl->elf, ELF_T_SYM, 1, EV_CURRENT)) ERROR (gettext ("\ section [%2d] '%s': invalid symbol index in sh_info\n"), - idx, section_name (ebl, ehdr, idx)); + idx, section_name (ebl, idx)); if (shdr->sh_flags != 0) ERROR (gettext ("section [%2d] '%s': sh_flags not zero\n"), - idx, section_name (ebl, ehdr, idx)); + idx, section_name (ebl, idx)); + + GElf_Sym sym_data; + GElf_Sym *sym = gelf_getsym (elf_getdata (symscn, NULL), shdr->sh_info, + &sym_data); + if (sym == NULL) + ERROR (gettext ("\ +section [%2d] '%s': cannot get symbol for signature\n"), + idx, section_name (ebl, idx)); + else if (strcmp (elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name), + "") == 0) + ERROR (gettext ("\ +section [%2d] '%s': signature symbol canot be empty string\n"), + idx, section_name (ebl, idx)); if (be_strict && shdr->sh_entsize != elf32_fsize (ELF_T_WORD, 1, EV_CURRENT)) ERROR (gettext ("section [%2d] '%s': sh_flags not set correctly\n"), - idx, section_name (ebl, ehdr, idx)); + idx, section_name (ebl, idx)); } Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL); if (data == NULL) ERROR (gettext ("section [%2d] '%s': cannot get data: %s\n"), - idx, section_name (ebl, ehdr, idx), elf_errmsg (-1)); + idx, section_name (ebl, idx), elf_errmsg (-1)); else { size_t elsize = elf32_fsize (ELF_T_WORD, 1, EV_CURRENT); @@ -1716,22 +2419,22 @@ section [%2d] '%s': invalid symbol index in sh_info\n"), if (data->d_size % elsize != 0) ERROR (gettext ("\ section [%2d] '%s': section size not multiple of sizeof(Elf32_Word)\n"), - idx, section_name (ebl, ehdr, idx)); + idx, section_name (ebl, idx)); if (data->d_size < elsize) ERROR (gettext ("\ section [%2d] '%s': section group without flags word\n"), - idx, section_name (ebl, ehdr, idx)); + idx, section_name (ebl, idx)); else if (be_strict) { if (data->d_size < 2 * elsize) ERROR (gettext ("\ section [%2d] '%s': section group without member\n"), - idx, section_name (ebl, ehdr, idx)); + idx, section_name (ebl, idx)); else if (data->d_size < 3 * elsize) ERROR (gettext ("\ section [%2d] '%s': section group with only one member\n"), - idx, section_name (ebl, ehdr, idx)); + idx, section_name (ebl, idx)); } #if ALLOW_UNALIGNED @@ -1741,7 +2444,7 @@ section [%2d] '%s': section group with only one member\n"), #endif if ((val & ~GRP_COMDAT) != 0) ERROR (gettext ("section [%2d] '%s': unknown section group flags\n"), - idx, section_name (ebl, ehdr, idx)); + idx, section_name (ebl, idx)); for (cnt = elsize; cnt < data->d_size; cnt += elsize) { @@ -1754,97 +2457,48 @@ section [%2d] '%s': section group with only one member\n"), if (val > shnum) ERROR (gettext ("\ section [%2d] '%s': section index %Zu out of range\n"), - idx, section_name (ebl, ehdr, idx), cnt / elsize); + idx, section_name (ebl, idx), cnt / elsize); else { GElf_Shdr refshdr_mem; - GElf_Shdr *refshdr; - - refshdr = gelf_getshdr (elf_getscn (ebl->elf, val), - &refshdr_mem); + GElf_Shdr *refshdr = gelf_getshdr (elf_getscn (ebl->elf, val), + &refshdr_mem); if (refshdr == NULL) ERROR (gettext ("\ section [%2d] '%s': cannot get section header for element %zu: %s\n"), - idx, section_name (ebl, ehdr, idx), cnt / elsize, + idx, section_name (ebl, idx), cnt / elsize, elf_errmsg (-1)); else { if (refshdr->sh_type == SHT_GROUP) ERROR (gettext ("\ section [%2d] '%s': section group contains another group [%2d] '%s'\n"), - idx, section_name (ebl, ehdr, idx), - val, section_name (ebl, ehdr, val)); + idx, section_name (ebl, idx), + val, section_name (ebl, val)); if ((refshdr->sh_flags & SHF_GROUP) == 0) ERROR (gettext ("\ section [%2d] '%s': element %Zu references section [%2d] '%s' without SHF_GROUP flag set\n"), - idx, section_name (ebl, ehdr, idx), cnt / elsize, - val, section_name (ebl, ehdr, val)); + idx, section_name (ebl, idx), cnt / elsize, + val, section_name (ebl, val)); } if (++scnref[val] == 2) ERROR (gettext ("\ section [%2d] '%s' is contained in more than one section group\n"), - val, section_name (ebl, ehdr, val)); + val, section_name (ebl, val)); } } } } -static bool has_loadable_segment; -static bool has_interp_segment; - -static const struct -{ - const char *name; - size_t namelen; - GElf_Word type; - enum { unused, exact, atleast } attrflag; - GElf_Word attr; - GElf_Word attr2; -} special_sections[] = - { - /* See figure 4-14 in the gABI. */ - { ".bss", 5, SHT_NOBITS, exact, SHF_ALLOC | SHF_WRITE, 0 }, - { ".comment", 8, SHT_PROGBITS, exact, 0, 0 }, - { ".data", 6, SHT_PROGBITS, exact, SHF_ALLOC | SHF_WRITE, 0 }, - { ".data1", 7, SHT_PROGBITS, exact, SHF_ALLOC | SHF_WRITE, 0 }, - { ".debug", 7, SHT_PROGBITS, exact, 0, 0 }, - { ".dynamic", 9, SHT_DYNAMIC, atleast, SHF_ALLOC, SHF_WRITE }, - { ".dynstr", 8, SHT_STRTAB, exact, SHF_ALLOC, 0 }, - { ".dynsym", 8, SHT_DYNSYM, exact, SHF_ALLOC, 0 }, - { ".fini", 6, SHT_PROGBITS, exact, SHF_ALLOC | SHF_EXECINSTR, 0 }, - { ".fini_array", 12, SHT_FINI_ARRAY, exact, SHF_ALLOC | SHF_WRITE, 0 }, - { ".got", 5, SHT_PROGBITS, unused, 0, 0 }, // XXX more info? - { ".hash", 6, SHT_HASH, exact, SHF_ALLOC, 0 }, - { ".init", 6, SHT_PROGBITS, exact, SHF_ALLOC | SHF_EXECINSTR, 0 }, - { ".init_array", 12, SHT_INIT_ARRAY, exact, SHF_ALLOC | SHF_WRITE, 0 }, - { ".interp", 8, SHT_PROGBITS, atleast, 0, SHF_ALLOC }, // XXX more tests? - { ".line", 6, SHT_PROGBITS, exact, 0, 0 }, - { ".note", 6, SHT_NOTE, exact, 0, 0 }, - { ".plt", 5, SHT_PROGBITS, unused, 0, 0 }, // XXX more tests - { ".preinit_array", 15, SHT_PREINIT_ARRAY, exact, SHF_ALLOC | SHF_WRITE, 0 }, - { ".rela", 5, SHT_RELA, atleast, 0, SHF_ALLOC }, // XXX more tests - { ".rel", 4, SHT_REL, atleast, 0, SHF_ALLOC }, // XXX more tests - { ".rodata", 8, SHT_PROGBITS, exact, SHF_ALLOC, 0 }, - { ".rodata1", 9, SHT_PROGBITS, exact, SHF_ALLOC, 0 }, - { ".shstrtab", 10, SHT_STRTAB, exact, 0, 0 }, - { ".strtab", 8, SHT_STRTAB, atleast, 0, SHF_ALLOC }, // XXX more tests - { ".symtab", 8, SHT_SYMTAB, atleast, 0, SHF_ALLOC }, // XXX more tests - { ".symtab_shndx", 14, SHT_SYMTAB_SHNDX, atleast, 0, SHF_ALLOC }, // XXX more tests - { ".tbss", 6, SHT_NOBITS, exact, SHF_ALLOC | SHF_WRITE | SHF_TLS, 0 }, - { ".tdata", 7, SHT_PROGBITS, exact, SHF_ALLOC | SHF_WRITE | SHF_TLS, 0 }, - { ".tdata1", 8, SHT_PROGBITS, exact, SHF_ALLOC | SHF_WRITE | SHF_TLS, 0 }, - { ".text", 6, SHT_PROGBITS, exact, SHF_ALLOC | SHF_EXECINSTR, 0 } - }; -#define nspecial_sections \ - (sizeof (special_sections) / sizeof (special_sections[0])) - - static const char * section_flags_string (GElf_Word flags, char *buf, size_t len) { + if (flags == 0) + return "none"; + static const struct { GElf_Word flag; @@ -1867,9 +2521,8 @@ section_flags_string (GElf_Word flags, char *buf, size_t len) const size_t nknown_flags = sizeof (known_flags) / sizeof (known_flags[0]); char *cp = buf; - size_t cnt; - for (cnt = 0; cnt < nknown_flags; ++cnt) + for (size_t cnt = 0; cnt < nknown_flags; ++cnt) if (flags & known_flags[cnt].flag) { if (cp != buf && len > 1) @@ -1894,14 +2547,126 @@ section_flags_string (GElf_Word flags, char *buf, size_t len) } +static int +has_copy_reloc (Ebl *ebl, unsigned int symscnndx, unsigned int symndx) +{ + /* First find the relocation section for the symbol table. */ + Elf_Scn *scn = NULL; + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = NULL; + while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) + { + shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr != NULL + && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA) + && shdr->sh_link == symscnndx) + /* Found the section. */ + break; + } + + if (scn == NULL) + return 0; + + Elf_Data *data = elf_getdata (scn, NULL); + if (data == NULL) + return 0; + + if (shdr->sh_type == SHT_REL) + for (int i = 0; (size_t) i < shdr->sh_size / shdr->sh_entsize; ++i) + { + GElf_Rel rel_mem; + GElf_Rel *rel = gelf_getrel (data, i, &rel_mem); + if (rel == NULL) + continue; + + if (GELF_R_SYM (rel->r_info) == symndx + && ebl_copy_reloc_p (ebl, GELF_R_TYPE (rel->r_info))) + return 1; + } + else + for (int i = 0; (size_t) i < shdr->sh_size / shdr->sh_entsize; ++i) + { + GElf_Rela rela_mem; + GElf_Rela *rela = gelf_getrela (data, i, &rela_mem); + if (rela == NULL) + continue; + + if (GELF_R_SYM (rela->r_info) == symndx + && ebl_copy_reloc_p (ebl, GELF_R_TYPE (rela->r_info))) + return 1; + } + + return 0; +} + + +static int +in_nobits_scn (Ebl *ebl, unsigned int shndx) +{ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (elf_getscn (ebl->elf, shndx), &shdr_mem); + return shdr != NULL && shdr->sh_type == SHT_NOBITS; +} + + +static struct version_namelist +{ + const char *objname; + const char *name; + GElf_Versym ndx; + enum { ver_def, ver_need } type; + struct version_namelist *next; +} *version_namelist; + + +static int +add_version (const char *objname, const char *name, GElf_Versym ndx, int type) +{ + /* Check that there are no duplications. */ + struct version_namelist *nlp = version_namelist; + while (nlp != NULL) + { + if (((nlp->objname == NULL && objname == NULL) + || (nlp->objname != NULL && objname != NULL + && strcmp (nlp->objname, objname) == 0)) + && strcmp (nlp->name, name) == 0) + return nlp->type == ver_def ? 1 : -1; + nlp = nlp->next; + } + + nlp = xmalloc (sizeof (*nlp)); + nlp->objname = objname; + nlp->name = name; + nlp->ndx = ndx; + nlp->type = type; + nlp->next = version_namelist; + version_namelist = nlp; + + return 0; +} + + static void -check_versym (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) +check_versym (Ebl *ebl, int idx) { - /* The number of elements in the version symbol table must be the - same as the number of symbols. */ + Elf_Scn *scn = elf_getscn (ebl->elf, idx); + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr == NULL) + /* The error has already been reported. */ + return; + + Elf_Data *data = elf_getdata (scn, NULL); + if (data == NULL) + { + ERROR (gettext ("section [%2d] '%s': cannot get section data\n"), + idx, section_name (ebl, idx)); + return; + } + + Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link); GElf_Shdr symshdr_mem; - GElf_Shdr *symshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), - &symshdr_mem); + GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem); if (symshdr == NULL) /* The error has already been reported. */ return; @@ -1910,33 +2675,693 @@ check_versym (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) { ERROR (gettext ("\ section [%2d] '%s' refers in sh_link to section [%2d] '%s' which is no dynamic symbol table\n"), - idx, section_name (ebl, ehdr, idx), - shdr->sh_link, section_name (ebl, ehdr, shdr->sh_link)); + idx, section_name (ebl, idx), + shdr->sh_link, section_name (ebl, shdr->sh_link)); return; } + /* The number of elements in the version symbol table must be the + same as the number of symbols. */ if (shdr->sh_size / shdr->sh_entsize != symshdr->sh_size / symshdr->sh_entsize) ERROR (gettext ("\ section [%2d] '%s' has different number of entries than symbol table [%2d] '%s'\n"), - idx, section_name (ebl, ehdr, idx), - shdr->sh_link, section_name (ebl, ehdr, shdr->sh_link)); + idx, section_name (ebl, idx), + shdr->sh_link, section_name (ebl, shdr->sh_link)); + + Elf_Data *symdata = elf_getdata (symscn, NULL); + if (symdata == NULL) + /* The error has already been reported. */ + return; + + for (int cnt = 1; (size_t) cnt < shdr->sh_size / shdr->sh_entsize; ++cnt) + { + GElf_Versym versym_mem; + GElf_Versym *versym = gelf_getversym (data, cnt, &versym_mem); + if (versym == NULL) + { + ERROR (gettext ("\ +section [%2d] '%s': symbol %d: cannot read version data\n"), + idx, section_name (ebl, idx), cnt); + break; + } + + GElf_Sym sym_mem; + GElf_Sym *sym = gelf_getsym (symdata, cnt, &sym_mem); + if (sym == NULL) + /* Already reported elsewhere. */ + continue; - // XXX TODO A lot more tests - // check value of the fields. local symbols must have zero entries. - // nonlocal symbols refer to valid version. Check that version index - // in bound. + if (*versym == VER_NDX_GLOBAL) + { + /* Global symbol. Make sure it is not defined as local. */ + if (GELF_ST_BIND (sym->st_info) == STB_LOCAL) + ERROR (gettext ("\ +section [%2d] '%s': symbol %d: local symbol with global scope\n"), + idx, section_name (ebl, idx), cnt); + } + else if (*versym != VER_NDX_LOCAL) + { + /* Versioned symbol. Make sure it is not defined as local. */ + if (!gnuld && GELF_ST_BIND (sym->st_info) == STB_LOCAL) + ERROR (gettext ("\ +section [%2d] '%s': symbol %d: local symbol with version\n"), + idx, section_name (ebl, idx), cnt); + + /* Look through the list of defined versions and locate the + index we need for this symbol. */ + struct version_namelist *runp = version_namelist; + while (runp != NULL) + if (runp->ndx == (*versym & (GElf_Versym) 0x7fff)) + break; + else + runp = runp->next; + + if (runp == NULL) + ERROR (gettext ("\ +section [%2d] '%s': symbol %d: invalid version index %d\n"), + idx, section_name (ebl, idx), cnt, (int) *versym); + else if (sym->st_shndx == SHN_UNDEF + && runp->type == ver_def) + ERROR (gettext ("\ +section [%2d] '%s': symbol %d: version index %d is for defined version\n"), + idx, section_name (ebl, idx), cnt, (int) *versym); + else if (sym->st_shndx != SHN_UNDEF + && runp->type == ver_need) + { + /* Unless this symbol has a copy relocation associated + this must not happen. */ + if (!has_copy_reloc (ebl, shdr->sh_link, cnt) + && !in_nobits_scn (ebl, sym->st_shndx)) + ERROR (gettext ("\ +section [%2d] '%s': symbol %d: version index %d is for requested version\n"), + idx, section_name (ebl, idx), cnt, (int) *versym); + } + } + } } -static void -check_sections (Ebl *ebl, GElf_Ehdr *ehdr) +static int +unknown_dependency_p (Elf *elf, GElf_Ehdr *ehdr, const char *fname) { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = NULL; + + int i; + for (i = 0; i < ehdr->e_phnum; ++i) + if ((phdr = gelf_getphdr (elf, i, &phdr_mem)) != NULL + && phdr->p_type == PT_DYNAMIC) + break; + + if (i == ehdr->e_phnum) + return 1; + assert (phdr != NULL); + Elf_Scn *scn = gelf_offscn (elf, phdr->p_offset); GElf_Shdr shdr_mem; - GElf_Shdr *shdr; - size_t cnt; - bool dot_interp_section = false; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + Elf_Data *data = elf_getdata (scn, NULL); + if (shdr != NULL && shdr->sh_type == SHT_DYNAMIC && data != NULL) + for (size_t j = 0; j < shdr->sh_size / shdr->sh_entsize; ++j) + { + GElf_Dyn dyn_mem; + GElf_Dyn *dyn = gelf_getdyn (data, j, &dyn_mem); + if (dyn != NULL && dyn->d_tag == DT_NEEDED) + { + const char *str = elf_strptr (elf, shdr->sh_link, dyn->d_un.d_val); + if (str != NULL && strcmp (str, fname) == 0) + /* Found it. */ + return 0; + } + } + + return 1; +} + + +static unsigned int nverneed; + +static void +check_verneed (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) +{ + if (++nverneed == 2) + ERROR (gettext ("more than one version reference section present\n")); + + GElf_Shdr strshdr_mem; + GElf_Shdr *strshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), + &strshdr_mem); + if (strshdr == NULL) + return; + if (strshdr->sh_type != SHT_STRTAB) + ERROR (gettext ("\ +section [%2d] '%s': sh_link does not link to string table\n"), + idx, section_name (ebl, idx)); + + Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL); + if (data == NULL) + { + ERROR (gettext ("section [%2d] '%s': cannot get section data\n"), + idx, section_name (ebl, idx)); + return; + } + unsigned int offset = 0; + for (int cnt = shdr->sh_info; --cnt >= 0; ) + { + /* Get the data at the next offset. */ + GElf_Verneed needmem; + GElf_Verneed *need = gelf_getverneed (data, offset, &needmem); + if (need == NULL) + break; + + unsigned int auxoffset = offset + need->vn_aux; + + if (need->vn_version != EV_CURRENT) + ERROR (gettext ("\ +section [%2d] '%s': entry %d has wrong version %d\n"), + idx, section_name (ebl, idx), cnt, (int) need->vn_version); + + if (need->vn_cnt > 0 && need->vn_aux < gelf_fsize (ebl->elf, ELF_T_VNEED, + 1, EV_CURRENT)) + ERROR (gettext ("\ +section [%2d] '%s': entry %d has wrong offset of auxiliary data\n"), + idx, section_name (ebl, idx), cnt); + + const char *libname = elf_strptr (ebl->elf, shdr->sh_link, + need->vn_file); + if (libname == NULL) + { + ERROR (gettext ("\ +section [%2d] '%s': entry %d has invalid file reference\n"), + idx, section_name (ebl, idx), cnt); + goto next_need; + } + + /* Check that there is a DT_NEEDED entry for the referenced library. */ + if (unknown_dependency_p (ebl->elf, ehdr, libname)) + ERROR (gettext ("\ +section [%2d] '%s': entry %d references unknown dependency\n"), + idx, section_name (ebl, idx), cnt); + + for (int cnt2 = need->vn_cnt; --cnt2 >= 0; ) + { + GElf_Vernaux auxmem; + GElf_Vernaux *aux = gelf_getvernaux (data, auxoffset, &auxmem); + if (aux == NULL) + break; + + if ((aux->vna_flags & ~VER_FLG_WEAK) != 0) + ERROR (gettext ("\ +section [%2d] '%s': auxiliary entry %d of entry %d has unknown flag\n"), + idx, section_name (ebl, idx), need->vn_cnt - cnt2, cnt); + + const char *verstr = elf_strptr (ebl->elf, shdr->sh_link, + aux->vna_name); + if (verstr == NULL) + ERROR (gettext ("\ +section [%2d] '%s': auxiliary entry %d of entry %d has invalid name reference\n"), + idx, section_name (ebl, idx), need->vn_cnt - cnt2, cnt); + else + { + GElf_Word hashval = elf_hash (verstr); + if (hashval != aux->vna_hash) + ERROR (gettext ("\ +section [%2d] '%s': auxiliary entry %d of entry %d has wrong hash value: %#x, expected %#x\n"), + idx, section_name (ebl, idx), need->vn_cnt - cnt2, + cnt, (int) hashval, (int) aux->vna_hash); + + int res = add_version (libname, verstr, aux->vna_other, + ver_need); + if (unlikely (res !=0)) + { + assert (res > 0); + ERROR (gettext ("\ +section [%2d] '%s': auxiliary entry %d of entry %d has duplicate version name '%s'\n"), + idx, section_name (ebl, idx), need->vn_cnt - cnt2, + cnt, verstr); + } + } + + if ((aux->vna_next != 0 || cnt2 > 0) + && aux->vna_next < gelf_fsize (ebl->elf, ELF_T_VNAUX, 1, + EV_CURRENT)) + { + ERROR (gettext ("\ +section [%2d] '%s': auxiliary entry %d of entry %d has wrong next field\n"), + idx, section_name (ebl, idx), need->vn_cnt - cnt2, cnt); + break; + } + + auxoffset += MAX (aux->vna_next, + gelf_fsize (ebl->elf, ELF_T_VNAUX, 1, EV_CURRENT)); + } + + /* Find the next offset. */ + next_need: + offset += need->vn_next; + + if ((need->vn_next != 0 || cnt > 0) + && offset < auxoffset) + ERROR (gettext ("\ +section [%2d] '%s': entry %d has invalid offset to next entry\n"), + idx, section_name (ebl, idx), cnt); + } +} + + +static unsigned int nverdef; + +static void +check_verdef (Ebl *ebl, GElf_Shdr *shdr, int idx) +{ + if (++nverdef == 2) + ERROR (gettext ("more than one version definition section present\n")); + + GElf_Shdr strshdr_mem; + GElf_Shdr *strshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), + &strshdr_mem); + if (strshdr == NULL) + return; + if (strshdr->sh_type != SHT_STRTAB) + ERROR (gettext ("\ +section [%2d] '%s': sh_link does not link to string table\n"), + idx, section_name (ebl, idx)); + + Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL); + if (data == NULL) + { + no_data: + ERROR (gettext ("section [%2d] '%s': cannot get section data\n"), + idx, section_name (ebl, idx)); + return; + } + + /* Iterate over all version definition entries. We check that there + is a BASE entry and that each index is unique. To do the later + we collection the information in a list which is later + examined. */ + struct namelist + { + const char *name; + struct namelist *next; + } *namelist = NULL; + struct namelist *refnamelist = NULL; + + bool has_base = false; + unsigned int offset = 0; + for (int cnt = shdr->sh_info; --cnt >= 0; ) + { + /* Get the data at the next offset. */ + GElf_Verdef defmem; + GElf_Verdef *def = gelf_getverdef (data, offset, &defmem); + if (def == NULL) + goto no_data; + + if ((def->vd_flags & VER_FLG_BASE) != 0) + { + if (has_base) + ERROR (gettext ("\ +section [%2d] '%s': more than one BASE definition\n"), + idx, section_name (ebl, idx)); + if (def->vd_ndx != VER_NDX_GLOBAL) + ERROR (gettext ("\ +section [%2d] '%s': BASE definition must have index VER_NDX_GLOBAL\n"), + idx, section_name (ebl, idx)); + has_base = true; + } + if ((def->vd_flags & ~(VER_FLG_BASE|VER_FLG_WEAK)) != 0) + ERROR (gettext ("\ +section [%2d] '%s': entry %d has unknown flag\n"), + idx, section_name (ebl, idx), cnt); + + if (def->vd_version != EV_CURRENT) + ERROR (gettext ("\ +section [%2d] '%s': entry %d has wrong version %d\n"), + idx, section_name (ebl, idx), cnt, (int) def->vd_version); + + if (def->vd_cnt > 0 && def->vd_aux < gelf_fsize (ebl->elf, ELF_T_VDEF, + 1, EV_CURRENT)) + ERROR (gettext ("\ +section [%2d] '%s': entry %d has wrong offset of auxiliary data\n"), + idx, section_name (ebl, idx), cnt); + + unsigned int auxoffset = offset + def->vd_aux; + GElf_Verdaux auxmem; + GElf_Verdaux *aux = gelf_getverdaux (data, auxoffset, &auxmem); + if (aux == NULL) + goto no_data; + + const char *name = elf_strptr (ebl->elf, shdr->sh_link, aux->vda_name); + if (name == NULL) + { + ERROR (gettext ("\ +section [%2d] '%s': entry %d has invalid name reference\n"), + idx, section_name (ebl, idx), cnt); + goto next_def; + } + GElf_Word hashval = elf_hash (name); + if (def->vd_hash != hashval) + ERROR (gettext ("\ +section [%2d] '%s': entry %d has wrong hash value: %#x, expected %#x\n"), + idx, section_name (ebl, idx), cnt, (int) hashval, + (int) def->vd_hash); + + int res = add_version (NULL, name, def->vd_ndx, ver_def); + if (unlikely (res !=0)) + { + assert (res > 0); + ERROR (gettext ("\ +section [%2d] '%s': entry %d has duplicate version name '%s'\n"), + idx, section_name (ebl, idx), cnt, name); + } + + struct namelist *newname = alloca (sizeof (*newname)); + newname->name = name; + newname->next = namelist; + namelist = newname; + + auxoffset += aux->vda_next; + for (int cnt2 = 1; cnt2 < def->vd_cnt; ++cnt2) + { + aux = gelf_getverdaux (data, auxoffset, &auxmem); + if (aux == NULL) + goto no_data; + + name = elf_strptr (ebl->elf, shdr->sh_link, aux->vda_name); + if (name == NULL) + ERROR (gettext ("\ +section [%2d] '%s': entry %d has invalid name reference in auxiliary data\n"), + idx, section_name (ebl, idx), cnt); + else + { + newname = alloca (sizeof (*newname)); + newname->name = name; + newname->next = refnamelist; + refnamelist = newname; + } + + if ((aux->vda_next != 0 || cnt2 + 1 < def->vd_cnt) + && aux->vda_next < gelf_fsize (ebl->elf, ELF_T_VDAUX, 1, + EV_CURRENT)) + { + ERROR (gettext ("\ +section [%2d] '%s': entry %d has wrong next field in auxiliary data\n"), + idx, section_name (ebl, idx), cnt); + break; + } + + auxoffset += MAX (aux->vda_next, + gelf_fsize (ebl->elf, ELF_T_VDAUX, 1, EV_CURRENT)); + } + + /* Find the next offset. */ + next_def: + offset += def->vd_next; + + if ((def->vd_next != 0 || cnt > 0) + && offset < auxoffset) + ERROR (gettext ("\ +section [%2d] '%s': entry %d has invalid offset to next entry\n"), + idx, section_name (ebl, idx), cnt); + } + + if (!has_base) + ERROR (gettext ("section [%2d] '%s': no BASE definition\n"), + idx, section_name (ebl, idx)); + + /* Check whether the referenced names are available. */ + while (namelist != NULL) + { + struct version_namelist *runp = version_namelist; + while (runp != NULL) + { + if (runp->type == ver_def + && strcmp (runp->name, namelist->name) == 0) + break; + runp = runp->next; + } + + if (runp == NULL) + ERROR (gettext ("\ +section [%2d] '%s': unknown parent version '%s'\n"), + idx, section_name (ebl, idx), namelist->name); + + namelist = namelist->next; + } +} + +static void +check_attributes (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) +{ + if (shdr->sh_size == 0) + { + ERROR (gettext ("section [%2d] '%s': empty object attributes section\n"), + idx, section_name (ebl, idx)); + return; + } + + Elf_Data *data = elf_rawdata (elf_getscn (ebl->elf, idx), NULL); + if (data == NULL || data->d_size == 0) + { + ERROR (gettext ("section [%2d] '%s': cannot get section data\n"), + idx, section_name (ebl, idx)); + return; + } + + inline size_t pos (const unsigned char *p) + { + return p - (const unsigned char *) data->d_buf; + } + + const unsigned char *p = data->d_buf; + if (*p++ != 'A') + { + ERROR (gettext ("section [%2d] '%s': unrecognized attribute format\n"), + idx, section_name (ebl, idx)); + return; + } + + inline size_t left (void) + { + return (const unsigned char *) data->d_buf + data->d_size - p; + } + + while (left () >= 4) + { + uint32_t len; + memcpy (&len, p, sizeof len); + + if (len == 0) + ERROR (gettext ("\ +section [%2d] '%s': offset %zu: zero length field in attribute section\n"), + idx, section_name (ebl, idx), pos (p)); + + if (MY_ELFDATA != ehdr->e_ident[EI_DATA]) + CONVERT (len); + + if (len > left ()) + { + ERROR (gettext ("\ +section [%2d] '%s': offset %zu: invalid length in attribute section\n"), + idx, section_name (ebl, idx), pos (p)); + break; + } + + const unsigned char *name = p + sizeof len; + p += len; + + unsigned const char *q = memchr (name, '\0', len); + if (q == NULL) + { + ERROR (gettext ("\ +section [%2d] '%s': offset %zu: unterminated vendor name string\n"), + idx, section_name (ebl, idx), pos (p)); + continue; + } + ++q; + + if (q - name == sizeof "gnu" && !memcmp (name, "gnu", sizeof "gnu")) + while (q < p) + { + unsigned const char *chunk = q; + + unsigned int subsection_tag; + get_uleb128 (subsection_tag, q); + + if (q >= p) + { + ERROR (gettext ("\ +section [%2d] '%s': offset %zu: endless ULEB128 in attribute subsection tag\n"), + idx, section_name (ebl, idx), pos (chunk)); + break; + } + uint32_t subsection_len; + if (p - q < (ptrdiff_t) sizeof subsection_len) + { + ERROR (gettext ("\ +section [%2d] '%s': offset %zu: truncated attribute section\n"), + idx, section_name (ebl, idx), pos (q)); + break; + } + + memcpy (&subsection_len, q, sizeof subsection_len); + if (subsection_len == 0) + { + ERROR (gettext ("\ +section [%2d] '%s': offset %zu: zero length field in attribute subsection\n"), + idx, section_name (ebl, idx), pos (q)); + + q += sizeof subsection_len; + continue; + } + + if (MY_ELFDATA != ehdr->e_ident[EI_DATA]) + CONVERT (subsection_len); + + if (p - chunk < (ptrdiff_t) subsection_len) + { + ERROR (gettext ("\ +section [%2d] '%s': offset %zu: invalid length in attribute subsection\n"), + idx, section_name (ebl, idx), pos (q)); + break; + } + + const unsigned char *subsection_end = chunk + subsection_len; + chunk = q; + q = subsection_end; + + if (subsection_tag != 1) /* Tag_File */ + ERROR (gettext ("\ +section [%2d] '%s': offset %zu: attribute subsection has unexpected tag %u\n"), + idx, section_name (ebl, idx), pos (chunk), subsection_tag); + else + { + chunk += sizeof subsection_len; + while (chunk < q) + { + unsigned int tag; + get_uleb128 (tag, chunk); + + uint64_t value = 0; + const unsigned char *r = chunk; + if (tag == 32 || (tag & 1) == 0) + { + get_uleb128 (value, r); + if (r > q) + { + ERROR (gettext ("\ +section [%2d] '%s': offset %zu: endless ULEB128 in attribute tag\n"), + idx, section_name (ebl, idx), pos (chunk)); + break; + } + } + if (tag == 32 || (tag & 1) != 0) + { + r = memchr (r, '\0', q - r); + if (r == NULL) + { + ERROR (gettext ("\ +section [%2d] '%s': offset %zu: unterminated string in attribute\n"), + idx, section_name (ebl, idx), pos (chunk)); + break; + } + ++r; + } + + const char *tag_name = NULL; + const char *value_name = NULL; + if (!ebl_check_object_attribute (ebl, (const char *) name, + tag, value, + &tag_name, &value_name)) + ERROR (gettext ("\ +section [%2d] '%s': offset %zu: unrecognized attribute tag %u\n"), + idx, section_name (ebl, idx), pos (chunk), tag); + else if ((tag & 1) == 0 && value_name == NULL) + ERROR (gettext ("\ +section [%2d] '%s': offset %zu: unrecognized %s attribute value %" PRIu64 "\n"), + idx, section_name (ebl, idx), pos (chunk), + tag_name, value); + + chunk = r; + } + } + } + else + ERROR (gettext ("\ +section [%2d] '%s': offset %zu: vendor '%s' unknown\n"), + idx, section_name (ebl, idx), pos (p), name); + } + + if (left () != 0) + ERROR (gettext ("\ +section [%2d] '%s': offset %zu: extra bytes after last attribute section\n"), + idx, section_name (ebl, idx), pos (p)); +} + +static bool has_loadable_segment; +static bool has_interp_segment; + +static const struct +{ + const char *name; + size_t namelen; + GElf_Word type; + enum { unused, exact, atleast, exact_or_gnuld } attrflag; + GElf_Word attr; + GElf_Word attr2; +} special_sections[] = + { + /* See figure 4-14 in the gABI. */ + { ".bss", 5, SHT_NOBITS, exact, SHF_ALLOC | SHF_WRITE, 0 }, + { ".comment", 8, SHT_PROGBITS, exact, 0, 0 }, + { ".data", 6, SHT_PROGBITS, exact, SHF_ALLOC | SHF_WRITE, 0 }, + { ".data1", 7, SHT_PROGBITS, exact, SHF_ALLOC | SHF_WRITE, 0 }, + { ".debug_str", 11, SHT_PROGBITS, exact_or_gnuld, SHF_MERGE | SHF_STRINGS, 0 }, + { ".debug", 6, SHT_PROGBITS, exact, 0, 0 }, + { ".dynamic", 9, SHT_DYNAMIC, atleast, SHF_ALLOC, SHF_WRITE }, + { ".dynstr", 8, SHT_STRTAB, exact, SHF_ALLOC, 0 }, + { ".dynsym", 8, SHT_DYNSYM, exact, SHF_ALLOC, 0 }, + { ".fini", 6, SHT_PROGBITS, exact, SHF_ALLOC | SHF_EXECINSTR, 0 }, + { ".fini_array", 12, SHT_FINI_ARRAY, exact, SHF_ALLOC | SHF_WRITE, 0 }, + { ".got", 5, SHT_PROGBITS, unused, 0, 0 }, // XXX more info? + { ".hash", 6, SHT_HASH, exact, SHF_ALLOC, 0 }, + { ".init", 6, SHT_PROGBITS, exact, SHF_ALLOC | SHF_EXECINSTR, 0 }, + { ".init_array", 12, SHT_INIT_ARRAY, exact, SHF_ALLOC | SHF_WRITE, 0 }, + { ".interp", 8, SHT_PROGBITS, atleast, 0, SHF_ALLOC }, // XXX more tests? + { ".line", 6, SHT_PROGBITS, exact, 0, 0 }, + { ".note", 6, SHT_NOTE, atleast, 0, SHF_ALLOC }, + { ".plt", 5, SHT_PROGBITS, unused, 0, 0 }, // XXX more tests + { ".preinit_array", 15, SHT_PREINIT_ARRAY, exact, SHF_ALLOC | SHF_WRITE, 0 }, + { ".rela", 5, SHT_RELA, atleast, 0, SHF_ALLOC }, // XXX more tests + { ".rel", 4, SHT_REL, atleast, 0, SHF_ALLOC }, // XXX more tests + { ".rodata", 8, SHT_PROGBITS, atleast, SHF_ALLOC, SHF_MERGE | SHF_STRINGS }, + { ".rodata1", 9, SHT_PROGBITS, atleast, SHF_ALLOC, SHF_MERGE | SHF_STRINGS }, + { ".shstrtab", 10, SHT_STRTAB, exact, 0, 0 }, + { ".strtab", 8, SHT_STRTAB, atleast, 0, SHF_ALLOC }, // XXX more tests + { ".symtab", 8, SHT_SYMTAB, atleast, 0, SHF_ALLOC }, // XXX more tests + { ".symtab_shndx", 14, SHT_SYMTAB_SHNDX, atleast, 0, SHF_ALLOC }, // XXX more tests + { ".tbss", 6, SHT_NOBITS, exact, SHF_ALLOC | SHF_WRITE | SHF_TLS, 0 }, + { ".tdata", 7, SHT_PROGBITS, exact, SHF_ALLOC | SHF_WRITE | SHF_TLS, 0 }, + { ".tdata1", 8, SHT_PROGBITS, exact, SHF_ALLOC | SHF_WRITE | SHF_TLS, 0 }, + { ".text", 6, SHT_PROGBITS, exact, SHF_ALLOC | SHF_EXECINSTR, 0 }, + + /* The following are GNU extensions. */ + { ".gnu.version", 13, SHT_GNU_versym, exact, SHF_ALLOC, 0 }, + { ".gnu.version_d", 15, SHT_GNU_verdef, exact, SHF_ALLOC, 0 }, + { ".gnu.version_r", 15, SHT_GNU_verneed, exact, SHF_ALLOC, 0 }, + { ".gnu.attributes", 16, SHT_GNU_ATTRIBUTES, exact, 0, 0 }, + }; +#define nspecial_sections \ + (sizeof (special_sections) / sizeof (special_sections[0])) + +#define IS_KNOWN_SPECIAL(idx, string, prefix) \ + (special_sections[idx].namelen == sizeof string - (prefix ? 1 : 0) \ + && !memcmp (special_sections[idx].name, string, \ + sizeof string - (prefix ? 1 : 0))) + +static void +check_sections (Ebl *ebl, GElf_Ehdr *ehdr) +{ if (ehdr->e_shoff == 0) /* No section header. */ return; @@ -1947,7 +3372,8 @@ check_sections (Ebl *ebl, GElf_Ehdr *ehdr) /* Check the zeroth section first. It must not have any contents and the section header must contain nonzero value at most in the sh_size and sh_link fields. */ - shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem); + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem); if (shdr == NULL) ERROR (gettext ("cannot get section header of zeroth section\n")); else @@ -1978,17 +3404,22 @@ zeroth section has nonzero size value while ELF header has nonzero shnum value\n zeroth section has nonzero link value while ELF header does not signal overflow in shstrndx\n")); } - for (cnt = 1; cnt < shnum; ++cnt) - { - Elf_Scn *scn; + int *segment_flags = xcalloc (ehdr->e_phnum, sizeof segment_flags[0]); - scn = elf_getscn (ebl->elf, cnt); - shdr = gelf_getshdr (scn, &shdr_mem); + bool dot_interp_section = false; + + size_t hash_idx = 0; + size_t gnu_hash_idx = 0; + + size_t versym_scnndx = 0; + for (size_t cnt = 1; cnt < shnum; ++cnt) + { + shdr = gelf_getshdr (elf_getscn (ebl->elf, cnt), &shdr_mem); if (shdr == NULL) { ERROR (gettext ("\ cannot get section header for section [%2zu] '%s': %s\n"), - cnt, section_name (ebl, ehdr, cnt), elf_errmsg (-1)); + cnt, section_name (ebl, cnt), elf_errmsg (-1)); continue; } @@ -2009,7 +3440,19 @@ cannot get section header for section [%2zu] '%s': %s\n"), char stbuf2[100]; char stbuf3[100]; - if (shdr->sh_type != special_sections[s].type) + GElf_Word good_type = special_sections[s].type; + if (IS_KNOWN_SPECIAL (s, ".plt", false) + && ebl_bss_plt_p (ebl, ehdr)) + good_type = SHT_NOBITS; + + /* In a debuginfo file, any normal section can be SHT_NOBITS. + This is only invalid for DWARF sections and .shstrtab. */ + if (shdr->sh_type != good_type + && (shdr->sh_type != SHT_NOBITS + || !is_debuginfo + || IS_KNOWN_SPECIAL (s, ".debug_str", false) + || IS_KNOWN_SPECIAL (s, ".debug", true) + || IS_KNOWN_SPECIAL (s, ".shstrtab", false))) ERROR (gettext ("\ section [%2d] '%s' has wrong type: expected %s, is %s\n"), (int) cnt, scnname, @@ -2018,12 +3461,14 @@ section [%2d] '%s' has wrong type: expected %s, is %s\n"), ebl_section_type_name (ebl, shdr->sh_type, stbuf2, sizeof (stbuf2))); - if (special_sections[s].attrflag == exact) + if (special_sections[s].attrflag == exact + || special_sections[s].attrflag == exact_or_gnuld) { /* Except for the link order and group bit all the other bits should match exactly. */ if ((shdr->sh_flags & ~(SHF_LINK_ORDER | SHF_GROUP)) - != special_sections[s].attr) + != special_sections[s].attr + && (special_sections[s].attrflag == exact || !gnuld)) ERROR (gettext ("\ section [%2zu] '%s' has wrong flags: expected %s, is %s\n"), cnt, scnname, @@ -2115,33 +3560,51 @@ section [%2zu] '%s' has SHF_ALLOC flag not set but there are loadable segments\n if (shdr->sh_entsize != 0 && shdr->sh_size % shdr->sh_entsize) ERROR (gettext ("\ section [%2zu] '%s': size not multiple of entry size\n"), - cnt, section_name (ebl, ehdr, cnt)); + cnt, section_name (ebl, cnt)); if (elf_strptr (ebl->elf, shstrndx, shdr->sh_name) == NULL) ERROR (gettext ("cannot get section header\n")); if (shdr->sh_type >= SHT_NUM + && shdr->sh_type != SHT_GNU_ATTRIBUTES && shdr->sh_type != SHT_GNU_LIBLIST && shdr->sh_type != SHT_CHECKSUM && shdr->sh_type != SHT_GNU_verdef && shdr->sh_type != SHT_GNU_verneed - && shdr->sh_type != SHT_GNU_versym) - ERROR (gettext ("unsupported section type %d\n"), (int) shdr->sh_type); + && shdr->sh_type != SHT_GNU_versym + && ebl_section_type_name (ebl, shdr->sh_type, NULL, 0) == NULL) + ERROR (gettext ("section [%2zu] '%s' has unsupported type %d\n"), + cnt, section_name (ebl, cnt), + (int) shdr->sh_type); #define ALL_SH_FLAGS (SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR | SHF_MERGE \ | SHF_STRINGS | SHF_INFO_LINK | SHF_LINK_ORDER \ | SHF_OS_NONCONFORMING | SHF_GROUP | SHF_TLS) - if (shdr->sh_flags & ~ALL_SH_FLAGS) - ERROR (gettext ("section [%2zu] '%s' contain unknown flag(s) %d\n"), - cnt, section_name (ebl, ehdr, cnt), - (int) shdr->sh_flags & ~ALL_SH_FLAGS); - else if (shdr->sh_flags & SHF_TLS) + if (shdr->sh_flags & ~(GElf_Xword) ALL_SH_FLAGS) + { + GElf_Xword sh_flags = shdr->sh_flags & ~(GElf_Xword) ALL_SH_FLAGS; + if (sh_flags & SHF_MASKPROC) + { + if (!ebl_machine_section_flag_check (ebl, + sh_flags & SHF_MASKPROC)) + ERROR (gettext ("section [%2zu] '%s'" + " contains invalid processor-specific flag(s)" + " %#" PRIx64 "\n"), + cnt, section_name (ebl, cnt), sh_flags & SHF_MASKPROC); + sh_flags &= ~(GElf_Xword) SHF_MASKPROC; + } + if (sh_flags != 0) + ERROR (gettext ("section [%2zu] '%s' contains unknown flag(s)" + " %#" PRIx64 "\n"), + cnt, section_name (ebl, cnt), sh_flags); + } + if (shdr->sh_flags & SHF_TLS) { // XXX Correct? if (shdr->sh_addr != 0 && !gnuld) ERROR (gettext ("\ section [%2zu] '%s': thread-local data sections address not zero\n"), - cnt, section_name (ebl, ehdr, cnt)); + cnt, section_name (ebl, cnt)); // XXX TODO more tests!? } @@ -2149,27 +3612,52 @@ section [%2zu] '%s': thread-local data sections address not zero\n"), if (shdr->sh_link >= shnum) ERROR (gettext ("\ section [%2zu] '%s': invalid section reference in link value\n"), - cnt, section_name (ebl, ehdr, cnt)); + cnt, section_name (ebl, cnt)); if (SH_INFO_LINK_P (shdr) && shdr->sh_info >= shnum) ERROR (gettext ("\ section [%2zu] '%s': invalid section reference in info value\n"), - cnt, section_name (ebl, ehdr, cnt)); + cnt, section_name (ebl, cnt)); if ((shdr->sh_flags & SHF_MERGE) == 0 && (shdr->sh_flags & SHF_STRINGS) != 0 && be_strict) ERROR (gettext ("\ section [%2zu] '%s': strings flag set without merge flag\n"), - cnt, section_name (ebl, ehdr, cnt)); + cnt, section_name (ebl, cnt)); if ((shdr->sh_flags & SHF_MERGE) != 0 && shdr->sh_entsize == 0) ERROR (gettext ("\ section [%2zu] '%s': merge flag set but entry size is zero\n"), - cnt, section_name (ebl, ehdr, cnt)); + cnt, section_name (ebl, cnt)); if (shdr->sh_flags & SHF_GROUP) - check_scn_group (ebl, ehdr, cnt); + check_scn_group (ebl, cnt); + + if (shdr->sh_flags & SHF_EXECINSTR) + { + switch (shdr->sh_type) + { + case SHT_PROGBITS: + break; + + case SHT_NOBITS: + if (is_debuginfo) + break; + default: + ERROR (gettext ("\ +section [%2zu] '%s' has unexpected type %d for an executable section\n"), + cnt, section_name (ebl, cnt), shdr->sh_type); + break; + } + + if ((shdr->sh_flags & SHF_WRITE) + && !ebl_check_special_section (ebl, cnt, shdr, + section_name (ebl, cnt))) + ERROR (gettext ("\ +section [%2zu] '%s' is both executable and writable\n"), + cnt, section_name (ebl, cnt)); + } if (ehdr->e_type != ET_REL && (shdr->sh_flags & SHF_ALLOC) != 0) { @@ -2186,28 +3674,55 @@ section [%2zu] '%s': merge flag set but entry size is zero\n"), || (phdr->p_type == PT_TLS && (shdr->sh_flags & SHF_TLS) != 0)) && phdr->p_offset <= shdr->sh_offset - && phdr->p_offset + phdr->p_memsz > shdr->sh_offset) + && (phdr->p_offset + phdr->p_filesz > shdr->sh_offset + || (phdr->p_offset + phdr->p_memsz > shdr->sh_offset + && shdr->sh_type == SHT_NOBITS))) { /* Found the segment. */ if (phdr->p_offset + phdr->p_memsz < shdr->sh_offset + shdr->sh_size) ERROR (gettext ("\ section [%2zu] '%s' not fully contained in segment of program header entry %d\n"), - cnt, section_name (ebl, ehdr, cnt), pcnt); + cnt, section_name (ebl, cnt), pcnt); if (shdr->sh_type == SHT_NOBITS) { - if (shdr->sh_offset < phdr->p_offset + phdr->p_filesz) + if (shdr->sh_offset < phdr->p_offset + phdr->p_filesz + && !is_debuginfo) ERROR (gettext ("\ section [%2zu] '%s' has type NOBITS but is read from the file in segment of program header entry %d\n"), - cnt, section_name (ebl, ehdr, cnt), pcnt); + cnt, section_name (ebl, cnt), pcnt); } else { - if (shdr->sh_offset >= phdr->p_offset + phdr->p_filesz) + const GElf_Off end = phdr->p_offset + phdr->p_filesz; + if (shdr->sh_offset > end || + (shdr->sh_offset == end && shdr->sh_size != 0)) ERROR (gettext ("\ section [%2zu] '%s' has not type NOBITS but is not read from the file in segment of program header entry %d\n"), - cnt, section_name (ebl, ehdr, cnt), pcnt); + cnt, section_name (ebl, cnt), pcnt); + } + + if (shdr->sh_type != SHT_NOBITS) + { + if ((shdr->sh_flags & SHF_EXECINSTR) != 0) + { + segment_flags[pcnt] |= PF_X; + if ((phdr->p_flags & PF_X) == 0) + ERROR (gettext ("\ +section [%2zu] '%s' is executable in nonexecutable segment %d\n"), + cnt, section_name (ebl, cnt), pcnt); + } + + if ((shdr->sh_flags & SHF_WRITE) != 0) + { + segment_flags[pcnt] |= PF_W; + if (0 /* XXX vdso images have this */ + && (phdr->p_flags & PF_W) == 0) + ERROR (gettext ("\ +section [%2zu] '%s' is writable in unwritable segment %d\n"), + cnt, section_name (ebl, cnt), pcnt); + } } break; @@ -2216,51 +3731,83 @@ section [%2zu] '%s' has not type NOBITS but is not read from the file in segment if (pcnt == ehdr->e_phnum) ERROR (gettext ("\ section [%2zu] '%s': alloc flag set but section not in any loaded segment\n"), - cnt, section_name (ebl, ehdr, cnt)); + cnt, section_name (ebl, cnt)); } if (cnt == shstrndx && shdr->sh_type != SHT_STRTAB) ERROR (gettext ("\ section [%2zu] '%s': ELF header says this is the section header string table but type is not SHT_TYPE\n"), - cnt, section_name (ebl, ehdr, cnt)); + cnt, section_name (ebl, cnt)); switch (shdr->sh_type) { - case SHT_SYMTAB: case SHT_DYNSYM: - check_symtab (ebl, ehdr, cnt); + if (ehdr->e_type == ET_REL) + ERROR (gettext ("\ +section [%2zu] '%s': relocatable files cannot have dynamic symbol tables\n"), + cnt, section_name (ebl, cnt)); + /* FALLTHROUGH */ + case SHT_SYMTAB: + check_symtab (ebl, ehdr, shdr, cnt); break; case SHT_RELA: - check_rela (ebl, ehdr, cnt); + check_rela (ebl, ehdr, shdr, cnt); break; case SHT_REL: - check_rel (ebl, ehdr, cnt); + check_rel (ebl, ehdr, shdr, cnt); break; case SHT_DYNAMIC: - check_dynamic (ebl, ehdr, cnt); + check_dynamic (ebl, ehdr, shdr, cnt); break; case SHT_SYMTAB_SHNDX: - check_symtab_shndx (ebl, ehdr, cnt); + check_symtab_shndx (ebl, ehdr, shdr, cnt); break; case SHT_HASH: - check_hash (ebl, ehdr, cnt); + check_hash (shdr->sh_type, ebl, ehdr, shdr, cnt); + hash_idx = cnt; + break; + + case SHT_GNU_HASH: + check_hash (shdr->sh_type, ebl, ehdr, shdr, cnt); + gnu_hash_idx = cnt; break; case SHT_NULL: - check_null (ebl, ehdr, shdr, cnt); + check_null (ebl, shdr, cnt); break; case SHT_GROUP: check_group (ebl, ehdr, shdr, cnt); break; + case SHT_NOTE: + check_note_section (ebl, ehdr, shdr, cnt); + break; + case SHT_GNU_versym: - check_versym (ebl, ehdr, shdr, cnt); + /* We cannot process this section now since we have no guarantee + that the verneed and verdef sections have already been read. + Just remember the section index. */ + if (versym_scnndx != 0) + ERROR (gettext ("more than one version symbol table present\n")); + versym_scnndx = cnt; + break; + + case SHT_GNU_verneed: + check_verneed (ebl, ehdr, shdr, cnt); + break; + + case SHT_GNU_verdef: + check_verdef (ebl, shdr, cnt); + break; + + case SHT_GNU_ATTRIBUTES: + check_attributes (ebl, ehdr, shdr, cnt); break; default: @@ -2272,151 +3819,210 @@ section [%2zu] '%s': ELF header says this is the section header string table but if (has_interp_segment && !dot_interp_section) ERROR (gettext ("INTERP program header entry but no .interp section\n")); - free (scnref); -} - - -static void -check_note (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Phdr *phdr, int cnt) -{ - if (ehdr->e_type != ET_CORE && ehdr->e_type != ET_REL - && ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) - ERROR (gettext ("\ -phdr[%d]: no note entries defined for the type of file\n"), - cnt); + if (!is_debuginfo) + for (int pcnt = 0; pcnt < ehdr->e_phnum; ++pcnt) + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = gelf_getphdr (ebl->elf, pcnt, &phdr_mem); + if (phdr != NULL && (phdr->p_type == PT_LOAD || phdr->p_type == PT_TLS)) + { + if ((phdr->p_flags & PF_X) != 0 + && (segment_flags[pcnt] & PF_X) == 0) + ERROR (gettext ("\ +loadable segment [%u] is executable but contains no executable sections\n"), + pcnt); - char *notemem = gelf_rawchunk (ebl->elf, phdr->p_offset, phdr->p_filesz); + if ((phdr->p_flags & PF_W) != 0 + && (segment_flags[pcnt] & PF_W) == 0) + ERROR (gettext ("\ +loadable segment [%u] is writable but contains no writable sections\n"), + pcnt); + } + } - /* ELF64 files often use note section entries in the 32-bit format. - The p_align field is set to 8 in case the 64-bit format is used. - In case the p_align value is 0 or 4 the 32-bit format is - used. */ - GElf_Xword align = phdr->p_align == 0 || phdr->p_align == 4 ? 4 : 8; -#define ALIGNED_LEN(len) (((len) + align - 1) & ~(align - 1)) + free (segment_flags); - GElf_Xword idx = 0; - while (idx < phdr->p_filesz) + if (version_namelist != NULL) { - uint64_t namesz; - uint64_t descsz; - uint64_t type; - uint32_t namesz32; - uint32_t descsz32; + if (versym_scnndx == 0) + ERROR (gettext ("\ +no .gnu.versym section present but .gnu.versym_d or .gnu.versym_r section exist\n")); + else + check_versym (ebl, versym_scnndx); - if (align == 4) + /* Check for duplicate index numbers. */ + do { - uint32_t *ptr = (uint32_t *) (notemem + idx); - - if ((__BYTE_ORDER == __LITTLE_ENDIAN - && ehdr->e_ident[EI_DATA] == ELFDATA2MSB) - || (__BYTE_ORDER == __BIG_ENDIAN - && ehdr->e_ident[EI_DATA] == ELFDATA2LSB)) - { - namesz32 = namesz = bswap_32 (*ptr); - ++ptr; - descsz32 = descsz = bswap_32 (*ptr); - ++ptr; - type = bswap_32 (*ptr); - } - else + struct version_namelist *runp = version_namelist->next; + while (runp != NULL) { - namesz32 = namesz = *ptr++; - descsz32 = descsz = *ptr++; - type = *ptr; + if (version_namelist->ndx == runp->ndx) + { + ERROR (gettext ("duplicate version index %d\n"), + (int) version_namelist->ndx); + break; + } + runp = runp->next; } + + struct version_namelist *old = version_namelist; + version_namelist = version_namelist->next; + free (old); } - else - { - uint64_t *ptr = (uint64_t *) (notemem + idx); - uint32_t *ptr32 = (uint32_t *) (notemem + idx); + while (version_namelist != NULL); + } + else if (versym_scnndx != 0) + ERROR (gettext ("\ +.gnu.versym section present without .gnu.versym_d or .gnu.versym_r\n")); - if ((__BYTE_ORDER == __LITTLE_ENDIAN - && ehdr->e_ident[EI_DATA] == ELFDATA2MSB) - || (__BYTE_ORDER == __BIG_ENDIAN - && ehdr->e_ident[EI_DATA] == ELFDATA2LSB)) - { - namesz = bswap_64 (*ptr); - ++ptr; - descsz = bswap_64 (*ptr); - ++ptr; - type = bswap_64 (*ptr); - - namesz32 = bswap_32 (*ptr32); - ++ptr32; - descsz32 = bswap_32 (*ptr32); - } - else - { - namesz = *ptr++; - descsz = *ptr++; - type = *ptr; + if (hash_idx != 0 && gnu_hash_idx != 0) + compare_hash_gnu_hash (ebl, ehdr, hash_idx, gnu_hash_idx); - namesz32 = *ptr32++; - descsz32 = *ptr32; - } - } + free (scnref); +} - if (idx + 3 * align > phdr->p_filesz - || (idx + 3 * align + ALIGNED_LEN (namesz) + ALIGNED_LEN (descsz) - > phdr->p_filesz)) - { - if (ehdr->e_ident[EI_CLASS] == ELFCLASS64 - && idx + 3 * 4 <= phdr->p_filesz - && (idx + 3 * 4 + ALIGNED_LEN (namesz32) + ALIGNED_LEN (descsz32) - <= phdr->p_filesz)) - ERROR (gettext ("\ -phdr[%d]: note entries probably in form of a 32-bit ELF file\n"), cnt); - else - ERROR (gettext ("phdr[%d]: extra %zu bytes after last note\n"), - cnt, (size_t) (phdr->p_filesz - idx)); - break; - } + +static GElf_Off +check_note_data (Ebl *ebl, const GElf_Ehdr *ehdr, + Elf_Data *data, int shndx, int phndx, GElf_Off start) +{ + size_t offset = 0; + size_t last_offset = 0; + GElf_Nhdr nhdr; + size_t name_offset; + size_t desc_offset; + while (offset < data->d_size + && (offset = gelf_getnote (data, offset, + &nhdr, &name_offset, &desc_offset)) > 0) + { + last_offset = offset; /* Make sure it is one of the note types we know about. */ if (ehdr->e_type == ET_CORE) - { - switch (type) - { - case NT_PRSTATUS: - case NT_FPREGSET: - case NT_PRPSINFO: - case NT_TASKSTRUCT: /* NT_PRXREG on Solaris. */ - case NT_PLATFORM: - case NT_AUXV: - case NT_GWINDOWS: - case NT_ASRS: - case NT_PSTATUS: - case NT_PSINFO: - case NT_PRCRED: - case NT_UTSNAME: - case NT_LWPSTATUS: - case NT_LWPSINFO: - case NT_PRFPXREG: - /* Known type. */ + switch (nhdr.n_type) + { + case NT_PRSTATUS: + case NT_FPREGSET: + case NT_PRPSINFO: + case NT_TASKSTRUCT: /* NT_PRXREG on Solaris. */ + case NT_PLATFORM: + case NT_AUXV: + case NT_GWINDOWS: + case NT_ASRS: + case NT_PSTATUS: + case NT_PSINFO: + case NT_PRCRED: + case NT_UTSNAME: + case NT_LWPSTATUS: + case NT_LWPSINFO: + case NT_PRFPXREG: + /* Known type. */ + break; + + default: + if (shndx == 0) + ERROR (gettext ("\ +phdr[%d]: unknown core file note type %" PRIu32 " at offset %" PRIu64 "\n"), + phndx, (uint32_t) nhdr.n_type, start + offset); + else + ERROR (gettext ("\ +section [%2d] '%s': unknown core file note type %" PRIu32 + " at offset %Zu\n"), + shndx, section_name (ebl, shndx), + (uint32_t) nhdr.n_type, offset); + } + else + switch (nhdr.n_type) + { + case NT_GNU_ABI_TAG: + case NT_GNU_HWCAP: + case NT_GNU_BUILD_ID: + break; + + case 0: + /* Linux vDSOs use a type 0 note for the kernel version word. */ + if (nhdr.n_namesz == sizeof "Linux" + && !memcmp (data->d_buf + name_offset, "Linux", sizeof "Linux")) break; - default: + default: + if (shndx == 0) ERROR (gettext ("\ -phdr[%d]: unknown core file note type %" PRIu64 " at offset %" PRIu64 "\n"), - cnt, type, idx); - } - } - else - { - if (type != NT_VERSION) - ERROR (gettext ("\ -phdr[%d]: unknown object file note type %" PRIu64 " at offset %" PRIu64 "\n"), - cnt, type, idx); - } +phdr[%d]: unknown object file note type %" PRIu32 " at offset %Zu\n"), + phndx, (uint32_t) nhdr.n_type, offset); + else + ERROR (gettext ("\ +section [%2d] '%s': unknown object file note type %" PRIu32 + " at offset %Zu\n"), + shndx, section_name (ebl, shndx), + (uint32_t) nhdr.n_type, offset); + } + } - /* Move to the next entry. */ - idx += 3 * align + ALIGNED_LEN (namesz) + ALIGNED_LEN (descsz); + return last_offset; +} - } +static void +check_note (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Phdr *phdr, int cnt) +{ + if (ehdr->e_type != ET_CORE && ehdr->e_type != ET_REL + && ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) + ERROR (gettext ("\ +phdr[%d]: no note entries defined for the type of file\n"), + cnt); + + if (is_debuginfo) + /* The p_offset values in a separate debug file are bogus. */ + return; - gelf_freechunk (ebl->elf, notemem); + if (phdr->p_filesz == 0) + return; + + GElf_Off notes_size = 0; + Elf_Data *data = elf_getdata_rawchunk (ebl->elf, + phdr->p_offset, phdr->p_filesz, + ELF_T_NHDR); + if (data != NULL) + notes_size = check_note_data (ebl, ehdr, data, 0, cnt, phdr->p_offset); + + if (notes_size == 0) + ERROR (gettext ("phdr[%d]: cannot get content of note section: %s\n"), + cnt, elf_errmsg (-1)); + else if (notes_size != phdr->p_filesz) + ERROR (gettext ("phdr[%d]: extra %" PRIu64 " bytes after last note\n"), + cnt, phdr->p_filesz - notes_size); } +static void +check_note_section (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) +{ + if (shdr->sh_size == 0) + return; + + Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL); + if (data == NULL) + { + ERROR (gettext ("section [%2d] '%s': cannot get section data\n"), + idx, section_name (ebl, idx)); + return; + } + + if (ehdr->e_type != ET_CORE && ehdr->e_type != ET_REL + && ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) + ERROR (gettext ("\ +section [%2d] '%s': no note entries defined for the type of file\n"), + idx, section_name (ebl, idx)); + + GElf_Off notes_size = check_note_data (ebl, ehdr, data, idx, 0, 0); + + if (notes_size == 0) + ERROR (gettext ("section [%2d] '%s': cannot get content of note section\n"), + idx, section_name (ebl, idx)); + else if (notes_size != shdr->sh_size) + ERROR (gettext ("section [%2d] '%s': extra %" PRIu64 + " bytes after last note\n"), + idx, section_name (ebl, idx), shdr->sh_size - notes_size); +} static void check_program_header (Ebl *ebl, GElf_Ehdr *ehdr) @@ -2447,10 +4053,12 @@ only executables, shared objects, and core files can have program headers\n")); } if (phdr->p_type >= PT_NUM && phdr->p_type != PT_GNU_EH_FRAME - && phdr->p_type != PT_GNU_STACK) + && phdr->p_type != PT_GNU_STACK && phdr->p_type != PT_GNU_RELRO + /* Check for a known machine-specific type. */ + && ebl_segment_type_name (ebl, phdr->p_type, NULL, 0) == NULL) ERROR (gettext ("\ -program header entry %d: unknown program header entry type\n"), - cnt); +program header entry %d: unknown program header entry type %#" PRIx64 "\n"), + cnt, (uint64_t) phdr->p_type); if (phdr->p_type == PT_LOAD) has_loadable_segment = true; @@ -2471,9 +4079,33 @@ more than one INTERP entry in program header\n")); } else if (phdr->p_type == PT_NOTE) check_note (ebl, ehdr, phdr, cnt); - else if (phdr->p_type == PT_DYNAMIC - && ehdr->e_type == ET_EXEC && ! has_interp_segment) - ERROR (gettext ("static executable cannot have dynamic sections\n")); + else if (phdr->p_type == PT_DYNAMIC) + { + if (ehdr->e_type == ET_EXEC && ! has_interp_segment) + ERROR (gettext ("\ +static executable cannot have dynamic sections\n")); + else + { + /* Check that the .dynamic section, if it exists, has + the same address. */ + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr != NULL && shdr->sh_type == SHT_DYNAMIC) + { + if (phdr->p_offset != shdr->sh_offset) + ERROR (gettext ("\ +dynamic section reference in program header has wrong offset\n")); + if (phdr->p_memsz != shdr->sh_size) + ERROR (gettext ("\ +dynamic section size mismatch in program and section header\n")); + break; + } + } + } + } else if (phdr->p_type == PT_GNU_RELRO) { if (++num_pt_relro == 2) @@ -2488,7 +4120,7 @@ more than one GNU_RELRO entry in program header\n")); GElf_Phdr phdr2_mem; GElf_Phdr *phdr2; - phdr2 = gelf_getphdr (ebl->elf, cnt, &phdr2_mem); + phdr2 = gelf_getphdr (ebl->elf, inner, &phdr2_mem); if (phdr2 == NULL) continue; @@ -2500,20 +4132,50 @@ more than one GNU_RELRO entry in program header\n")); if ((phdr2->p_flags & PF_W) == 0) ERROR (gettext ("\ loadable segment GNU_RELRO applies to is not writable\n")); - if ((phdr2->p_flags & PF_X) != 0) + if ((phdr2->p_flags &~ PF_W) != (phdr->p_flags &~ PF_W)) ERROR (gettext ("\ -loadable segment GNU_RELRO applies to is executable\n")); +loadable segment [%u] flags do not match GNU_RELRO [%u] flags\n"), + cnt, inner); break; } } if (inner >= ehdr->e_phnum) ERROR (gettext ("\ -GNU_RELRO segment not contained in a loaded segment\n")); +%s segment not contained in a loaded segment\n"), "GNU_RELRO"); } } + else if (phdr->p_type == PT_PHDR) + { + /* Check that the region is in a writable segment. */ + int inner; + for (inner = 0; inner < ehdr->e_phnum; ++inner) + { + GElf_Phdr phdr2_mem; + GElf_Phdr *phdr2; + + phdr2 = gelf_getphdr (ebl->elf, inner, &phdr2_mem); + if (phdr2 != NULL + && phdr2->p_type == PT_LOAD + && phdr->p_vaddr >= phdr2->p_vaddr + && (phdr->p_vaddr + phdr->p_memsz + <= phdr2->p_vaddr + phdr2->p_memsz)) + break; + } - if (phdr->p_filesz > phdr->p_memsz) + if (inner >= ehdr->e_phnum) + ERROR (gettext ("\ +%s segment not contained in a loaded segment\n"), "PHDR"); + + /* Check that offset in segment corresponds to offset in ELF + header. */ + if (phdr->p_offset != ehdr->e_phoff) + ERROR (gettext ("\ +program header offset in ELF header and PHDR entry do not match")); + } + + if (phdr->p_filesz > phdr->p_memsz + && (phdr->p_memsz != 0 || phdr->p_type != PT_NOTE)) ERROR (gettext ("\ program header entry %d: file size greater than memory size\n"), cnt); @@ -2538,6 +4200,12 @@ process_elf_file (Elf *elf, const char *prefix, const char *suffix, { /* Reset variables. */ ndynamic = 0; + nverneed = 0; + nverdef = 0; + textrel = false; + needed_textrel = false; + has_loadable_segment = false; + has_interp_segment = false; GElf_Ehdr ehdr_mem; GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); @@ -2573,6 +4241,13 @@ process_elf_file (Elf *elf, const char *prefix, const char *suffix, headers at all. */ check_sections (ebl, ehdr); + /* Report if no relocation section needed the text relocation flag. */ + if (textrel && !needed_textrel) + ERROR (gettext ("text relocation flag set but not needed\n")); + /* Free the resources. */ ebl_closebackend (ebl); } + + +#include "debugpred.h" diff --git a/src/findtextrel.c b/src/findtextrel.c new file mode 100644 index 00000000..245db7fe --- /dev/null +++ b/src/findtextrel.c @@ -0,0 +1,611 @@ +/* Locate source files or functions which caused text relocations. + Copyright (C) 2005, 2006, 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + Written by Ulrich Drepper <drepper@redhat.com>, 2005. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <argp.h> +#include <assert.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <gelf.h> +#include <libdw.h> +#include <libintl.h> +#include <locale.h> +#include <search.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + + +struct segments +{ + GElf_Addr from; + GElf_Addr to; +}; + + +/* Name and version of program. */ +static void print_version (FILE *stream, struct argp_state *state); +void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; + +/* Bug report address. */ +const char *argp_program_bug_address = PACKAGE_BUGREPORT; + +/* Values for the parameters which have no short form. */ +#define OPT_DEBUGINFO 0x100 + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + { NULL, 0, NULL, 0, N_("Input Selection:"), 0 }, + { "root", 'r', "PATH", 0, N_("Prepend PATH to all file names"), 0 }, + { "debuginfo", OPT_DEBUGINFO, "PATH", 0, + N_("Use PATH as root of debuginfo hierarchy"), 0 }, + + { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 }, + { NULL, 0, NULL, 0, NULL, 0 } +}; + +/* Short description of program. */ +static const char doc[] = N_("\ +Locate source of text relocations in FILEs (a.out by default)."); + +/* Strings for arguments in help texts. */ +static const char args_doc[] = N_("[FILE...]"); + +/* Prototype for option handler. */ +static error_t parse_opt (int key, char *arg, struct argp_state *state); + +/* Data structure to communicate with argp functions. */ +static struct argp argp = +{ + options, parse_opt, args_doc, doc, NULL, NULL, NULL +}; + + +/* Print symbols in file named FNAME. */ +static int process_file (const char *fname, bool more_than_one); + +/* Check for text relocations in the given file. The segment + information is known. */ +static void check_rel (size_t nsegments, struct segments segments[nsegments], + GElf_Addr addr, Elf *elf, Elf_Scn *symscn, Dwarf *dw, + const char *fname, bool more_than_one, + void **knownsrcs); + + + +/* User-provided root directory. */ +static const char *rootdir = "/"; + +/* Root of debuginfo directory hierarchy. */ +static const char *debuginfo_root; + + +int +main (int argc, char *argv[]) +{ + int remaining; + int result = 0; + + /* Set locale. */ + (void) setlocale (LC_ALL, ""); + + /* Make sure the message catalog can be found. */ + (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR); + + /* Initialize the message catalog. */ + (void) textdomain (PACKAGE_TARNAME); + + /* Parse and process arguments. */ + (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL); + + /* Tell the library which version we are expecting. */ + elf_version (EV_CURRENT); + + /* If the user has not specified the root directory for the + debuginfo hierarchy, we have to determine it ourselves. */ + if (debuginfo_root == NULL) + { + // XXX The runtime should provide this information. +#if defined __ia64__ || defined __alpha__ + debuginfo_root = "/usr/lib/debug"; +#else + debuginfo_root = (sizeof (long int) == 4 + ? "/usr/lib/debug" : "/usr/lib64/debug"); +#endif + } + + if (remaining == argc) + result = process_file ("a.out", false); + else + { + /* Process all the remaining files. */ + const bool more_than_one = remaining + 1 < argc; + + do + result |= process_file (argv[remaining], more_than_one); + while (++remaining < argc); + } + + return result; +} + + +/* Print the version information. */ +static void +print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) +{ + fprintf (stream, "findtextrel (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION); + fprintf (stream, gettext ("\ +Copyright (C) %s Red Hat, Inc.\n\ +This is free software; see the source for copying conditions. There is NO\n\ +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ +"), "2008"); + fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); +} + + +/* Handle program arguments. */ +static error_t +parse_opt (int key, char *arg, + struct argp_state *state __attribute__ ((unused))) +{ + switch (key) + { + case 'r': + rootdir = arg; + break; + + case OPT_DEBUGINFO: + debuginfo_root = arg; + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + + +static void +noop (void *arg __attribute__ ((unused))) +{ +} + + +static int +process_file (const char *fname, bool more_than_one) +{ + int result = 0; + void *knownsrcs = NULL; + + size_t fname_len = strlen (fname); + size_t rootdir_len = strlen (rootdir); + const char *real_fname = fname; + if (fname[0] == '/' && (rootdir[0] != '/' || rootdir[1] != '\0')) + { + /* Prepend the user-provided root directory. */ + char *new_fname = alloca (rootdir_len + fname_len + 2); + *((char *) mempcpy (stpcpy (mempcpy (new_fname, rootdir, rootdir_len), + "/"), + fname, fname_len)) = '\0'; + real_fname = new_fname; + } + + int fd = open64 (real_fname, O_RDONLY); + if (fd == -1) + { + error (0, errno, gettext ("cannot open '%s'"), fname); + return 1; + } + + Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL); + if (elf == NULL) + { + error (0, 0, gettext ("cannot create ELF descriptor for '%s': %s"), + fname, elf_errmsg (-1)); + goto err_close; + } + + /* Make sure the file is a DSO. */ + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); + if (ehdr == NULL) + { + error (0, 0, gettext ("cannot get ELF header '%s': %s"), + fname, elf_errmsg (-1)); + err_elf_close: + elf_end (elf); + err_close: + close (fd); + return 1; + } + + if (ehdr->e_type != ET_DYN) + { + error (0, 0, gettext ("'%s' is not a DSO or PIE"), fname); + goto err_elf_close; + } + + /* Determine whether the DSO has text relocations at all and locate + the symbol table. */ + Elf_Scn *symscn = NULL; + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (elf, scn)) != NULL) + { + /* Handle the section if it is a symbol table. */ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + + if (shdr == NULL) + { + error (0, 0, + gettext ("getting get section header of section %zu: %s"), + elf_ndxscn (scn), elf_errmsg (-1)); + goto err_elf_close; + } + + if (shdr->sh_type == SHT_DYNAMIC) + { + Elf_Data *data = elf_getdata (scn, NULL); + + for (size_t cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize; + ++cnt) + { + GElf_Dyn dynmem; + GElf_Dyn *dyn; + + dyn = gelf_getdyn (data, cnt, &dynmem); + if (dyn == NULL) + { + error (0, 0, gettext ("cannot read dynamic section: %s"), + elf_errmsg (-1)); + goto err_elf_close; + } + + if (dyn->d_tag == DT_TEXTREL + || (dyn->d_tag == DT_FLAGS + && (dyn->d_un.d_val & DF_TEXTREL) != 0)) + goto have_textrel; + } + } + else if (shdr->sh_type == SHT_SYMTAB) + symscn = scn; + } + + error (0, 0, gettext ("no text relocations reported in '%s'"), fname); + return 1; + + have_textrel:; + int fd2 = -1; + Elf *elf2 = NULL; + /* Get the address ranges for the loaded segments. */ + size_t nsegments_max = 10; + size_t nsegments = 0; + struct segments *segments + = (struct segments *) malloc (nsegments_max * sizeof (segments[0])); + if (segments == NULL) + error (1, errno, gettext ("while reading ELF file")); + + for (int i = 0; i < ehdr->e_phnum; ++i) + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem); + if (phdr == NULL) + { + error (0, 0, + gettext ("cannot get program header index at offset %d: %s"), + i, elf_errmsg (-1)); + result = 1; + goto next; + } + + if (phdr->p_type == PT_LOAD && (phdr->p_flags & PF_W) == 0) + { + if (nsegments == nsegments_max) + { + nsegments_max *= 2; + segments + = (struct segments *) realloc (segments, + nsegments_max + * sizeof (segments[0])); + if (segments == NULL) + { + error (0, 0, gettext ("\ +cannot get program header index at offset %d: %s"), + i, elf_errmsg (-1)); + result = 1; + goto next; + } + } + + segments[nsegments].from = phdr->p_vaddr; + segments[nsegments].to = phdr->p_vaddr + phdr->p_memsz; + ++nsegments; + } + } + + if (nsegments > 0) + { + + Dwarf *dw = dwarf_begin_elf (elf, DWARF_C_READ, NULL); + /* Look for debuginfo files if the information is not the in + opened file itself. This makes only sense if the input file + is specified with an absolute path. */ + if (dw == NULL && fname[0] == '/') + { + size_t debuginfo_rootlen = strlen (debuginfo_root); + char *difname = (char *) alloca (rootdir_len + debuginfo_rootlen + + fname_len + 8); + strcpy (mempcpy (stpcpy (mempcpy (mempcpy (difname, rootdir, + rootdir_len), + debuginfo_root, + debuginfo_rootlen), + "/"), + fname, fname_len), + ".debug"); + + fd2 = open64 (difname, O_RDONLY); + if (fd2 != -1 + && (elf2 = elf_begin (fd2, ELF_C_READ_MMAP, NULL)) != NULL) + dw = dwarf_begin_elf (elf2, DWARF_C_READ, NULL); + } + + /* Look at all relocations and determine which modify + write-protected segments. */ + scn = NULL; + while ((scn = elf_nextscn (elf, scn)) != NULL) + { + /* Handle the section if it is a symbol table. */ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + + if (shdr == NULL) + { + error (0, 0, + gettext ("cannot get section header of section %Zu: %s"), + elf_ndxscn (scn), elf_errmsg (-1)); + result = 1; + goto next; + } + + if ((shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA) + && symscn == NULL) + { + symscn = elf_getscn (elf, shdr->sh_link); + if (symscn == NULL) + { + error (0, 0, gettext ("\ +cannot get symbol table section %zu in '%s': %s"), + (size_t) shdr->sh_link, fname, elf_errmsg (-1)); + result = 1; + goto next; + } + } + + if (shdr->sh_type == SHT_REL) + { + Elf_Data *data = elf_getdata (scn, NULL); + + for (int cnt = 0; + (size_t) cnt < shdr->sh_size / shdr->sh_entsize; + ++cnt) + { + GElf_Rel rel_mem; + GElf_Rel *rel = gelf_getrel (data, cnt, &rel_mem); + if (rel == NULL) + { + error (0, 0, gettext ("\ +cannot get relocation at index %d in section %zu in '%s': %s"), + cnt, elf_ndxscn (scn), fname, elf_errmsg (-1)); + result = 1; + goto next; + } + + check_rel (nsegments, segments, rel->r_offset, elf, + symscn, dw, fname, more_than_one, &knownsrcs); + } + } + else if (shdr->sh_type == SHT_RELA) + { + Elf_Data *data = elf_getdata (scn, NULL); + + for (int cnt = 0; + (size_t) cnt < shdr->sh_size / shdr->sh_entsize; + ++cnt) + { + GElf_Rela rela_mem; + GElf_Rela *rela = gelf_getrela (data, cnt, &rela_mem); + if (rela == NULL) + { + error (0, 0, gettext ("\ +cannot get relocation at index %d in section %zu in '%s': %s"), + cnt, elf_ndxscn (scn), fname, elf_errmsg (-1)); + result = 1; + goto next; + } + + check_rel (nsegments, segments, rela->r_offset, elf, + symscn, dw, fname, more_than_one, &knownsrcs); + } + } + } + + dwarf_end (dw); + } + + next: + elf_end (elf); + elf_end (elf2); + close (fd); + if (fd2 != -1) + close (fd2); + + tdestroy (knownsrcs, noop); + + return result; +} + + +static int +ptrcompare (const void *p1, const void *p2) +{ + if ((uintptr_t) p1 < (uintptr_t) p2) + return -1; + if ((uintptr_t) p1 > (uintptr_t) p2) + return 1; + return 0; +} + + +static void +check_rel (size_t nsegments, struct segments segments[nsegments], + GElf_Addr addr, Elf *elf, Elf_Scn *symscn, Dwarf *dw, + const char *fname, bool more_than_one, void **knownsrcs) +{ + for (size_t cnt = 0; cnt < nsegments; ++cnt) + if (segments[cnt].from <= addr && segments[cnt].to > addr) + { + Dwarf_Die die_mem; + Dwarf_Die *die; + Dwarf_Line *line; + const char *src; + + if (more_than_one) + printf ("%s: ", fname); + + if ((die = dwarf_addrdie (dw, addr, &die_mem)) != NULL + && (line = dwarf_getsrc_die (die, addr)) != NULL + && (src = dwarf_linesrc (line, NULL, NULL)) != NULL) + { + /* There can be more than one relocation against one file. + Try to avoid multiple messages. And yes, the code uses + pointer comparison. */ + if (tfind (src, knownsrcs, ptrcompare) == NULL) + { + printf (gettext ("%s not compiled with -fpic/-fPIC\n"), src); + tsearch (src, knownsrcs, ptrcompare); + } + return; + } + else + { + /* At least look at the symbol table to see which function + the modified address is in. */ + Elf_Data *symdata = elf_getdata (symscn, NULL); + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (symscn, &shdr_mem); + if (shdr != NULL) + { + GElf_Addr lowaddr = 0; + int lowidx = -1; + GElf_Addr highaddr = ~0ul; + int highidx = -1; + GElf_Sym sym_mem; + GElf_Sym *sym; + + for (int i = 0; (size_t) i < shdr->sh_size / shdr->sh_entsize; + ++i) + { + sym = gelf_getsym (symdata, i, &sym_mem); + if (sym == NULL) + continue; + + if (sym->st_value < addr && sym->st_value > lowaddr) + { + lowaddr = sym->st_value; + lowidx = i; + } + if (sym->st_value > addr && sym->st_value < highaddr) + { + highaddr = sym->st_value; + highidx = i; + } + } + + if (lowidx != -1) + { + sym = gelf_getsym (symdata, lowidx, &sym_mem); + assert (sym != NULL); + + const char *lowstr = elf_strptr (elf, shdr->sh_link, + sym->st_name); + + if (sym->st_value + sym->st_size > addr) + { + /* It is this function. */ + if (tfind (lowstr, knownsrcs, ptrcompare) == NULL) + { + printf (gettext ("\ +the file containing the function '%s' is not compiled with -fpic/-fPIC\n"), + lowstr); + tsearch (lowstr, knownsrcs, ptrcompare); + } + } + else if (highidx == -1) + printf (gettext ("\ +the file containing the function '%s' might not be compiled with -fpic/-fPIC\n"), + lowstr); + else + { + sym = gelf_getsym (symdata, highidx, &sym_mem); + assert (sym != NULL); + + printf (gettext ("\ +either the file containing the function '%s' or the file containing the function '%s' is not compiled with -fpic/-fPIC\n"), + lowstr, elf_strptr (elf, shdr->sh_link, + sym->st_name)); + } + return; + } + else if (highidx != -1) + { + sym = gelf_getsym (symdata, highidx, &sym_mem); + assert (sym != NULL); + + printf (gettext ("\ +the file containing the function '%s' might not be compiled with -fpic/-fPIC\n"), + elf_strptr (elf, shdr->sh_link, sym->st_name)); + return; + } + } + } + + printf (gettext ("\ +a relocation modifies memory at offset %llu in a write-protected segment\n"), + (unsigned long long int) addr); + break; + } +} + + +#include "debugpred.h" diff --git a/src/i386_ld.c b/src/i386_ld.c index 28304ca0..2702ef85 100644 --- a/src/i386_ld.c +++ b/src/i386_ld.c @@ -1,15 +1,27 @@ -/* Copyright (C) 2001, 2002, 2003, 2004 Red Hat, Inc. +/* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2008 Red Hat, Inc. + This file is part of Red Hat elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 2001. - This program is Open Source software; you can redistribute it and/or - modify it under the terms of the Open Software License version 1.0 as - published by the Open Source Initiative. + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. - You should have received a copy of the Open Software License along - with this program; if not, you may obtain a copy of the Open Software - License version 1.0 from http://www.opensource.org/licenses/osl.php or - by writing the Open Source Initiative c/o Lawrence Rosen, Esq., - 3001 King Ranch Road, Ukiah, CA 95482. */ + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ #ifdef HAVE_CONFIG_H # include <config.h> @@ -38,8 +50,10 @@ static int (*old_open_outfile) (struct ld_state *, int, int, int); static int -elf_i386_open_outfile (struct ld_state *statep, int machine, int klass, - int data) +elf_i386_open_outfile (struct ld_state *statep, + int machine __attribute__ ((unused)), + int klass __attribute__ ((unused)), + int data __attribute__ ((unused))) { /* This backend only handles 32-bit object files. */ /* XXX For now just use the generic backend. */ @@ -50,15 +64,15 @@ elf_i386_open_outfile (struct ld_state *statep, int machine, int klass, /* Process relocations for the output in a relocatable file. This only means adjusting offset and symbol indices. */ static void -elf_i386_relocate_section (struct ld_state *statep, Elf_Scn *outscn, - struct scninfo *firstp, +elf_i386_relocate_section (struct ld_state *statep __attribute__ ((unused)), + Elf_Scn *outscn, struct scninfo *firstp, const Elf32_Word *dblindirect) { struct scninfo *runp; Elf_Data *data; /* Iterate over all the input sections. Appropriate data buffers in the - output sections were already created. I get them iteratively, too. */ + output sections were already created. */ runp = firstp; data = NULL; do @@ -135,8 +149,8 @@ elf_i386_relocate_section (struct ld_state *statep, Elf_Scn *outscn, assert (xndx < SHN_LORESERVE || xndx > SHN_HIRESERVE); /* We fortunately don't have to do much. The relocations - mostly get only updates of the offset. Only is a - relocation referred to a section do we have to do + mostly get only updates of the offset. Only for a + relocation referring to a section do we have to do something. In this case the reference to the sections has no direct equivalent since the part the input section contributes need not start at the same offset as in the @@ -145,15 +159,13 @@ elf_i386_relocate_section (struct ld_state *statep, Elf_Scn *outscn, itself. */ if (XELF_ST_TYPE (sym->st_info) == STT_SECTION) { - Elf32_Word toadd; - - /* We expect here on R_386_32 relocations. */ + /* We expect here only R_386_32 relocations. */ assert (XELF_R_TYPE (rel->r_info) == R_386_32); /* Avoid writing to the section memory if this is effectively a no-op since it might save a copy-on-write operation. */ - toadd = file->scninfo[xndx].offset; + Elf32_Word toadd = file->scninfo[xndx].offset; if (toadd != 0) add_4ubyte_unaligned (reltgtdata->d_buf + rel->r_offset, toadd); @@ -202,8 +214,9 @@ elf_i386_initialize_plt (struct ld_state *statep, Elf_Scn *scn) relocation routines) and one for each function we call in a DSO. */ data->d_size = (1 + statep->nplt) * PLT_ENTRY_SIZE; data->d_buf = xcalloc (1, data->d_size); - data->d_align = 8; + assert (data->d_type == ELF_T_BYTE); data->d_off = 0; + data->d_align = 8; statep->nplt_used = 1; } @@ -220,9 +233,10 @@ elf_i386_initialize_pltrel (struct ld_state *statep, Elf_Scn *scn) elf_errmsg (-1)); /* One relocation per PLT entry. */ - data->d_size = statep->nplt * sizeof (Elf32_Rel); - data->d_buf = xcalloc (1, data->d_size); + size_t size = statep->nplt * sizeof (Elf32_Rel); + data->d_buf = xcalloc (1, size); data->d_type = ELF_T_REL; + data->d_size = size; data->d_align = 4; data->d_off = 0; } @@ -231,26 +245,45 @@ elf_i386_initialize_pltrel (struct ld_state *statep, Elf_Scn *scn) static void elf_i386_initialize_got (struct ld_state *statep, Elf_Scn *scn) { - Elf_Data *data; - - /* If we have no .plt we don't need the special entries we normally - create for it. The other contents is created later. */ - if (statep->ngot + statep->nplt == 0) - return; + /* If we come here we better need a GOT. */ + assert (statep->ngot != 0); - data = elf_newdata (scn); + Elf_Data *data = elf_newdata (scn); if (data == NULL) error (EXIT_FAILURE, 0, gettext ("cannot allocate GOT section: %s"), elf_errmsg (-1)); - /* We construct the .got section in pieces. Here we only add the data + /* Just a single word per GOT entry is needed. */ + size_t size = statep->ngot * sizeof (Elf32_Addr); + data->d_buf = xcalloc (1, size); + data->d_size = size; + data->d_type = ELF_T_WORD; + data->d_off = 0; + data->d_align = sizeof (Elf32_Addr); +} + + +static void +elf_i386_initialize_gotplt (struct ld_state *statep, Elf_Scn *scn) +{ + /* If we come here we better need a PLT. */ + assert (statep->nplt != 0); + + Elf_Data *data = elf_newdata (scn); + if (data == NULL) + error (EXIT_FAILURE, 0, gettext ("cannot allocate GOTPLT section: %s"), + elf_errmsg (-1)); + + /* We construct the .got.plt section in pieces. Here we only add the data structures which are used by the PLT. This includes three reserved entries at the beginning (the first will contain a pointer to the .dynamic section), and one word for each PLT entry. */ - data->d_size = (3 + statep->ngot + statep->nplt) * sizeof (Elf32_Addr); - data->d_buf = xcalloc (1, data->d_size); - data->d_align = sizeof (Elf32_Addr); + size_t size = (3 + statep->nplt) * sizeof (Elf32_Addr); + data->d_buf = xcalloc (1, size); + data->d_type = ELF_T_WORD; + data->d_size = size; data->d_off = 0; + data->d_align = sizeof (Elf32_Addr); } @@ -262,7 +295,8 @@ static const unsigned char elf_i386_plt0_entry[PLT_ENTRY_SIZE] = 0, 0, 0, 0, /* replaced with address of .got + 4. */ 0xff, 0x25, /* jmp indirect */ 0, 0, 0, 0, /* replaced with address of .got + 8. */ - 0, 0, 0, 0 /* pad out to 16 bytes. */ + 0x0f, 0x0b, /* ud2a, to prevent further decoding. */ + 0, 0 /* pad out to 16 bytes. */ }; /* Type describing the first PLT entry in non-PIC. */ @@ -283,7 +317,8 @@ static const unsigned char elf_i386_pic_plt0_entry[PLT_ENTRY_SIZE] = { 0xff, 0xb3, 4, 0, 0, 0, /* pushl 4(%ebx) */ 0xff, 0xa3, 8, 0, 0, 0, /* jmp *8(%ebx) */ - 0, 0, 0, 0 /* pad out to 16 bytes. */ + 0x0f, 0x0b, /* ud2a, to prevent further decoding. */ + 0, 0 /* pad out to 16 bytes. */ }; /* Contents of all but the first PLT entry in executable. */ @@ -324,46 +359,48 @@ struct plt_entry static void -elf_i386_finalize_plt (struct ld_state *statep, size_t nsym, size_t nsym_dyn) +elf_i386_finalize_plt (struct ld_state *statep, size_t nsym, + size_t nsym_local, struct symbol **ndxtosym) { + if (unlikely (statep->nplt + statep->ngot == 0)) + /* Nothing to be done. */ + return; + Elf_Scn *scn; XElf_Shdr_vardef (shdr); Elf_Data *data; - Elf_Data *symdata = NULL; - Elf_Data *dynsymdata; - size_t cnt; const bool build_dso = statep->file_type == dso_file_type; - if (unlikely (statep->nplt + statep->ngot == 0)) - /* Nothing to be done. */ - return; - - /* Get the address of the got section. */ - scn = elf_getscn (statep->outelf, statep->gotscnidx); + /* Get the address of the .got.plt section. */ + scn = elf_getscn (statep->outelf, statep->gotpltscnidx); xelf_getshdr (scn, shdr); data = elf_getdata (scn, NULL); assert (shdr != NULL && data != NULL); + /* The address points to the .got.plt section, not the .got section. */ Elf32_Addr gotaddr = shdr->sh_addr; - /* Now create the initial values for the .got section. The first - word contains the address of the .dynamic section. */ + /* Now create the initial values for the .got.plt section. The + first word contains the address of the .dynamic section. The + second and third entry are left empty for use by the dynamic + linker. The following entries are pointers to the instructions + following the initial jmp instruction in the corresponding PLT + entry. */ xelf_getshdr (elf_getscn (statep->outelf, statep->dynamicscnidx), shdr); assert (shdr != NULL); ((Elf32_Word *) data->d_buf)[0] = shdr->sh_addr; - /* The second and third entry are left empty for use by the dynamic - linker. The following entries are pointers to the instructions - following the initial jmp instruction in the corresponding PLT - entry. Since the first PLT entry is special the first used one - has the index 1. */ + /* The PLT contains code which a user of a function jumps to. The first + PLT entry is special, so the first used one has the index 1. */ scn = elf_getscn (statep->outelf, statep->pltscnidx); - xelf_getshdr (scn, shdr); - assert (shdr != NULL); + XElf_Shdr_vardef (pltshdr); + xelf_getshdr (scn, pltshdr); + assert (pltshdr != NULL); - dynsymdata = elf_getdata (elf_getscn (statep->outelf, statep->dynsymscnidx), - NULL); + Elf_Data *dynsymdata = elf_getdata (elf_getscn (statep->outelf, + statep->dynsymscnidx), NULL); assert (dynsymdata != NULL); + Elf_Data *symdata = NULL; if (statep->symscnidx != 0) { symdata = elf_getdata (elf_getscn (statep->outelf, statep->symscnidx), @@ -371,43 +408,40 @@ elf_i386_finalize_plt (struct ld_state *statep, size_t nsym, size_t nsym_dyn) assert (symdata != NULL); } - for (cnt = 0; cnt < statep->nplt; ++cnt) - { - assert ((4 + cnt) * sizeof (Elf32_Word) <= data->d_size); - - /* Address in the PLT. */ - Elf32_Addr pltentryaddr = shdr->sh_addr + (1 + cnt) * PLT_ENTRY_SIZE; - - /* Point the GOT entry at the PLT entry, after the initial jmp. */ - ((Elf32_Word *) data->d_buf)[3 + cnt] = pltentryaddr + 6; - - /* The value of the symbol is the address of the corresponding PLT - entry. Store the address, also for the normal symbol table if - this is necessary. */ - ((Elf32_Sym *) dynsymdata->d_buf)[1 + cnt].st_value = pltentryaddr; - - if (symdata != NULL) - ((Elf32_Sym *) symdata->d_buf)[nsym - statep->nplt + cnt].st_value - = pltentryaddr; - } - /* Create the .plt section. */ scn = elf_getscn (statep->outelf, statep->pltscnidx); - data = elf_getdata (scn, NULL); - assert (data != NULL); + Elf_Data *pltdata = elf_getdata (scn, NULL); + assert (pltdata != NULL); - /* Create the first entry. */ - assert (data->d_size >= PLT_ENTRY_SIZE); + /* Also create the .rel.plt section data. It simply means relocations + addressing the corresponding entry in the .got.plt section. The + section name is misleading. */ + scn = elf_getscn (statep->outelf, statep->pltrelscnidx); + xelf_getshdr (scn, shdr); + Elf_Data *reldata = elf_getdata (scn, NULL); + assert (shdr != NULL && reldata != NULL); + + /* Update the sh_link to point to the section being modified. We + point it here (correctly) to the .got.plt section. Some linkers + (e.g., the GNU binutils linker) point to the .plt section. This + is wrong since the .plt section isn't modified even though the + name .rel.plt suggests that this is correct. */ + shdr->sh_link = statep->dynsymscnidx; + shdr->sh_info = statep->gotpltscnidx; + (void) xelf_update_shdr (scn, shdr); + + /* Create the first entry of the .plt section. */ + assert (pltdata->d_size >= PLT_ENTRY_SIZE); if (build_dso) /* Copy the entry. It's complete, no relocation needed. */ - memcpy (data->d_buf, elf_i386_pic_plt0_entry, PLT_ENTRY_SIZE); + memcpy (pltdata->d_buf, elf_i386_pic_plt0_entry, PLT_ENTRY_SIZE); else { /* Copy the skeleton. */ - memcpy (data->d_buf, elf_i386_plt0_entry, PLT_ENTRY_SIZE); + memcpy (pltdata->d_buf, elf_i386_plt0_entry, PLT_ENTRY_SIZE); /* And fill in the addresses. */ - struct plt0_entry *addr = (struct plt0_entry *) data->d_buf; + struct plt0_entry *addr = (struct plt0_entry *) pltdata->d_buf; addr->gotp4_addr = target_bswap_32 (gotaddr + 4); addr->gotp8_addr = target_bswap_32 (gotaddr + 8); } @@ -419,54 +453,68 @@ elf_i386_finalize_plt (struct ld_state *statep, size_t nsym, size_t nsym_dyn) const unsigned char *plt_template = build_dso ? elf_i386_pic_plt_entry : elf_i386_plt_entry; - for (cnt = 0; cnt < statep->nplt; ++cnt) + for (size_t idx = nsym_local; idx < nsym; ++idx) { - struct plt_entry *addr; + struct symbol *symbol = ndxtosym[idx]; + if (symbol == NULL || symbol->type != STT_FUNC + || ndxtosym[idx]->outdynsymidx == 0 + // XXX is the following test correct? + || ! ndxtosym[idx]->in_dso) + continue; + + size_t pltidx = symbol->merge.value; - /* Copy the template. */ - assert (data->d_size >= (2 + cnt) * PLT_ENTRY_SIZE); - addr = (struct plt_entry *) ((char *) data->d_buf - + (1 + cnt) * PLT_ENTRY_SIZE); + assert (pltidx > 0); + assert ((3 + pltidx) * sizeof (Elf32_Word) <= data->d_size); + + /* Address in the PLT. */ + Elf32_Addr pltentryaddr = (pltshdr->sh_addr + pltidx * PLT_ENTRY_SIZE); + + /* Point the GOT entry at the PLT entry, after the initial jmp. */ + ((Elf32_Word *) data->d_buf)[2 + pltidx] = pltentryaddr + 6; + + /* If the symbol is defined, adjust the address. */ + if (((Elf32_Sym *) dynsymdata->d_buf)[ndxtosym[idx]->outdynsymidx].st_shndx != SHN_UNDEF) + { + /* The value of the symbol is the address of the corresponding PLT + entry. Store the address, also for the normal symbol table if + this is necessary. */ + ((Elf32_Sym *) dynsymdata->d_buf)[pltidx].st_value = pltentryaddr; + + if (symdata != NULL) + { + assert(nsym - statep->nplt + (pltidx - 1) == idx); + ((Elf32_Sym *) symdata->d_buf)[nsym - statep->nplt + + (pltidx - 1)].st_value + = pltentryaddr; + } + } + + /* Copy the PLT entry template. */ + assert (pltdata->d_size >= (1 + pltidx) * PLT_ENTRY_SIZE); + struct plt_entry *addr = (struct plt_entry *) ((char *) pltdata->d_buf + + (pltidx + * PLT_ENTRY_SIZE)); memcpy (addr, plt_template, PLT_ENTRY_SIZE); /* And once more, fill in the addresses. First the address of this symbol in .got. */ addr->offset_got = target_bswap_32 (gotaddr_off - + (3 + cnt) * sizeof (Elf32_Addr)); + + (2 + pltidx) * sizeof (Elf32_Addr)); /* Offset into relocation table. */ - addr->push_imm = target_bswap_32 (cnt * sizeof (Elf32_Rel)); + addr->push_imm = target_bswap_32 ((pltidx - 1) * sizeof (Elf32_Rel)); /* Offset to start of .plt. */ - addr->plt0_offset = target_bswap_32 (-(2 + cnt) * PLT_ENTRY_SIZE); - } + addr->plt0_offset = target_bswap_32 (-(1 + pltidx) * PLT_ENTRY_SIZE); - /* Create the .rel.plt section data. It simply means relocations - addressing the corresponding entry in the .got section. The - section name is misleading. */ - scn = elf_getscn (statep->outelf, statep->pltrelscnidx); - xelf_getshdr (scn, shdr); - data = elf_getdata (scn, NULL); - assert (shdr != NULL && data != NULL); - - /* Update the sh_link to point to the section being modified. We - point it here (correctly) to the .got section. Some linkers - (e.g., the GNU binutils linker) point to the .plt section. This - is wrong since the .plt section isn't modified even though the - name .rel.plt suggests that this is correct. */ - shdr->sh_link = statep->dynsymscnidx; - shdr->sh_info = statep->gotscnidx; - (void) xelf_update_shdr (scn, shdr); - for (cnt = 0; cnt < statep->nplt; ++cnt) - { XElf_Rel_vardef (rel); - - assert ((1 + cnt) * sizeof (Elf32_Rel) <= data->d_size); - xelf_getrel_ptr (data, cnt, rel); - rel->r_offset = gotaddr + (3 + cnt) * sizeof (Elf32_Addr); + assert (pltidx * sizeof (Elf32_Rel) <= reldata->d_size); + xelf_getrel_ptr (reldata, pltidx - 1, rel); + rel->r_offset = gotaddr + (2 + pltidx) * sizeof (Elf32_Addr); /* The symbol table entries for the functions from DSOs are at - the end of the symbol table. */ - rel->r_info = XELF_R_INFO (1 + cnt, R_386_JMP_SLOT); - (void) xelf_update_rel (data, cnt, rel); + the beginning of the symbol table. */ + rel->r_info = XELF_R_INFO (ndxtosym[idx]->outdynsymidx, R_386_JMP_SLOT); + (void) xelf_update_rel (reldata, pltidx - 1, rel); } } @@ -503,18 +551,28 @@ elf_i386_count_relocations (struct ld_state *statep, struct scninfo *scninfo) /* XXX Should we complain about failing accesses? */ if (rel != NULL) { - int r_sym = XELF_R_SYM (rel->r_info); + Elf32_Word r_sym = XELF_R_SYM (rel->r_info); + + /* Symbols in COMDAT group sections which are discarded do + not have to be relocated. */ + if (r_sym >= scninfo->fileinfo->nlocalsymbols + && unlikely (scninfo->fileinfo->symref[r_sym] == NULL)) + continue; switch (XELF_R_TYPE (rel->r_info)) { case R_386_GOT32: - if (! scninfo->fileinfo->symref[r_sym]->defined) - relsize += sizeof (Elf32_Rel); + if (! scninfo->fileinfo->symref[r_sym]->defined + || scninfo->fileinfo->symref[r_sym]->in_dso + || statep->file_type == dso_file_type) + { + relsize += sizeof (Elf32_Rel); + ++statep->nrel_got; + } - /* This relocation is not emitted in the output file but - requires a GOT entry. */ + /* Even if this relocation is not emitted in the output + file it requires a GOT entry. */ ++statep->ngot; - ++statep->nrel_got; /* FALLTHROUGH */ @@ -531,6 +589,8 @@ elf_i386_count_relocations (struct ld_state *statep, struct scninfo *scninfo) if (statep->file_type == dso_file_type) { relsize += sizeof (Elf32_Rel); + // XXX Do we have to check whether the target + // XXX section is read-only first? statep->dt_flags |= DF_TEXTREL; } else @@ -550,10 +610,9 @@ elf_i386_count_relocations (struct ld_state *statep, struct scninfo *scninfo) } } else if (statep->file_type == dso_file_type - && r_sym >= SCNINFO_SHDR (scninfo->fileinfo->scninfo[shdr->sh_link].shdr).sh_info - && scninfo->fileinfo->symref[r_sym]->outdynsymidx != 0 && XELF_R_TYPE (rel->r_info) == R_386_32) relsize += sizeof (Elf32_Rel); + break; case R_386_PLT32: @@ -566,10 +625,9 @@ elf_i386_count_relocations (struct ld_state *statep, struct scninfo *scninfo) If the symbol is not defined and we are not creating a statically linked binary, then we need in any case a PLT entry. */ - if (! scninfo->fileinfo->symref[r_sym]->defined) + if (! scninfo->fileinfo->symref[r_sym]->defined + && !statep->statically) { - assert (!statep->statically); - sym = scninfo->fileinfo->symref[r_sym]; sym->type = STT_FUNC; sym->in_dso = 1; @@ -588,7 +646,36 @@ elf_i386_count_relocations (struct ld_state *statep, struct scninfo *scninfo) } break; + case R_386_TLS_LDO_32: + if (statep->file_type != executable_file_type) + abort (); + /* We do not need a relocation in the output file. */ + break; + + case R_386_TLS_LE: + /* We never need a relocation in the output file. */ + break; + + case R_386_TLS_IE: + if (statep->file_type == dso_file_type) + error (EXIT_FAILURE, 0, gettext ("initial-executable TLS relocation cannot be used ")); + if (!scninfo->fileinfo->symref[r_sym]->defined + || scninfo->fileinfo->symref[r_sym]->in_dso) + { + abort (); + } + break; + case R_386_TLS_GD: + if (statep->file_type != executable_file_type + || !scninfo->fileinfo->symref[r_sym]->defined + || scninfo->fileinfo->symref[r_sym]->in_dso) + { + abort (); + } + break; + + case R_386_TLS_GOTIE: case R_386_TLS_LDM: case R_386_TLS_GD_32: case R_386_TLS_GD_PUSH: @@ -598,7 +685,6 @@ elf_i386_count_relocations (struct ld_state *statep, struct scninfo *scninfo) case R_386_TLS_LDM_PUSH: case R_386_TLS_LDM_CALL: case R_386_TLS_LDM_POP: - case R_386_TLS_LDO_32: case R_386_TLS_IE_32: case R_386_TLS_LE_32: /* XXX */ @@ -631,7 +717,7 @@ elf_i386_count_relocations (struct ld_state *statep, struct scninfo *scninfo) static void elf_i386_create_relocations (struct ld_state *statep, - const Elf32_Word *dblindirect) + const Elf32_Word *dblindirect __attribute__ ((unused))) { /* Get the address of the got section. */ Elf_Scn *pltscn = elf_getscn (statep->outelf, statep->pltscnidx); @@ -640,15 +726,25 @@ elf_i386_create_relocations (struct ld_state *statep, Elf32_Addr pltaddr = shdr->sh_addr; Elf_Scn *gotscn = elf_getscn (statep->outelf, statep->gotscnidx); - shdr = elf32_getshdr (gotscn); + // XXX Adjust the address, if necessary, for relro + Elf_Data *gotdata = NULL; + if (statep->need_got) + { + gotdata = elf_getdata (gotscn, NULL); + assert (gotdata != NULL); + } + + Elf_Scn *gotpltscn = elf_getscn (statep->outelf, statep->gotpltscnidx); + shdr = elf32_getshdr (gotpltscn); assert (shdr != NULL); Elf32_Addr gotaddr = shdr->sh_addr; Elf_Scn *reldynscn = elf_getscn (statep->outelf, statep->reldynscnidx); Elf_Data *reldyndata = elf_getdata (reldynscn, NULL); + assert (reldyndata != NULL); size_t nreldyn = 0; -#define ngot_used (3 + statep->nplt + nreldyn) + size_t ngotconst = statep->nrel_got; struct scninfo *first = statep->rellist->next; struct scninfo *runp = first; @@ -658,7 +754,7 @@ elf_i386_create_relocations (struct ld_state *statep, Elf_Data *reldata = elf_getdata (runp->scn, NULL); int nrels = rshdr->sh_size / rshdr->sh_entsize; - /* We will need the following vlaues a couple of times. Help + /* We will need the following values a couple of times. Help the compiler and improve readability. */ struct symbol **symref = runp->fileinfo->symref; struct scninfo *scninfo = runp->fileinfo->scninfo; @@ -677,8 +773,7 @@ elf_i386_create_relocations (struct ld_state *statep, /* Cache the access to the symbol table data. */ Elf_Data *symdata = elf_getdata (scninfo[rshdr->sh_link].scn, NULL); - int cnt; - for (cnt = 0; cnt < nrels; ++cnt) + for (int cnt = 0; cnt < nrels; ++cnt) { XElf_Rel_vardef (rel); XElf_Rel *rel2; @@ -693,24 +788,30 @@ elf_i386_create_relocations (struct ld_state *statep, XElf_Sym_vardef (sym); xelf_getsym (symdata, idx, sym); - /* The value just depends on the position of the referenced + /* The value only depends on the position of the referenced section in the output file and the addend. */ value = scninfo[sym->st_shndx].offset + sym->st_value; } - else if (symref[idx]->in_dso) + else { - /* MERGE.VALUE contains the PLT index. We have to add 1 since - there is this one special PLT entry at the beginning. */ - assert (symref[idx]->merge.value != 0 - || symref[idx]->type != STT_FUNC); - value = pltaddr + symref[idx]->merge.value * PLT_ENTRY_SIZE; + if (symref[idx] == NULL) + /* Symbol in ignored COMDAT group section. */ + continue; + + value = symref[idx]->merge.value; + if (symref[idx]->in_dso) + { + /* MERGE.VALUE contains the PLT index. If this is not for + a function the actual value will be computed later. */ + assert (value != 0 || symref[idx]->type != STT_FUNC); + value = pltaddr + value * PLT_ENTRY_SIZE; + } } - else - value = symref[idx]->merge.value; /* Address of the relocated memory in the data buffer. */ - void *relloc = (char *) data->d_buf + rel->r_offset; + unsigned char *relloc = (unsigned char *) data->d_buf + rel->r_offset; + uint32_t thisgotidx; switch (XELF_R_TYPE (rel->r_info)) { /* These three cases can be handled together since the @@ -746,6 +847,7 @@ elf_i386_create_relocations (struct ld_state *statep, = XELF_R_INFO (symref[idx]->outdynsymidx, R_386_COPY); (void) xelf_update_rel (reldyndata, nreldyn, rel2); ++nreldyn; + assert (nreldyn <= statep->nrel_got); /* Update the symbol table record for the new address. */ @@ -779,8 +881,7 @@ elf_i386_create_relocations (struct ld_state *statep, } } else if (statep->file_type == dso_file_type - && idx >= SCNINFO_SHDR (scninfo[rshdr->sh_link].shdr).sh_info - && symref[idx]->outdynsymidx != 0) + && XELF_R_TYPE (rel->r_info) == R_386_32) { #if NATIVE_ELF != 0 xelf_getrel_ptr (reldyndata, nreldyn, rel2); @@ -788,10 +889,18 @@ elf_i386_create_relocations (struct ld_state *statep, rel2 = &rel_mem; #endif rel2->r_offset = value; - rel2->r_info - = XELF_R_INFO (symref[idx]->outdynsymidx, R_386_32); + + /* For symbols we do not export we generate a relative + relocation. */ + if (idx < SCNINFO_SHDR (scninfo[rshdr->sh_link].shdr).sh_info + || symref[idx]->outdynsymidx == 0) + rel2->r_info = XELF_R_INFO (0, R_386_RELATIVE); + else + rel2->r_info + = XELF_R_INFO (symref[idx]->outdynsymidx, R_386_32); (void) xelf_update_rel (reldyndata, nreldyn, rel2); ++nreldyn; + assert (nreldyn <= statep->nrel_got); value = 0; } @@ -799,31 +908,142 @@ elf_i386_create_relocations (struct ld_state *statep, break; case R_386_GOT32: - store_4ubyte_unaligned (relloc, ngot_used * sizeof (Elf32_Addr)); + if (! symref[idx]->defined || symref[idx]->in_dso) + { + thisgotidx = nreldyn++; + assert (thisgotidx < statep->nrel_got); - /* Add a relocation to initialize the GOT entry. */ + /* Add a relocation to initialize the GOT entry. */ #if NATIVE_ELF != 0 - xelf_getrel_ptr (reldyndata, nreldyn, rel2); + xelf_getrel_ptr (reldyndata, thisgotidx, rel2); #else - rel2 = &rel_mem; + rel2 = &rel_mem; #endif - rel2->r_offset = gotaddr + ngot_used * sizeof (Elf32_Addr); - rel2->r_info - = XELF_R_INFO (symref[idx]->outdynsymidx, R_386_GLOB_DAT); - (void) xelf_update_rel (reldyndata, nreldyn, rel2); - ++nreldyn; + rel2->r_offset = gotaddr + ((thisgotidx - statep->ngot) + * sizeof (Elf32_Addr)); + rel2->r_info + = XELF_R_INFO (symref[idx]->outdynsymidx, R_386_GLOB_DAT); + (void) xelf_update_rel (reldyndata, thisgotidx, rel2); + } + else if (statep->file_type != dso_file_type) + { + thisgotidx = ngotconst++; + assert (thisgotidx < statep->ngot); + + /* We have to use a GOT since the generated code + requires it but we know the address and therefore + do not need a relocation. */ + ((uint32_t *) gotdata->d_buf)[thisgotidx] = value; + } + else + { + thisgotidx = nreldyn++; + assert (thisgotidx < statep->nrel_got); + + // XXX generate a relative relocation. + abort (); + } + + store_4ubyte_unaligned (relloc, + (thisgotidx - statep->ngot) + * sizeof (Elf32_Addr)); break; case R_386_GOTOFF: add_4ubyte_unaligned (relloc, value - gotaddr); break; + case R_386_TLS_LE: + value = symref[idx]->merge.value - ld_state.tls_tcb; + store_4ubyte_unaligned (relloc, value); + break; + + case R_386_TLS_IE: + if (symref[idx]->defined && !symref[idx]->in_dso) + { + /* The symbol is defined in the executable. + Perform the IE->LE optimization. + There are multiple versions, though. + + First version: mov ADDR,REG. */ + if (relloc[-2] == 0x8b + && ((relloc[-1] & 0xc7) == 0x05)) + { + relloc[-2] = 0xc7; + relloc[-1] = 0xc0 | ((relloc[-1] >> 3) & 7); + store_4ubyte_unaligned (relloc, (symref[idx]->merge.value + - ld_state.tls_tcb)); + } + else + { + abort (); + } + } + else + { + abort (); + } + break; + + case R_386_TLS_LDO_32: + value = symref[idx]->merge.value - ld_state.tls_start; + store_4ubyte_unaligned (relloc, value); + break; + + case R_386_TLS_GD: + if (ld_state.file_type == executable_file_type) + { + if (symref[idx]->defined && !symref[idx]->in_dso) + { + /* The symbol is defined in the executable. + Perform the GD->LE optimization. */ + static const char gd_to_le[] = + { + /* mov %gs:0x0,%eax */ + 0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, + /* sub $OFFSET,%eax */ + 0x81, 0xe8 + }; +#ifndef NDEBUG + static const char gd_text[] = + { + /* lea 0x0(,%ebx,1),%eax */ + 0x8d, 0x04, 0x1d, 0x00, 0x00, 0x00, 0x00, + /* call ___tls_get_addr */ + 0xe8 + }; + assert (memcmp (relloc - 3, gd_text, sizeof (gd_text)) + == 0); +#endif + relloc = mempcpy (relloc - 3, gd_to_le, + sizeof (gd_to_le)); + value = ld_state.tls_tcb- symref[idx]->merge.value; + store_4ubyte_unaligned (relloc, value); + + /* We have to skip over the next relocation which is + the matching R_i386_PLT32 for __tls_get_addr. */ + ++cnt; +#ifndef NDEBUG + assert (cnt < nrels); + XElf_Off old_offset = rel->r_offset; + xelf_getrel (reldata, cnt, rel); + assert (rel != NULL); + assert (XELF_R_TYPE (rel->r_info) == R_386_PLT32); + idx = XELF_R_SYM (rel->r_info); + assert (strcmp (symref[idx]->name, "___tls_get_addr") + == 0); + assert (old_offset + 5 == rel->r_offset); +#endif + + break; + } + } + abort (); + break; + case R_386_32PLT: case R_386_TLS_TPOFF: - case R_386_TLS_IE: case R_386_TLS_GOTIE: - case R_386_TLS_LE: - case R_386_TLS_GD: case R_386_TLS_LDM: case R_386_16: case R_386_PC16: @@ -837,11 +1057,9 @@ elf_i386_create_relocations (struct ld_state *statep, case R_386_TLS_LDM_PUSH: case R_386_TLS_LDM_CALL: case R_386_TLS_LDM_POP: - case R_386_TLS_LDO_32: case R_386_TLS_IE_32: case R_386_TLS_LE_32: // XXX For now fall through - printf("ignored relocation %d\n", (int) XELF_R_TYPE (rel->r_info)); break; case R_386_NONE: @@ -878,6 +1096,7 @@ elf_i386_ld_init (struct ld_state *statep) statep->callbacks.initialize_pltrel = elf_i386_initialize_pltrel; statep->callbacks.initialize_got = elf_i386_initialize_got; + statep->callbacks.initialize_gotplt = elf_i386_initialize_gotplt; statep->callbacks.finalize_plt = elf_i386_finalize_plt; @@ -1,15 +1,27 @@ -/* Copyright (C) 2001, 2002, 2003, 2004 Red Hat, Inc. +/* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 2001. - This program is Open Source software; you can redistribute it and/or - modify it under the terms of the Open Software License version 1.0 as - published by the Open Source Initiative. + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. - You should have received a copy of the Open Software License along - with this program; if not, you may obtain a copy of the Open Software - License version 1.0 from http://www.opensource.org/licenses/osl.php or - by writing the Open Source Initiative c/o Lawrence Rosen, Esq., - 3001 King Ranch Road, Ukiah, CA 95482. */ + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ #ifdef HAVE_CONFIG_H # include <config.h> @@ -38,6 +50,9 @@ static void print_version (FILE *stream, struct argp_state *state); void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; +/* Bug report address. */ +const char *argp_program_bug_address = PACKAGE_BUGREPORT; + /* Values for the various options. */ enum @@ -47,7 +62,6 @@ enum ARGP_static, ARGP_dynamic, ARGP_pagesize, - ARGP_rpath, ARGP_rpath_link, ARGP_runpath, ARGP_runpath_link, @@ -56,6 +70,11 @@ enum ARGP_no_gc_sections, ARGP_no_undefined, ARGP_conserve, + ARGP_as_needed, + ARGP_no_as_needed, + ARGP_eh_frame_hdr, + ARGP_hash_style, + ARGP_build_id, #if YYDEBUG ARGP_yydebug, #endif @@ -65,119 +84,113 @@ enum /* Definitions of arguments for argp functions. */ static const struct argp_option options[] = { - /* XXX This list will be reordered and section names will be added. - Just not right now. */ + { NULL, 0, NULL, 0, N_("Input File Control:"), 0 }, { "whole-archive", ARGP_whole_archive, NULL, 0, - N_("Include whole archives in the output from now on.") }, + N_("Include whole archives in the output from now on."), 0 }, { "no-whole-archive", ARGP_no_whole_archive, NULL, 0, - N_("Stop including the whole arhives in the output.") }, - - { "output", 'o', N_("FILE"), 0, N_("Place output in FILE.") }, - - { NULL, 'O', N_("LEVEL"), OPTION_ARG_OPTIONAL, - N_("Set optimization level to LEVEL.") }, - - { "verbose", 'v', NULL, 0, N_("Verbose messages.") }, - { "trace", 't', NULL, 0, N_("Trace file opens.") }, - { "conserve-memory", ARGP_conserve, NULL, 0, - N_("Trade speed for less memory usage") }, - - { NULL, 'z', "KEYWORD", OPTION_HIDDEN, NULL }, + N_("Stop including the whole arhives in the output."), 0 }, + { NULL, 'l', N_("FILE"), OPTION_HIDDEN, NULL, 0 }, + { "start-group", '(', NULL, 0, N_("Start a group."), 0 }, + { "end-group", ')', NULL, 0, N_("End a group."), 0 }, + { NULL, 'L', N_("PATH"), 0, + N_("Add PATH to list of directories files are searched in."), 0 }, + { "as-needed", ARGP_as_needed, NULL, 0, + N_("Only set DT_NEEDED for following dynamic libs if actually used"), 0 }, + { "no-as-needed", ARGP_no_as_needed, NULL, 0, + N_("Always set DT_NEEDED for following dynamic libs"), 0 }, + { "rpath-link", ARGP_rpath_link, "PATH", OPTION_HIDDEN, NULL, 0 }, + { NULL, 'i', NULL, 0, N_("Ignore LD_LIBRARY_PATH environment variable."), + 0 }, + + { NULL, 0, NULL, 0, N_("Output File Control:"), 0 }, + { "output", 'o', N_("FILE"), 0, N_("Place output in FILE."), 0 }, + { NULL, 'z', "KEYWORD", OPTION_HIDDEN, NULL, 0 }, { "-z nodefaultlib", '\0', NULL, OPTION_DOC, - N_("Object is marked to not use default search path at runtime.") }, + N_("Object is marked to not use default search path at runtime."), 0 }, { "-z allextract", '\0', NULL, OPTION_DOC, - N_("Same as --whole-archive.") }, + N_("Same as --whole-archive."), 0 }, { "-z defaultextract", '\0', NULL, OPTION_DOC, N_("\ -Default rules of extracting from archive; weak references are not enough.") }, +Default rules of extracting from archive; weak references are not enough."), + 0 }, { "-z weakextract", '\0', NULL, OPTION_DOC, - N_("Weak references cause extraction from archive.") }, + N_("Weak references cause extraction from archive."), 0 }, { "-z muldefs", '\0', NULL, OPTION_DOC, - N_("Allow multiple definitions; first is used.") }, + N_("Allow multiple definitions; first is used."), 0 }, { "-z defs | nodefs", '\0', NULL, OPTION_DOC, - N_("Disallow/allow undefined symbols in DSOs.") }, - { "no-undefined", ARGP_no_undefined, NULL, OPTION_HIDDEN, NULL }, + N_("Disallow/allow undefined symbols in DSOs."), 0 }, + { "no-undefined", ARGP_no_undefined, NULL, OPTION_HIDDEN, NULL, 0 }, { "-z origin", '\0', NULL, OPTION_DOC, - N_("Object requires immediate handling of $ORIGIN.") }, + N_("Object requires immediate handling of $ORIGIN."), 0 }, { "-z now", '\0', NULL, OPTION_DOC, - N_("Relocation will not be processed lazily.") }, + N_("Relocation will not be processed lazily."), 0 }, { "-z nodelete", '\0', NULL, OPTION_DOC, - N_("Object cannot be unloaded at runtime.") }, + N_("Object cannot be unloaded at runtime."), 0 }, { "-z initfirst", '\0', NULL, OPTION_DOC, - N_("Mark object to be initialized first.") }, + N_("Mark object to be initialized first."), 0 }, { "-z lazyload | nolazyload", '\0', NULL, OPTION_DOC, - N_("Enable/disable lazy-loading flag for following dependencies.") }, + N_("Enable/disable lazy-loading flag for following dependencies."), 0 }, { "-z nodlopen", '\0', NULL, OPTION_DOC, - N_("Mark object as not loadable with 'dlopen'.") }, + N_("Mark object as not loadable with 'dlopen'."), 0 }, { "-z ignore | record", '\0', NULL, OPTION_DOC, - N_("Ignore/record dependencies on unused DSOs.") }, + N_("Ignore/record dependencies on unused DSOs."), 0 }, { "-z systemlibrary", '\0', NULL, OPTION_DOC, - N_("Generated DSO will be a system library.") }, - - { NULL, 'l', N_("FILE"), OPTION_HIDDEN, NULL }, - - { NULL, '(', NULL, 0, N_("Start a group.") }, - { NULL, ')', NULL, 0, N_("End a group.") }, - - { NULL, 'L', N_("PATH"), 0, - N_("Add PATH to list of directories files are searched in.") }, - - { NULL, 'c', N_("FILE"), 0, N_("Use linker script in FILE.") }, - - { "entry", 'e', N_("ADDRESS"), 0, N_("Set entry point address.") }, - - { "static", ARGP_static, NULL, OPTION_HIDDEN, NULL }, + N_("Generated DSO will be a system library."), 0 }, + { "entry", 'e', N_("ADDRESS"), 0, N_("Set entry point address."), 0 }, + { "static", ARGP_static, NULL, OPTION_HIDDEN, NULL, 0 }, { "-B static", ARGP_static, NULL, OPTION_DOC, - N_("Do not link against shared libraries.") }, - { "dynamic", ARGP_dynamic, NULL, OPTION_HIDDEN, NULL }, + N_("Do not link against shared libraries."), 0 }, + { "dynamic", ARGP_dynamic, NULL, OPTION_HIDDEN, NULL, 0 }, { "-B dynamic", ARGP_dynamic, NULL, OPTION_DOC, - N_("Prefer linking against shared libraries.") }, - - { "export-dynamic", 'E', NULL, 0, N_("Export all dynamic symbols.") }, - - { "strip-all", 's', NULL, 0, N_("Strip all symbols.") }, - { "strip-debug", 'S', NULL, 0, N_("Strip debugging symbols.") }, - + N_("Prefer linking against shared libraries."), 0 }, + { "export-dynamic", 'E', NULL, 0, N_("Export all dynamic symbols."), 0 }, + { "strip-all", 's', NULL, 0, N_("Strip all symbols."), 0 }, + { "strip-debug", 'S', NULL, 0, N_("Strip debugging symbols."), 0 }, { "pagesize", ARGP_pagesize, "SIZE", 0, - N_("Assume pagesize for the target system to be SIZE.") }, - - { "rpath", ARGP_rpath, "PATH", OPTION_HIDDEN, NULL }, - { "rpath-link", ARGP_rpath_link, "PATH", OPTION_HIDDEN, NULL }, - - { "runpath", ARGP_runpath, "PATH", 0, N_("Set runtime DSO search path.") }, + N_("Assume pagesize for the target system to be SIZE."), 0 }, + { "rpath", 'R', "PATH", OPTION_HIDDEN, NULL, 0 }, + { "runpath", ARGP_runpath, "PATH", 0, N_("Set runtime DSO search path."), + 0 }, { "runpath-link", ARGP_runpath_link, "PATH", 0, - N_("Set link time DSO search path.") }, - - { NULL, 'i', NULL, 0, N_("Ignore LD_LIBRARY_PATH environment variable.") }, - - { "version-script", ARGP_version_script, "FILE", 0, - N_("Read version information from FILE.") }, - - { "emulation", 'm', "NAME", 0, N_("Set emulation to NAME.") }, - - { "shared", 'G', NULL, 0, N_("Generate dynamic shared object.") }, - { NULL, 'r', NULL, 0L, N_("Generate relocatable object.") }, - - { NULL, 'B', "KEYWORD", OPTION_HIDDEN, "" }, + N_("Set link time DSO search path."), 0 }, + { "shared", 'G', NULL, 0, N_("Generate dynamic shared object."), 0 }, + { NULL, 'r', NULL, 0L, N_("Generate relocatable object."), 0 }, + { NULL, 'B', "KEYWORD", OPTION_HIDDEN, "", 0 }, { "-B local", 'B', NULL, OPTION_DOC, - N_("Causes symbol not assigned to a version be reduced to local.") }, - - { "gc-sections", ARGP_gc_sections, NULL, 0, N_("Remove unused sections.") }, + N_("Causes symbol not assigned to a version be reduced to local."), 0 }, + { "gc-sections", ARGP_gc_sections, NULL, 0, N_("Remove unused sections."), + 0 }, { "no-gc-sections", ARGP_no_gc_sections, NULL, 0, - N_("Don't remove unused sections.") }, - - { "soname", 'h', "NAME", 0, N_("Set soname of shared object.") }, - { "dynamic-linker", 'I', "NAME", 0, N_("Set the dynamic linker name.") }, - - { NULL, 'Q', "YN", OPTION_HIDDEN, NULL }, + N_("Don't remove unused sections."), 0 }, + { "soname", 'h', "NAME", 0, N_("Set soname of shared object."), 0 }, + { "dynamic-linker", 'I', "NAME", 0, N_("Set the dynamic linker name."), 0 }, + { NULL, 'Q', "YN", OPTION_HIDDEN, NULL, 0 }, { "-Q y | n", 'Q', NULL, OPTION_DOC, - N_("Add/suppress addition indentifying link-editor to .comment section") }, - + N_("Add/suppress addition indentifying link-editor to .comment section."), + 0 }, + { "eh-frame-hdr", ARGP_eh_frame_hdr, NULL, 0, + N_("Create .eh_frame_hdr section"), 0 }, + { "hash-style", ARGP_hash_style, "STYLE", 0, + N_("Set hash style to sysv, gnu or both."), 0 }, + { "build-id", ARGP_build_id, "STYLE", OPTION_ARG_OPTIONAL, + N_("Generate build ID note (md5, sha1 (default), uuid)."), 0 }, + + { NULL, 0, NULL, 0, N_("Linker Operation Control:"), 0 }, + { "verbose", 'v', NULL, 0, N_("Verbose messages."), 0 }, + { "trace", 't', NULL, 0, N_("Trace file opens."), 0 }, + { "conserve-memory", ARGP_conserve, NULL, 0, + N_("Trade speed for less memory usage"), 0 }, + { NULL, 'O', N_("LEVEL"), OPTION_ARG_OPTIONAL, + N_("Set optimization level to LEVEL."), 0 }, + { NULL, 'c', N_("FILE"), 0, N_("Use linker script in FILE."), 0 }, #if YYDEBUG { "yydebug", ARGP_yydebug, NULL, 0, - N_("Select to get parser debug information") }, + N_("Select to get parser debug information"), 0 }, #endif + { "version-script", ARGP_version_script, "FILE", 0, + N_("Read version information from FILE."), 0 }, + { "emulation", 'm', "NAME", 0, N_("Set emulation to NAME."), 0 }, - { NULL, 0, NULL, 0, NULL } + { NULL, 0, NULL, 0, NULL, 0 } }; /* Short description of program. */ @@ -187,20 +200,18 @@ static const char doc[] = N_("Combine object and archive files."); static const char args_doc[] = N_("[FILE]..."); /* Prototype for option handler. */ +static void replace_args (int argc, char *argv[]); static error_t parse_opt_1st (int key, char *arg, struct argp_state *state); static error_t parse_opt_2nd (int key, char *arg, struct argp_state *state); -/* Function to print some extra text in the help message. */ -static char *more_help (int key, const char *text, void *input); - /* Data structure to communicate with argp functions. */ static struct argp argp_1st = { - options, parse_opt_1st, args_doc, doc, NULL, more_help + options, parse_opt_1st, args_doc, doc, NULL, NULL, NULL }; static struct argp argp_2nd = { - options, parse_opt_2nd, args_doc, doc, NULL, more_help + options, parse_opt_2nd, args_doc, doc, NULL, NULL, NULL }; @@ -292,10 +303,10 @@ main (int argc, char *argv[]) setlocale (LC_ALL, ""); /* Make sure the message catalog can be found. */ - bindtextdomain (PACKAGE, LOCALEDIR); + bindtextdomain (PACKAGE_TARNAME, LOCALEDIR); /* Initialize the message catalog. */ - textdomain (PACKAGE); + textdomain (PACKAGE_TARNAME); /* Before we start tell the ELF library which version we are using. */ elf_version (EV_CURRENT); @@ -309,6 +320,9 @@ main (int argc, char *argv[]) #define obstack_chunk_free free obstack_init (&ld_state.smem); + /* Recognize old-style parameters for compatibility. */ + replace_args (argc, argv); + /* One quick pass over the parameters which allows us to scan for options with global effect which influence the rest of the processing. */ argp_parse (&argp_1st, argc, argv, ARGP_IN_ORDER, &remaining, NULL); @@ -324,6 +338,11 @@ main (int argc, char *argv[]) /* Determine which ELF backend to use. */ determine_output_format (); + /* If no hash style was specific default to the oldand slow SysV + method. */ + if (unlikely (ld_state.hash_style == hash_style_none)) + ld_state.hash_style = hash_style_sysv; + /* Prepare state. */ err = ld_prepare_state (emulation); if (err != 0) @@ -334,7 +353,7 @@ main (int argc, char *argv[]) statements in the script. This simply must not happen. */ ldin = fopen (linker_script, "r"); if (ldin == NULL) - error (EXIT_FAILURE, errno, gettext ("cannot open linker script \"%s\""), + error (EXIT_FAILURE, errno, gettext ("cannot open linker script '%s'"), linker_script); /* No need for locking. */ __fsetlocking (ldin, FSETLOCKING_BYCALLER); @@ -478,30 +497,60 @@ main (int argc, char *argv[]) } -static char * -more_help (int key, const char *text, void *input) +static void +replace_args (int argc, char *argv[]) { - char *buf; + static const struct + { + const char *from; + const char *to; + } args[] = + { + { "-export-dynamic", "--export-dynamic" }, + { "-dynamic-linker", "--dynamic-linker" }, + { "-static", "--static" }, + }; + const size_t nargs = sizeof (args) / sizeof (args[0]); + + for (int i = 1; i < argc; ++i) + if (argv[i][0] == '-' && islower (argv[i][1]) && argv[i][2] != '\0') + for (size_t j = 0; j < nargs; ++j) + if (strcmp (argv[i], args[j].from) == 0) + { + argv[i] = (char *) args[j].to; + break; + } +} - switch (key) - { - case ARGP_KEY_HELP_EXTRA: - /* We print some extra information. */ - if (asprintf (&buf, gettext ("Please report bugs to %s.\n"), - PACKAGE_BUGREPORT) < 0) - buf = NULL; - return buf; - default: - break; +static int +valid_hexarg (const char *arg) +{ + if (strncasecmp (arg, "0x", 2) != 0) + return 0; + + arg += 2; + do + { + if (isxdigit (arg[0]) && isxdigit (arg[1])) + { + arg += 2; + if (arg[0] == '-' || arg[0] == ':') + ++arg; + } + else + return 0; } - return (char *) text; + while (*arg != '\0'); + + return 1; } /* Quick scan of the parameter list for options with global effect. */ static error_t -parse_opt_1st (int key, char *arg, struct argp_state *state) +parse_opt_1st (int key, char *arg, + struct argp_state *state __attribute__ ((unused))) { switch (key) { @@ -594,7 +643,7 @@ parse_opt_1st (int key, char *arg, struct argp_state *state) else { error (0, 0, - gettext ("invalid page size value \"%s\": ignored"), + gettext ("invalid page size value '%s': ignored"), arg); ld_state.pagesize = 0; } @@ -602,7 +651,7 @@ parse_opt_1st (int key, char *arg, struct argp_state *state) } break; - case ARGP_rpath: + case 'R': add_rxxpath (&ld_state.rpath, arg); break; @@ -623,6 +672,33 @@ parse_opt_1st (int key, char *arg, struct argp_state *state) ld_state.gc_sections = key == ARGP_gc_sections; break; + case ARGP_eh_frame_hdr: + ld_state.eh_frame_hdr = true; + break; + + case ARGP_hash_style: + if (strcmp (arg, "gnu") == 0) + ld_state.hash_style = hash_style_gnu; + else if (strcmp (arg, "both") == 0) + ld_state.hash_style = hash_style_gnu | hash_style_sysv; + else if (strcmp (arg, "sysv") == 0) + ld_state.hash_style = hash_style_sysv; + else + error (EXIT_FAILURE, 0, gettext ("invalid hash style '%s'"), arg); + break; + + case ARGP_build_id: + if (arg == NULL) + ld_state.build_id = "sha1"; + else if (strcmp (arg, "uuid") != 0 + && strcmp (arg, "md5") != 0 + && strcmp (arg, "sha1") != 0 + && !valid_hexarg (arg)) + error (EXIT_FAILURE, 0, gettext ("invalid build-ID style '%s'"), arg); + else + ld_state.build_id = arg; + break; + case 's': if (arg == NULL) { @@ -639,6 +715,8 @@ parse_opt_1st (int key, char *arg, struct argp_state *state) case 'O': case ARGP_whole_archive: case ARGP_no_whole_archive: + case ARGP_as_needed: + case ARGP_no_as_needed: case 'L': case '(': case ')': @@ -685,7 +763,8 @@ parse_opt_1st (int key, char *arg, struct argp_state *state) /* Handle program arguments for real. */ static error_t -parse_opt_2nd (int key, char *arg, struct argp_state *state) +parse_opt_2nd (int key, char *arg, + struct argp_state *state __attribute__ ((unused))) { static bool group_start_requested; static bool group_end_requested; @@ -734,6 +813,13 @@ parse_opt_2nd (int key, char *arg, struct argp_state *state) ld_state.extract_rule = defaultextract; break; + case ARGP_as_needed: + ld_state.as_needed = true; + break; + case ARGP_no_as_needed: + ld_state.as_needed = false; + break; + case ARGP_static: case ARGP_dynamic: /* Enable/disable use for DSOs. */ @@ -865,14 +951,14 @@ load_needed (void) /* Print the version information. */ static void -print_version (FILE *stream, struct argp_state *state) +print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) { - fprintf (stream, "ld (%s) %s\n", PACKAGE_NAME, VERSION); + fprintf (stream, "ld (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION); fprintf (stream, gettext ("\ Copyright (C) %s Red Hat, Inc.\n\ This is free software; see the source for copying conditions. There is NO\n\ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ -"), "2004"); +"), "2008"); fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); } @@ -911,17 +997,19 @@ parse_z_option (const char *arg) /* This is only meaningful if we create a DSO. */ && ld_state.file_type == dso_file_type) ld_state.dt_flags_1 |= DF_1_NOOPEN; - else if (strcmp (arg, "ignore") == 0) - ld_state.ignore_unused_dsos = true; - else if (strcmp (arg, "record") == 0) - ld_state.ignore_unused_dsos = false; else if (strcmp (arg, "systemlibrary") == 0) ld_state.is_system_library = true; + else if (strcmp (arg, "execstack") == 0) + ld_state.execstack = execstack_true; + else if (strcmp (arg, "noexecstack") == 0) + ld_state.execstack = execstack_false_force; else if (strcmp (arg, "allextract") != 0 && strcmp (arg, "defaultextract") != 0 && strcmp (arg, "weakextract") != 0 && strcmp (arg, "lazyload") != 0 - && strcmp (arg, "nolazyload") != 0) + && strcmp (arg, "nolazyload") != 0 + && strcmp (arg, "ignore") != 0 + && strcmp (arg, "record") != 0) error (0, 0, gettext ("unknown option `-%c %s'"), 'z', arg); } @@ -939,6 +1027,10 @@ parse_z_option_2 (const char *arg) ld_state.lazyload = true; else if (strcmp (arg, "nolazyload") == 0) ld_state.lazyload = false; + else if (strcmp (arg, "ignore") == 0) + ld_state.as_needed = true; + else if (strcmp (arg, "record") == 0) + ld_state.as_needed = false; } @@ -1109,6 +1201,7 @@ ld_new_inputfile (const char *fname, enum file_type type) newfile->soname = newfile->fname = newfile->rfname = fname; newfile->file_type = type; newfile->extract_rule = ld_state.extract_rule; + newfile->as_needed = ld_state.as_needed; newfile->lazyload = ld_state.lazyload; newfile->status = not_opened; @@ -1349,7 +1442,7 @@ read_version_script (const char *fname) or absolute) path. No search along a path will be performed. */ ldin = fopen (fname, "r"); if (ldin == NULL) - error (EXIT_FAILURE, errno, gettext ("cannot read version script \"%s\""), + error (EXIT_FAILURE, errno, gettext ("cannot read version script '%s'"), fname); /* No need for locking. */ __fsetlocking (ldin, FSETLOCKING_BYCALLER); @@ -1514,7 +1607,12 @@ create_special_section_symbol (struct symbol **symp, const char *name) abort (); (*symp)->defined = 1; + (*symp)->local = 1; + (*symp)->hidden = 1; (*symp)->type = STT_OBJECT; ++ld_state.nsymtab; } + + +#include "debugpred.h" @@ -1,15 +1,27 @@ -/* Copyright (C) 2001, 2002, 2003 Red Hat, Inc. +/* Copyright (C) 2001, 2002, 2003, 2005, 2006, 2008 Red Hat, Inc. + This file is part of Red Hat elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 2001. - This program is Open Source software; you can redistribute it and/or - modify it under the terms of the Open Software License version 1.0 as - published by the Open Source Initiative. + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. - You should have received a copy of the Open Software License along - with this program; if not, you may obtain a copy of the Open Software - License version 1.0 from http://www.opensource.org/licenses/osl.php or - by writing the Open Source Initiative c/o Lawrence Rosen, Esq., - 3001 King Ranch Road, Ukiah, CA 95482. */ + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ #ifndef LD_H #define LD_H 1 @@ -103,6 +115,10 @@ struct usedfiles used in a reference. */ bool used; + /* True when file should be added to DT_NEEDED list only when + directly referenced. */ + bool as_needed; + /* If nonzero this is the archive sequence number which can be used to determine whether back refernces from -( -) or GROUP statements have to be followed. */ @@ -158,11 +174,16 @@ struct usedfiles Elf32_Word allsectionsidx; /* True if the section is used. */ bool used; + /* True if section is an unused COMDAT section. */ + bool unused_comdat; + /* True if this is a COMDAT group section. */ + bool comdat_group; /* Section group number. This is the index of the SHT_GROUP section. */ Elf32_Word grpid; /* Pointer back to the containing file information structure. */ struct usedfiles *fileinfo; - /* List of symbols in this section (set only for merge-able sections). */ + /* List of symbols in this section (set only for merge-able sections + and group sections). */ struct symbol *symbols; /* Size of relocations in this section. Only used for relocation sections. */ @@ -379,15 +400,21 @@ struct callbacks DL_CALL_FCT ((state)->callbacks.initialize_pltrel, (state, scn)) /* Finalize the .plt section the what belongs to them. */ - void (*finalize_plt) (struct ld_state *, size_t, size_t); -#define FINALIZE_PLT(state, nsym, nsym_dyn) \ - DL_CALL_FCT ((state)->callbacks.finalize_plt, (state, nsym, nsym_dyn)) + void (*finalize_plt) (struct ld_state *, size_t, size_t, struct symbol **); +#define FINALIZE_PLT(state, nsym, nsym_dyn, ndxtosym) \ + DL_CALL_FCT ((state)->callbacks.finalize_plt, (state, nsym, nsym_dyn, \ + ndxtosym)) /* Create the data structures for the .got section and initialize it. */ void (*initialize_got) (struct ld_state *, Elf_Scn *scn); #define INITIALIZE_GOT(state, scn) \ DL_CALL_FCT ((state)->callbacks.initialize_got, (state, scn)) + /* Create the data structures for the .got.plt section and initialize it. */ + void (*initialize_gotplt) (struct ld_state *, Elf_Scn *scn); +#define INITIALIZE_GOTPLT(state, scn) \ + DL_CALL_FCT ((state)->callbacks.initialize_gotplt, (state, scn)) + /* Return the tag corresponding to the native relocation type for the platform. */ int (*rel_type) (struct ld_state *); @@ -428,6 +455,8 @@ struct symbol unsigned int weak:1; unsigned int added:1; unsigned int merged:1; + unsigned int local:1; + unsigned int hidden:1; /* Nonzero if the symbol is on the from_dso list. */ unsigned int on_dsolist:1; /* Nonzero if symbol needs copy relocation, reset when the @@ -464,6 +493,7 @@ struct filename_list struct filename_list *next; bool group_start; bool group_end; + bool as_needed; }; @@ -649,15 +679,18 @@ struct scnhead scn_normal, /* Section from the input file(s). */ scn_dot_interp, /* Generated .interp section. */ scn_dot_got, /* Generated .got section. */ + scn_dot_gotplt, /* Generated .got.plt section. */ scn_dot_dynrel, /* Generated .rel.dyn section. */ scn_dot_dynamic, /* Generated .dynamic section. */ scn_dot_dynsym, /* Generated .dynsym section. */ scn_dot_dynstr, /* Generated .dynstr section. */ scn_dot_hash, /* Generated .hash section. */ + scn_dot_gnu_hash, /* Generated .gnu.hash section. */ scn_dot_plt, /* Generated .plt section. */ scn_dot_pltrel, /* Generated .rel.plt section. */ scn_dot_version, /* Generated .gnu.version section. */ - scn_dot_version_r /* Generated .gnu.version_r section. */ + scn_dot_version_r, /* Generated .gnu.version_r section. */ + scn_dot_note_gnu_build_id /* Generated .note.gnu.build-id section. */ } kind; /* True is the section is used in the output. */ @@ -778,6 +811,10 @@ struct ld_state /* If true static linking is requested. */ bool statically; + /* If true, add DT_NEEDED entries for following files if they are + needed. */ + bool as_needed; + /* How to extract elements from archives. */ enum extract_rule extract_rule; @@ -900,8 +937,9 @@ struct ld_state Elf32_Word dynsymscnidx; /* Dynamic symbol string table section. */ Elf32_Word dynstrscnidx; - /* Dynamic symbol hash table. */ + /* Dynamic symbol hash tables. */ size_t hashscnidx; + size_t gnuhashscnidx; /* Procedure linkage table section. */ Elf32_Word pltscnidx; @@ -912,6 +950,8 @@ struct ld_state /* Global offset table section. */ Elf32_Word gotscnidx; + /* And the part of the PLT. */ + Elf32_Word gotpltscnidx; /* This section will hole all non-PLT relocations. */ Elf32_Word reldynscnidx; @@ -927,6 +967,11 @@ struct ld_state /* Index of next version. */ int nextveridx; + /* TLS segment. */ + bool need_tls; + XElf_Addr tls_start; + XElf_Addr tls_tcb; + /* Hash table for version symbol strings. Only strings without special characters are hashed here. */ ld_version_str_tab version_str_tab; @@ -935,6 +980,14 @@ struct ld_state bool default_bind_local; bool default_bind_global; + /* Execuatable stack selection. */ + enum execstack + { + execstack_false = 0, + execstack_true, + execstack_false_force + } execstack; + /* True if only used sections are used. */ bool gc_sections; @@ -978,15 +1031,29 @@ struct ld_state /* Lazy-loading state for dependencies. */ bool lazyload; - /* True is DSOs which are not used in the linking process are not - recorded. */ - bool ignore_unused_dsos; + /* True if an .eh_frame_hdr section should be generated. */ + bool eh_frame_hdr; + + /* What hash style to generate. */ + enum + { + hash_style_none = 0, + hash_style_sysv = 1, +#define GENERATE_SYSV_HASH ((ld_state.hash_style & hash_style_sysv) != 0) + hash_style_gnu = 2 +#define GENERATE_GNU_HASH ((ld_state.hash_style & hash_style_gnu) != 0) + } + hash_style; /* True if in executables all global symbols should be exported in the dynamic symbol table. */ bool export_all_dynamic; + /* Build-ID style. NULL is none. */ + const char *build_id; + Elf32_Word buildidscnidx; + /* If DSO is generated, this is the SONAME. */ const char *soname; @@ -1054,9 +1121,12 @@ extern bool dynamically_linked_p (void); /* Helper functions for the architecture specific code. */ /* Checked whether the symbol is undefined and referenced from a DSO. */ -extern bool linked_from_dso_p (struct scninfo *scninfo, int symidx); +extern bool linked_from_dso_p (struct scninfo *scninfo, size_t symidx); +#ifdef __GNUC_STDC_INLINE__ +__attribute__ ((__gnu_inline__)) +#endif extern inline bool -linked_from_dso_p (struct scninfo *scninfo, int symidx) +linked_from_dso_p (struct scninfo *scninfo, size_t symidx) { struct usedfiles *file = scninfo->fileinfo; diff --git a/src/ldgeneric.c b/src/ldgeneric.c index a33e9fcc..8df2a57e 100644 --- a/src/ldgeneric.c +++ b/src/ldgeneric.c @@ -1,21 +1,34 @@ -/* Copyright (C) 2001, 2002, 2003, 2004 Red Hat, Inc. +/* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2008 Red Hat, Inc. + This file is part of Red Hat elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 2001. - This program is Open Source software; you can redistribute it and/or - modify it under the terms of the Open Software License version 1.0 as - published by the Open Source Initiative. + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. - You should have received a copy of the Open Software License along - with this program; if not, you may obtain a copy of the Open Software - License version 1.0 from http://www.opensource.org/licenses/osl.php or - by writing the Open Source Initiative c/o Lawrence Rosen, Esq., - 3001 King Ranch Road, Ukiah, CA 95482. */ + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ #ifdef HAVE_CONFIG_H # include <config.h> #endif #include <assert.h> +#include <ctype.h> #include <dlfcn.h> #include <errno.h> #include <error.h> @@ -32,9 +45,23 @@ #include <sys/param.h> #include <sys/stat.h> -#include <system.h> +#include <elf-knowledge.h> #include "ld.h" #include "list.h" +#include <md5.h> +#include <sha1.h> +#include <system.h> + + +/* Header of .eh_frame_hdr section. */ +struct unw_eh_frame_hdr +{ + unsigned char version; + unsigned char eh_frame_ptr_enc; + unsigned char fde_count_enc; + unsigned char table_enc; +}; +#define EH_FRAME_HDR_VERSION 1 /* Prototypes for local functions. */ @@ -65,8 +92,11 @@ static void ld_generic_initialize_plt (struct ld_state *statep, Elf_Scn *scn); static void ld_generic_initialize_pltrel (struct ld_state *statep, Elf_Scn *scn); static void ld_generic_initialize_got (struct ld_state *statep, Elf_Scn *scn); +static void ld_generic_initialize_gotplt (struct ld_state *statep, + Elf_Scn *scn); static void ld_generic_finalize_plt (struct ld_state *statep, size_t nsym, - size_t nsym_dyn); + size_t nsym_dyn, + struct symbol **ndxtosymp); static int ld_generic_rel_type (struct ld_state *statep); static void ld_generic_count_relocations (struct ld_state *statep, struct scninfo *scninfo); @@ -137,7 +167,7 @@ dynamically_linked_p (void) bool -linked_from_dso_p (struct scninfo *scninfo, int symidx) +linked_from_dso_p (struct scninfo *scninfo, size_t symidx) { struct usedfiles *file = scninfo->fileinfo; @@ -197,6 +227,7 @@ ld_prepare_state (const char *emulation) ld_state.callbacks.initialize_plt = ld_generic_initialize_plt; ld_state.callbacks.initialize_pltrel = ld_generic_initialize_pltrel; ld_state.callbacks.initialize_got = ld_generic_initialize_got; + ld_state.callbacks.initialize_gotplt = ld_generic_initialize_gotplt; ld_state.callbacks.finalize_plt = ld_generic_finalize_plt; ld_state.callbacks.rel_type = ld_generic_rel_type; ld_state.callbacks.count_relocations = ld_generic_count_relocations; @@ -491,6 +522,38 @@ open_along_path (struct usedfiles *fileinfo) } +static int +matching_group_comdat_scn (const XElf_Sym *sym, size_t shndx, + struct usedfiles *fileinfo, struct symbol *oldp) +{ + if ((shndx >= SHN_LORESERVE && shndx <= SHN_HIRESERVE) + || (oldp->scndx >= SHN_LORESERVE && oldp->scndx <= SHN_HIRESERVE)) + /* Cannot be a group COMDAT section. */ + return 0; + + size_t newgrpid = fileinfo->scninfo[shndx].grpid; + size_t oldgrpid = oldp->file->scninfo[oldp->scndx].grpid; + if (newgrpid == 0 || oldgrpid == 0) + return 0; + + assert (SCNINFO_SHDR (fileinfo->scninfo[newgrpid].shdr).sh_type + == SHT_GROUP); + assert (SCNINFO_SHDR (oldp->file->scninfo[oldgrpid].shdr).sh_type + == SHT_GROUP); + + if (! fileinfo->scninfo[newgrpid].comdat_group + || ! oldp->file->scninfo[oldgrpid].comdat_group) + return 0; + + if (strcmp (fileinfo->scninfo[newgrpid].symbols->name, + oldp->file->scninfo[oldgrpid].symbols->name) != 0) + return 0; + + /* This is a matching, duplicate COMDAT group section. Ignore it. */ + return 1; +} + + static void check_type_and_size (const XElf_Sym *sym, struct usedfiles *fileinfo, struct symbol *oldp) @@ -528,7 +591,7 @@ Warning: size of `%s' changed from %" PRIu64 " in %s to %" PRIu64 " in %s"), static int -check_definition (const XElf_Sym *sym, size_t symidx, +check_definition (const XElf_Sym *sym, size_t shndx, size_t symidx, struct usedfiles *fileinfo, struct symbol *oldp) { int result = 0; @@ -536,9 +599,9 @@ check_definition (const XElf_Sym *sym, size_t symidx, bool new_in_dso = FILEINFO_EHDR (fileinfo->ehdr).e_type == ET_DYN; bool use_new_def = false; - if (sym->st_shndx != SHN_UNDEF + if (shndx != SHN_UNDEF && (! oldp->defined - || (sym->st_shndx != SHN_COMMON && oldp->common && ! new_in_dso) + || (shndx != SHN_COMMON && oldp->common && ! new_in_dso) || (old_in_dso && ! new_in_dso))) { /* We found a definition for a previously undefined symbol or a @@ -568,10 +631,17 @@ check_definition (const XElf_Sym *sym, size_t symidx, /* Use the values of the definition from now on. */ use_new_def = true; } - else if (sym->st_shndx != SHN_UNDEF + else if (shndx != SHN_UNDEF + && oldp->defined + && matching_group_comdat_scn (sym, shndx, fileinfo, oldp)) + /* The duplicate symbol is in a group COMDAT section with the same + signature as the one containing the original definition. + Just ignore the second definition. */ + /* nothing */; + else if (shndx != SHN_UNDEF && unlikely (! oldp->common) && oldp->defined - && sym->st_shndx != SHN_COMMON + && shndx != SHN_COMMON /* Multiple definitions are no fatal errors if the -z muldefs flag is used. We don't warn about the multiple definition unless we are told to be verbose. */ @@ -584,7 +654,6 @@ check_definition (const XElf_Sym *sym, size_t symidx, struct usedfiles *oldfile; const char *scnname; Elf32_Word xndx; - size_t shndx; size_t shnum; if (elf_getshnum (fileinfo->elf, &shnum) < 0) @@ -593,15 +662,14 @@ check_definition (const XElf_Sym *sym, size_t symidx, elf_errmsg (-1)); /* XXX Use only ebl_section_name. */ - if (sym->st_shndx < SHN_LORESERVE // || sym->st_shndx > SHN_HIRESERVE - && sym->st_shndx < shnum) + if (shndx < SHN_LORESERVE || (shndx > SHN_HIRESERVE && shndx < shnum)) scnname = elf_strptr (fileinfo->elf, fileinfo->shstrndx, - SCNINFO_SHDR (fileinfo->scninfo[sym->st_shndx].shdr).sh_name); + SCNINFO_SHDR (fileinfo->scninfo[shndx].shdr).sh_name); else // XXX extended section - scnname = ebl_section_name (ld_state.ebl, sym->st_shndx, 0, - buf, sizeof (buf), NULL, shnum); + scnname = ebl_section_name (ld_state.ebl, shndx, 0, buf, sizeof (buf), + NULL, shnum); /* XXX Print source file and line number. */ print_file_name (stderr, fileinfo, 1, 0); @@ -616,23 +684,16 @@ check_definition (const XElf_Sym *sym, size_t symidx, oldfile = oldp->file; xelf_getsymshndx (oldfile->symtabdata, oldfile->xndxdata, oldp->symidx, oldsym, xndx); - if (oldsym == NULL) - /* This should never happen since the same call - succeeded before. */ - abort (); - - shndx = oldsym->st_shndx; - if (unlikely (oldsym->st_shndx == SHN_XINDEX)) - shndx = xndx; + assert (oldsym != NULL); /* XXX Use only ebl_section_name. */ - if (shndx < SHN_LORESERVE || shndx > SHN_HIRESERVE) + if (oldp->scndx < SHN_LORESERVE || oldp->scndx > SHN_HIRESERVE) scnname = elf_strptr (oldfile->elf, oldfile->shstrndx, SCNINFO_SHDR (oldfile->scninfo[shndx].shdr).sh_name); else - scnname = ebl_section_name (ld_state.ebl, oldsym->st_shndx, shndx, buf, - sizeof (buf), NULL, shnum); + scnname = ebl_section_name (ld_state.ebl, oldp->scndx, oldp->scndx, + buf, sizeof (buf), NULL, shnum); /* XXX Print source file and line number. */ print_file_name (stderr, oldfile, 1, 0); @@ -643,7 +704,7 @@ check_definition (const XElf_Sym *sym, size_t symidx, result = 1; } else if (old_in_dso && fileinfo->file_type == relocatable_file_type - && sym->st_shndx != SHN_UNDEF) + && shndx != SHN_UNDEF) /* We use the definition from a normal relocatable file over the definition in a DSO. This is what the dynamic linker would do, too. */ @@ -662,7 +723,7 @@ check_definition (const XElf_Sym *sym, size_t symidx, oldp->on_dsolist = 1; } - else if (oldp->common && sym->st_shndx == SHN_COMMON) + else if (oldp->common && shndx == SHN_COMMON) { /* The symbol size is the largest of all common definitions. */ oldp->size = MAX (oldp->size, sym->st_size); @@ -691,23 +752,23 @@ check_definition (const XElf_Sym *sym, size_t symidx, oldp->size = sym->st_size; oldp->type = XELF_ST_TYPE (sym->st_info); oldp->symidx = symidx; - oldp->scndx = sym->st_shndx; + oldp->scndx = shndx; //oldp->symscndx = THESYMSCNDX must be passed; oldp->file = fileinfo; oldp->defined = 1; oldp->in_dso = new_in_dso; - oldp->common = sym->st_shndx == SHN_COMMON; + oldp->common = shndx == SHN_COMMON; if (likely (fileinfo->file_type == relocatable_file_type)) { /* If the definition comes from a DSO we pertain the weak flag and it's indicating whether the reference is weak or not. */ oldp->weak = XELF_ST_BIND (sym->st_info) == STB_WEAK; - if (sym->st_shndx != SHN_COMMON) + // XXX Really exclude SHN_ABS? + if (shndx != SHN_COMMON && shndx != SHN_ABS) { struct scninfo *ignore; - mark_section_used (&fileinfo->scninfo[sym->st_shndx], - sym->st_shndx, &ignore); + mark_section_used (&fileinfo->scninfo[shndx], shndx, &ignore); } } @@ -726,7 +787,7 @@ check_definition (const XElf_Sym *sym, size_t symidx, oldp->on_dsolist = 1; } - else if (sym->st_shndx == SHN_COMMON) + else if (shndx == SHN_COMMON) { /* Store the alignment. */ oldp->merge.value = sym->st_value; @@ -820,14 +881,12 @@ mark_section_group (struct usedfiles *fileinfo, Elf32_Word shndx, Elf32_Word idx = grpref[--cnt]; XElf_Shdr *shdr = &SCNINFO_SHDR (fileinfo->scninfo[idx].shdr); - if (fileinfo->scninfo[idx].grpid != 0) + if (fileinfo->scninfo[idx].grpid != grpscn->grpid) error (EXIT_FAILURE, 0, gettext ("\ -%s: section [%2d] '%s' is in more than one section group"), +%s: section [%2d] '%s' is not in the correct section group"), fileinfo->fname, (int) idx, elf_strptr (fileinfo->elf, fileinfo->shstrndx, shdr->sh_name)); - fileinfo->scninfo[idx].grpid = grpscn->grpid; - if (ld_state.strip == strip_none /* If we are stripping, remove debug sections. */ || (!ebl_debugscn_p (ld_state.ebl, @@ -968,7 +1027,36 @@ add_section (struct usedfiles *fileinfo, struct scninfo *scninfo) } /* XXX Possibly unaligned memory access. */ - is_comdat = ((Elf32_Word *) grpscndata->d_buf)[0] & GRP_COMDAT; + if ((((Elf32_Word *) grpscndata->d_buf)[0] & GRP_COMDAT) != 0) + { + /* We have to compare the group signatures. There might + be sections with the same name but belonging to + groups with different signatures. This means we have + to compare the new group signature with all those + already collected. There might also be some + non-group sections in the mix. */ + struct scninfo *runp = queued->last; + do + { + if (SCNINFO_SHDR (runp->shdr).sh_flags & SHF_GROUP) + { + struct scninfo *grpscn2 + = find_section_group (runp->fileinfo, + elf_ndxscn (runp->scn), + &grpscndata); + + if (strcmp (grpscn->symbols->name, + grpscn2->symbols->name) == 0) + { + scninfo->unused_comdat = is_comdat = true; + break; + } + } + + runp = runp->next; + } + while (runp != queued->last); + } } if (!is_comdat) @@ -977,7 +1065,8 @@ add_section (struct usedfiles *fileinfo, struct scninfo *scninfo) scninfo->next = queued->last->next; queued->last = queued->last->next = scninfo; - queued->flags = SH_FLAGS_COMBINE (queued->flags, shdr->sh_flags); + queued->flags = ebl_sh_flags_combine (ld_state.ebl, queued->flags, + shdr->sh_flags); queued->align = MAX (queued->align, shdr->sh_addralign); } } @@ -996,6 +1085,9 @@ add_section (struct usedfiles *fileinfo, struct scninfo *scninfo) queued->segment_nr = ~0; queued->last = scninfo->next = scninfo; + /* Check whether we need a TLS segment. */ + ld_state.need_tls |= (shdr->sh_flags & SHF_TLS) != 0; + /* Add to the hash table and possibly overwrite existing value. */ ld_section_tab_insert (&ld_state.section_tab, hval, queued); } @@ -1003,7 +1095,7 @@ add_section (struct usedfiles *fileinfo, struct scninfo *scninfo) static int -add_relocatable_file (struct usedfiles *fileinfo, int secttype) +add_relocatable_file (struct usedfiles *fileinfo, GElf_Word secttype) { size_t scncnt; size_t cnt; @@ -1016,6 +1108,10 @@ add_relocatable_file (struct usedfiles *fileinfo, int secttype) size_t nsymbols = 0; size_t nlocalsymbols = 0; bool has_merge_sections = false; + bool has_tls_symbols = false; + /* Unless we have different information we assume the code needs + an executable stack. */ + enum execstack execstack = execstack_true; /* Prerequisites. */ assert (fileinfo->elf != NULL); @@ -1069,6 +1165,7 @@ add_relocatable_file (struct usedfiles *fileinfo, int secttype) /* Check whether this section is marked as merge-able. */ has_merge_sections |= (shdr->sh_flags & SHF_MERGE) != 0; + has_tls_symbols |= (shdr->sh_flags & SHF_TLS) != 0; /* Get the ELF section header and data. */ /* Make the file structure available. */ @@ -1166,6 +1263,7 @@ add_relocatable_file (struct usedfiles *fileinfo, int secttype) symscn = elf_getscn (fileinfo->elf, shdr->sh_link); xelf_getshdr (symscn, symshdr); symdata = elf_getdata (symscn, NULL); + if (symshdr != NULL) { XElf_Sym_vardef (sym); @@ -1177,9 +1275,26 @@ add_relocatable_file (struct usedfiles *fileinfo, int secttype) { struct symbol *symbol = fileinfo->scninfo[cnt].symbols; - symbol->name = elf_strptr (fileinfo->elf, symshdr->sh_link, - sym->st_name); - symbol->symidx = shdr->sh_info; +#ifndef NO_HACKS + if (XELF_ST_TYPE (sym->st_info) == STT_SECTION) + { + XElf_Shdr_vardef (buggyshdr); + xelf_getshdr (elf_getscn (fileinfo->elf, sym->st_shndx), + buggyshdr); + + symbol->name = elf_strptr (fileinfo->elf, + FILEINFO_EHDR (fileinfo->ehdr).e_shstrndx, + buggyshdr->sh_name); + symbol->symidx = -1; + } + else +#endif + { + symbol->name = elf_strptr (fileinfo->elf, + symshdr->sh_link, + sym->st_name); + symbol->symidx = shdr->sh_info; + } symbol->file = fileinfo; } } @@ -1192,6 +1307,35 @@ add_relocatable_file (struct usedfiles *fileinfo, int secttype) shdr->sh_name), elf_errmsg (-1)); + + /* For all the sections which are part of this group, add + the reference. */ + if (data == NULL) + error (EXIT_FAILURE, 0, gettext ("\ +%s: cannot get content of section group [%2zd] '%s': %s'"), + fileinfo->fname, elf_ndxscn (fileinfo->scninfo[cnt].scn), + elf_strptr (fileinfo->elf, fileinfo->shstrndx, + shdr->sh_name), + elf_errmsg (-1)); + + Elf32_Word *grpdata = (Elf32_Word *) data->d_buf; + if (grpdata[0] & GRP_COMDAT) + fileinfo->scninfo[cnt].comdat_group = true; + for (size_t inner = 1; inner < data->d_size / sizeof (Elf32_Word); + ++inner) + { + if (grpdata[inner] >= scncnt) + error (EXIT_FAILURE, 0, gettext ("\ +%s: group member %zu of section group [%2zd] '%s' has too high index: %" PRIu32), + fileinfo->fname, + inner, elf_ndxscn (fileinfo->scninfo[cnt].scn), + elf_strptr (fileinfo->elf, fileinfo->shstrndx, + shdr->sh_name), + grpdata[inner]); + + fileinfo->scninfo[grpdata[inner]].grpid = cnt; + } + /* The 'used' flag is used to indicate when the information in the section group is used to mark all other sections as used. So it must not be true yet. */ @@ -1222,9 +1366,26 @@ add_relocatable_file (struct usedfiles *fileinfo, int secttype) || shdr->sh_type == SHT_INIT_ARRAY || shdr->sh_type == SHT_FINI_ARRAY || shdr->sh_type == SHT_PREINIT_ARRAY)) - add_section (fileinfo, &fileinfo->scninfo[cnt]); + { + /* Check whether the section needs to be executable. */ + if (shdr->sh_type == SHT_PROGBITS + && (shdr->sh_flags & SHF_EXECINSTR) == 0 + && strcmp (elf_strptr (fileinfo->elf, fileinfo->shstrndx, + shdr->sh_name), + ".note.GNU-stack") == 0) + execstack = execstack_false; + + add_section (fileinfo, &fileinfo->scninfo[cnt]); + } } + /* Now we know more about the requirements for an executable stack + of the result. */ + if (fileinfo->file_type == relocatable_file_type + && execstack == execstack_true + && ld_state.execstack != execstack_false_force) + ld_state.execstack = execstack_true; + /* Handle the symbols. Record defined and undefined symbols in the hash table. In theory there can be a file without any symbol table. */ @@ -1233,7 +1394,7 @@ add_relocatable_file (struct usedfiles *fileinfo, int secttype) /* In case this file contains merge-able sections we have to locate the symbols which are in these sections. */ fileinfo->has_merge_sections = has_merge_sections; - if (likely (has_merge_sections)) + if (likely (has_merge_sections || has_tls_symbols)) { fileinfo->symref = (struct symbol **) obstack_calloc (&ld_state.smem, @@ -1265,8 +1426,9 @@ add_relocatable_file (struct usedfiles *fileinfo, int secttype) if (XELF_ST_TYPE (sym->st_info) != STT_SECTION && (shndx < SHN_LORESERVE || shndx > SHN_HIRESERVE) - && (SCNINFO_SHDR (fileinfo->scninfo[shndx].shdr).sh_flags - & SHF_MERGE)) + && ((SCNINFO_SHDR (fileinfo->scninfo[shndx].shdr).sh_flags + & SHF_MERGE) + || XELF_ST_TYPE (sym->st_info) == STT_TLS)) { /* Create a symbol record for this symbol and add it to the list for this section. */ @@ -1278,6 +1440,7 @@ add_relocatable_file (struct usedfiles *fileinfo, int secttype) newp->symidx = cnt; newp->scndx = shndx; newp->file = fileinfo; + newp->defined = 1; fileinfo->symref[cnt] = newp; if (fileinfo->scninfo[shndx].symbols == NULL) @@ -1334,8 +1497,13 @@ add_relocatable_file (struct usedfiles *fileinfo, int secttype) if (unlikely (shndx == SHN_ABS) && secttype == SHT_DYNSYM) continue; - /* If the DSO uses symbols determine whether this is the default - version. Otherwise we'll ignore the symbol. */ + if ((shndx < SHN_LORESERVE || shndx > SHN_HIRESERVE) + && fileinfo->scninfo[shndx].unused_comdat) + /* The symbol is not used. */ + continue; + + /* If the DSO uses symbol versions determine whether this is + the default version. Otherwise we'll ignore the symbol. */ if (versymdata != NULL) { XElf_Versym versym; @@ -1358,9 +1526,9 @@ add_relocatable_file (struct usedfiles *fileinfo, int secttype) _GLOBAL_OFFSET_TABLE_, _DYNAMIC. */ // XXX This loop is hot and the following tests hardly ever match. // XXX Maybe move the tests somewhere they are executed less often. - if (((unlikely (hval == 165832675) + if (((unlikely (hval == 165832675ul) && strcmp (search.name, "_DYNAMIC") == 0) - || (unlikely (hval == 102264335) + || (unlikely (hval == 102264335ul) && strcmp (search.name, "_GLOBAL_OFFSET_TABLE_") == 0)) && sym->st_shndx != SHN_UNDEF /* If somebody defines such a variable in a relocatable we @@ -1373,7 +1541,7 @@ add_relocatable_file (struct usedfiles *fileinfo, int secttype) struct symbol *newp; if (likely (oldp == NULL)) { - /* No symbol of this name know. Add it. */ + /* No symbol of this name known. Add it. */ newp = (struct symbol *) obstack_alloc (&ld_state.smem, sizeof (*newp)); newp->name = search.name; @@ -1389,6 +1557,8 @@ add_relocatable_file (struct usedfiles *fileinfo, int secttype) newp->weak = XELF_ST_BIND (sym->st_info) == STB_WEAK; newp->added = 0; newp->merged = 0; + newp->local = 0; + newp->hidden = 0; newp->need_copy = 0; newp->on_dsolist = 0; newp->in_dso = secttype == SHT_DYNSYM; @@ -1431,7 +1601,8 @@ add_relocatable_file (struct usedfiles *fileinfo, int secttype) else if (hval == 6672457 && strcmp (newp->name, "_fini") == 0) ld_state.fini_symbol = newp; } - else if (unlikely (check_definition (sym, cnt, fileinfo, oldp) != 0)) + else if (unlikely (check_definition (sym, shndx, cnt, fileinfo, oldp) + != 0)) /* A fatal error (multiple definition of a symbol) occurred, no need to continue. */ return 1; @@ -1522,8 +1693,11 @@ ld_handle_filename_list (struct filename_list *fnames) curp = runp->real = ld_new_inputfile (runp->name, relocatable_file_type); /* Set flags for group handling. */ - runp->real->group_start = runp->group_start; - runp->real->group_end = runp->group_end; + curp->group_start = runp->group_start; + curp->group_end = runp->group_end; + + /* Set as-needed flag from the file, not the command line. */ + curp->as_needed = runp->as_needed; /* Read the file and everything else which comes up, including handling groups. */ @@ -1590,7 +1764,7 @@ open_elf (struct usedfiles *fileinfo, Elf *elf) __fsetlocking (ldin, FSETLOCKING_BYCALLER); if (ldin == NULL) - error (EXIT_FAILURE, errno, gettext ("cannot open \"%s\""), + error (EXIT_FAILURE, errno, gettext ("cannot open '%s'"), fileinfo->rfname); /* Parse the file. If it is a linker script no problems will be @@ -1702,15 +1876,16 @@ extract_from_archive (struct usedfiles *fileinfo) static int archive_seq; int res = 0; - /* This is an archive we are not using completely. Give it a - unique number. */ - fileinfo->archive_seq = ++archive_seq; + if (fileinfo->archive_seq == 0) + /* This is an archive we are not using completely. Give it a + unique number. */ + fileinfo->archive_seq = ++archive_seq; /* If there are no unresolved symbols don't do anything. */ + assert (ld_state.extract_rule == defaultextract + || ld_state.extract_rule == weakextract); if ((likely (ld_state.extract_rule == defaultextract) - && ld_state.nunresolved_nonweak == 0) - || (unlikely (ld_state.extract_rule == weakextract) - && ld_state.nunresolved == 0)) + ? ld_state.nunresolved_nonweak : ld_state.nunresolved) == 0) return 0; Elf_Arsym *syms; @@ -1735,7 +1910,6 @@ extract_from_archive (struct usedfiles *fileinfo) the first definition. */ // XXX Is this a compatible behavior? bool any_used; - int nround = 0; do { any_used = false; @@ -1793,7 +1967,7 @@ extract_from_archive (struct usedfiles *fileinfo) } } - if (++nround == 1) + if (any_used) { /* This is an archive therefore it must have a number. */ assert (fileinfo->archive_seq != 0); @@ -1851,6 +2025,18 @@ file_process2 (struct usedfiles *fileinfo) return 1; } + /* Make sure the file type matches the backend. */ + if (FILEINFO_EHDR (fileinfo->ehdr).e_machine + != ebl_get_elfmachine (ld_state.ebl)) + { + fprintf (stderr, gettext ("\ +%s: input file incompatible with ELF machine type %s\n"), + fileinfo->rfname, + ebl_backend_name (ld_state.ebl)); + fileinfo->status = closed; + return 1; + } + /* Determine the section header string table section index. */ if (unlikely (elf_getshstrndx (fileinfo->elf, &fileinfo->shstrndx) < 0)) @@ -1974,9 +2160,22 @@ cannot use DSO '%s' when generating relocatable object file"), res = 0; } else - /* Extract only the members from the archive which are - currently referenced by unresolved symbols. */ - res = extract_from_archive (fileinfo); + { + if (ld_state.group_start_requested + && ld_state.group_start_archive == NULL) + ld_state.group_start_archive = fileinfo; + + if (ld_state.archives == NULL) + ld_state.archives = fileinfo; + + if (ld_state.tailarchives != NULL) + ld_state.tailarchives->next = fileinfo; + ld_state.tailarchives = fileinfo; + + /* Extract only the members from the archive which are + currently referenced by unresolved symbols. */ + res = extract_from_archive (fileinfo); + } } else /* This should never happen, we know about no other types. */ @@ -2032,7 +2231,7 @@ ld_generic_file_process (int fd, struct usedfiles *fileinfo, /* We found the file. Now test whether it is a file type we can handle. - XXX Do we have to have the ability to start from a given + XXX Do we need to have the ability to start from a given position in the search path again to look for another file if the one found has not the right type? */ res = open_elf (fileinfo, elf_begin (fileinfo->fd, @@ -2075,19 +2274,24 @@ ld_generic_file_process (int fd, struct usedfiles *fileinfo, runp = runp->next; } while (runp != fileinfo->next); + + /* Do not do this again. */ + ld_state.archives = NULL; + + /* Do not move on to the next archive. */ + *nextp = fileinfo->next = NULL; } } else if (unlikely (fileinfo->group_end)) { - /* This is the end of a group. We possibly of to go back. + /* This is the end of a group. We possibly have to go back. Determine which file we would go back to and see whether it makes sense. If there has not been an archive we don't have to do anything. */ - if (!ld_state.group_start_requested) + if (ld_state.group_start_requested) { if (ld_state.group_start_archive != ld_state.tailarchives) - /* The loop would include more than one archive, add the - pointer. */ + /* The loop includes more than one archive, add the pointer. */ { *nextp = ld_state.tailarchives->group_backref = ld_state.group_start_archive; @@ -2106,6 +2310,7 @@ ld_generic_file_process (int fd, struct usedfiles *fileinfo, /* Clear the flags. */ ld_state.group_start_requested = false; + ld_state.group_start_archive = NULL; fileinfo->group_end = false; } @@ -2238,6 +2443,11 @@ ld_generic_generate_sections (struct ld_state *statep) /* The relocation section type. */ int rel_type = REL_TYPE (&ld_state) == DT_REL ? SHT_REL : SHT_RELA; + /* When requested, every output file will have a build ID section. */ + if (statep->build_id != NULL) + new_generated_scn (scn_dot_note_gnu_build_id, ".note.gnu.build-id", + SHT_NOTE, SHF_ALLOC, 0, 4); + /* When building dynamically linked object we have to include a section containing a string describing the interpreter. This should be at the very beginning of the file together with the @@ -2249,15 +2459,15 @@ ld_generic_generate_sections (struct ld_state *statep) program header. */ if (dynamically_linked_p ()) { - int ndt_needed; /* Use any versioning (defined or required)? */ bool use_versioning = false; /* Use version requirements? */ bool need_version = false; /* First the .interp section. */ - new_generated_scn (scn_dot_interp, ".interp", SHT_PROGBITS, SHF_ALLOC, - 0, 1); + if (ld_state.interp != NULL || ld_state.file_type != dso_file_type) + new_generated_scn (scn_dot_interp, ".interp", SHT_PROGBITS, SHF_ALLOC, + 0, 1); /* Now the .dynamic section. */ new_generated_scn (scn_dot_dynamic, ".dynamic", SHT_DYNAMIC, @@ -2277,13 +2487,13 @@ ld_generic_generate_sections (struct ld_state *statep) 0, 1); /* And a hashing table. */ // XXX For Linux/Alpha we need other sizes unless they change... - new_generated_scn (scn_dot_hash, ".hash", SHT_HASH, SHF_ALLOC, - sizeof (Elf32_Word), sizeof (Elf32_Word)); - - /* By default we add all DSOs provided on the command line. If - the user added '-z ignore' to the command line we only add - those which are actually used. */ - ndt_needed = ld_state.ignore_unused_dsos ? 0 : ld_state.ndsofiles; + if (GENERATE_SYSV_HASH) + new_generated_scn (scn_dot_hash, ".hash", SHT_HASH, SHF_ALLOC, + sizeof (Elf32_Word), sizeof (Elf32_Word)); + if (GENERATE_GNU_HASH) + new_generated_scn (scn_dot_gnu_hash, ".gnu.hash", SHT_GNU_HASH, + SHF_ALLOC, sizeof (Elf32_Word), + sizeof (Elf32_Word)); /* Create the section associated with the PLT if necessary. */ if (ld_state.nplt > 0) @@ -2304,8 +2514,11 @@ ld_generic_generate_sections (struct ld_state *statep) : xelf_fsize (ld_state.outelf, ELF_T_RELA, 1), xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1)); - /* This means we will also need the .got section. */ - ld_state.need_got = true; + /* XXX We might need a function which returns the section flags. */ + new_generated_scn (scn_dot_gotplt, ".got.plt", SHT_PROGBITS, + SHF_ALLOC | SHF_WRITE, + xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1), + xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1)); /* Mark all used DSOs as used. Determine whether any referenced object uses symbol versioning. */ @@ -2359,29 +2572,29 @@ ld_generic_generate_sections (struct ld_state *statep) new_generated_scn (scn_dot_version_r, ".gnu.version_r", SHT_GNU_verneed, SHF_ALLOC, 0, xelf_fsize (ld_state.outelf, ELF_T_WORD, 1)); + } - /* Now count the used DSOs since this is what the user - wants. */ - ndt_needed = 0; - if (ld_state.ndsofiles > 0) - { - struct usedfiles *frunp = ld_state.dsofiles; + /* Now count the used DSOs since this is what the user + wants. */ + int ndt_needed = 0; + if (ld_state.ndsofiles > 0) + { + struct usedfiles *frunp = ld_state.dsofiles; - do - if (! ld_state.ignore_unused_dsos || frunp->used) - { - ++ndt_needed; - if (frunp->lazyload) - /* We have to create another dynamic section - entry for the DT_POSFLAG_1 entry. - - XXX Once more functionality than the - lazyloading flag are suppported the test - must be extended. */ - ++ndt_needed; - } - while ((frunp = frunp->next) != ld_state.dsofiles); - } + do + if (! frunp->as_needed || frunp->used) + { + ++ndt_needed; + if (frunp->lazyload) + /* We have to create another dynamic section + entry for the DT_POSFLAG_1 entry. + + XXX Once more functionality than the lazyloading + flag are suppported the test must be + extended. */ + ++ndt_needed; + } + while ((frunp = frunp->next) != ld_state.dsofiles); } if (use_versioning) @@ -2459,7 +2672,7 @@ ld_generic_open_outfile (struct ld_state *statep, int machine, int klass, { strcpy (mempcpy (tempfname, ld_state.outfname, outfname_len), ".XXXXXX"); - /* The useof mktemp() here is fine. We do not want to use + /* The use of mktemp() here is fine. We do not want to use mkstemp() since then the umask isn't used. And the output file will have these permissions anyhow. Any intruder could change the file later if it would be possible now. */ @@ -2654,7 +2867,7 @@ match_section (const char *osectname, struct filemask_section_name *sectmask, const char *brfname = basename (runp->fileinfo->rfname); /* If the section isn't used, the name doesn't match the positive - inclusion list or the name does match the negative inclusion + inclusion list, or the name does match the negative inclusion list, ignore the section. */ if (!runp->used || (sectmask->filemask != NULL @@ -2699,7 +2912,9 @@ match_section (const char *osectname, struct filemask_section_name *sectmask, newp->kind = scn_normal; newp->name = osectname; newp->type = SCNINFO_SHDR (found->shdr).sh_type; - newp->flags = SCNINFO_SHDR (found->shdr).sh_flags; + /* Executable or DSO do not have section groups. Drop that + information. */ + newp->flags = SCNINFO_SHDR (found->shdr).sh_flags & ~SHF_GROUP; newp->segment_nr = segment_nr; newp->last = found->next = found; newp->used = true; @@ -2730,9 +2945,12 @@ match_section (const char *osectname, struct filemask_section_name *sectmask, /* XXX Any better choice? */ queued->type = SHT_PROGBITS; if (queued->flags != SCNINFO_SHDR (found->shdr).sh_flags) + /* Executable or DSO do not have section groups. Drop that + information. */ queued->flags = ebl_sh_flags_combine (ld_state.ebl, queued->flags, - SCNINFO_SHDR (found->shdr).sh_flags); + SCNINFO_SHDR (found->shdr).sh_flags + & ~SHF_GROUP); /* Accumulate the relocation section size. */ queued->relsize += found->relsize; @@ -2765,7 +2983,7 @@ sort_sections_lscript (void) renaming the containing section in the output file. */ ld_state.nallsections = 0; size_t segment_nr; - size_t last_writable = ~0; + size_t last_writable = ~0ul; for (segment_nr = 0; segment != NULL; segment = segment->next, ++segment_nr) { struct output_rule *orule; @@ -2801,7 +3019,7 @@ sort_sections_lscript (void) This is why it is important to get the last block. */ if (ld_state.ncopy > 0 || ld_state.common_syms != NULL) { - if (last_writable == ~0) + if (last_writable == ~0ul) error (EXIT_FAILURE, 0, "no writable segment"); if (ld_state.allsections[last_writable]->type != SHT_NOBITS) @@ -3161,7 +3379,7 @@ reduce_symbol_p (XElf_Sym *sym, struct Ebl_Strent *strent) { search.id = strndupa (str, version - str); if (*++version == VER_CHR) - /* Skip the second '@' signalling a default definition. */ + /* Skip the second '@' signaling a default definition. */ ++version; } else @@ -3332,7 +3550,8 @@ optimal_bucket_size (Elf32_Word *hashcodes, size_t maxcnt, int optlevel) memset (counts, '\0', (maxcnt + 1) * sizeof (uint32_t)); /* Determine how often each hash bucket is used. */ - for (inner = 0; inner < maxcnt; ++inner) + assert (hashcodes[0] == 0); + for (inner = 1; inner < maxcnt; ++inner) ++lengths[hashcodes[inner] % size]; /* Determine the lengths. */ @@ -3393,6 +3612,17 @@ optimal_bucket_size (Elf32_Word *hashcodes, size_t maxcnt, int optlevel) } +static void +optimal_gnu_hash_size (Elf32_Word *hashcodes, size_t maxcnt, int optlevel, + size_t *bitmask_nwords, size_t *shift, size_t *nbuckets) +{ + // XXX Implement something real + *bitmask_nwords = 256; + *shift = 6; + *nbuckets = 3 * maxcnt / 2; +} + + static XElf_Addr find_entry_point (void) { @@ -3452,7 +3682,7 @@ find_entry_point (void) { if (ld_state.entry != NULL) error (0, 0, gettext ("\ -cannot find entry symbol \"%s\": defaulting to %#0*" PRIx64), +cannot find entry symbol '%s': defaulting to %#0*" PRIx64), ld_state.entry, xelf_getclass (ld_state.outelf) == ELFCLASS32 ? 10 : 18, (uint64_t) result); @@ -3479,9 +3709,9 @@ fillin_special_symbol (struct symbol *symst, size_t scnidx, size_t nsym, /* The name offset will be filled in later. */ sym->st_name = 0; /* Traditionally: globally visible. */ - sym->st_info = XELF_ST_INFO (STB_GLOBAL, symst->type); - /* No special visibility or so. */ - sym->st_other = 0; + sym->st_info = XELF_ST_INFO (symst->local ? STB_LOCAL : STB_GLOBAL, + symst->type); + sym->st_other = symst->hidden ? STV_HIDDEN : STV_DEFAULT; /* Reference to the GOT or dynamic section. Since the GOT and dynamic section are only created for executables and DSOs it cannot be that the section index is too large. */ @@ -3490,6 +3720,8 @@ fillin_special_symbol (struct symbol *symst, size_t scnidx, size_t nsym, sym->st_shndx = scnidx; /* We want the beginning of the section. */ sym->st_value = 0; + // XXX What size? + sym->st_size = 0; /* Determine the size of the section. */ if (scnidx != SHN_ABS) @@ -3568,66 +3800,491 @@ allocate_version_names (struct usedfiles *runp, struct Ebl_Strtab *dynstrtab) } -XElf_Off +static XElf_Off create_verneed_data (XElf_Off offset, Elf_Data *verneeddata, struct usedfiles *runp, int *ntotal) { - size_t verneed_size = xelf_fsize (ld_state.outelf, ELF_T_VNEED, 1); - size_t vernaux_size = xelf_fsize (ld_state.outelf, ELF_T_VNAUX, 1); - int need_offset; - bool filled = false; - GElf_Verneed verneed; - GElf_Vernaux vernaux; - int ndef = 0; -size_t cnt; - - /* If this DSO has no versions skip it. */ - if (runp->nverdefused == 0) - return offset; - - /* We fill in the Verneed record last. Remember the - offset. */ - need_offset = offset; - offset += verneed_size; - - for (cnt = 2; cnt <= runp->nverdef; ++cnt) - if (runp->verdefused[cnt] != 0) - { - assert (runp->verdefent[cnt] != NULL); + size_t verneed_size = xelf_fsize (ld_state.outelf, ELF_T_VNEED, 1); + size_t vernaux_size = xelf_fsize (ld_state.outelf, ELF_T_VNAUX, 1); + int need_offset; + bool filled = false; + GElf_Verneed verneed; + GElf_Vernaux vernaux; + int ndef = 0; + size_t cnt; - if (filled) - { - vernaux.vna_next = vernaux_size; - (void) gelf_update_vernaux (verneeddata, offset, - &vernaux); - offset += vernaux_size; - } + /* If this DSO has no versions skip it. */ + if (runp->nverdefused == 0) + return offset; - vernaux.vna_hash - = elf_hash (ebl_string (runp->verdefent[cnt])); - vernaux.vna_flags = 0; - vernaux.vna_other = runp->verdefused[cnt]; - vernaux.vna_name = ebl_strtaboffset (runp->verdefent[cnt]); - filled = true; - ++ndef; - } + /* We fill in the Verneed record last. Remember the offset. */ + need_offset = offset; + offset += verneed_size; + + for (cnt = 2; cnt <= runp->nverdef; ++cnt) + if (runp->verdefused[cnt] != 0) + { + assert (runp->verdefent[cnt] != NULL); + + if (filled) + { + vernaux.vna_next = vernaux_size; + (void) gelf_update_vernaux (verneeddata, offset, &vernaux); + offset += vernaux_size; + } + + vernaux.vna_hash = elf_hash (ebl_string (runp->verdefent[cnt])); + vernaux.vna_flags = 0; + vernaux.vna_other = runp->verdefused[cnt]; + vernaux.vna_name = ebl_strtaboffset (runp->verdefent[cnt]); + filled = true; + ++ndef; + } + + assert (filled); + vernaux.vna_next = 0; + (void) gelf_update_vernaux (verneeddata, offset, &vernaux); + offset += vernaux_size; + + verneed.vn_version = VER_NEED_CURRENT; + verneed.vn_cnt = ndef; + verneed.vn_file = ebl_strtaboffset (runp->verdefent[1]); + /* The first auxiliary entry is always found directly + after the verneed entry. */ + verneed.vn_aux = verneed_size; + verneed.vn_next = --*ntotal > 0 ? offset - need_offset : 0; + (void) gelf_update_verneed (verneeddata, need_offset, &verneed); + + return offset; +} + + +/* Callback for qsort to sort dynamic string table. */ +static Elf32_Word *global_hashcodes; +static size_t global_nbuckets; +static int +sortfct_hashval (const void *p1, const void *p2) +{ + size_t idx1 = *(size_t *) p1; + size_t idx2 = *(size_t *) p2; + + int def1 = ndxtosym[idx1]->defined && !ndxtosym[idx1]->in_dso; + int def2 = ndxtosym[idx2]->defined && !ndxtosym[idx2]->in_dso; + + if (! def1 && def2) + return -1; + if (def1 && !def2) + return 1; + if (! def1) + return 0; + + Elf32_Word hval1 = (global_hashcodes[ndxtosym[idx1]->outdynsymidx] + % global_nbuckets); + Elf32_Word hval2 = (global_hashcodes[ndxtosym[idx2]->outdynsymidx] + % global_nbuckets); + + if (hval1 < hval2) + return -1; + if (hval1 > hval2) + return 1; + return 0; +} + + +/* Sort the dynamic symbol table. The GNU hash table lookup assumes + that all symbols with the same hash value module the bucket table + size follow one another. This avoids the extra hash chain table. + There is no need (and no way) to perform this operation if we do + not use the new hash table format. */ +static void +create_gnu_hash (size_t nsym_local, size_t nsym, size_t nsym_dyn, + Elf32_Word *gnuhashcodes) +{ + size_t gnu_bitmask_nwords = 0; + size_t gnu_shift = 0; + size_t gnu_nbuckets = 0; + Elf32_Word *gnu_bitmask = NULL; + Elf32_Word *gnu_buckets = NULL; + Elf32_Word *gnu_chain = NULL; + XElf_Shdr_vardef (shdr); + + /* Determine the "optimal" bucket size. */ + optimal_gnu_hash_size (gnuhashcodes, nsym_dyn, ld_state.optlevel, + &gnu_bitmask_nwords, &gnu_shift, &gnu_nbuckets); + + /* Create the .gnu.hash section data structures. */ + Elf_Scn *hashscn = elf_getscn (ld_state.outelf, ld_state.gnuhashscnidx); + xelf_getshdr (hashscn, shdr); + Elf_Data *hashdata = elf_newdata (hashscn); + if (shdr == NULL || hashdata == NULL) + error (EXIT_FAILURE, 0, gettext ("\ +cannot create GNU hash table section for output file: %s"), + elf_errmsg (-1)); + + shdr->sh_link = ld_state.dynsymscnidx; + (void) xelf_update_shdr (hashscn, shdr); + + hashdata->d_size = (xelf_fsize (ld_state.outelf, ELF_T_ADDR, + gnu_bitmask_nwords) + + (4 + gnu_nbuckets + nsym_dyn) * sizeof (Elf32_Word)); + hashdata->d_buf = xcalloc (1, hashdata->d_size); + hashdata->d_align = sizeof (Elf32_Word); + hashdata->d_type = ELF_T_WORD; + hashdata->d_off = 0; + + ((Elf32_Word *) hashdata->d_buf)[0] = gnu_nbuckets; + ((Elf32_Word *) hashdata->d_buf)[2] = gnu_bitmask_nwords; + ((Elf32_Word *) hashdata->d_buf)[3] = gnu_shift; + gnu_bitmask = &((Elf32_Word *) hashdata->d_buf)[4]; + gnu_buckets = &gnu_bitmask[xelf_fsize (ld_state.outelf, ELF_T_ADDR, + gnu_bitmask_nwords) + / sizeof (*gnu_buckets)]; + gnu_chain = &gnu_buckets[gnu_nbuckets]; +#ifndef NDEBUG + void *endp = &gnu_chain[nsym_dyn]; +#endif + assert (endp == (void *) ((char *) hashdata->d_buf + hashdata->d_size)); + + + size_t *remap = xmalloc (nsym_dyn * sizeof (size_t)); +#ifndef NDEBUG + size_t nsym_dyn_cnt = 1; +#endif + for (size_t cnt = nsym_local; cnt < nsym; ++cnt) + if (symstrent[cnt] != NULL) + { + assert (ndxtosym[cnt]->outdynsymidx > 0); + assert (ndxtosym[cnt]->outdynsymidx < nsym_dyn); + remap[ndxtosym[cnt]->outdynsymidx] = cnt; +#ifndef NDEBUG + ++nsym_dyn_cnt; +#endif + } + assert (nsym_dyn_cnt == nsym_dyn); + + // XXX Until we can rely on qsort_r use global variables. + global_hashcodes = gnuhashcodes; + global_nbuckets = gnu_nbuckets; + qsort (remap + 1, nsym_dyn - 1, sizeof (size_t), sortfct_hashval); + + bool bm32 = (xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1) + == sizeof (Elf32_Word)); + + size_t first_defined = 0; + Elf64_Word bitmask_idxbits = gnu_bitmask_nwords - 1; + Elf32_Word last_bucket = 0; + for (size_t cnt = 1; cnt < nsym_dyn; ++cnt) + { + if (first_defined == 0) + { + if (! ndxtosym[remap[cnt]]->defined + || ndxtosym[remap[cnt]]->in_dso) + goto next; + + ((Elf32_Word *) hashdata->d_buf)[1] = first_defined = cnt; + } + + Elf32_Word hval = gnuhashcodes[ndxtosym[remap[cnt]]->outdynsymidx]; + + if (bm32) + { + Elf32_Word *bsw = &gnu_bitmask[(hval / 32) & bitmask_idxbits]; + assert ((void *) gnu_bitmask <= (void *) bsw); + assert ((void *) bsw < (void *) gnu_buckets); + *bsw |= 1 << (hval & 31); + *bsw |= 1 << ((hval >> gnu_shift) & 31); + } + else + { + Elf64_Word *bsw = &((Elf64_Word *) gnu_bitmask)[(hval / 64) + & bitmask_idxbits]; + assert ((void *) gnu_bitmask <= (void *) bsw); + assert ((void *) bsw < (void *) gnu_buckets); + *bsw |= 1 << (hval & 63); + *bsw |= 1 << ((hval >> gnu_shift) & 63); + } + + size_t this_bucket = hval % gnu_nbuckets; + if (cnt == first_defined || this_bucket != last_bucket) + { + if (cnt != first_defined) + { + /* Terminate the previous chain. */ + assert ((void *) &gnu_chain[cnt - first_defined - 1] < endp); + gnu_chain[cnt - first_defined - 1] |= 1; + } + + assert (this_bucket < gnu_nbuckets); + gnu_buckets[this_bucket] = cnt; + last_bucket = this_bucket; + } + + assert (cnt >= first_defined); + assert (cnt - first_defined < nsym_dyn); + gnu_chain[cnt - first_defined] = hval & ~1u; + + next: + ndxtosym[remap[cnt]]->outdynsymidx = cnt; + } + + /* Terminate the last chain. */ + if (first_defined != 0) + { + assert (nsym_dyn > first_defined); + assert (nsym_dyn - first_defined - 1 < nsym_dyn); + gnu_chain[nsym_dyn - first_defined - 1] |= 1; + + hashdata->d_size -= first_defined * sizeof (Elf32_Word); + } + else + /* We do not need any hash table. */ + // XXX + do { } while (0); + + free (remap); +} + + +/* Create the SysV-style hash table. */ +static void +create_hash (size_t nsym_local, size_t nsym, size_t nsym_dyn, + Elf32_Word *hashcodes) +{ + size_t nbucket = 0; + Elf32_Word *bucket = NULL; + Elf32_Word *chain = NULL; + XElf_Shdr_vardef (shdr); + + /* Determine the "optimal" bucket size. If we also generate the + new-style hash function there is no need to waste effort and + space on the old one which should not be used. Make it as small + as possible. */ + if (GENERATE_GNU_HASH) + nbucket = 1; + else + nbucket = optimal_bucket_size (hashcodes, nsym_dyn, ld_state.optlevel); + /* Create the .hash section data structures. */ + Elf_Scn *hashscn = elf_getscn (ld_state.outelf, ld_state.hashscnidx); + xelf_getshdr (hashscn, shdr); + Elf_Data *hashdata = elf_newdata (hashscn); + if (shdr == NULL || hashdata == NULL) + error (EXIT_FAILURE, 0, gettext ("\ +cannot create hash table section for output file: %s"), + elf_errmsg (-1)); + + shdr->sh_link = ld_state.dynsymscnidx; + (void) xelf_update_shdr (hashscn, shdr); + + hashdata->d_size = (2 + nsym_dyn + nbucket) * sizeof (Elf32_Word); + hashdata->d_buf = xcalloc (1, hashdata->d_size); + hashdata->d_align = sizeof (Elf32_Word); + hashdata->d_type = ELF_T_WORD; + hashdata->d_off = 0; + + ((Elf32_Word *) hashdata->d_buf)[0] = nbucket; + ((Elf32_Word *) hashdata->d_buf)[1] = nsym_dyn; + bucket = &((Elf32_Word *) hashdata->d_buf)[2]; + chain = &((Elf32_Word *) hashdata->d_buf)[2 + nbucket]; + + for (size_t cnt = nsym_local; cnt < nsym; ++cnt) + if (symstrent[cnt] != NULL) + { + size_t dynidx = ndxtosym[cnt]->outdynsymidx; + size_t hashidx = hashcodes[dynidx] % nbucket; + if (bucket[hashidx] == 0) + bucket[hashidx] = dynidx; + else + { + hashidx = bucket[hashidx]; + while (chain[hashidx] != 0) + hashidx = chain[hashidx]; - assert (filled); - vernaux.vna_next = 0; - (void) gelf_update_vernaux (verneeddata, offset, &vernaux); - offset += vernaux_size; - - verneed.vn_version = VER_NEED_CURRENT; - verneed.vn_cnt = ndef; - verneed.vn_file = ebl_strtaboffset (runp->verdefent[1]); - /* The first auxiliary entry is always found directly - after the verneed entry. */ - verneed.vn_aux = verneed_size; - verneed.vn_next = --*ntotal > 0 ? offset - need_offset : 0; - (void) gelf_update_verneed (verneeddata, need_offset, - &verneed); - - return offset; + chain[hashidx] = dynidx; + } + } +} + + +static void +create_build_id_section (Elf_Scn *scn) +{ + /* We know how large the section will be so we can create it now. */ + Elf_Data *d = elf_newdata (scn); + if (d == NULL) + error (EXIT_FAILURE, 0, gettext ("cannot create build ID section: %s"), + elf_errmsg (-1)); + + d->d_type = ELF_T_BYTE; + d->d_version = EV_CURRENT; + + /* The note section header. */ + assert (sizeof (Elf32_Nhdr) == sizeof (Elf64_Nhdr)); + d->d_size = sizeof (GElf_Nhdr); + /* The string is four bytes long. */ + d->d_size += sizeof (ELF_NOTE_GNU); + assert (d->d_size % 4 == 0); + + if (strcmp (ld_state.build_id, "md5") == 0 + || strcmp (ld_state.build_id, "uuid") == 0) + d->d_size += 16; + else if (strcmp (ld_state.build_id, "sha1") == 0) + d->d_size += 20; + else + { + assert (ld_state.build_id[0] == '0' && ld_state.build_id[1] == 'x'); + /* Use an upper limit of the possible number of bytes generated + from the string. */ + d->d_size += strlen (ld_state.build_id) / 2; + } + + d->d_buf = xcalloc (d->d_size, 1); + d->d_off = 0; + d->d_align = 0; +} + + +static void +compute_hash_sum (void (*hashfct) (const void *, size_t, void *), void *ctx) +{ + /* The call cannot fail. */ + size_t shstrndx; + (void) elf_getshstrndx (ld_state.outelf, &shstrndx); + + const char *ident = elf_getident (ld_state.outelf, NULL); + bool same_byte_order = ((ident[EI_DATA] == ELFDATA2LSB + && __BYTE_ORDER == __LITTLE_ENDIAN) + || (ident[EI_DATA] == ELFDATA2MSB + && __BYTE_ORDER == __BIG_ENDIAN)); + + /* Iterate over all sections to find those which are not strippable. */ + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (ld_state.outelf, scn)) != NULL) + { + /* Get the section header. */ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + assert (shdr != NULL); + + if (SECTION_STRIP_P (shdr, elf_strptr (ld_state.outelf, shstrndx, + shdr->sh_name), true)) + /* The section can be stripped. Don't use it. */ + continue; + + /* Do not look at NOBITS sections. */ + if (shdr->sh_type == SHT_NOBITS) + continue; + + /* Iterate through the list of data blocks. */ + Elf_Data *data = NULL; + while ((data = INTUSE(elf_getdata) (scn, data)) != NULL) + /* If the file byte order is the same as the host byte order + process the buffer directly. If the data is just a stream + of bytes which the library will not convert we can use it + as well. */ + if (likely (same_byte_order) || data->d_type == ELF_T_BYTE) + hashfct (data->d_buf, data->d_size, ctx); + else + { + /* Convert the data to file byte order. */ + if (gelf_xlatetof (ld_state.outelf, data, data, ident[EI_DATA]) + == NULL) + error (EXIT_FAILURE, 0, gettext ("\ +cannot convert section data to file format: %s"), + elf_errmsg (-1)); + + hashfct (data->d_buf, data->d_size, ctx); + + /* And convert it back. */ + if (gelf_xlatetom (ld_state.outelf, data, data, ident[EI_DATA]) + == NULL) + error (EXIT_FAILURE, 0, gettext ("\ +cannot convert section data to memory format: %s"), + elf_errmsg (-1)); + } + } +} + + +/* Iterate over the sections */ +static void +compute_build_id (void) +{ + Elf_Data *d = elf_getdata (elf_getscn (ld_state.outelf, + ld_state.buildidscnidx), NULL); + assert (d != NULL); + + GElf_Nhdr *hdr = d->d_buf; + hdr->n_namesz = sizeof (ELF_NOTE_GNU); + hdr->n_type = NT_GNU_BUILD_ID; + char *dp = mempcpy (hdr + 1, ELF_NOTE_GNU, sizeof (ELF_NOTE_GNU)); + + if (strcmp (ld_state.build_id, "sha1") == 0) + { + /* Compute the SHA1 sum of various parts of the generated file. + We compute the hash sum over the external representation. */ + struct sha1_ctx ctx; + sha1_init_ctx (&ctx); + + /* Compute the hash sum by running over all sections. */ + compute_hash_sum ((void (*) (const void *, size_t, void *)) sha1_process_bytes, + &ctx); + + /* We are done computing the checksum. */ + (void) sha1_finish_ctx (&ctx, dp); + + hdr->n_descsz = SHA1_DIGEST_SIZE; + } + else if (strcmp (ld_state.build_id, "md5") == 0) + { + /* Compute the MD5 sum of various parts of the generated file. + We compute the hash sum over the external representation. */ + struct md5_ctx ctx; + md5_init_ctx (&ctx); + + /* Compute the hash sum by running over all sections. */ + compute_hash_sum ((void (*) (const void *, size_t, void *)) md5_process_bytes, + &ctx); + + /* We are done computing the checksum. */ + (void) md5_finish_ctx (&ctx, dp); + + hdr->n_descsz = MD5_DIGEST_SIZE; + } + else if (strcmp (ld_state.build_id, "uuid") == 0) + { + int fd = open ("/dev/urandom", O_RDONLY); + if (fd == -1) + error (EXIT_FAILURE, errno, gettext ("cannot open '%s'"), + "/dev/urandom"); + + if (TEMP_FAILURE_RETRY (read (fd, dp, 16)) != 16) + error (EXIT_FAILURE, 0, gettext ("cannot read enough data for UUID")); + + close (fd); + + hdr->n_descsz = 16; + } + else + { + const char *cp = ld_state.build_id + 2; + + /* The form of the string has been verified before so here we can + simplify the scanning. */ + do + { + if (isxdigit (cp[0])) + { + char ch1 = tolower (cp[0]); + char ch2 = tolower (cp[1]); + + *dp++ = (((isdigit (ch1) ? ch1 - '0' : ch1 - 'a' + 10) << 4) + | (isdigit (ch2) ? ch2 - '0' : ch2 - 'a' + 10)); + } + else + ++cp; + } + while (*cp != '\0'); + } } @@ -3642,7 +4299,7 @@ size_t cnt; For executables (shared or not) we have to create the program header, additional sections like the .interp, eventually (in addition) create a dynamic symbol table and a dynamic section. Also the relocations -have to be processed differently. */ + have to be processed differently. */ static int ld_generic_create_outfile (struct ld_state *statep) { @@ -3684,6 +4341,7 @@ ld_generic_create_outfile (struct ld_state *statep) Elf_Data *dynsymdata = NULL; Elf_Data *dynstrdata = NULL; Elf32_Word *hashcodes = NULL; + Elf32_Word *gnuhashcodes = NULL; size_t nsym_dyn_allocated = 0; Elf_Scn *versymscn = NULL; Elf_Data *versymdata = NULL; @@ -3827,6 +4485,17 @@ ld_generic_create_outfile (struct ld_state *statep) continue; } + if (unlikely (head->kind == scn_dot_gotplt)) + { + /* Remember the index of this section. */ + ld_state.gotpltscnidx = elf_ndxscn (scn); + + /* Give the backend the change to initialize the section. */ + INITIALIZE_GOTPLT (&ld_state, scn); + + continue; + } + if (unlikely (head->kind == scn_dot_dynrel)) { Elf_Data *outdata; @@ -3908,7 +4577,7 @@ ld_generic_create_outfile (struct ld_state *statep) struct usedfiles *frunp = ld_state.dsofiles; do - if (! ld_state.ignore_unused_dsos || frunp->used) + if (! frunp->as_needed || frunp->used) frunp->sonameent = ebl_strtabadd (dynstrtab, frunp->soname, 0); while ((frunp = frunp->next) != ld_state.dsofiles); @@ -3969,6 +4638,14 @@ ld_generic_create_outfile (struct ld_state *statep) continue; } + if (unlikely (head->kind == scn_dot_gnu_hash)) + { + /* Remember the index of this section. */ + ld_state.gnuhashscnidx = elf_ndxscn (scn); + + continue; + } + if (unlikely (head->kind == scn_dot_plt)) { /* Remember the index of this section. */ @@ -4007,6 +4684,16 @@ ld_generic_create_outfile (struct ld_state *statep) continue; } + if (unlikely (head->kind == scn_dot_note_gnu_build_id)) + { + /* Remember the index of this section. */ + ld_state.buildidscnidx = elf_ndxscn (scn); + + create_build_id_section (scn); + + continue; + } + /* If we come here we must be handling a normal section. */ assert (head->kind == scn_normal); @@ -4444,7 +5131,7 @@ ld_generic_create_outfile (struct ld_state *statep) gettext ("cannot create section for output file: %s"), elf_errmsg (-1)); - outdata->d_buf = (void *) "\0ld (Red Hat " PACKAGE ") " VERSION; + outdata->d_buf = (void *) "\0ld (" PACKAGE_NAME ") " PACKAGE_VERSION; outdata->d_size = strlen ((char *) outdata->d_buf + 1) + 2; outdata->d_off = offset; outdata->d_type = ELF_T_BYTE; @@ -4481,7 +5168,8 @@ ld_generic_create_outfile (struct ld_state *statep) if (ld_state.got_symbol != NULL) { assert (nsym < nsym_allocated); - fillin_special_symbol (ld_state.got_symbol, ld_state.gotscnidx, + // XXX Fix so that it works even if no PLT is needed. + fillin_special_symbol (ld_state.got_symbol, ld_state.gotpltscnidx, nsym++, symdata, strtab); } @@ -4510,6 +5198,7 @@ ld_generic_create_outfile (struct ld_state *statep) file = ld_state.relfiles->next; symdata = elf_getdata (elf_getscn (ld_state.outelf, ld_state.symscnidx), NULL); + do { size_t maxcnt; @@ -4568,11 +5257,11 @@ ld_generic_create_outfile (struct ld_state *statep) continue; #if NATIVE_ELF != 0 - /* Copy old data. */ - XElf_Sym *sym2 = sym; - assert (nsym < nsym_allocated); - xelf_getsym (symdata, nsym, sym); - *sym = *sym2; + /* Copy old data. We create a temporary copy because the + symbol might still be discarded. */ + XElf_Sym sym_mem; + sym_mem = *sym; + sym = &sym_mem; #endif if (sym->st_shndx != SHN_UNDEF @@ -4677,6 +5366,7 @@ section index too large in dynamic symbol table")); /* Once we know the name this field will get the correct offset. For now set it to zero which means no name associated. */ + GElf_Word st_name = sym->st_name; sym->st_name = 0; /* If we had to merge sections we have a completely new @@ -4687,7 +5377,7 @@ section index too large in dynamic symbol table")); /* Create the record in the output sections. */ assert (nsym < nsym_allocated); - xelf_update_symshndx (symdata, xndxdata, nsym, sym, xndx, 0); + xelf_update_symshndx (symdata, xndxdata, nsym, sym, xndx, 1); /* Add the reference to the symbol record in case we need it. Find the symbol if this has not happened yet. We do @@ -4695,12 +5385,33 @@ section index too large in dynamic symbol table")); if (defp == NULL && cnt >= file->nlocalsymbols) { defp = file->symref[cnt]; - assert (defp != NULL); + + if (defp == NULL) + { + /* This is a symbol in a discarded COMDAT section. + Find the definition we actually use. */ + // XXX The question is: do we have to do this here + // XXX or can we do it earlier when we discard the + // XXX section. + struct symbol search; + search.name = elf_strptr (file->elf, file->symstridx, + st_name); + struct symbol *realp + = ld_symbol_tab_find (&ld_state.symbol_tab, + elf_hash (search.name), &search); + if (realp == NULL) + // XXX What to do here? + error (EXIT_FAILURE, 0, + "couldn't find symbol from COMDAT section"); + + file->symref[cnt] = realp; + + continue; + } } - /* Store the reference to the symbol record. The - sorting code will have to keep this array in the - correct order, too. */ + /* Store the reference to the symbol record. The sorting + code will have to keep this array in the correct order, too. */ ndxtosym[nsym] = defp; /* One more entry finished. */ @@ -4717,7 +5428,6 @@ section index too large in dynamic symbol table")); nothing. */ assert (xndxdata == NULL || need_xndx); - /* Create the version related sections. */ if (ld_state.verneedscnidx != 0) { @@ -4760,6 +5470,10 @@ section index too large in dynamic symbol table")); if (reduce_symbol_p (sym, symstrent[cnt])) { + // XXX Check whether this is correct... + assert (ndxtosym[cnt]->outdynsymidx != 0); + ndxtosym[cnt]->outdynsymidx = 0; + sym->st_info = XELF_ST_INFO (STB_LOCAL, XELF_ST_TYPE (sym->st_info)); (void) xelf_update_sym (symdata, cnt, sym); @@ -5077,7 +5791,7 @@ section index too large in dynamic symbol table")); gelf_getversym (symp->file->versymdata, symp->symidx, &versym); - (void) gelf_update_versym (versymdata, nsym_dyn, + (void) gelf_update_versym (versymdata, symp->outdynsymidx, &symp->file->verdefused[versym]); } } @@ -5116,16 +5830,16 @@ cannot create dynamic symbol table for output file: %s"), /* We need one more array which contains the hash codes of the symbol names. */ - hashcodes = (Elf32_Word *) xcalloc (nsym_dyn_allocated, + hashcodes = (Elf32_Word *) xcalloc (__builtin_popcount ((int) ld_state.hash_style) + * nsym_dyn_allocated, sizeof (Elf32_Word)); + gnuhashcodes = hashcodes; + if (GENERATE_SYSV_HASH) + gnuhashcodes += nsym_dyn_allocated; /* We have and empty entry at the beginning. */ nsym_dyn = 1; - /* We don't mix PLT symbols and others. */ - size_t plt_idx = 1; - size_t obj_idx = 1 + ld_state.nplt; - /* Populate the table. */ for (cnt = nsym_local; cnt < nsym; ++cnt) { @@ -5150,59 +5864,73 @@ section index too large in dynamic symbol table")); if (XELF_ST_TYPE (sym->st_info) == STT_FILE || XELF_ST_VISIBILITY (sym->st_other) == STV_INTERNAL || XELF_ST_VISIBILITY (sym->st_other) == STV_HIDDEN - || (!ndxtosym[cnt]->in_dso && ndxtosym[cnt]->defined)) + || (!ld_state.export_all_dynamic + && !ndxtosym[cnt]->in_dso && ndxtosym[cnt]->defined)) { symstrent[cnt] = NULL; continue; } - size_t idx; - if (ndxtosym[cnt]->in_dso && ndxtosym[cnt]->type == STT_FUNC) - { - idx = plt_idx++; - assert (idx < 1 + ld_state.nplt); - } - else - { - idx = obj_idx++; - assert (idx < nsym_dyn_allocated); - } + /* Store the index of the symbol in the dynamic symbol + table. This is a preliminary value in case we use the + GNU-style hash table. */ + ndxtosym[cnt]->outdynsymidx = nsym_dyn; + + /* Create a new string table entry. */ + const char *str = ndxtosym[cnt]->name; + symstrent[cnt] = ebl_strtabadd (dynstrtab, str, 0); + if (GENERATE_SYSV_HASH) + hashcodes[nsym_dyn] = elf_hash (str); + if (GENERATE_GNU_HASH) + gnuhashcodes[nsym_dyn] = elf_gnu_hash (str); + ++nsym_dyn; + } + + if (ld_state.file_type != relocatable_file_type) + { + /* Finalize the dynamic string table. */ + ebl_strtabfinalize (dynstrtab, dynstrdata); + + assert (ld_state.hashscnidx != 0 || ld_state.gnuhashscnidx != 0); + + /* Create the GNU-style hash table. */ + if (GENERATE_GNU_HASH) + create_gnu_hash (nsym_local, nsym, nsym_dyn, gnuhashcodes); + + /* Create the SysV-style hash table. This has to happen + after the GNU-style table is created since + CREATE-GNU-HASH might reorder the dynamic symbol table. */ + if (GENERATE_SYSV_HASH) + create_hash (nsym_local, nsym, nsym_dyn, hashcodes); + } /* Add the version information. */ - if (versymdata != NULL) + if (versymdata != NULL) + for (cnt = nsym_local; cnt < nsym; ++cnt) + if (symstrent[cnt] != NULL) { struct symbol *symp = ndxtosym[cnt]; - if (symp->file->verdefdata != NULL) + /* Synthetic symbols (i.e., those with no file attached) + have no version information. */ + if (symp->file != NULL && symp->file->verdefdata != NULL) { GElf_Versym versym; gelf_getversym (symp->file->versymdata, symp->symidx, &versym); - (void) gelf_update_versym (versymdata, idx, + (void) gelf_update_versym (versymdata, symp->outdynsymidx, &symp->file->verdefused[versym]); } else { /* XXX Add support for version definitions. */ GElf_Versym global = VER_NDX_GLOBAL; - (void) gelf_update_versym (versymdata, idx, &global); + (void) gelf_update_versym (versymdata, nsym_dyn, &global); } } - /* Store the index of the symbol in the dynamic symbol table. */ - ndxtosym[cnt]->outdynsymidx = idx; - - /* Create a new string table entry. */ - const char *str = ndxtosym[cnt]->name; - symstrent[cnt] = ebl_strtabadd (dynstrtab, str, 0); - hashcodes[idx] = elf_hash (str); - ++nsym_dyn; - } - assert (nsym_dyn == obj_idx); - assert (ld_state.nplt + 1 == plt_idx); - /* Update the information about the symbol section. */ if (versymdata != NULL) { @@ -5223,52 +5951,11 @@ section index too large in dynamic symbol table")); if (ld_state.file_type != relocatable_file_type) { - size_t nbucket; - Elf32_Word *bucket; - Elf32_Word *chain; - size_t nchain; - Elf_Scn *hashscn; - Elf_Data *hashdata; - - /* Finalize the dynamic string table. */ - ebl_strtabfinalize (dynstrtab, dynstrdata); - - /* Determine the "optimal" bucket size. */ - nbucket = optimal_bucket_size (hashcodes, nsym_dyn, ld_state.optlevel); - - /* Create the .hash section data structures. */ - assert (ld_state.hashscnidx != 0); - hashscn = elf_getscn (ld_state.outelf, ld_state.hashscnidx); - xelf_getshdr (hashscn, shdr); - hashdata = elf_newdata (hashscn); - if (shdr == NULL || hashdata == NULL) - error (EXIT_FAILURE, 0, gettext ("\ -cannot create hash table section for output file: %s"), - elf_errmsg (-1)); - - shdr->sh_link = ld_state.dynsymscnidx; - (void) xelf_update_shdr (hashscn, shdr); - - hashdata->d_size = (2 + nsym_dyn + nbucket) * sizeof (Elf32_Word); - hashdata->d_buf = xcalloc (1, hashdata->d_size); - hashdata->d_align = sizeof (Elf32_Word); - hashdata->d_type = ELF_T_WORD; - hashdata->d_off = 0; - - ((Elf32_Word *) hashdata->d_buf)[0] = nbucket; - ((Elf32_Word *) hashdata->d_buf)[1] = nsym_dyn; - bucket = &((Elf32_Word *) hashdata->d_buf)[2]; - chain = &((Elf32_Word *) hashdata->d_buf)[2 + nbucket]; - - /* Haven't yet filled in any chain value. */ - nchain = 0; - /* Now put the names in. */ for (cnt = nsym_local; cnt < nsym; ++cnt) if (symstrent[cnt] != NULL) { XElf_Sym_vardef (sym); - size_t hashidx; size_t dynidx = ndxtosym[cnt]->outdynsymidx; #if NATIVE_ELF != 0 @@ -5284,27 +5971,10 @@ cannot create hash table section for output file: %s"), sym->st_name = ebl_strtaboffset (symstrent[cnt]); (void) xelf_update_sym (dynsymdata, dynidx, sym); - - /* Add to the hash table. */ - hashidx = hashcodes[dynidx] % nbucket; - if (bucket[hashidx] == 0) - bucket[hashidx] = dynidx; - else - { - hashidx = bucket[hashidx]; - while (chain[hashidx] != 0) - hashidx = chain[hashidx]; - - chain[hashidx] = dynidx; - } } free (hashcodes); - /* We don't need the map from the symbol table index to the symbol - structure anymore. */ - free (ndxtosym); - /* Create the required version section. */ if (ld_state.verneedscnidx != 0) { @@ -5377,10 +6047,6 @@ cannot create hash table section for output file: %s"), /* Write the updated info back. */ (void) xelf_update_shdr (dynsymscn, shdr); } - else - /* We don't need the map from the symbol table index to the symbol - structure anymore. */ - free (ndxtosym); /* We don't need the string table anymore. */ free (symstrent); @@ -5413,7 +6079,7 @@ cannot create hash table section for output file: %s"), strtab_ent = ebl_strtabadd (ld_state.shstrtab, ".strtab", 8); /* At this point we would have to test for failures in the allocation. But we skip this. First, the problem will be caught - latter when doing more allocations for the section header table. + later when doing more allocations for the section header table. Even if this would not be the case all that would happen is that the section names are empty. The binary would still be usable if it is an executable or a DSO. Not adding the test here saves @@ -5518,11 +6184,7 @@ cannot create hash table section for output file: %s"), groups = ld_state.groups; while (groups != NULL) { - Elf_Scn *scn; - struct scngroup *oldp; - Elf32_Word si; - - scn = elf_getscn (ld_state.outelf, groups->outscnidx); + Elf_Scn *scn = elf_getscn (ld_state.outelf, groups->outscnidx); xelf_getshdr (scn, shdr); assert (shdr != NULL); @@ -5533,7 +6195,8 @@ cannot create hash table section for output file: %s"), shdr->sh_entsize = sizeof (Elf32_Word); /* Determine the index for the signature symbol. */ - si = groups->symbol->file->symindirect[groups->symbol->symidx]; + Elf32_Word si + = groups->symbol->file->symindirect[groups->symbol->symidx]; if (si == 0) { assert (groups->symbol->file->symref[groups->symbol->symidx] @@ -5545,7 +6208,7 @@ cannot create hash table section for output file: %s"), (void) xelf_update_shdr (scn, shdr); - oldp = groups; + struct scngroup *oldp = groups; groups = groups->next; free (oldp); } @@ -5553,13 +6216,6 @@ cannot create hash table section for output file: %s"), if (ld_state.file_type != relocatable_file_type) { - size_t nphdr; - XElf_Addr addr; - struct output_segment *segment; - Elf_Scn *scn; - Elf32_Word nsec; - XElf_Phdr_vardef (phdr); - /* Every executable needs a program header. The number of entries varies. One exists for each segment. Each SHT_NOTE section gets one, too. For dynamically linked executables we have to create @@ -5567,8 +6223,12 @@ cannot create hash table section for output file: %s"), section. First count the number of segments. XXX Determine whether the segment is non-empty. */ - nphdr = 0; - segment = ld_state.output_segments; + size_t nphdr = 0; + + /* We always add a PT_GNU_stack entry. */ + ++nphdr; + + struct output_segment *segment = ld_state.output_segments; while (segment != NULL) { ++nphdr; @@ -5578,12 +6238,23 @@ cannot create hash table section for output file: %s"), /* Add the number of SHT_NOTE sections. We counted them earlier. */ nphdr += ld_state.nnotesections; - /* If we create a DSO or the file is linked against DSOs we have three - more entries: INTERP, PHDR, DYNAMIC. */ + /* If we create a DSO or the file is linked against DSOs we have + at least one more entry: DYNAMIC. If an interpreter is + specified we add PHDR and INTERP, too. */ if (dynamically_linked_p ()) - nphdr += 3; + { + ++nphdr; + + if (ld_state.interp != NULL || ld_state.file_type != dso_file_type) + nphdr += 2; + } + + /* If we need a TLS segment we need an entry for that. */ + if (ld_state.need_tls) + ++nphdr; /* Create the program header structure. */ + XElf_Phdr_vardef (phdr); if (xelf_newphdr (ld_state.outelf, nphdr) == 0) error (EXIT_FAILURE, 0, gettext ("cannot create program header: %s"), elf_errmsg (-1)); @@ -5599,17 +6270,30 @@ cannot create hash table section for output file: %s"), /* Now determine the memory addresses of all the sections and segments. */ - nsec = 0; - scn = elf_getscn (ld_state.outelf, ld_state.allsections[nsec]->scnidx); + Elf32_Word nsec = 0; + Elf_Scn *scn = elf_getscn (ld_state.outelf, + ld_state.allsections[nsec]->scnidx); xelf_getshdr (scn, shdr); assert (shdr != NULL); /* The address we start with is the offset of the first (not zeroth) section. */ - addr = shdr->sh_offset; + XElf_Addr addr = shdr->sh_offset; + XElf_Addr tls_offset = 0; + XElf_Addr tls_start = ~((XElf_Addr) 0); + XElf_Addr tls_end = 0; + XElf_Off tls_filesize = 0; + XElf_Addr tls_align = 0; /* The index of the first loadable segment. */ - nphdr = 1 + (dynamically_linked_p () == true) * 2; + nphdr = 0; + if (dynamically_linked_p ()) + { + ++nphdr; + if (ld_state.interp != NULL + || ld_state.file_type != dso_file_type) + nphdr += 2; + } segment = ld_state.output_segments; while (segment != NULL) @@ -5619,15 +6303,13 @@ cannot create hash table section for output file: %s"), XElf_Off nobits_size = 0; XElf_Off memsize = 0; - /* the minimum alignment is a page size. */ + /* The minimum alignment is a page size. */ segment->align = ld_state.pagesize; for (orule = segment->output_rules; orule != NULL; orule = orule->next) if (orule->tag == output_section) { - XElf_Off oldoff; - /* See whether this output rule corresponds to the next section. Yes, this is a pointer comparison. */ if (ld_state.allsections[nsec]->name @@ -5657,6 +6339,22 @@ cannot create hash table section for output file: %s"), /* Remember the address. */ ld_state.allsections[nsec]->addr = addr; + + /* Handle TLS sections. */ + if (unlikely (shdr->sh_flags & SHF_TLS)) + { + if (tls_start > addr) + { + tls_start = addr; + tls_offset = shdr->sh_offset; + } + if (tls_end < addr + shdr->sh_size) + tls_end = addr + shdr->sh_size; + if (shdr->sh_type != SHT_NOBITS) + tls_filesize += shdr->sh_size; + if (shdr->sh_addralign > tls_align) + tls_align = shdr->sh_addralign; + } } if (first_section) @@ -5679,12 +6377,19 @@ cannot create hash table section for output file: %s"), first_section = false; } - memsize = shdr->sh_offset - segment->offset + shdr->sh_size; - if (nobits_size != 0 && shdr->sh_type != SHT_NOTE) - error (EXIT_FAILURE, 0, gettext ("\ -internal error: nobits section follows nobits section")); - if (shdr->sh_type == SHT_NOBITS) - nobits_size += shdr->sh_size; + /* NOBITS TLS sections are not laid out in address space + along with the other sections. */ + if (shdr->sh_type != SHT_NOBITS + || (shdr->sh_flags & SHF_TLS) == 0) + { + memsize = (shdr->sh_offset - segment->offset + + shdr->sh_size); + if (nobits_size != 0 && shdr->sh_type != SHT_NOTE) + error (EXIT_FAILURE, 0, gettext ("\ +internal error: non-nobits section follows nobits section")); + if (shdr->sh_type == SHT_NOBITS) + nobits_size += shdr->sh_size; + } /* Determine the new address which is computed using the difference of the offsets on the sections. Note @@ -5692,7 +6397,7 @@ internal error: nobits section follows nobits section")); other in the section header table are also consecutive in the file. This is true here because libelf constructs files this way. */ - oldoff = shdr->sh_offset; + XElf_Off oldoff = shdr->sh_offset; if (++nsec >= ld_state.nallsections) break; @@ -5767,20 +6472,43 @@ internal error: nobits section follows nobits section")); xelf_getehdr (ld_state.outelf, ehdr); assert (ehdr != NULL); - xelf_getphdr_ptr (ld_state.outelf, 1, phdr); - phdr->p_type = PT_PHDR; - phdr->p_offset = ehdr->e_phoff; - phdr->p_vaddr = ld_state.output_segments->addr + phdr->p_offset; - phdr->p_paddr = phdr->p_vaddr; - phdr->p_filesz = ehdr->e_phnum * ehdr->e_phentsize; - phdr->p_memsz = phdr->p_filesz; - phdr->p_flags = 0; /* No need to set PF_R or so. */ + /* Add the TLS information. */ + if (ld_state.need_tls) + { + xelf_getphdr_ptr (ld_state.outelf, nphdr, phdr); + phdr->p_type = PT_TLS; + phdr->p_offset = tls_offset; + phdr->p_vaddr = tls_start; + phdr->p_paddr = tls_start; + phdr->p_filesz = tls_filesize; + phdr->p_memsz = tls_end - tls_start; + phdr->p_flags = PF_R; + phdr->p_align = tls_align; + ld_state.tls_tcb = tls_end; + ld_state.tls_start = tls_start; + + (void) xelf_update_phdr (ld_state.outelf, nphdr, phdr); + ++nphdr; + } + + /* Add the stack information. */ + xelf_getphdr_ptr (ld_state.outelf, nphdr, phdr); + phdr->p_type = PT_GNU_STACK; + phdr->p_offset = 0; + phdr->p_vaddr = 0; + phdr->p_paddr = 0; + phdr->p_filesz = 0; + phdr->p_memsz = 0; + phdr->p_flags = (PF_R | PF_W + | (ld_state.execstack == execstack_true ? PF_X : 0)); phdr->p_align = xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1); - (void) xelf_update_phdr (ld_state.outelf, 0, phdr); + (void) xelf_update_phdr (ld_state.outelf, nphdr, phdr); + ++nphdr; - /* Adjust the addresses in the addresses of the symbol according - to the load addresses of the sections. */ + + /* Adjust the addresses in the address fields of the symbol + records according to the load addresses of the sections. */ if (ld_state.need_symtab) for (cnt = 1; cnt < nsym; ++cnt) { @@ -5826,7 +6554,6 @@ internal error: nobits section follows nobits section")); } } - /* Now is a good time to determine the values of all the symbols we encountered. */ // XXX This loop is very inefficient. The hash tab iterator also @@ -5880,24 +6607,41 @@ internal error: nobits section follows nobits section")); { Elf_Scn *outscn; - assert (ld_state.interpscnidx != 0); - xelf_getshdr (elf_getscn (ld_state.outelf, ld_state.interpscnidx), - shdr); - assert (shdr != NULL); + int idx = 0; + if (ld_state.interp != NULL || ld_state.file_type != dso_file_type) + { + assert (ld_state.interpscnidx != 0); + xelf_getshdr (elf_getscn (ld_state.outelf, + ld_state.interpscnidx), shdr); + assert (shdr != NULL); - /* The interpreter string. */ - // XXX Do we need to support files (DSOs) without interpreters? - xelf_getphdr_ptr (ld_state.outelf, 1, phdr); - phdr->p_type = PT_INTERP; - phdr->p_offset = shdr->sh_offset; - phdr->p_vaddr = shdr->sh_addr; - phdr->p_paddr = phdr->p_vaddr; - phdr->p_filesz = shdr->sh_size; - phdr->p_memsz = phdr->p_filesz; - phdr->p_flags = 0; /* No need to set PF_R or so. */ - phdr->p_align = 1; /* It's a string. */ + xelf_getphdr_ptr (ld_state.outelf, idx, phdr); + phdr->p_type = PT_PHDR; + phdr->p_offset = ehdr->e_phoff; + phdr->p_vaddr = ld_state.output_segments->addr + phdr->p_offset; + phdr->p_paddr = phdr->p_vaddr; + phdr->p_filesz = ehdr->e_phnum * ehdr->e_phentsize; + phdr->p_memsz = phdr->p_filesz; + phdr->p_flags = 0; /* No need to set PF_R or so. */ + phdr->p_align = xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1); + + (void) xelf_update_phdr (ld_state.outelf, idx, phdr); + ++idx; + + /* The interpreter string. */ + xelf_getphdr_ptr (ld_state.outelf, idx, phdr); + phdr->p_type = PT_INTERP; + phdr->p_offset = shdr->sh_offset; + phdr->p_vaddr = shdr->sh_addr; + phdr->p_paddr = phdr->p_vaddr; + phdr->p_filesz = shdr->sh_size; + phdr->p_memsz = phdr->p_filesz; + phdr->p_flags = 0; /* No need to set PF_R or so. */ + phdr->p_align = 1; /* It's a string. */ - (void) xelf_update_phdr (ld_state.outelf, 1, phdr); + (void) xelf_update_phdr (ld_state.outelf, idx, phdr); + ++idx; + } /* The pointer to the dynamic section. We this we need to get the information for the dynamic section first. */ @@ -5906,7 +6650,7 @@ internal error: nobits section follows nobits section")); xelf_getshdr (outscn, shdr); assert (shdr != NULL); - xelf_getphdr_ptr (ld_state.outelf, 2, phdr); + xelf_getphdr_ptr (ld_state.outelf, idx, phdr); phdr->p_type = PT_DYNAMIC; phdr->p_offset = shdr->sh_offset; phdr->p_vaddr = shdr->sh_addr; @@ -5916,7 +6660,7 @@ internal error: nobits section follows nobits section")); phdr->p_flags = 0; /* No need to set PF_R or so. */ phdr->p_align = shdr->sh_addralign; - (void) xelf_update_phdr (ld_state.outelf, 2, phdr); + (void) xelf_update_phdr (ld_state.outelf, idx, phdr); /* Fill in the reference to the .dynstr section. */ assert (ld_state.dynstrscnidx != 0); @@ -5933,7 +6677,7 @@ internal error: nobits section follows nobits section")); struct usedfiles *runp = ld_state.dsofiles->next; do - if (! ld_state.ignore_unused_dsos || runp->used) + if (runp->used || !runp->as_needed) { /* Add the position-dependent flag if necessary. */ if (runp->lazyload) @@ -6020,8 +6764,9 @@ internal error: nobits section follows nobits section")); /* Add the entries related to the .plt. */ if (ld_state.nplt > 0) { - xelf_getshdr (elf_getscn (ld_state.outelf, ld_state.gotscnidx), - shdr); + // XXX Make this work if there is no PLT + xelf_getshdr (elf_getscn (ld_state.outelf, + ld_state.gotpltscnidx), shdr); assert (shdr != NULL); new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, // XXX This should probably be machine @@ -6201,8 +6946,18 @@ internal error: nobits section follows nobits section")); free (ld_state.dblindirect); - /* Finalize the .plt section the what belongs to them. */ - FINALIZE_PLT (statep, nsym, nsym_dyn); + /* Finalize the .plt section and what else belongs to it. */ + FINALIZE_PLT (statep, nsym, nsym_local, ndxtosym); + + + /* Finally, if we have to compute the build ID. */ + if (ld_state.build_id != NULL) + compute_build_id (); + + + /* We don't need the map from the symbol table index to the symbol + structure anymore. */ + free (ndxtosym); return 0; } @@ -6278,7 +7033,7 @@ ld_generic_special_section_number_p (struct ld_state *statep, size_t number) static bool ld_generic_section_type_p (struct ld_state *statep, GElf_Word type) { - if ((type >= SHT_NULL && type < SHT_NUM) + if (type < SHT_NUM /* XXX Enable the following two when implemented. */ // || type == SHT_GNU_LIBLIST // || type == SHT_CHECKSUM @@ -6334,7 +7089,19 @@ ld_generic_initialize_got (struct ld_state *statep, Elf_Scn *scn) static void -ld_generic_finalize_plt (struct ld_state *statep, size_t nsym, size_t nsym_dyn) +ld_generic_initialize_gotplt (struct ld_state *statep, Elf_Scn *scn) +{ + /* This cannot be implemented generally. There should have been a + machine dependent implementation and we should never have arrived + here. */ + error (EXIT_FAILURE, 0, gettext ("no machine specific '%s' implementation"), + "initialize_gotplt"); +} + + +static void +ld_generic_finalize_plt (struct ld_state *statep, size_t nsym, size_t nsym_dyn, + struct symbol **ndxtosymp) { /* By default we assume that nothing has to be done. */ } diff --git a/src/ldlex.c b/src/ldlex.c index e0bf0a5f..dbbdf07a 100644 --- a/src/ldlex.c +++ b/src/ldlex.c @@ -1,8 +1,13 @@ +#line 2 "ldlex.c" + +#line 4 "ldlex.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + #define yy_create_buffer ld_create_buffer #define yy_delete_buffer ld_delete_buffer -#define yy_scan_buffer ld_scan_buffer -#define yy_scan_string ld_scan_string -#define yy_scan_bytes ld_scan_bytes #define yy_flex_debug ld_flex_debug #define yy_init_buffer ld_init_buffer #define yy_flush_buffer ld_flush_buffer @@ -11,76 +16,117 @@ #define yyin ldin #define yyleng ldleng #define yylex ldlex +#define yylineno ldlineno #define yyout ldout #define yyrestart ldrestart #define yytext ldtext -#define yylineno ldlineno - -#line 20 "ldlex.c" -/* A lexical scanner generated by flex */ - -/* Scanner skeleton version: - * $Header: /home/daffy/u0/vern/flex/RCS/flex.skl,v 2.91 96/09/10 16:58:48 vern Exp $ - */ +#define yywrap ldwrap +#define yyalloc ldalloc +#define yyrealloc ldrealloc +#define yyfree ldfree #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 35 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ #include <stdio.h> -#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> +/* end standard C headers. */ -/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */ -#ifdef c_plusplus -#ifndef __cplusplus -#define __cplusplus -#endif +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 #endif +#include <inttypes.h> +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; +#endif /* ! C99 */ -#ifdef __cplusplus +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif -#include <stdlib.h> +#endif /* ! FLEXINT_H */ -/* Use prototypes in function declarations. */ -#define YY_USE_PROTOS +#ifdef __cplusplus /* The "const" storage-class-modifier is valid. */ #define YY_USE_CONST #else /* ! __cplusplus */ -#if __STDC__ +/* C99 requires __STDC__ to be defined as 1. */ +#if defined (__STDC__) -#define YY_USE_PROTOS #define YY_USE_CONST -#endif /* __STDC__ */ +#endif /* defined (__STDC__) */ #endif /* ! __cplusplus */ -#ifdef __TURBOC__ - #pragma warn -rch - #pragma warn -use -#include <io.h> -#include <stdlib.h> -#define YY_USE_CONST -#define YY_USE_PROTOS -#endif - #ifdef YY_USE_CONST #define yyconst const #else #define yyconst #endif - -#ifdef YY_USE_PROTOS -#define YY_PROTO(proto) proto -#else -#define YY_PROTO(proto) () -#endif - /* Returned upon end-of-file. */ #define YY_NULL 0 @@ -95,71 +141,83 @@ * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ -#define BEGIN yy_start = 1 + 2 * +#define BEGIN (yy_start) = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ -#define YY_START ((yy_start - 1) / 2) +#define YY_START (((yy_start) - 1) / 2) #define YYSTATE YY_START /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) /* Special action meaning "start processing a new file". */ -#define YY_NEW_FILE yyrestart( yyin ) +#define YY_NEW_FILE ldrestart(ldin ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ +#ifndef YY_BUF_SIZE #define YY_BUF_SIZE 16384 +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif -extern int yyleng; -extern FILE *yyin, *yyout; +extern int ldleng; + +extern FILE *ldin, *ldout; #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 -/* The funky do-while in the following #define is used to turn the definition - * int a single C statement (which needs a semi-colon terminator). This - * avoids problems with code like: - * - * if ( condition_holds ) - * yyless( 5 ); - * else - * do_something_else(); - * - * Prior to using the do-while the compiler would get upset at the - * "else" because it interpreted the "if" statement as being all - * done when it reached the ';' after the yyless() call. - */ - -/* Return all but the first 'n' matched characters back to the input stream. */ - + /* Note: We specifically omit the test for yy_rule_can_match_eol because it requires + * access to the local variable yy_act. Since yyless() is a macro, it would break + * existing scanners that call yyless() from OUTSIDE ldlex. + * One obvious solution it to make yy_act a global. I tried that, and saw + * a 5% performance hit in a non-ldlineno scanner, because yy_act is + * normally declared as a register variable-- so it is not worth it. + */ + #define YY_LESS_LINENO(n) \ + do { \ + int yyl;\ + for ( yyl = n; yyl < ldleng; ++yyl )\ + if ( ldtext[yyl] == '\n' )\ + --ldlineno;\ + }while(0) + +/* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ { \ - /* Undo effects of setting up yytext. */ \ - *yy_cp = yy_hold_char; \ + /* Undo effects of setting up ldtext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = (yy_hold_char); \ YY_RESTORE_YY_MORE_OFFSET \ - yy_c_buf_p = yy_cp = yy_bp + n - YY_MORE_ADJ; \ - YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up ldtext again */ \ } \ while ( 0 ) -#define unput(c) yyunput( c, yytext_ptr ) - -/* The following is because we cannot portably get our hands on size_t - * (without autoconf's help, which isn't available because we want - * flex-generated scanners to compile on their own). - */ -typedef unsigned int yy_size_t; +#define unput(c) yyunput( c, (yytext_ptr) ) +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; @@ -196,12 +254,16 @@ struct yy_buffer_state */ int yy_at_bol; + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + /* Whether to try to fill the input buffer when we reach the * end of it. */ int yy_fill_buffer; int yy_buffer_status; + #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process @@ -211,179 +273,166 @@ struct yy_buffer_state * possible backing-up. * * When we actually see the EOF, we change the status to "new" - * (via yyrestart()), so that the user can continue scanning by - * just pointing yyin at a new input file. + * (via ldrestart()), so that the user can continue scanning by + * just pointing ldin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ -static YY_BUFFER_STATE yy_current_buffer = 0; +/* Stack of input buffers. */ +static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ +static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ +static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". + * + * Returns the top of the stack, or NULL. */ -#define YY_CURRENT_BUFFER yy_current_buffer +#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ + ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ + : NULL) +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] -/* yy_hold_char holds the character lost when yytext is formed. */ +/* yy_hold_char holds the character lost when ldtext is formed. */ static char yy_hold_char; - static int yy_n_chars; /* number of characters read into yy_ch_buf */ - - -int yyleng; +int ldleng; /* Points to current character in buffer. */ static char *yy_c_buf_p = (char *) 0; -static int yy_init = 1; /* whether we need to initialize */ +static int yy_init = 0; /* whether we need to initialize */ static int yy_start = 0; /* start state number */ -/* Flag which is used to allow yywrap()'s to do buffer switches - * instead of setting up a fresh yyin. A bit of a hack ... +/* Flag which is used to allow ldwrap()'s to do buffer switches + * instead of setting up a fresh ldin. A bit of a hack ... */ static int yy_did_buffer_switch_on_eof; -void yyrestart YY_PROTO(( FILE *input_file )); +void ldrestart (FILE *input_file ); +void ld_switch_to_buffer (YY_BUFFER_STATE new_buffer ); +YY_BUFFER_STATE ld_create_buffer (FILE *file,int size ); +void ld_delete_buffer (YY_BUFFER_STATE b ); +void ld_flush_buffer (YY_BUFFER_STATE b ); +void ldpush_buffer_state (YY_BUFFER_STATE new_buffer ); +void ldpop_buffer_state (void ); + +static void ldensure_buffer_stack (void ); +static void ld_load_buffer_state (void ); +static void ld_init_buffer (YY_BUFFER_STATE b,FILE *file ); -void yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer )); -void yy_load_buffer_state YY_PROTO(( void )); -YY_BUFFER_STATE yy_create_buffer YY_PROTO(( FILE *file, int size )); -void yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b )); -void yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file )); -void yy_flush_buffer YY_PROTO(( YY_BUFFER_STATE b )); -#define YY_FLUSH_BUFFER yy_flush_buffer( yy_current_buffer ) +#define YY_FLUSH_BUFFER ld_flush_buffer(YY_CURRENT_BUFFER ) -YY_BUFFER_STATE yy_scan_buffer YY_PROTO(( char *base, yy_size_t size )); -YY_BUFFER_STATE yy_scan_string YY_PROTO(( yyconst char *yy_str )); -YY_BUFFER_STATE yy_scan_bytes YY_PROTO(( yyconst char *bytes, int len )); +YY_BUFFER_STATE ld_scan_buffer (char *base,yy_size_t size ); +YY_BUFFER_STATE ld_scan_string (yyconst char *yy_str ); +YY_BUFFER_STATE ld_scan_bytes (yyconst char *bytes,int len ); -static void *yy_flex_alloc YY_PROTO(( yy_size_t )); -static void *yy_flex_realloc YY_PROTO(( void *, yy_size_t )); -static void yy_flex_free YY_PROTO(( void * )); +void *ldalloc (yy_size_t ); +void *ldrealloc (void *,yy_size_t ); +void ldfree (void * ); -#define yy_new_buffer yy_create_buffer +#define yy_new_buffer ld_create_buffer #define yy_set_interactive(is_interactive) \ { \ - if ( ! yy_current_buffer ) \ - yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ - yy_current_buffer->yy_is_interactive = is_interactive; \ + if ( ! YY_CURRENT_BUFFER ){ \ + ldensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + ld_create_buffer(ldin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } #define yy_set_bol(at_bol) \ { \ - if ( ! yy_current_buffer ) \ - yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ - yy_current_buffer->yy_at_bol = at_bol; \ + if ( ! YY_CURRENT_BUFFER ){\ + ldensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + ld_create_buffer(ldin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ } -#define YY_AT_BOL() (yy_current_buffer->yy_at_bol) +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) +/* Begin user sect3 */ -#define YY_USES_REJECT - -#define yywrap() 1 +#define ldwrap(n) 1 #define YY_SKIP_YYWRAP + typedef unsigned char YY_CHAR; -FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; + +FILE *ldin = (FILE *) 0, *ldout = (FILE *) 0; + typedef int yy_state_type; -extern int yylineno; -int yylineno = 1; -extern char *yytext; -#define yytext_ptr yytext -static yy_state_type yy_get_previous_state YY_PROTO(( void )); -static yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state )); -static int yy_get_next_buffer YY_PROTO(( void )); -static void yy_fatal_error YY_PROTO(( yyconst char msg[] )); +extern int ldlineno; + +int ldlineno = 1; + +extern char *ldtext; +#define yytext_ptr ldtext + +static yy_state_type yy_get_previous_state (void ); +static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); +static int yy_get_next_buffer (void ); +static void yy_fatal_error (yyconst char msg[] ); /* Done after the current pattern has been matched and before the - * corresponding action - sets up yytext. + * corresponding action - sets up ldtext. */ #define YY_DO_BEFORE_ACTION \ - yytext_ptr = yy_bp; \ - yyleng = (int) (yy_cp - yy_bp); \ - yy_hold_char = *yy_cp; \ + (yytext_ptr) = yy_bp; \ + ldleng = (size_t) (yy_cp - yy_bp); \ + (yy_hold_char) = *yy_cp; \ *yy_cp = '\0'; \ - yy_c_buf_p = yy_cp; - -#define YY_NUM_RULES 49 -#define YY_END_OF_BUFFER 50 -static yyconst short int yy_acclist[373] = - { 0, - 50, 48, 49, 47, 48, 49, 47, 49, 40, 48, - 49, 41, 48, 49, 31, 48, 49, 32, 48, 49, - 38, 45, 48, 49, 36, 48, 49, 43, 48, 49, - 37, 48, 49, 45, 48, 49, 39, 48, 49, 44, - 45, 48, 49, 44, 45, 48, 49, 33, 48, 49, - 34, 48, 49, 35, 48, 49, 45, 48, 49, 45, - 48, 49, 45, 48, 49, 45, 48, 49, 45, 48, - 49, 45, 48, 49, 45, 48, 49, 45, 48, 49, - 45, 48, 49, 45, 48, 49, 48, 49, 48, 49, - 45, 48, 49, 45, 48, 49, 29, 48, 49, 42, - - 48, 49, 30, 48, 49, 48, 49, 9, 49, 9, - 49, 47, 45, 46, 45, 46, 10, 46, 44, 45, - 46, 44, 45, 46, 44, 45, 46, 45, 46, 44, - 45, 46, 45, 46, 45, 46, 45, 46, 45, 46, - 45, 46, 45, 46, 45, 46, 45, 46, 45, 46, - 45, 46, 45, 46, 45, 46, 45, 46, 28, 45, - 46, 45, 46, 44, 45, 46, 45, 46, 45, 46, - 45, 46, 45, 46, 45, 46, 45, 46, 45, 46, - 45, 46, 45, 46, 45, 46, 45, 46, 45, 46, - 45, 46, 45, 46, 45, 46, 45, 46, 45, 46, - - 44, 45, 46, 45, 46, 45, 46, 45, 46, 45, - 46, 45, 46, 45, 46, 18, 45, 46, 45, 46, - 45, 46, 45, 46, 45, 46, 45, 46, 45, 46, - 26, 45, 46, 45, 46, 45, 46, 45, 46, 11, - 45, 46, 12, 45, 46, 45, 46, 15, 45, 46, - 16, 45, 46, 45, 46, 45, 46, 45, 46, 45, - 46, 45, 46, 45, 46, 45, 46, 45, 46, 45, - 46, 45, 46, 45, 46, 17, 45, 46, 45, 46, - 45, 46, 45, 46, 45, 46, 45, 46, 45, 46, - 45, 46, 45, 46, 19, 2, 6, 45, 46, 45, - - 46, 45, 46, 22, 45, 46, 45, 46, 24, 45, - 46, 45, 46, 27, 45, 46, 14, 4, 1, 8, - 5, 45, 46, 45, 46, 21, 45, 46, 45, 46, - 45, 46, 45, 46, 45, 46, 45, 46, 45, 46, - 3, 7, 45, 46, 45, 46, 23, 45, 46, 45, - 46, 45, 46, 45, 46, 45, 46, 13, 45, 46, - 45, 46, 45, 46, 20, 45, 46, 45, 46, 25, - 45, 46 - } ; + (yy_c_buf_p) = yy_cp; -static yyconst short int yy_accept[212] = +#define YY_NUM_RULES 50 +#define YY_END_OF_BUFFER 51 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static yyconst flex_int16_t yy_accept[219] = { 0, - 1, 1, 1, 1, 1, 2, 4, 7, 9, 12, - 15, 18, 21, 25, 28, 31, 34, 37, 40, 44, - 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, - 78, 81, 84, 87, 89, 91, 94, 97, 100, 103, - 106, 108, 110, 112, 113, 114, 115, 117, 119, 122, - 125, 128, 130, 133, 135, 137, 139, 141, 143, 145, - 147, 149, 151, 153, 155, 157, 159, 159, 160, 162, - 164, 164, 164, 164, 164, 167, 169, 171, 173, 175, - 177, 179, 181, 183, 185, 187, 189, 191, 193, 195, - 197, 197, 199, 201, 201, 201, 201, 201, 201, 201, - - 204, 206, 208, 210, 212, 214, 216, 219, 221, 223, - 225, 227, 229, 231, 234, 236, 236, 238, 240, 240, - 240, 240, 240, 240, 240, 240, 240, 243, 246, 248, - 251, 254, 256, 258, 260, 262, 264, 266, 268, 270, - 272, 274, 274, 274, 274, 274, 274, 274, 274, 274, - 276, 279, 281, 283, 285, 287, 289, 291, 293, 295, - 296, 296, 297, 297, 297, 297, 298, 298, 298, 300, - 302, 304, 307, 309, 312, 314, 317, 318, 318, 319, - 320, 320, 321, 322, 324, 326, 329, 331, 333, 333, - 333, 335, 337, 339, 341, 342, 343, 345, 347, 350, - - 352, 354, 356, 358, 361, 363, 365, 368, 370, 373, - 373 + 0, 0, 0, 0, 51, 49, 48, 48, 41, 42, + 32, 33, 39, 37, 44, 38, 46, 40, 45, 45, + 34, 35, 36, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 49, 49, 46, 46, 30, 43, 31, + 49, 9, 9, 48, 46, 47, 46, 10, 45, 45, + 45, 46, 45, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 0, 29, 46, + 46, 0, 0, 0, 0, 45, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 0, 46, 46, 0, 0, 0, 0, 0, + + 0, 45, 46, 46, 46, 46, 46, 46, 46, 19, + 46, 46, 46, 46, 46, 46, 27, 46, 0, 46, + 46, 0, 0, 0, 0, 0, 0, 0, 0, 11, + 46, 13, 46, 16, 17, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 0, 0, 0, 0, 0, + 0, 0, 0, 46, 46, 18, 46, 46, 46, 46, + 46, 46, 46, 46, 20, 0, 2, 0, 0, 0, + 6, 0, 0, 46, 46, 46, 46, 23, 46, 25, + 46, 28, 15, 0, 4, 1, 0, 8, 5, 46, + 46, 46, 22, 46, 46, 0, 0, 12, 46, 46, + + 46, 46, 3, 7, 46, 46, 24, 46, 46, 46, + 46, 14, 46, 46, 21, 46, 26, 0 } ; -static yyconst int yy_ec[256] = +static yyconst flex_int32_t yy_ec[256] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, @@ -415,10 +464,10 @@ static yyconst int yy_ec[256] = 1, 1, 1, 1, 1 } ; -static yyconst int yy_meta[67] = +static yyconst flex_int32_t yy_meta[67] = { 0, 1, 2, 2, 1, 1, 1, 2, 2, 3, 1, - 1, 1, 3, 1, 3, 3, 3, 2, 2, 1, + 1, 3, 3, 1, 3, 3, 3, 2, 2, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 1, 2, 3, 3, @@ -426,63 +475,65 @@ static yyconst int yy_meta[67] = 3, 3, 3, 2, 1, 2 } ; -static yyconst short int yy_base[215] = +static yyconst flex_int16_t yy_base[223] = { 0, - 0, 218, 0, 216, 219, 2141, 65, 67, 2141, 2141, - 2141, 2141, 0, 2141, 2141, 2141, 70, 208, 135, 62, - 2141, 2141, 2141, 0, 70, 127, 158, 171, 192, 211, - 260, 309, 215, 46, 0, 269, 273, 2141, 2141, 2141, - 42, 2141, 43, 70, 0, 0, 330, 0, 48, 322, - 333, 382, 385, 434, 437, 458, 471, 520, 507, 529, - 558, 564, 613, 609, 616, 647, 67, 2141, 666, 670, - 47, 160, 59, 158, 90, 703, 722, 725, 756, 760, - 789, 793, 822, 828, 851, 862, 873, 886, 907, 926, - 89, 945, 964, 65, 159, 158, 67, 157, 153, 985, - - 1004, 1008, 1039, 1042, 1073, 1076, 1107, 1111, 1130, 1161, - 1165, 1169, 1198, 1205, 1227, 155, 1234, 1256, 144, 143, - 139, 140, 137, 127, 112, 113, 1263, 1285, 1292, 1296, - 1325, 1329, 1336, 1365, 1386, 1389, 1420, 1441, 1444, 1465, - 1494, 106, 151, 83, 79, 77, 154, 61, 59, 1500, - 1503, 1534, 1553, 1556, 1587, 1590, 1609, 1640, 1644, 2141, - 58, 2141, 160, 162, 54, 2141, 169, 174, 1648, 1677, - 1698, 1701, 1722, 1735, 1757, 1766, 2141, 43, 2141, 2141, - 37, 2141, 2141, 1779, 1788, 1801, 1810, 1832, 177, 181, - 1839, 1861, 1868, 1892, 2141, 2141, 1901, 1905, 1936, 1945, - - 1958, 1980, 1989, 2002, 2011, 2024, 2047, 2055, 2068, 2141, - 2131, 76, 2134, 2137 + 0, 217, 0, 216, 216, 2250, 65, 67, 2250, 2250, + 2250, 2250, 0, 2250, 2250, 2250, 70, 206, 135, 62, + 2250, 2250, 2250, 0, 186, 70, 127, 146, 179, 210, + 259, 308, 246, 46, 0, 268, 297, 2250, 2250, 2250, + 25, 2250, 42, 70, 0, 0, 304, 0, 48, 224, + 337, 386, 389, 438, 441, 444, 493, 496, 545, 532, + 554, 583, 589, 638, 634, 641, 672, 73, 2250, 691, + 695, 38, 159, 47, 158, 69, 728, 747, 751, 780, + 784, 813, 819, 842, 848, 871, 877, 900, 908, 929, + 937, 958, 81, 966, 987, 52, 158, 155, 69, 154, + + 153, 995, 1018, 1031, 1039, 1070, 1062, 1091, 1120, 1127, + 1131, 1160, 1171, 1193, 1204, 1164, 1226, 1233, 151, 1237, + 1266, 142, 138, 134, 134, 132, 132, 124, 115, 1277, + 1288, 1301, 1322, 1341, 1345, 1374, 1380, 1409, 1430, 1433, + 1464, 1485, 1488, 1509, 1538, 114, 135, 110, 104, 81, + 145, 77, 75, 1545, 1549, 1578, 1582, 1589, 1611, 1633, + 1640, 1644, 1684, 1693, 2250, 68, 2250, 151, 154, 65, + 2250, 169, 171, 1697, 1728, 1737, 1750, 1772, 1781, 1794, + 1803, 1825, 2250, 57, 2250, 2250, 53, 2250, 2250, 1834, + 1847, 1838, 1869, 1878, 1900, 173, 181, 1907, 1929, 1936, + + 1960, 1967, 2250, 2250, 1989, 1996, 2000, 2040, 2051, 2029, + 2064, 2085, 2108, 2119, 2142, 2148, 2177, 2250, 2240, 89, + 2243, 2246 } ; -static yyconst short int yy_def[215] = +static yyconst flex_int16_t yy_def[223] = { 0, - 210, 1, 211, 211, 210, 210, 210, 210, 210, 210, - 210, 210, 212, 210, 210, 210, 213, 214, 213, 19, - 210, 210, 210, 212, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 210, 214, 19, 19, 210, 210, 210, - 210, 210, 210, 210, 212, 214, 19, 214, 19, 19, + 218, 1, 219, 219, 218, 218, 218, 218, 218, 218, + 218, 218, 220, 218, 218, 218, 221, 222, 221, 19, + 218, 218, 218, 220, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 218, 222, 19, 19, 218, 218, 218, + 218, 218, 218, 218, 220, 222, 19, 222, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 210, 210, 19, 19, - 210, 210, 210, 210, 52, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 218, 218, 19, + 19, 218, 218, 218, 218, 52, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 210, 19, 19, 210, 210, 210, 210, 210, 210, 19, + 19, 19, 218, 19, 19, 218, 218, 218, 218, 218, + 218, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 218, 19, + 19, 218, 218, 218, 218, 218, 218, 218, 218, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 210, 19, 19, 210, 210, - 210, 210, 210, 210, 210, 210, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 210, 210, 210, 210, 210, 210, 210, 210, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 210, - 210, 210, 210, 210, 210, 210, 210, 210, 19, 19, - 19, 19, 19, 19, 19, 19, 210, 210, 210, 210, - 210, 210, 210, 19, 19, 19, 19, 19, 210, 210, - 19, 19, 19, 19, 210, 210, 19, 19, 19, 19, - - 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, - 210, 210, 210, 210 + 19, 19, 19, 19, 19, 218, 218, 218, 218, 218, + 218, 218, 218, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 218, 218, 218, 218, 218, 218, + 218, 218, 218, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 218, 218, 218, 218, 218, 218, 218, 19, + 19, 19, 19, 19, 19, 218, 218, 19, 19, 19, + + 19, 19, 218, 218, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 0, 218, 218, + 218, 218 } ; -static yyconst short int yy_nxt[2208] = +static yyconst flex_int16_t yy_nxt[2317] = { 0, 6, 7, 8, 6, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 20, 21, 22, 23, @@ -491,245 +542,257 @@ static yyconst short int yy_nxt[2208] = 33, 17, 17, 17, 17, 34, 35, 6, 17, 17, 17, 17, 17, 17, 17, 36, 17, 17, 37, 17, 17, 17, 17, 38, 39, 40, 44, 44, 44, 44, - 46, 44, 44, 46, 46, 46, 50, 50, 45, 46, - 46, 46, 67, 46, 47, 47, 47, 67, 67, 46, - 47, 190, 53, 68, 53, 71, 73, 189, 72, 74, - - 47, 54, 47, 91, 47, 94, 95, 181, 91, 91, - 47, 178, 47, 168, 68, 167, 46, 97, 98, 53, - 100, 119, 100, 123, 47, 116, 120, 47, 124, 165, - 116, 116, 47, 164, 46, 46, 68, 163, 46, 46, - 46, 47, 47, 47, 46, 46, 46, 100, 46, 49, - 49, 50, 162, 162, 46, 166, 166, 47, 161, 47, - 55, 179, 179, 180, 180, 51, 149, 51, 148, 56, - 182, 182, 47, 47, 47, 183, 183, 52, 195, 195, - 147, 46, 196, 196, 47, 47, 47, 47, 47, 47, - 47, 146, 51, 145, 57, 144, 143, 52, 142, 46, - - 47, 47, 68, 47, 58, 126, 47, 47, 47, 125, - 122, 121, 99, 47, 96, 47, 48, 59, 210, 43, - 47, 41, 47, 210, 47, 47, 47, 47, 47, 47, - 47, 47, 210, 47, 47, 210, 210, 210, 210, 210, - 66, 47, 210, 47, 210, 47, 210, 47, 210, 47, - 60, 210, 210, 47, 47, 210, 210, 47, 210, 210, - 210, 210, 210, 210, 210, 210, 210, 210, 47, 210, - 210, 210, 47, 47, 47, 47, 47, 47, 210, 210, - 210, 61, 210, 47, 47, 47, 210, 47, 47, 47, - 47, 210, 47, 210, 210, 210, 62, 210, 210, 47, - - 210, 47, 47, 47, 210, 47, 210, 210, 210, 210, - 210, 47, 210, 210, 210, 47, 210, 47, 210, 210, - 210, 210, 47, 47, 47, 47, 47, 69, 210, 210, - 47, 47, 210, 70, 63, 47, 50, 50, 64, 47, - 210, 47, 210, 65, 47, 47, 47, 47, 47, 47, - 210, 47, 53, 210, 53, 210, 210, 210, 210, 210, - 47, 210, 47, 47, 47, 47, 47, 210, 210, 210, - 210, 47, 47, 210, 210, 47, 210, 210, 210, 53, - 210, 210, 210, 210, 47, 210, 210, 47, 210, 210, - 47, 210, 47, 210, 210, 47, 75, 75, 75, 47, - - 47, 47, 210, 75, 75, 75, 75, 75, 75, 210, - 210, 210, 47, 210, 47, 47, 210, 47, 210, 210, - 210, 210, 210, 210, 47, 210, 210, 47, 210, 210, - 210, 75, 75, 75, 75, 75, 75, 210, 210, 47, - 210, 210, 47, 210, 47, 210, 210, 47, 47, 47, - 47, 47, 47, 47, 210, 210, 210, 210, 210, 210, - 210, 210, 210, 76, 47, 210, 47, 47, 210, 47, - 210, 210, 47, 47, 47, 77, 47, 210, 210, 47, - 210, 78, 210, 210, 210, 47, 47, 47, 47, 210, - 47, 47, 210, 210, 47, 210, 47, 210, 210, 47, - - 47, 47, 210, 47, 210, 79, 210, 210, 210, 210, - 210, 210, 210, 47, 210, 47, 210, 210, 210, 210, - 47, 47, 47, 47, 210, 210, 210, 210, 47, 210, - 210, 210, 82, 47, 47, 47, 47, 47, 210, 47, - 210, 210, 210, 47, 47, 47, 210, 210, 210, 47, - 47, 210, 47, 210, 210, 80, 210, 210, 81, 47, - 210, 47, 47, 210, 47, 210, 210, 83, 210, 47, - 210, 47, 47, 47, 47, 210, 210, 47, 47, 47, - 47, 210, 47, 210, 210, 84, 47, 210, 47, 210, - 47, 47, 210, 210, 47, 210, 47, 210, 85, 210, - - 47, 210, 210, 210, 210, 210, 47, 210, 210, 210, - 210, 210, 210, 210, 210, 47, 210, 210, 210, 210, - 47, 47, 210, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 210, 86, 210, 210, 210, 210, 47, - 87, 47, 210, 47, 210, 47, 47, 210, 47, 210, - 210, 47, 89, 88, 210, 47, 210, 210, 47, 210, - 210, 47, 47, 47, 210, 210, 47, 210, 210, 210, - 47, 47, 210, 47, 210, 47, 210, 47, 47, 47, - 47, 47, 47, 90, 47, 47, 47, 210, 210, 47, - 210, 210, 210, 210, 210, 210, 47, 210, 47, 210, - - 47, 210, 47, 210, 47, 210, 210, 210, 47, 47, - 210, 210, 47, 210, 210, 210, 210, 47, 47, 47, - 210, 93, 210, 47, 210, 210, 92, 47, 47, 210, - 101, 210, 47, 47, 210, 47, 47, 47, 47, 47, - 47, 47, 210, 210, 210, 47, 210, 210, 210, 210, - 210, 210, 47, 210, 47, 47, 103, 47, 102, 210, - 47, 210, 210, 210, 47, 47, 210, 47, 210, 210, - 47, 47, 47, 210, 47, 47, 47, 210, 210, 47, - 210, 210, 47, 210, 47, 210, 47, 47, 47, 210, - 47, 210, 47, 210, 210, 104, 210, 210, 47, 105, - - 210, 210, 47, 47, 47, 47, 210, 47, 47, 47, - 210, 210, 210, 47, 106, 210, 210, 47, 47, 47, - 210, 47, 47, 47, 210, 47, 210, 210, 107, 210, - 210, 47, 210, 210, 210, 47, 47, 47, 47, 210, - 210, 210, 47, 47, 47, 210, 47, 210, 210, 210, - 47, 47, 47, 109, 47, 47, 210, 108, 47, 210, - 47, 210, 210, 210, 47, 47, 47, 47, 210, 210, - 47, 210, 210, 210, 210, 210, 47, 47, 47, 47, - 210, 47, 210, 47, 47, 47, 210, 47, 47, 47, - 47, 110, 47, 47, 47, 210, 210, 210, 111, 210, - - 47, 47, 47, 47, 47, 112, 210, 210, 47, 210, - 210, 113, 210, 47, 210, 47, 47, 210, 47, 47, - 210, 47, 47, 47, 47, 210, 210, 210, 47, 210, - 47, 210, 210, 210, 210, 47, 210, 47, 210, 47, - 47, 47, 47, 47, 210, 114, 210, 210, 47, 47, - 210, 210, 210, 210, 210, 210, 47, 210, 47, 47, - 47, 47, 210, 115, 47, 210, 210, 210, 47, 47, - 210, 210, 210, 210, 210, 47, 210, 47, 47, 47, - 47, 210, 210, 47, 210, 210, 210, 47, 47, 210, - 210, 210, 210, 210, 47, 117, 47, 210, 210, 47, - - 47, 47, 47, 210, 210, 210, 47, 47, 210, 210, - 210, 210, 210, 118, 210, 47, 210, 47, 47, 47, - 47, 47, 47, 47, 47, 210, 47, 47, 210, 210, - 210, 210, 210, 210, 47, 210, 47, 127, 47, 210, - 47, 210, 47, 210, 210, 210, 47, 47, 210, 210, - 47, 128, 210, 47, 47, 47, 47, 47, 47, 210, - 210, 47, 210, 210, 210, 47, 47, 210, 210, 47, - 47, 47, 47, 210, 47, 210, 210, 130, 129, 210, - 210, 47, 210, 210, 47, 210, 210, 47, 47, 47, - 47, 47, 47, 210, 210, 210, 47, 210, 210, 47, - - 210, 47, 210, 47, 47, 47, 47, 210, 47, 210, - 210, 131, 132, 210, 210, 47, 210, 210, 47, 210, - 210, 47, 47, 47, 210, 47, 47, 47, 210, 210, - 47, 210, 210, 47, 210, 47, 210, 47, 47, 47, - 210, 47, 210, 47, 47, 47, 47, 210, 210, 47, - 133, 210, 210, 47, 210, 210, 210, 210, 210, 210, - 47, 210, 47, 210, 47, 210, 210, 134, 47, 47, - 210, 210, 47, 47, 210, 47, 47, 47, 210, 47, - 47, 47, 210, 47, 47, 47, 210, 47, 136, 210, - 135, 47, 47, 47, 137, 47, 210, 47, 210, 47, - - 210, 47, 210, 47, 210, 210, 210, 47, 210, 210, - 210, 47, 47, 47, 47, 210, 210, 210, 47, 47, - 47, 47, 47, 47, 210, 210, 47, 47, 47, 210, - 47, 47, 138, 210, 210, 47, 210, 47, 210, 210, - 47, 47, 47, 47, 210, 210, 210, 47, 47, 47, - 47, 210, 210, 210, 210, 47, 139, 47, 210, 47, - 47, 210, 47, 210, 47, 210, 47, 47, 210, 47, - 47, 47, 47, 210, 210, 210, 47, 47, 47, 47, - 210, 210, 210, 140, 47, 210, 47, 210, 47, 47, - 210, 47, 210, 47, 210, 47, 47, 210, 47, 47, - - 47, 47, 210, 210, 210, 47, 47, 47, 47, 210, - 47, 47, 47, 47, 141, 47, 150, 47, 47, 210, - 47, 210, 47, 210, 47, 47, 47, 47, 47, 210, - 210, 210, 210, 210, 47, 210, 210, 210, 47, 47, - 47, 47, 47, 47, 47, 47, 210, 47, 210, 47, - 47, 47, 47, 47, 47, 47, 210, 47, 47, 47, - 210, 47, 210, 210, 151, 210, 47, 47, 47, 210, - 210, 47, 210, 210, 152, 210, 210, 210, 47, 47, - 47, 47, 47, 210, 210, 210, 47, 47, 210, 210, - 210, 47, 210, 47, 153, 47, 210, 47, 47, 210, - - 47, 47, 47, 47, 47, 47, 210, 47, 210, 210, - 154, 210, 210, 210, 210, 210, 47, 155, 47, 47, - 210, 47, 47, 210, 210, 210, 210, 47, 47, 210, - 210, 47, 210, 210, 47, 47, 47, 210, 210, 210, - 210, 210, 210, 47, 210, 210, 47, 210, 47, 210, - 47, 47, 47, 156, 210, 47, 47, 47, 47, 47, - 47, 210, 47, 210, 210, 210, 210, 157, 210, 210, - 210, 47, 210, 47, 47, 210, 47, 47, 158, 47, - 47, 47, 47, 47, 210, 210, 47, 210, 210, 210, - 210, 210, 210, 210, 210, 47, 210, 47, 47, 210, - - 210, 47, 210, 47, 210, 210, 47, 47, 47, 47, - 47, 160, 210, 210, 47, 47, 47, 47, 47, 47, - 210, 210, 47, 159, 47, 169, 47, 47, 210, 210, - 47, 210, 47, 47, 210, 47, 47, 210, 210, 210, - 210, 210, 47, 210, 210, 47, 210, 210, 47, 47, - 47, 47, 210, 210, 210, 210, 47, 47, 210, 210, - 47, 210, 47, 210, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 210, 210, 210, 47, 210, 210, 210, - 210, 172, 170, 47, 210, 47, 47, 210, 47, 210, - 210, 47, 210, 210, 210, 47, 47, 171, 47, 210, - - 210, 47, 47, 47, 47, 47, 47, 210, 210, 210, - 47, 210, 210, 47, 210, 47, 210, 47, 47, 47, - 47, 210, 47, 47, 47, 47, 210, 210, 174, 47, - 210, 210, 47, 210, 210, 173, 210, 210, 210, 47, - 210, 47, 210, 210, 47, 210, 210, 47, 210, 47, - 210, 47, 47, 210, 47, 47, 47, 175, 47, 47, - 47, 177, 47, 47, 47, 210, 47, 210, 210, 210, - 47, 47, 47, 176, 47, 210, 47, 210, 47, 210, - 47, 210, 47, 210, 210, 210, 47, 210, 210, 210, - 47, 47, 47, 47, 210, 210, 184, 47, 210, 210, - - 210, 47, 47, 185, 210, 47, 47, 47, 210, 47, - 47, 210, 47, 47, 47, 47, 47, 47, 210, 47, - 210, 210, 210, 186, 210, 210, 210, 210, 47, 210, - 47, 47, 210, 47, 47, 210, 47, 47, 47, 47, - 47, 210, 210, 47, 210, 210, 187, 210, 210, 47, - 47, 47, 47, 210, 47, 47, 210, 210, 47, 210, - 47, 210, 210, 47, 47, 47, 210, 47, 210, 210, - 210, 47, 47, 47, 210, 210, 210, 47, 210, 47, - 47, 47, 47, 210, 47, 188, 210, 47, 210, 47, - 210, 210, 47, 47, 47, 47, 47, 47, 47, 47, - - 210, 210, 47, 47, 47, 191, 210, 210, 47, 47, - 210, 47, 210, 210, 47, 47, 47, 47, 47, 47, - 47, 47, 192, 47, 47, 47, 47, 210, 47, 210, - 47, 47, 210, 47, 210, 210, 47, 210, 210, 193, - 47, 47, 47, 47, 210, 47, 47, 47, 47, 210, - 47, 210, 47, 47, 47, 47, 210, 194, 47, 210, - 210, 210, 47, 47, 47, 210, 210, 47, 197, 47, - 210, 47, 47, 210, 47, 47, 47, 47, 210, 210, - 210, 47, 47, 47, 47, 210, 210, 210, 210, 47, - 210, 47, 210, 47, 47, 210, 47, 198, 47, 210, - - 47, 47, 210, 47, 199, 210, 47, 47, 47, 210, - 47, 210, 210, 200, 210, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 210, 210, 210, 210, - 47, 47, 201, 47, 47, 47, 210, 202, 210, 210, - 210, 210, 210, 47, 210, 210, 210, 47, 210, 47, - 47, 47, 47, 210, 47, 210, 210, 210, 47, 47, - 47, 47, 47, 47, 210, 210, 47, 47, 47, 203, - 210, 210, 47, 47, 47, 47, 210, 47, 47, 210, - 210, 210, 210, 204, 210, 210, 210, 47, 47, 210, - 47, 210, 210, 47, 47, 47, 47, 210, 47, 210, - - 47, 205, 47, 47, 47, 47, 210, 47, 210, 210, - 47, 210, 47, 210, 206, 47, 47, 47, 47, 47, - 47, 47, 47, 210, 210, 47, 47, 47, 210, 210, - 210, 47, 47, 210, 47, 210, 210, 47, 47, 47, - 47, 47, 47, 47, 47, 210, 47, 210, 210, 207, - 210, 47, 210, 47, 47, 210, 47, 210, 210, 47, - 208, 47, 47, 47, 47, 210, 47, 210, 47, 47, - 47, 47, 210, 47, 210, 210, 210, 47, 210, 47, - 210, 47, 47, 47, 47, 47, 47, 47, 210, 47, - 210, 210, 209, 210, 210, 210, 210, 47, 47, 210, - - 47, 210, 210, 210, 47, 210, 210, 210, 210, 47, - 47, 210, 47, 210, 210, 210, 210, 47, 210, 210, - 210, 210, 210, 210, 210, 47, 210, 210, 210, 210, - 47, 42, 42, 42, 47, 210, 47, 46, 210, 46, - 5, 210, 210, 210, 210, 210, 210, 210, 210, 210, - 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, - 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, - 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, - 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, - 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, - - 210, 210, 210, 210, 210, 210, 210 + 46, 44, 44, 46, 46, 46, 50, 50, 72, 46, + 46, 73, 68, 46, 47, 47, 47, 68, 68, 46, + 47, 45, 53, 69, 53, 74, 96, 97, 75, 102, + + 47, 102, 47, 56, 47, 99, 100, 197, 122, 93, + 47, 196, 57, 123, 93, 93, 46, 119, 187, 53, + 69, 184, 119, 119, 47, 126, 102, 47, 69, 173, + 127, 172, 47, 170, 46, 46, 167, 167, 46, 46, + 46, 47, 47, 47, 46, 46, 171, 171, 46, 49, + 49, 50, 185, 185, 46, 186, 186, 47, 169, 47, + 47, 47, 47, 58, 168, 51, 166, 51, 153, 47, + 188, 188, 189, 189, 203, 203, 47, 52, 47, 59, + 152, 46, 204, 204, 47, 151, 150, 149, 47, 47, + 148, 147, 51, 47, 47, 47, 146, 52, 69, 46, + + 47, 47, 47, 47, 60, 129, 128, 125, 47, 47, + 124, 47, 101, 98, 48, 218, 47, 54, 47, 43, + 41, 47, 218, 55, 47, 47, 47, 218, 47, 218, + 218, 218, 218, 218, 218, 218, 47, 218, 50, 50, + 47, 47, 47, 47, 218, 218, 218, 218, 47, 61, + 218, 218, 47, 218, 53, 218, 53, 218, 218, 218, + 47, 47, 47, 218, 218, 218, 47, 47, 218, 218, + 218, 67, 47, 47, 47, 47, 47, 218, 47, 218, + 62, 53, 47, 47, 47, 218, 47, 218, 47, 47, + 218, 47, 218, 218, 218, 63, 218, 218, 47, 218, + + 47, 47, 218, 47, 218, 218, 218, 218, 47, 218, + 47, 47, 47, 47, 218, 218, 47, 218, 47, 47, + 47, 47, 47, 47, 47, 47, 70, 47, 218, 47, + 47, 218, 218, 64, 47, 218, 47, 65, 47, 47, + 47, 218, 66, 218, 218, 218, 47, 218, 218, 218, + 47, 47, 47, 47, 47, 218, 218, 71, 218, 47, + 218, 47, 218, 218, 218, 47, 47, 47, 218, 47, + 47, 218, 218, 218, 218, 218, 218, 218, 218, 47, + 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, + 218, 218, 218, 218, 47, 218, 218, 218, 218, 47, + + 76, 76, 76, 47, 47, 47, 218, 76, 76, 76, + 76, 76, 76, 218, 218, 218, 47, 218, 47, 47, + 218, 47, 218, 218, 218, 218, 218, 218, 47, 218, + 218, 47, 218, 218, 218, 76, 76, 76, 76, 76, + 76, 218, 218, 47, 218, 218, 47, 218, 47, 218, + 218, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 218, 218, 218, 218, 218, 218, 77, 47, 218, + 47, 47, 218, 47, 47, 218, 47, 218, 218, 218, + 47, 218, 79, 47, 218, 218, 47, 218, 218, 78, + 218, 218, 218, 218, 218, 47, 218, 218, 47, 218, + + 47, 47, 218, 47, 218, 218, 47, 47, 47, 47, + 47, 47, 47, 218, 218, 218, 80, 218, 218, 218, + 218, 218, 218, 47, 218, 47, 47, 218, 47, 218, + 81, 218, 218, 218, 218, 47, 218, 218, 47, 218, + 218, 218, 218, 218, 218, 218, 47, 47, 47, 218, + 47, 218, 218, 47, 218, 47, 218, 84, 47, 47, + 47, 47, 47, 218, 47, 218, 218, 218, 47, 47, + 47, 218, 218, 218, 47, 47, 218, 47, 218, 218, + 82, 218, 218, 83, 47, 218, 47, 47, 218, 47, + 218, 218, 85, 218, 47, 218, 47, 47, 47, 47, + + 218, 218, 47, 47, 47, 47, 218, 47, 218, 218, + 86, 47, 218, 47, 218, 47, 47, 218, 218, 47, + 218, 47, 218, 87, 218, 47, 218, 218, 218, 218, + 218, 47, 218, 218, 218, 218, 218, 218, 218, 218, + 47, 218, 218, 218, 218, 47, 47, 218, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 218, 88, + 218, 218, 218, 218, 47, 89, 47, 218, 47, 218, + 47, 47, 218, 47, 218, 218, 47, 91, 90, 218, + 47, 218, 218, 47, 218, 218, 47, 47, 47, 218, + 218, 47, 218, 218, 218, 47, 47, 218, 47, 218, + + 47, 218, 47, 47, 47, 47, 47, 47, 92, 47, + 47, 47, 218, 218, 47, 218, 218, 218, 218, 218, + 218, 47, 218, 47, 218, 47, 218, 47, 218, 47, + 218, 218, 218, 47, 47, 218, 218, 47, 218, 218, + 218, 218, 47, 47, 47, 218, 95, 218, 47, 218, + 218, 94, 47, 47, 218, 103, 218, 47, 47, 218, + 47, 47, 47, 47, 218, 47, 47, 47, 218, 218, + 47, 218, 218, 218, 218, 218, 218, 47, 218, 47, + 104, 47, 218, 47, 218, 47, 218, 105, 218, 47, + 47, 218, 218, 47, 47, 47, 47, 218, 47, 47, + + 47, 218, 218, 218, 47, 218, 218, 218, 47, 47, + 47, 106, 47, 47, 47, 218, 47, 218, 218, 218, + 218, 218, 47, 107, 218, 218, 47, 47, 47, 47, + 218, 218, 218, 47, 47, 47, 218, 47, 218, 218, + 218, 47, 47, 47, 109, 47, 47, 218, 218, 47, + 218, 47, 108, 218, 218, 47, 47, 47, 47, 218, + 218, 47, 47, 47, 47, 218, 218, 218, 218, 218, + 47, 218, 47, 218, 47, 47, 47, 110, 47, 218, + 47, 47, 218, 111, 47, 47, 47, 47, 218, 218, + 47, 47, 47, 47, 218, 218, 112, 218, 218, 47, + + 218, 47, 218, 47, 47, 47, 218, 47, 218, 47, + 47, 218, 218, 47, 47, 47, 47, 113, 218, 47, + 218, 218, 47, 47, 47, 218, 218, 218, 47, 218, + 47, 218, 47, 47, 47, 218, 114, 218, 47, 47, + 115, 218, 47, 47, 47, 47, 218, 218, 218, 218, + 47, 47, 47, 47, 116, 218, 218, 47, 218, 47, + 218, 47, 47, 218, 218, 47, 218, 47, 218, 47, + 47, 47, 47, 47, 47, 117, 218, 218, 218, 47, + 47, 47, 47, 218, 218, 218, 47, 218, 47, 218, + 47, 47, 218, 218, 47, 118, 47, 218, 47, 47, + + 47, 47, 47, 47, 218, 218, 218, 218, 47, 47, + 47, 47, 218, 218, 218, 47, 120, 47, 218, 47, + 47, 218, 218, 47, 218, 47, 218, 47, 47, 47, + 218, 218, 47, 47, 47, 218, 121, 47, 218, 218, + 218, 218, 218, 218, 47, 47, 47, 47, 47, 47, + 47, 130, 47, 47, 47, 47, 131, 47, 218, 218, + 47, 47, 218, 47, 218, 218, 218, 218, 218, 47, + 218, 47, 218, 47, 218, 47, 47, 47, 47, 218, + 47, 47, 132, 218, 47, 47, 47, 218, 47, 218, + 218, 218, 47, 47, 47, 218, 47, 134, 218, 218, + + 47, 47, 47, 218, 47, 47, 47, 47, 218, 133, + 218, 218, 47, 218, 218, 218, 218, 218, 218, 47, + 218, 47, 218, 47, 47, 218, 218, 47, 218, 135, + 218, 218, 47, 47, 47, 47, 47, 218, 218, 218, + 218, 47, 47, 47, 218, 47, 47, 47, 47, 218, + 47, 218, 47, 47, 218, 218, 136, 47, 218, 47, + 218, 47, 47, 47, 218, 218, 218, 218, 218, 47, + 137, 218, 218, 47, 47, 47, 47, 47, 47, 47, + 47, 218, 47, 218, 47, 47, 47, 47, 47, 47, + 47, 218, 47, 47, 47, 218, 47, 138, 142, 218, + + 139, 47, 47, 47, 218, 218, 47, 47, 47, 47, + 218, 218, 218, 47, 218, 218, 140, 47, 47, 47, + 47, 47, 47, 47, 218, 47, 47, 218, 47, 141, + 218, 218, 218, 47, 47, 47, 47, 218, 218, 218, + 47, 47, 47, 218, 218, 218, 47, 47, 47, 47, + 47, 47, 47, 47, 218, 47, 47, 218, 47, 218, + 218, 47, 143, 47, 218, 47, 47, 47, 47, 47, + 218, 218, 218, 218, 218, 47, 218, 218, 218, 47, + 47, 47, 47, 47, 218, 218, 144, 218, 47, 218, + 47, 47, 47, 47, 47, 47, 47, 218, 47, 47, + + 218, 218, 47, 47, 47, 218, 218, 47, 47, 47, + 218, 218, 218, 154, 218, 47, 47, 47, 47, 47, + 47, 218, 218, 47, 145, 218, 218, 218, 47, 218, + 47, 47, 218, 47, 47, 218, 47, 47, 47, 47, + 218, 218, 218, 47, 218, 47, 155, 218, 218, 218, + 47, 218, 47, 218, 47, 47, 47, 47, 47, 47, + 47, 47, 218, 47, 47, 218, 218, 218, 218, 218, + 218, 47, 218, 47, 218, 47, 218, 47, 218, 47, + 218, 218, 218, 47, 47, 218, 218, 47, 47, 47, + 47, 218, 218, 218, 47, 47, 47, 218, 47, 218, + + 218, 218, 47, 47, 47, 218, 47, 47, 218, 156, + 47, 218, 47, 218, 218, 218, 47, 218, 157, 218, + 218, 218, 47, 47, 47, 47, 218, 218, 218, 218, + 218, 47, 218, 218, 218, 218, 47, 47, 158, 47, + 218, 47, 47, 218, 47, 47, 47, 47, 47, 47, + 218, 47, 218, 218, 159, 218, 218, 218, 218, 218, + 47, 160, 47, 47, 218, 47, 47, 218, 218, 218, + 218, 47, 47, 218, 218, 47, 218, 218, 47, 47, + 47, 218, 218, 218, 218, 218, 218, 47, 218, 218, + 47, 218, 47, 218, 47, 47, 47, 161, 218, 47, + + 47, 47, 47, 47, 47, 218, 47, 218, 218, 218, + 218, 162, 218, 218, 218, 47, 218, 47, 47, 218, + 47, 47, 163, 47, 47, 47, 47, 47, 218, 218, + 47, 218, 218, 218, 218, 218, 218, 218, 218, 47, + 218, 47, 47, 218, 218, 47, 218, 47, 218, 218, + 47, 47, 47, 47, 47, 165, 218, 218, 218, 47, + 47, 47, 218, 47, 47, 47, 47, 164, 47, 174, + 47, 47, 218, 218, 175, 47, 218, 47, 218, 47, + 47, 47, 218, 218, 218, 218, 218, 47, 218, 218, + 218, 47, 47, 47, 47, 47, 47, 47, 47, 218, + + 47, 218, 47, 47, 47, 47, 47, 47, 47, 218, + 47, 47, 47, 218, 47, 218, 218, 218, 218, 47, + 47, 47, 218, 218, 47, 47, 47, 47, 218, 218, + 176, 47, 218, 177, 218, 47, 178, 218, 218, 47, + 47, 47, 218, 47, 47, 218, 47, 47, 47, 47, + 218, 47, 218, 47, 47, 47, 47, 218, 47, 47, + 47, 218, 218, 47, 218, 47, 218, 218, 47, 218, + 47, 218, 47, 47, 47, 47, 47, 218, 180, 218, + 218, 179, 47, 218, 218, 218, 47, 218, 218, 218, + 47, 218, 181, 218, 218, 47, 218, 47, 47, 47, + + 47, 47, 47, 218, 218, 218, 47, 47, 47, 47, + 183, 47, 47, 47, 47, 218, 47, 182, 218, 218, + 218, 218, 190, 47, 218, 47, 47, 47, 218, 47, + 218, 218, 218, 218, 218, 47, 218, 218, 218, 47, + 218, 47, 47, 47, 47, 218, 47, 218, 218, 218, + 47, 47, 47, 47, 47, 47, 218, 218, 47, 47, + 47, 218, 218, 192, 47, 47, 47, 47, 218, 47, + 47, 218, 218, 218, 218, 193, 191, 218, 218, 47, + 47, 218, 47, 218, 218, 47, 47, 47, 47, 218, + 47, 218, 47, 218, 47, 47, 47, 47, 218, 47, + + 218, 218, 47, 218, 47, 194, 218, 47, 47, 47, + 47, 47, 47, 47, 47, 218, 218, 47, 47, 47, + 218, 218, 218, 47, 47, 218, 47, 218, 218, 47, + 218, 195, 218, 47, 47, 47, 47, 218, 47, 47, + 47, 47, 218, 47, 218, 47, 218, 218, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 198, 218, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 218, + 47, 218, 200, 199, 218, 218, 47, 47, 218, 47, + 47, 218, 47, 47, 47, 47, 218, 47, 218, 47, + 218, 47, 47, 47, 47, 47, 47, 218, 218, 47, + + 47, 47, 218, 218, 47, 218, 218, 201, 47, 47, + 47, 47, 218, 218, 47, 47, 47, 218, 218, 218, + 47, 47, 47, 47, 218, 202, 47, 218, 218, 218, + 47, 47, 47, 218, 218, 47, 218, 47, 218, 47, + 47, 218, 47, 47, 47, 47, 218, 218, 218, 47, + 47, 47, 47, 218, 218, 218, 218, 47, 205, 47, + 218, 47, 47, 218, 47, 218, 47, 218, 47, 47, + 218, 47, 206, 218, 47, 47, 47, 218, 47, 218, + 218, 47, 47, 47, 218, 218, 47, 218, 208, 218, + 47, 47, 47, 47, 218, 218, 207, 47, 47, 47, + + 218, 218, 47, 47, 47, 47, 218, 218, 218, 47, + 47, 47, 47, 218, 47, 47, 47, 47, 218, 47, + 209, 47, 47, 218, 47, 218, 47, 218, 210, 47, + 47, 47, 47, 218, 218, 218, 218, 218, 47, 218, + 218, 218, 47, 47, 47, 47, 47, 218, 218, 218, + 213, 47, 218, 47, 47, 47, 47, 47, 47, 47, + 218, 47, 47, 218, 211, 47, 47, 47, 218, 218, + 47, 47, 47, 218, 218, 218, 212, 218, 47, 47, + 47, 47, 47, 47, 218, 218, 47, 218, 218, 214, + 218, 47, 218, 47, 47, 218, 47, 47, 218, 47, + + 47, 47, 47, 218, 218, 218, 47, 218, 47, 218, + 218, 218, 218, 47, 218, 47, 218, 47, 218, 218, + 218, 47, 47, 47, 47, 218, 47, 47, 218, 218, + 218, 218, 218, 47, 47, 47, 218, 218, 47, 218, + 47, 218, 47, 218, 218, 218, 215, 47, 218, 47, + 47, 47, 218, 218, 218, 216, 47, 47, 47, 218, + 218, 47, 47, 47, 47, 47, 218, 218, 218, 218, + 47, 218, 47, 218, 47, 218, 47, 218, 47, 218, + 47, 47, 218, 218, 47, 217, 218, 218, 218, 218, + 47, 47, 47, 47, 218, 218, 218, 218, 218, 47, + + 218, 218, 218, 218, 47, 47, 218, 47, 218, 47, + 47, 218, 218, 218, 218, 218, 218, 218, 218, 47, + 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, + 218, 218, 218, 218, 47, 218, 218, 218, 218, 47, + 42, 42, 42, 47, 218, 47, 46, 218, 46, 5, + 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, + 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, + 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, + 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, + 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, + + 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, + 218, 218, 218, 218, 218, 218 } ; -static yyconst short int yy_chk[2208] = +static yyconst flex_int16_t yy_chk[2317] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -738,273 +801,303 @@ static yyconst short int yy_chk[2208] = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 7, 7, 8, 8, - 17, 44, 44, 17, 17, 17, 20, 20, 212, 17, - 17, 17, 34, 17, 25, 25, 25, 34, 34, 17, - 49, 181, 20, 34, 20, 41, 43, 178, 41, 43, - - 25, 25, 25, 67, 20, 71, 71, 165, 67, 67, - 49, 161, 25, 149, 67, 148, 17, 73, 73, 20, - 75, 94, 75, 97, 20, 91, 94, 25, 97, 146, - 91, 91, 25, 145, 17, 19, 91, 144, 19, 19, - 19, 26, 26, 26, 19, 19, 19, 75, 19, 19, - 19, 19, 143, 143, 19, 147, 147, 26, 142, 26, - 26, 163, 163, 164, 164, 19, 126, 19, 125, 26, - 167, 167, 27, 27, 27, 168, 168, 19, 189, 189, - 124, 19, 190, 190, 26, 28, 28, 28, 27, 26, - 27, 123, 19, 122, 27, 121, 120, 19, 119, 19, - - 27, 28, 116, 28, 28, 99, 29, 29, 29, 98, - 96, 95, 74, 28, 72, 27, 18, 29, 5, 4, - 27, 2, 29, 0, 29, 30, 30, 30, 28, 33, - 33, 33, 0, 28, 29, 0, 0, 0, 0, 0, - 33, 30, 0, 30, 0, 33, 0, 33, 0, 29, - 30, 0, 0, 30, 29, 0, 0, 33, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 30, 0, - 0, 0, 33, 30, 31, 31, 31, 33, 0, 0, - 0, 31, 0, 36, 36, 36, 0, 37, 37, 37, - 31, 0, 31, 0, 0, 0, 31, 0, 0, 36, - - 0, 36, 31, 37, 0, 37, 0, 0, 0, 0, - 0, 36, 0, 0, 0, 37, 0, 31, 0, 0, - 0, 0, 31, 32, 32, 32, 36, 36, 0, 0, - 37, 36, 0, 37, 32, 37, 50, 50, 32, 32, - 0, 32, 0, 32, 47, 47, 47, 51, 51, 51, - 0, 32, 50, 0, 50, 0, 0, 0, 0, 0, - 47, 0, 47, 51, 50, 51, 32, 0, 0, 0, - 0, 32, 47, 0, 0, 51, 0, 0, 0, 50, - 0, 0, 0, 0, 50, 0, 0, 47, 0, 0, - 51, 0, 47, 0, 0, 51, 52, 52, 52, 53, - - 53, 53, 0, 52, 52, 52, 52, 52, 52, 0, - 0, 0, 52, 0, 52, 53, 0, 53, 0, 0, - 0, 0, 0, 0, 52, 0, 0, 53, 0, 0, - 0, 52, 52, 52, 52, 52, 52, 0, 0, 52, - 0, 0, 53, 0, 52, 0, 0, 53, 54, 54, - 54, 55, 55, 55, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 54, 54, 0, 54, 55, 0, 55, - 0, 0, 56, 56, 56, 55, 54, 0, 0, 55, - 0, 56, 0, 0, 0, 57, 57, 57, 56, 0, - 56, 54, 0, 0, 55, 0, 54, 0, 0, 55, - - 56, 57, 0, 57, 0, 57, 0, 0, 0, 0, - 0, 0, 0, 57, 0, 56, 0, 0, 0, 0, - 56, 59, 59, 59, 0, 0, 0, 0, 57, 0, - 0, 0, 59, 57, 58, 58, 58, 59, 0, 59, - 0, 0, 0, 60, 60, 60, 0, 0, 0, 59, - 58, 0, 58, 0, 0, 58, 0, 0, 58, 60, - 0, 60, 58, 0, 59, 0, 0, 60, 0, 59, - 0, 60, 61, 61, 61, 0, 0, 58, 62, 62, - 62, 0, 58, 0, 0, 61, 60, 0, 61, 0, - 61, 60, 0, 0, 62, 0, 62, 0, 62, 0, - - 61, 0, 0, 0, 0, 0, 62, 0, 0, 0, - 0, 0, 0, 0, 0, 61, 0, 0, 0, 0, - 61, 62, 0, 64, 64, 64, 62, 63, 63, 63, - 65, 65, 65, 0, 63, 0, 0, 0, 0, 64, - 63, 64, 0, 63, 0, 63, 65, 0, 65, 0, - 0, 64, 65, 64, 0, 63, 0, 0, 65, 0, - 0, 66, 66, 66, 0, 0, 64, 0, 0, 0, - 63, 64, 0, 65, 0, 63, 0, 66, 65, 66, - 69, 69, 69, 66, 70, 70, 70, 0, 0, 66, - 0, 0, 0, 0, 0, 0, 69, 0, 69, 0, - - 70, 0, 70, 0, 66, 0, 0, 0, 69, 66, - 0, 0, 70, 0, 0, 0, 0, 76, 76, 76, - 0, 70, 0, 69, 0, 0, 69, 70, 69, 0, - 76, 0, 70, 76, 0, 76, 77, 77, 77, 78, - 78, 78, 0, 0, 0, 76, 0, 0, 0, 0, - 0, 0, 77, 0, 77, 78, 78, 78, 77, 0, - 76, 0, 0, 0, 77, 76, 0, 78, 0, 0, - 79, 79, 79, 0, 80, 80, 80, 0, 0, 77, - 0, 0, 78, 0, 77, 0, 79, 78, 79, 0, - 80, 0, 80, 0, 0, 79, 0, 0, 79, 80, - - 0, 0, 80, 81, 81, 81, 0, 82, 82, 82, - 0, 0, 0, 79, 81, 0, 0, 80, 79, 81, - 0, 81, 80, 82, 0, 82, 0, 0, 82, 0, - 0, 81, 0, 0, 0, 82, 83, 83, 83, 0, - 0, 0, 84, 84, 84, 0, 81, 0, 0, 0, - 82, 81, 83, 84, 83, 82, 0, 83, 84, 0, - 84, 0, 0, 0, 83, 85, 85, 85, 0, 0, - 84, 0, 0, 0, 0, 0, 86, 86, 86, 83, - 0, 85, 0, 85, 83, 84, 0, 87, 87, 87, - 84, 85, 86, 85, 86, 0, 0, 0, 86, 0, - - 88, 88, 88, 87, 86, 87, 0, 0, 85, 0, - 0, 88, 0, 85, 0, 87, 88, 0, 88, 86, - 0, 89, 89, 89, 86, 0, 0, 0, 88, 0, - 87, 0, 0, 0, 0, 87, 0, 89, 0, 89, - 90, 90, 90, 88, 0, 89, 0, 0, 88, 89, - 0, 0, 0, 0, 0, 0, 90, 0, 90, 92, - 92, 92, 0, 90, 89, 0, 0, 0, 90, 89, - 0, 0, 0, 0, 0, 92, 0, 92, 93, 93, - 93, 0, 0, 90, 0, 0, 0, 92, 90, 0, - 0, 0, 0, 0, 93, 92, 93, 0, 0, 100, - - 100, 100, 92, 0, 0, 0, 93, 92, 0, 0, - 0, 0, 0, 93, 0, 100, 0, 100, 101, 101, - 101, 93, 102, 102, 102, 0, 93, 100, 0, 0, - 0, 0, 0, 0, 101, 0, 101, 101, 102, 0, - 102, 0, 100, 0, 0, 0, 101, 100, 0, 0, - 102, 102, 0, 103, 103, 103, 104, 104, 104, 0, - 0, 101, 0, 0, 0, 102, 101, 0, 0, 103, - 102, 103, 104, 0, 104, 0, 0, 104, 103, 0, - 0, 103, 0, 0, 104, 0, 0, 105, 105, 105, - 106, 106, 106, 0, 0, 0, 103, 0, 0, 104, - - 0, 103, 0, 105, 104, 105, 106, 0, 106, 0, - 0, 105, 106, 0, 0, 105, 0, 0, 106, 0, - 0, 107, 107, 107, 0, 108, 108, 108, 0, 0, - 105, 0, 0, 106, 0, 105, 0, 107, 106, 107, - 0, 108, 0, 108, 109, 109, 109, 0, 0, 107, - 108, 0, 0, 108, 0, 0, 0, 0, 0, 0, - 109, 0, 109, 0, 107, 0, 0, 109, 108, 107, - 0, 0, 109, 108, 0, 110, 110, 110, 0, 111, - 111, 111, 0, 112, 112, 112, 0, 109, 111, 0, - 110, 110, 109, 110, 112, 111, 0, 111, 0, 112, - - 0, 112, 0, 110, 0, 0, 0, 111, 0, 0, - 0, 112, 113, 113, 113, 0, 0, 0, 110, 114, - 114, 114, 111, 110, 0, 0, 112, 111, 113, 0, - 113, 112, 113, 0, 0, 114, 0, 114, 0, 0, - 113, 115, 115, 115, 0, 0, 0, 114, 117, 117, - 117, 0, 0, 0, 0, 113, 115, 115, 0, 115, - 113, 0, 114, 0, 117, 0, 117, 114, 0, 115, - 118, 118, 118, 0, 0, 0, 117, 127, 127, 127, - 0, 0, 0, 117, 115, 0, 118, 0, 118, 115, - 0, 117, 0, 127, 0, 127, 117, 0, 118, 128, - - 128, 128, 0, 0, 0, 127, 129, 129, 129, 0, - 130, 130, 130, 118, 118, 128, 129, 128, 118, 0, - 127, 0, 129, 0, 129, 127, 130, 128, 130, 0, - 0, 0, 0, 0, 129, 0, 0, 0, 130, 131, - 131, 131, 128, 132, 132, 132, 0, 128, 0, 129, - 133, 133, 133, 130, 129, 131, 0, 131, 130, 132, - 0, 132, 0, 0, 132, 0, 133, 131, 133, 0, - 0, 132, 0, 0, 133, 0, 0, 0, 133, 134, - 134, 134, 131, 0, 0, 0, 132, 131, 0, 0, - 0, 132, 0, 133, 134, 134, 0, 134, 133, 0, - - 135, 135, 135, 136, 136, 136, 0, 134, 0, 0, - 135, 0, 0, 0, 0, 0, 135, 136, 135, 136, - 0, 136, 134, 0, 0, 0, 0, 134, 135, 0, - 0, 136, 0, 0, 137, 137, 137, 0, 0, 0, - 0, 0, 0, 135, 0, 0, 136, 0, 135, 0, - 137, 136, 137, 137, 0, 138, 138, 138, 139, 139, - 139, 0, 137, 0, 0, 0, 0, 138, 0, 0, - 0, 138, 0, 138, 139, 0, 139, 137, 139, 140, - 140, 140, 137, 138, 0, 0, 139, 0, 0, 0, - 0, 0, 0, 0, 0, 140, 0, 140, 138, 0, - - 0, 139, 0, 138, 0, 0, 139, 140, 141, 141, - 141, 141, 0, 0, 150, 150, 150, 151, 151, 151, - 0, 0, 140, 140, 141, 150, 141, 140, 0, 0, - 150, 0, 150, 151, 0, 151, 141, 0, 0, 0, - 0, 0, 150, 0, 0, 151, 0, 0, 152, 152, - 152, 141, 0, 0, 0, 0, 141, 150, 0, 0, - 151, 0, 150, 0, 152, 151, 152, 153, 153, 153, - 154, 154, 154, 0, 0, 0, 152, 0, 0, 0, - 0, 154, 152, 153, 0, 153, 154, 0, 154, 0, - 0, 152, 0, 0, 0, 153, 152, 153, 154, 0, - - 0, 155, 155, 155, 156, 156, 156, 0, 0, 0, - 153, 0, 0, 154, 0, 153, 0, 155, 154, 155, - 156, 0, 156, 157, 157, 157, 0, 0, 156, 155, - 0, 0, 156, 0, 0, 155, 0, 0, 0, 157, - 0, 157, 0, 0, 155, 0, 0, 156, 0, 155, - 0, 157, 156, 0, 158, 158, 158, 157, 159, 159, - 159, 159, 169, 169, 169, 0, 157, 0, 0, 0, - 158, 157, 158, 158, 159, 0, 159, 0, 169, 0, - 169, 0, 158, 0, 0, 0, 159, 0, 0, 0, - 169, 170, 170, 170, 0, 0, 169, 158, 0, 0, - - 0, 159, 158, 170, 0, 169, 159, 170, 0, 170, - 169, 0, 171, 171, 171, 172, 172, 172, 0, 170, - 0, 0, 0, 171, 0, 0, 0, 0, 171, 0, - 171, 172, 0, 172, 170, 0, 173, 173, 173, 170, - 171, 0, 0, 172, 0, 0, 173, 0, 0, 174, - 174, 174, 173, 0, 173, 171, 0, 0, 172, 0, - 171, 0, 0, 172, 173, 174, 0, 174, 0, 0, - 0, 175, 175, 175, 0, 0, 0, 174, 0, 173, - 176, 176, 176, 0, 173, 175, 0, 175, 0, 175, - 0, 0, 174, 184, 184, 184, 176, 174, 176, 175, - - 0, 0, 185, 185, 185, 184, 0, 0, 176, 184, - 0, 184, 0, 0, 175, 186, 186, 186, 185, 175, - 185, 184, 185, 176, 187, 187, 187, 0, 176, 0, - 185, 186, 0, 186, 0, 0, 184, 0, 0, 187, - 187, 184, 187, 186, 0, 185, 188, 188, 188, 0, - 185, 0, 187, 191, 191, 191, 0, 188, 186, 0, - 0, 0, 188, 186, 188, 0, 0, 187, 191, 191, - 0, 191, 187, 0, 188, 192, 192, 192, 0, 0, - 0, 191, 193, 193, 193, 0, 0, 0, 0, 188, - 0, 192, 0, 192, 188, 0, 191, 192, 193, 0, - - 193, 191, 0, 192, 193, 0, 194, 194, 194, 0, - 193, 0, 0, 194, 0, 197, 197, 197, 192, 198, - 198, 198, 194, 192, 194, 193, 0, 0, 0, 0, - 193, 197, 197, 197, 194, 198, 0, 198, 0, 0, - 0, 0, 0, 197, 0, 0, 0, 198, 0, 194, - 199, 199, 199, 0, 194, 0, 0, 0, 197, 200, - 200, 200, 198, 197, 0, 0, 199, 198, 199, 200, - 0, 0, 201, 201, 201, 200, 0, 200, 199, 0, - 0, 0, 0, 201, 0, 0, 0, 200, 201, 0, - 201, 0, 0, 199, 202, 202, 202, 0, 199, 0, - - 201, 202, 200, 203, 203, 203, 0, 200, 0, 0, - 202, 0, 202, 0, 203, 201, 204, 204, 204, 203, - 201, 203, 202, 0, 0, 205, 205, 205, 0, 0, - 0, 203, 204, 0, 204, 0, 0, 202, 206, 206, - 206, 205, 202, 205, 204, 0, 203, 0, 0, 205, - 0, 203, 0, 205, 206, 0, 206, 0, 0, 204, - 206, 207, 207, 207, 204, 0, 206, 0, 205, 208, - 208, 208, 0, 205, 0, 0, 0, 207, 0, 207, - 0, 206, 209, 209, 209, 208, 206, 208, 0, 207, - 0, 0, 208, 0, 0, 0, 0, 208, 209, 0, - - 209, 0, 0, 0, 207, 0, 0, 0, 0, 207, - 209, 0, 208, 0, 0, 0, 0, 208, 0, 0, - 0, 0, 0, 0, 0, 209, 0, 0, 0, 0, - 209, 211, 211, 211, 213, 0, 213, 214, 0, 214, - 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, - 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, - 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, - 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, - 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, - 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, - - 210, 210, 210, 210, 210, 210, 210 + 17, 44, 44, 17, 17, 17, 20, 20, 41, 17, + 17, 41, 34, 17, 26, 26, 26, 34, 34, 17, + 49, 220, 20, 34, 20, 43, 72, 72, 43, 76, + + 26, 76, 26, 26, 20, 74, 74, 187, 96, 68, + 49, 184, 26, 96, 68, 68, 17, 93, 170, 20, + 68, 166, 93, 93, 20, 99, 76, 26, 93, 153, + 99, 152, 26, 150, 17, 19, 147, 147, 19, 19, + 19, 27, 27, 27, 19, 19, 151, 151, 19, 19, + 19, 19, 168, 168, 19, 169, 169, 27, 149, 27, + 28, 28, 28, 27, 148, 19, 146, 19, 129, 27, + 172, 172, 173, 173, 196, 196, 28, 19, 28, 28, + 128, 19, 197, 197, 27, 127, 126, 125, 28, 27, + 124, 123, 19, 29, 29, 29, 122, 19, 119, 19, + + 25, 25, 25, 28, 29, 101, 100, 98, 28, 29, + 97, 29, 75, 73, 18, 5, 25, 25, 25, 4, + 2, 29, 0, 25, 30, 30, 30, 0, 25, 0, + 0, 0, 0, 0, 0, 0, 29, 0, 50, 50, + 30, 29, 30, 25, 0, 0, 0, 0, 25, 30, + 0, 0, 30, 0, 50, 0, 50, 0, 0, 0, + 33, 33, 33, 0, 0, 0, 50, 30, 0, 0, + 0, 33, 30, 31, 31, 31, 33, 0, 33, 0, + 31, 50, 36, 36, 36, 0, 50, 0, 33, 31, + 0, 31, 0, 0, 0, 31, 0, 0, 36, 0, + + 36, 31, 0, 33, 0, 0, 0, 0, 33, 0, + 36, 37, 37, 37, 0, 0, 31, 0, 47, 47, + 47, 31, 32, 32, 32, 36, 36, 37, 0, 37, + 36, 0, 0, 32, 47, 0, 47, 32, 32, 37, + 32, 0, 32, 0, 0, 0, 47, 0, 0, 0, + 32, 51, 51, 51, 37, 0, 0, 37, 0, 37, + 0, 47, 0, 0, 0, 32, 47, 51, 0, 51, + 32, 0, 0, 0, 0, 0, 0, 0, 0, 51, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 51, 0, 0, 0, 0, 51, + + 52, 52, 52, 53, 53, 53, 0, 52, 52, 52, + 52, 52, 52, 0, 0, 0, 52, 0, 52, 53, + 0, 53, 0, 0, 0, 0, 0, 0, 52, 0, + 0, 53, 0, 0, 0, 52, 52, 52, 52, 52, + 52, 0, 0, 52, 0, 0, 53, 0, 52, 0, + 0, 53, 54, 54, 54, 55, 55, 55, 56, 56, + 56, 0, 0, 0, 0, 0, 0, 54, 54, 0, + 54, 55, 0, 55, 56, 0, 56, 0, 0, 0, + 54, 0, 56, 55, 0, 0, 56, 0, 0, 55, + 0, 0, 0, 0, 0, 54, 0, 0, 55, 0, + + 54, 56, 0, 55, 0, 0, 56, 57, 57, 57, + 58, 58, 58, 0, 0, 0, 57, 0, 0, 0, + 0, 0, 0, 57, 0, 57, 58, 0, 58, 0, + 58, 0, 0, 0, 0, 57, 0, 0, 58, 0, + 0, 0, 0, 0, 0, 0, 60, 60, 60, 0, + 57, 0, 0, 58, 0, 57, 0, 60, 58, 59, + 59, 59, 60, 0, 60, 0, 0, 0, 61, 61, + 61, 0, 0, 0, 60, 59, 0, 59, 0, 0, + 59, 0, 0, 59, 61, 0, 61, 59, 0, 60, + 0, 0, 61, 0, 60, 0, 61, 62, 62, 62, + + 0, 0, 59, 63, 63, 63, 0, 59, 0, 0, + 62, 61, 0, 62, 0, 62, 61, 0, 0, 63, + 0, 63, 0, 63, 0, 62, 0, 0, 0, 0, + 0, 63, 0, 0, 0, 0, 0, 0, 0, 0, + 62, 0, 0, 0, 0, 62, 63, 0, 65, 65, + 65, 63, 64, 64, 64, 66, 66, 66, 0, 64, + 0, 0, 0, 0, 65, 64, 65, 0, 64, 0, + 64, 66, 0, 66, 0, 0, 65, 66, 65, 0, + 64, 0, 0, 66, 0, 0, 67, 67, 67, 0, + 0, 65, 0, 0, 0, 64, 65, 0, 66, 0, + + 64, 0, 67, 66, 67, 70, 70, 70, 67, 71, + 71, 71, 0, 0, 67, 0, 0, 0, 0, 0, + 0, 70, 0, 70, 0, 71, 0, 71, 0, 67, + 0, 0, 0, 70, 67, 0, 0, 71, 0, 0, + 0, 0, 77, 77, 77, 0, 71, 0, 70, 0, + 0, 70, 71, 70, 0, 77, 0, 71, 77, 0, + 77, 78, 78, 78, 0, 79, 79, 79, 0, 0, + 77, 0, 0, 0, 0, 0, 0, 78, 0, 78, + 78, 79, 0, 79, 0, 77, 0, 79, 0, 78, + 77, 0, 0, 79, 80, 80, 80, 0, 81, 81, + + 81, 0, 0, 0, 78, 0, 0, 0, 79, 78, + 80, 80, 80, 79, 81, 0, 81, 0, 0, 0, + 0, 0, 80, 81, 0, 0, 81, 82, 82, 82, + 0, 0, 0, 83, 83, 83, 0, 80, 0, 0, + 0, 81, 80, 82, 83, 82, 81, 0, 0, 83, + 0, 83, 82, 0, 0, 82, 84, 84, 84, 0, + 0, 83, 85, 85, 85, 0, 0, 0, 0, 0, + 82, 0, 84, 0, 84, 82, 83, 84, 85, 0, + 85, 83, 0, 85, 84, 86, 86, 86, 0, 0, + 85, 87, 87, 87, 0, 0, 86, 0, 0, 84, + + 0, 86, 0, 86, 84, 85, 0, 87, 0, 87, + 85, 0, 0, 86, 88, 88, 88, 87, 0, 87, + 0, 0, 89, 89, 89, 0, 0, 0, 86, 0, + 88, 0, 88, 86, 87, 0, 88, 0, 89, 87, + 89, 0, 88, 90, 90, 90, 0, 0, 0, 0, + 89, 91, 91, 91, 90, 0, 0, 88, 0, 90, + 0, 90, 88, 0, 0, 89, 0, 91, 0, 91, + 89, 90, 92, 92, 92, 91, 0, 0, 0, 91, + 94, 94, 94, 0, 0, 0, 90, 0, 92, 0, + 92, 90, 0, 0, 91, 92, 94, 0, 94, 91, + + 92, 95, 95, 95, 0, 0, 0, 0, 94, 102, + 102, 102, 0, 0, 0, 92, 94, 95, 0, 95, + 92, 0, 0, 94, 0, 102, 0, 102, 94, 95, + 0, 0, 103, 103, 103, 0, 95, 102, 0, 0, + 0, 0, 0, 0, 95, 104, 104, 104, 103, 95, + 103, 103, 102, 105, 105, 105, 104, 102, 0, 0, + 103, 104, 0, 104, 0, 0, 0, 0, 0, 105, + 0, 105, 0, 104, 0, 103, 107, 107, 107, 0, + 103, 105, 105, 0, 106, 106, 106, 0, 104, 0, + 0, 0, 107, 104, 107, 0, 105, 107, 0, 0, + + 106, 105, 106, 0, 107, 108, 108, 108, 0, 106, + 0, 0, 106, 0, 0, 0, 0, 0, 0, 107, + 0, 108, 0, 108, 107, 0, 0, 106, 0, 108, + 0, 0, 106, 108, 109, 109, 109, 0, 0, 0, + 0, 110, 110, 110, 0, 111, 111, 111, 108, 0, + 109, 0, 109, 108, 0, 0, 109, 110, 0, 110, + 0, 111, 109, 111, 0, 0, 0, 0, 0, 110, + 111, 0, 0, 111, 112, 112, 112, 109, 116, 116, + 116, 0, 109, 0, 110, 113, 113, 113, 111, 110, + 112, 0, 112, 111, 116, 0, 116, 112, 116, 0, + + 113, 113, 112, 113, 0, 0, 116, 114, 114, 114, + 0, 0, 0, 113, 0, 0, 114, 112, 115, 115, + 115, 116, 112, 114, 0, 114, 116, 0, 113, 115, + 0, 0, 0, 113, 115, 114, 115, 0, 0, 0, + 117, 117, 117, 0, 0, 0, 115, 118, 118, 118, + 114, 120, 120, 120, 0, 114, 117, 0, 117, 0, + 0, 115, 118, 118, 0, 118, 115, 120, 117, 120, + 0, 0, 0, 0, 0, 118, 0, 0, 0, 120, + 121, 121, 121, 117, 0, 0, 120, 0, 117, 0, + 118, 130, 130, 130, 120, 118, 121, 0, 121, 120, + + 0, 0, 131, 131, 131, 0, 0, 130, 121, 130, + 0, 0, 0, 131, 0, 132, 132, 132, 131, 130, + 131, 0, 0, 121, 121, 0, 0, 0, 121, 0, + 131, 132, 0, 132, 130, 0, 133, 133, 133, 130, + 0, 0, 0, 132, 0, 131, 133, 0, 0, 0, + 131, 0, 133, 0, 133, 134, 134, 134, 132, 135, + 135, 135, 0, 132, 133, 0, 0, 0, 0, 0, + 0, 134, 0, 134, 0, 135, 0, 135, 0, 133, + 0, 0, 0, 134, 133, 0, 0, 135, 136, 136, + 136, 0, 0, 0, 137, 137, 137, 0, 134, 0, + + 0, 0, 135, 134, 136, 0, 136, 135, 0, 136, + 137, 0, 137, 0, 0, 0, 136, 0, 137, 0, + 0, 0, 137, 138, 138, 138, 0, 0, 0, 0, + 0, 136, 0, 0, 0, 0, 136, 137, 138, 138, + 0, 138, 137, 0, 139, 139, 139, 140, 140, 140, + 0, 138, 0, 0, 139, 0, 0, 0, 0, 0, + 139, 140, 139, 140, 0, 140, 138, 0, 0, 0, + 0, 138, 139, 0, 0, 140, 0, 0, 141, 141, + 141, 0, 0, 0, 0, 0, 0, 139, 0, 0, + 140, 0, 139, 0, 141, 140, 141, 141, 0, 142, + + 142, 142, 143, 143, 143, 0, 141, 0, 0, 0, + 0, 142, 0, 0, 0, 142, 0, 142, 143, 0, + 143, 141, 143, 144, 144, 144, 141, 142, 0, 0, + 143, 0, 0, 0, 0, 0, 0, 0, 0, 144, + 0, 144, 142, 0, 0, 143, 0, 142, 0, 0, + 143, 144, 145, 145, 145, 145, 0, 0, 0, 154, + 154, 154, 0, 155, 155, 155, 144, 144, 145, 154, + 145, 144, 0, 0, 155, 154, 0, 154, 0, 155, + 145, 155, 0, 0, 0, 0, 0, 154, 0, 0, + 0, 155, 156, 156, 156, 145, 157, 157, 157, 0, + + 145, 0, 154, 158, 158, 158, 155, 154, 156, 0, + 156, 155, 157, 0, 157, 0, 0, 0, 0, 158, + 156, 158, 0, 0, 157, 159, 159, 159, 0, 0, + 157, 158, 0, 158, 0, 156, 159, 0, 0, 157, + 156, 159, 0, 159, 157, 0, 158, 160, 160, 160, + 0, 158, 0, 159, 161, 161, 161, 0, 162, 162, + 162, 0, 0, 160, 0, 160, 0, 0, 159, 0, + 161, 0, 161, 159, 162, 160, 162, 0, 161, 0, + 0, 160, 161, 0, 0, 0, 162, 0, 0, 0, + 160, 0, 162, 0, 0, 160, 0, 161, 163, 163, + + 163, 162, 161, 0, 0, 0, 162, 164, 164, 164, + 164, 174, 174, 174, 163, 0, 163, 163, 0, 0, + 0, 0, 174, 164, 0, 164, 163, 174, 0, 174, + 0, 0, 0, 0, 0, 164, 0, 0, 0, 174, + 0, 163, 175, 175, 175, 0, 163, 0, 0, 0, + 164, 176, 176, 176, 174, 164, 0, 0, 175, 174, + 175, 0, 0, 176, 177, 177, 177, 176, 0, 176, + 175, 0, 0, 0, 0, 177, 175, 0, 0, 176, + 177, 0, 177, 0, 0, 175, 178, 178, 178, 0, + 175, 0, 177, 0, 176, 179, 179, 179, 0, 176, + + 0, 0, 178, 0, 178, 179, 0, 177, 180, 180, + 180, 179, 177, 179, 178, 0, 0, 181, 181, 181, + 0, 0, 0, 179, 180, 0, 180, 0, 0, 178, + 0, 181, 0, 181, 178, 181, 180, 0, 179, 182, + 182, 182, 0, 179, 0, 181, 0, 0, 190, 190, + 190, 180, 192, 192, 192, 182, 180, 182, 190, 0, + 181, 191, 191, 191, 190, 181, 190, 182, 192, 0, + 192, 0, 192, 191, 0, 0, 190, 191, 0, 191, + 192, 0, 182, 193, 193, 193, 0, 182, 0, 191, + 0, 190, 194, 194, 194, 192, 190, 0, 0, 193, + + 192, 193, 0, 0, 191, 0, 0, 194, 194, 191, + 194, 193, 0, 0, 195, 195, 195, 0, 0, 0, + 194, 198, 198, 198, 0, 195, 193, 0, 0, 0, + 195, 193, 195, 0, 0, 194, 0, 198, 0, 198, + 194, 0, 195, 199, 199, 199, 0, 0, 0, 198, + 200, 200, 200, 0, 0, 0, 0, 195, 199, 199, + 0, 199, 195, 0, 198, 0, 200, 0, 200, 198, + 0, 199, 200, 0, 201, 201, 201, 0, 200, 0, + 0, 202, 202, 202, 0, 0, 199, 0, 202, 0, + 201, 199, 201, 200, 0, 0, 201, 202, 200, 202, + + 0, 0, 201, 205, 205, 205, 0, 0, 0, 202, + 206, 206, 206, 0, 207, 207, 207, 201, 0, 205, + 205, 205, 201, 0, 202, 0, 206, 0, 206, 202, + 207, 205, 207, 0, 0, 0, 0, 0, 206, 0, + 0, 0, 207, 210, 210, 210, 205, 0, 0, 0, + 210, 205, 0, 206, 208, 208, 208, 207, 206, 210, + 0, 210, 207, 0, 208, 209, 209, 209, 0, 0, + 208, 210, 208, 0, 0, 0, 209, 0, 211, 211, + 211, 209, 208, 209, 0, 0, 210, 0, 0, 211, + 0, 210, 0, 209, 211, 0, 211, 208, 0, 212, + + 212, 212, 208, 0, 0, 0, 211, 0, 209, 0, + 0, 0, 0, 209, 0, 212, 0, 212, 0, 0, + 0, 211, 213, 213, 213, 0, 211, 212, 0, 0, + 0, 0, 0, 214, 214, 214, 0, 0, 213, 0, + 213, 0, 212, 0, 0, 0, 213, 212, 0, 214, + 213, 214, 0, 0, 0, 214, 215, 215, 215, 0, + 0, 214, 216, 216, 216, 213, 0, 0, 0, 0, + 213, 0, 215, 0, 215, 0, 214, 0, 216, 0, + 216, 214, 0, 0, 215, 216, 0, 0, 0, 0, + 216, 217, 217, 217, 0, 0, 0, 0, 0, 215, + + 0, 0, 0, 0, 215, 216, 0, 217, 0, 217, + 216, 0, 0, 0, 0, 0, 0, 0, 0, 217, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 217, 0, 0, 0, 0, 217, + 219, 219, 219, 221, 0, 221, 222, 0, 222, 218, + 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, + 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, + 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, + 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, + 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, + + 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, + 218, 218, 218, 218, 218, 218 } ; -static yy_state_type yy_state_buf[YY_BUF_SIZE + 2], *yy_state_ptr; -static char *yy_full_match; -static int yy_lp; -#define REJECT \ -{ \ -*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ \ -yy_cp = yy_full_match; /* restore poss. backed-over text */ \ -++yy_lp; \ -goto find_rule; \ -} +/* Table of booleans, true if rule could match eol. */ +static yyconst flex_int32_t yy_rule_can_match_eol[51] = + { 0, +1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, }; + +static yy_state_type yy_last_accepting_state; +static char *yy_last_accepting_cpos; + +extern int ld_flex_debug; +int ld_flex_debug = 0; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected #define yymore() yymore_used_but_not_detected #define YY_MORE_ADJ 0 #define YY_RESTORE_YY_MORE_OFFSET -char *yytext; -#line 1 "/home/drepper/gnu/elfutils/src/ldlex.l" -#define INITIAL 0 -#line 2 "/home/drepper/gnu/elfutils/src/ldlex.l" -/* Copyright (C) 2001, 2002, 2003, 2004 Red Hat, Inc. +char *ldtext; +#line 1 "ldlex.l" +#line 2 "ldlex.l" +/* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2008 Red Hat, Inc. + This file is part of Red Hat elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 2001. - This program is Open Source software; you can redistribute it and/or - modify it under the terms of the Open Software License version 1.0 as - published by the Open Source Initiative. + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. - You should have received a copy of the Open Software License along - with this program; if not, you may obtain a copy of the Open Software - License version 1.0 from http://www.opensource.org/licenses/osl.php or - by writing the Open Source Initiative c/o Lawrence Rosen, Esq., - 3001 King Ranch Road, Ukiah, CA 95482. */ + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ #ifdef HAVE_CONFIG_H # include <config.h> @@ -1035,6 +1128,11 @@ char *yytext; #undef fwrite #define fwrite(b, m, n, s) fwrite_unlocked (b, m, n, s) +/* ECHO must be redefined since the default implementation ignores + the return value of fwrite_unlocked. */ +#define ECHO do { size_t n__ __attribute__ ((unused)) \ + = fwrite (ldtext, ldleng, 1, ldout); } while (0) + /* Defined in ld.c. */ extern int ld_scan_version_script; @@ -1054,10 +1152,54 @@ static void push_state (enum prepstate); static int pop_state (void); static int handle_ifdef (void); static void invalid_char (int ch); -#define YY_NEVER_INTERACTIVE 1 + +#line 1157 "ldlex.c" + +#define INITIAL 0 #define IGNORE 1 -#line 1061 "ldlex.c" +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include <unistd.h> +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +static int yy_init_globals (void ); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int ldlex_destroy (void ); + +int ldget_debug (void ); + +void ldset_debug (int debug_flag ); + +YY_EXTRA_TYPE ldget_extra (void ); + +void ldset_extra (YY_EXTRA_TYPE user_defined ); + +FILE *ldget_in (void ); + +void ldset_in (FILE * in_str ); + +FILE *ldget_out (void ); + +void ldset_out (FILE * out_str ); + +int ldget_leng (void ); + +char *ldget_text (void ); + +int ldget_lineno (void ); + +void ldset_lineno (int line_number ); /* Macros after this point can all be overridden by user definitions in * section 1. @@ -1065,65 +1207,30 @@ static void invalid_char (int ch); #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus -extern "C" int yywrap YY_PROTO(( void )); +extern "C" int ldwrap (void ); #else -extern int yywrap YY_PROTO(( void )); +extern int ldwrap (void ); #endif #endif -#ifndef YY_NO_UNPUT -static void yyunput YY_PROTO(( int c, char *buf_ptr )); -#endif - + static void yyunput (int c,char *buf_ptr ); + #ifndef yytext_ptr -static void yy_flex_strncpy YY_PROTO(( char *, yyconst char *, int )); +static void yy_flex_strncpy (char *,yyconst char *,int ); #endif #ifdef YY_NEED_STRLEN -static int yy_flex_strlen YY_PROTO(( yyconst char * )); +static int yy_flex_strlen (yyconst char * ); #endif #ifndef YY_NO_INPUT -#ifdef __cplusplus -static int yyinput YY_PROTO(( void )); -#else -static int input YY_PROTO(( void )); -#endif -#endif - -#if YY_STACK_USED -static int yy_start_stack_ptr = 0; -static int yy_start_stack_depth = 0; -static int *yy_start_stack = 0; -#ifndef YY_NO_PUSH_STATE -static void yy_push_state YY_PROTO(( int new_state )); -#endif -#ifndef YY_NO_POP_STATE -static void yy_pop_state YY_PROTO(( void )); -#endif -#ifndef YY_NO_TOP_STATE -static int yy_top_state YY_PROTO(( void )); -#endif +#ifdef __cplusplus +static int yyinput (void ); #else -#define YY_NO_PUSH_STATE 1 -#define YY_NO_POP_STATE 1 -#define YY_NO_TOP_STATE 1 +static int input (void ); #endif -#ifdef YY_MALLOC_DECL -YY_MALLOC_DECL -#else -#if __STDC__ -#ifndef __cplusplus -#include <stdlib.h> -#endif -#else -/* Just try to get by without declaring the routines. This will fail - * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int) - * or sizeof(void*) != sizeof(int). - */ -#endif #endif /* Amount of stuff to slurp up with each read. */ @@ -1132,12 +1239,11 @@ YY_MALLOC_DECL #endif /* Copy whatever the last rule matched to the standard output. */ - #ifndef ECHO /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ -#define ECHO (void) fwrite( yytext, yyleng, 1, yyout ) +#define ECHO fwrite( ldtext, ldleng, 1, ldout ) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, @@ -1145,21 +1251,35 @@ YY_MALLOC_DECL */ #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ - if ( yy_current_buffer->yy_is_interactive ) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ { \ - int c = '*', n; \ + int c = '*'; \ + unsigned n; \ for ( n = 0; n < max_size && \ - (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + (c = getc( ldin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ if ( c == '\n' ) \ buf[n++] = (char) c; \ - if ( c == EOF && ferror( yyin ) ) \ + if ( c == EOF && ferror( ldin ) ) \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ result = n; \ } \ - else if ( ((result = fread( buf, 1, max_size, yyin )) == 0) \ - && ferror( yyin ) ) \ - YY_FATAL_ERROR( "input in flex scanner failed" ); + else \ + { \ + errno=0; \ + while ( (result = fread(buf, 1, max_size, ldin))==0 && ferror(ldin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(ldin); \ + } \ + }\ +\ + #endif /* No semi-colon after return; correct usage is to write "yyterminate();" - @@ -1180,14 +1300,20 @@ YY_MALLOC_DECL #define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) #endif +/* end tables serialization structures and prototypes */ + /* Default declaration of generated scanner - a define so the user can * easily add parameters. */ #ifndef YY_DECL -#define YY_DECL int yylex YY_PROTO(( void )) -#endif +#define YY_DECL_IS_OURS 1 + +extern int ldlex (void); -/* Code executed at the beginning of each rule, after yytext and yyleng +#define YY_DECL int ldlex (void) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after ldtext and ldleng * have been set up. */ #ifndef YY_USER_ACTION @@ -1200,18 +1326,20 @@ YY_MALLOC_DECL #endif #define YY_RULE_SETUP \ - if ( yyleng > 0 ) \ - yy_current_buffer->yy_at_bol = \ - (yytext[yyleng - 1] == '\n'); \ + if ( ldleng > 0 ) \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = \ + (ldtext[ldleng - 1] == '\n'); \ YY_USER_ACTION +/** The main scanner function which does all the work. + */ YY_DECL - { +{ register yy_state_type yy_current_state; - register char *yy_cp = NULL, *yy_bp = NULL; + register char *yy_cp, *yy_bp; register int yy_act; - -#line 79 "/home/drepper/gnu/elfutils/src/ldlex.l" + +#line 96 "ldlex.l" if (unlikely (ld_scan_version_script)) { @@ -1219,149 +1347,153 @@ YY_DECL return kVERSION_SCRIPT; } -#line 1223 "ldlex.c" +#line 1351 "ldlex.c" - if ( yy_init ) + if ( !(yy_init) ) { - yy_init = 0; + (yy_init) = 1; #ifdef YY_USER_INIT YY_USER_INIT; #endif - if ( ! yy_start ) - yy_start = 1; /* first start state */ + if ( ! (yy_start) ) + (yy_start) = 1; /* first start state */ - if ( ! yyin ) - yyin = stdin; + if ( ! ldin ) + ldin = stdin; - if ( ! yyout ) - yyout = stdout; + if ( ! ldout ) + ldout = stdout; - if ( ! yy_current_buffer ) - yy_current_buffer = - yy_create_buffer( yyin, YY_BUF_SIZE ); + if ( ! YY_CURRENT_BUFFER ) { + ldensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + ld_create_buffer(ldin,YY_BUF_SIZE ); + } - yy_load_buffer_state(); + ld_load_buffer_state( ); } while ( 1 ) /* loops until end-of-file is reached */ { - yy_cp = yy_c_buf_p; + yy_cp = (yy_c_buf_p); - /* Support of yytext. */ - *yy_cp = yy_hold_char; + /* Support of ldtext. */ + *yy_cp = (yy_hold_char); /* yy_bp points to the position in yy_ch_buf of the start of * the current run. */ yy_bp = yy_cp; - yy_current_state = yy_start; + yy_current_state = (yy_start); yy_current_state += YY_AT_BOL(); - yy_state_ptr = yy_state_buf; - *yy_state_ptr++ = yy_current_state; yy_match: do { register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 211 ) + if ( yy_current_state >= 219 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; - *yy_state_ptr++ = yy_current_state; ++yy_cp; } - while ( yy_base[yy_current_state] != 2141 ); + while ( yy_current_state != 218 ); + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); yy_find_action: - yy_current_state = *--yy_state_ptr; - yy_lp = yy_accept[yy_current_state]; -find_rule: /* we branch to this label when backing up */ - for ( ; ; ) /* until we find what rule we matched */ - { - if ( yy_lp && yy_lp < yy_accept[yy_current_state + 1] ) - { - yy_act = yy_acclist[yy_lp]; - { - yy_full_match = yy_cp; - break; - } - } - --yy_cp; - yy_current_state = *--yy_state_ptr; - yy_lp = yy_accept[yy_current_state]; - } + yy_act = yy_accept[yy_current_state]; YY_DO_BEFORE_ACTION; - if ( yy_act != YY_END_OF_BUFFER ) + if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] ) { int yyl; - for ( yyl = 0; yyl < yyleng; ++yyl ) - if ( yytext[yyl] == '\n' ) - ++yylineno; + for ( yyl = 0; yyl < ldleng; ++yyl ) + if ( ldtext[yyl] == '\n' ) + + ldlineno++; +; } do_action: /* This label is used only to access EOF actions. */ - switch ( yy_act ) { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = (yy_hold_char); + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + goto yy_find_action; + case 1: -*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ -yy_c_buf_p = yy_cp = yy_bp + 6; -YY_DO_BEFORE_ACTION; /* set up yytext again */ +/* rule 1 can match eol */ +*yy_cp = (yy_hold_char); /* undo effects of setting up ldtext */ +(yy_c_buf_p) = yy_cp = yy_bp + 6; +YY_DO_BEFORE_ACTION; /* set up ldtext again */ YY_RULE_SETUP -#line 86 "/home/drepper/gnu/elfutils/src/ldlex.l" +#line 103 "ldlex.l" { BEGIN (handle_ifdef ()); } YY_BREAK case 2: -*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ -yy_c_buf_p = yy_cp = yy_bp + 5; -YY_DO_BEFORE_ACTION; /* set up yytext again */ +/* rule 2 can match eol */ +*yy_cp = (yy_hold_char); /* undo effects of setting up ldtext */ +(yy_c_buf_p) = yy_cp = yy_bp + 5; +YY_DO_BEFORE_ACTION; /* set up ldtext again */ YY_RULE_SETUP -#line 87 "/home/drepper/gnu/elfutils/src/ldlex.l" +#line 104 "ldlex.l" { eat_to_eol (true); push_state (skip_to_endif); BEGIN (IGNORE); } YY_BREAK case 3: -*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ -yy_c_buf_p = yy_cp = yy_bp + 8; -YY_DO_BEFORE_ACTION; /* set up yytext again */ +/* rule 3 can match eol */ +*yy_cp = (yy_hold_char); /* undo effects of setting up ldtext */ +(yy_c_buf_p) = yy_cp = yy_bp + 8; +YY_DO_BEFORE_ACTION; /* set up ldtext again */ YY_RULE_SETUP -#line 90 "/home/drepper/gnu/elfutils/src/ldlex.l" +#line 107 "ldlex.l" { eat_to_eol (false); push_state (skip_to_endif); BEGIN (IGNORE); } YY_BREAK case 4: -*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ -yy_c_buf_p = yy_cp = yy_bp + 6; -YY_DO_BEFORE_ACTION; /* set up yytext again */ +/* rule 4 can match eol */ +*yy_cp = (yy_hold_char); /* undo effects of setting up ldtext */ +(yy_c_buf_p) = yy_cp = yy_bp + 6; +YY_DO_BEFORE_ACTION; /* set up ldtext again */ YY_RULE_SETUP -#line 93 "/home/drepper/gnu/elfutils/src/ldlex.l" +#line 110 "ldlex.l" { eat_to_eol (true) ; } YY_BREAK case 5: -*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ -yy_c_buf_p = yy_cp = yy_bp + 6; -YY_DO_BEFORE_ACTION; /* set up yytext again */ +/* rule 5 can match eol */ +*yy_cp = (yy_hold_char); /* undo effects of setting up ldtext */ +(yy_c_buf_p) = yy_cp = yy_bp + 6; +YY_DO_BEFORE_ACTION; /* set up ldtext again */ YY_RULE_SETUP -#line 95 "/home/drepper/gnu/elfutils/src/ldlex.l" +#line 112 "ldlex.l" { eat_to_eol (false); push_state (skip_to_endif); } YY_BREAK case 6: -*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ -yy_c_buf_p = yy_cp = yy_bp + 5; -YY_DO_BEFORE_ACTION; /* set up yytext again */ +/* rule 6 can match eol */ +*yy_cp = (yy_hold_char); /* undo effects of setting up ldtext */ +(yy_c_buf_p) = yy_cp = yy_bp + 5; +YY_DO_BEFORE_ACTION; /* set up ldtext again */ YY_RULE_SETUP -#line 97 "/home/drepper/gnu/elfutils/src/ldlex.l" +#line 114 "ldlex.l" { eat_to_eol (true); assert (prepdepth > 0); if (prepstate[prepdepth - 1] == skip_if) @@ -1373,11 +1505,12 @@ YY_RULE_SETUP } YY_BREAK case 7: -*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ -yy_c_buf_p = yy_cp = yy_bp + 8; -YY_DO_BEFORE_ACTION; /* set up yytext again */ +/* rule 7 can match eol */ +*yy_cp = (yy_hold_char); /* undo effects of setting up ldtext */ +(yy_c_buf_p) = yy_cp = yy_bp + 8; +YY_DO_BEFORE_ACTION; /* set up ldtext again */ YY_RULE_SETUP -#line 106 "/home/drepper/gnu/elfutils/src/ldlex.l" +#line 123 "ldlex.l" { assert (prepdepth > 0); if (prepstate[prepdepth - 1] == skip_if) { @@ -1388,198 +1521,205 @@ YY_RULE_SETUP } YY_BREAK case 8: -*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ -yy_c_buf_p = yy_cp = yy_bp + 6; -YY_DO_BEFORE_ACTION; /* set up yytext again */ +/* rule 8 can match eol */ +*yy_cp = (yy_hold_char); /* undo effects of setting up ldtext */ +(yy_c_buf_p) = yy_cp = yy_bp + 6; +YY_DO_BEFORE_ACTION; /* set up ldtext again */ YY_RULE_SETUP -#line 114 "/home/drepper/gnu/elfutils/src/ldlex.l" +#line 131 "ldlex.l" { eat_to_eol (true); BEGIN (pop_state ()); } YY_BREAK case 9: +/* rule 9 can match eol */ YY_RULE_SETUP -#line 116 "/home/drepper/gnu/elfutils/src/ldlex.l" +#line 133 "ldlex.l" { /* nothing */ } YY_BREAK case 10: YY_RULE_SETUP -#line 119 "/home/drepper/gnu/elfutils/src/ldlex.l" +#line 136 "ldlex.l" { eat_comment (); } YY_BREAK case 11: YY_RULE_SETUP -#line 121 "/home/drepper/gnu/elfutils/src/ldlex.l" +#line 138 "ldlex.l" { return kALIGN; } YY_BREAK case 12: YY_RULE_SETUP -#line 122 "/home/drepper/gnu/elfutils/src/ldlex.l" -{ return kENTRY; } +#line 139 "ldlex.l" +{ return kAS_NEEDED; } YY_BREAK case 13: YY_RULE_SETUP -#line 123 "/home/drepper/gnu/elfutils/src/ldlex.l" -{ return kEXCLUDE_FILE; } +#line 140 "ldlex.l" +{ return kENTRY; } YY_BREAK case 14: YY_RULE_SETUP -#line 124 "/home/drepper/gnu/elfutils/src/ldlex.l" -{ return kGLOBAL; } +#line 141 "ldlex.l" +{ return kEXCLUDE_FILE; } YY_BREAK case 15: YY_RULE_SETUP -#line 125 "/home/drepper/gnu/elfutils/src/ldlex.l" -{ return kGROUP; } +#line 142 "ldlex.l" +{ return kGLOBAL; } YY_BREAK case 16: YY_RULE_SETUP -#line 126 "/home/drepper/gnu/elfutils/src/ldlex.l" -{ return kINPUT; } +#line 143 "ldlex.l" +{ return kGROUP; } YY_BREAK case 17: YY_RULE_SETUP -#line 127 "/home/drepper/gnu/elfutils/src/ldlex.l" -{ return kINTERP; } +#line 144 "ldlex.l" +{ return kINPUT; } YY_BREAK case 18: YY_RULE_SETUP -#line 128 "/home/drepper/gnu/elfutils/src/ldlex.l" -{ return kKEEP; } +#line 145 "ldlex.l" +{ return kINTERP; } YY_BREAK case 19: YY_RULE_SETUP -#line 129 "/home/drepper/gnu/elfutils/src/ldlex.l" -{ return kLOCAL; } +#line 146 "ldlex.l" +{ return kKEEP; } YY_BREAK case 20: YY_RULE_SETUP -#line 130 "/home/drepper/gnu/elfutils/src/ldlex.l" -{ return kOUTPUT_FORMAT; } +#line 147 "ldlex.l" +{ return kLOCAL; } YY_BREAK case 21: YY_RULE_SETUP -#line 131 "/home/drepper/gnu/elfutils/src/ldlex.l" -{ return kPAGESIZE; } +#line 148 "ldlex.l" +{ return kOUTPUT_FORMAT; } YY_BREAK case 22: YY_RULE_SETUP -#line 132 "/home/drepper/gnu/elfutils/src/ldlex.l" -{ return kPROVIDE; } +#line 149 "ldlex.l" +{ return kPAGESIZE; } YY_BREAK case 23: YY_RULE_SETUP -#line 133 "/home/drepper/gnu/elfutils/src/ldlex.l" -{ return kSEARCH_DIR; } +#line 150 "ldlex.l" +{ return kPROVIDE; } YY_BREAK case 24: YY_RULE_SETUP -#line 134 "/home/drepper/gnu/elfutils/src/ldlex.l" -{ return kSEGMENT; } +#line 151 "ldlex.l" +{ return kSEARCH_DIR; } YY_BREAK case 25: YY_RULE_SETUP -#line 135 "/home/drepper/gnu/elfutils/src/ldlex.l" -{ return kSIZEOF_HEADERS; } +#line 152 "ldlex.l" +{ return kSEGMENT; } YY_BREAK case 26: YY_RULE_SETUP -#line 136 "/home/drepper/gnu/elfutils/src/ldlex.l" -{ return kSORT; } +#line 153 "ldlex.l" +{ return kSIZEOF_HEADERS; } YY_BREAK case 27: YY_RULE_SETUP -#line 137 "/home/drepper/gnu/elfutils/src/ldlex.l" -{ return kVERSION; } +#line 154 "ldlex.l" +{ return kSORT; } YY_BREAK case 28: YY_RULE_SETUP -#line 139 "/home/drepper/gnu/elfutils/src/ldlex.l" +#line 155 "ldlex.l" +{ return kVERSION; } + YY_BREAK +case 29: +YY_RULE_SETUP +#line 157 "ldlex.l" { int cnt = 1 ; ldlval.num = 0; - while (cnt < yyleng - 1) - ldlval.num |= attrib_convert (yytext[cnt++]); + while (cnt < ldleng - 1) + ldlval.num |= attrib_convert (ldtext[cnt++]); return kMODE; } YY_BREAK -case 29: +case 30: YY_RULE_SETUP -#line 145 "/home/drepper/gnu/elfutils/src/ldlex.l" +#line 163 "ldlex.l" { return '{'; } YY_BREAK -case 30: +case 31: YY_RULE_SETUP -#line 146 "/home/drepper/gnu/elfutils/src/ldlex.l" +#line 164 "ldlex.l" { return '}'; } YY_BREAK -case 31: +case 32: YY_RULE_SETUP -#line 147 "/home/drepper/gnu/elfutils/src/ldlex.l" +#line 165 "ldlex.l" { return '('; } YY_BREAK -case 32: +case 33: YY_RULE_SETUP -#line 148 "/home/drepper/gnu/elfutils/src/ldlex.l" +#line 166 "ldlex.l" { return ')'; } YY_BREAK -case 33: +case 34: YY_RULE_SETUP -#line 149 "/home/drepper/gnu/elfutils/src/ldlex.l" +#line 167 "ldlex.l" { return ':'; } YY_BREAK -case 34: +case 35: YY_RULE_SETUP -#line 150 "/home/drepper/gnu/elfutils/src/ldlex.l" +#line 168 "ldlex.l" { return ';'; } YY_BREAK -case 35: +case 36: YY_RULE_SETUP -#line 151 "/home/drepper/gnu/elfutils/src/ldlex.l" +#line 169 "ldlex.l" { return '='; } YY_BREAK -case 36: +case 37: YY_RULE_SETUP -#line 152 "/home/drepper/gnu/elfutils/src/ldlex.l" +#line 170 "ldlex.l" { ldlval.op = exp_plus; return kADD_OP; } YY_BREAK -case 37: +case 38: YY_RULE_SETUP -#line 153 "/home/drepper/gnu/elfutils/src/ldlex.l" +#line 171 "ldlex.l" { ldlval.op = exp_minus; return kADD_OP; } YY_BREAK -case 38: +case 39: YY_RULE_SETUP -#line 154 "/home/drepper/gnu/elfutils/src/ldlex.l" +#line 172 "ldlex.l" { return '*'; } YY_BREAK -case 39: +case 40: YY_RULE_SETUP -#line 155 "/home/drepper/gnu/elfutils/src/ldlex.l" +#line 173 "ldlex.l" { ldlval.op = exp_div; return kMUL_OP; } YY_BREAK -case 40: +case 41: YY_RULE_SETUP -#line 156 "/home/drepper/gnu/elfutils/src/ldlex.l" +#line 174 "ldlex.l" { ldlval.op = exp_mod; return kMUL_OP; } YY_BREAK -case 41: +case 42: YY_RULE_SETUP -#line 157 "/home/drepper/gnu/elfutils/src/ldlex.l" +#line 175 "ldlex.l" { return '&'; } YY_BREAK -case 42: +case 43: YY_RULE_SETUP -#line 158 "/home/drepper/gnu/elfutils/src/ldlex.l" +#line 176 "ldlex.l" { return '|'; } YY_BREAK -case 43: +case 44: YY_RULE_SETUP -#line 160 "/home/drepper/gnu/elfutils/src/ldlex.l" +#line 178 "ldlex.l" { return ','; } YY_BREAK -case 44: +case 45: YY_RULE_SETUP -#line 162 "/home/drepper/gnu/elfutils/src/ldlex.l" +#line 180 "ldlex.l" { char *endp; - ldlval.num = strtoumax (yytext, &endp, 0); + ldlval.num = strtoumax (ldtext, &endp, 0); if (*endp != '\0') { if (tolower (*endp) == 'k') @@ -1592,63 +1732,64 @@ YY_RULE_SETUP } return kNUM; } YY_BREAK -case 45: +case 46: YY_RULE_SETUP -#line 176 "/home/drepper/gnu/elfutils/src/ldlex.l" +#line 194 "ldlex.l" { ldlval.str = obstack_strndup (&ld_state.smem, - yytext, yyleng); + ldtext, ldleng); return kID; } YY_BREAK -case 46: +case 47: YY_RULE_SETUP -#line 180 "/home/drepper/gnu/elfutils/src/ldlex.l" +#line 198 "ldlex.l" { ldlval.str = obstack_strndup (&ld_state.smem, - yytext, yyleng); + ldtext, ldleng); return kFILENAME; } YY_BREAK -case 47: +case 48: +/* rule 48 can match eol */ YY_RULE_SETUP -#line 184 "/home/drepper/gnu/elfutils/src/ldlex.l" +#line 202 "ldlex.l" { /* IGNORE */ } YY_BREAK -case 48: +case 49: YY_RULE_SETUP -#line 186 "/home/drepper/gnu/elfutils/src/ldlex.l" -{ invalid_char (*yytext); } +#line 204 "ldlex.l" +{ invalid_char (*ldtext); } YY_BREAK -case 49: +case 50: YY_RULE_SETUP -#line 188 "/home/drepper/gnu/elfutils/src/ldlex.l" +#line 206 "ldlex.l" ECHO; YY_BREAK -#line 1625 "ldlex.c" - case YY_STATE_EOF(INITIAL): - case YY_STATE_EOF(IGNORE): - yyterminate(); +#line 1766 "ldlex.c" +case YY_STATE_EOF(INITIAL): +case YY_STATE_EOF(IGNORE): + yyterminate(); case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ - int yy_amount_of_matched_text = (int) (yy_cp - yytext_ptr) - 1; + int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ - *yy_cp = yy_hold_char; + *yy_cp = (yy_hold_char); YY_RESTORE_YY_MORE_OFFSET - if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_NEW ) + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user - * just pointed yyin at a new source and called - * yylex(). If so, then we have to assure - * consistency between yy_current_buffer and our + * just pointed ldin at a new source and called + * ldlex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ - yy_n_chars = yy_current_buffer->yy_n_chars; - yy_current_buffer->yy_input_file = yyin; - yy_current_buffer->yy_buffer_status = YY_BUFFER_NORMAL; + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = ldin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; } /* Note that here we test for yy_c_buf_p "<=" to the position @@ -1658,13 +1799,13 @@ ECHO; * end-of-buffer state). Contrast this with the test * in input(). */ - if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) { /* This was really a NUL. */ yy_state_type yy_next_state; - yy_c_buf_p = yytext_ptr + yy_amount_of_matched_text; + (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; - yy_current_state = yy_get_previous_state(); + yy_current_state = yy_get_previous_state( ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have @@ -1677,41 +1818,42 @@ ECHO; yy_next_state = yy_try_NUL_trans( yy_current_state ); - yy_bp = yytext_ptr + YY_MORE_ADJ; + yy_bp = (yytext_ptr) + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ - yy_cp = ++yy_c_buf_p; + yy_cp = ++(yy_c_buf_p); yy_current_state = yy_next_state; goto yy_match; } else { - yy_cp = yy_c_buf_p; + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); goto yy_find_action; } } - else switch ( yy_get_next_buffer() ) + else switch ( yy_get_next_buffer( ) ) { case EOB_ACT_END_OF_FILE: { - yy_did_buffer_switch_on_eof = 0; + (yy_did_buffer_switch_on_eof) = 0; - if ( yywrap() ) + if ( ldwrap( ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up - * yytext, we can now set up + * ldtext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ - yy_c_buf_p = yytext_ptr + YY_MORE_ADJ; + (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; @@ -1719,30 +1861,30 @@ ECHO; else { - if ( ! yy_did_buffer_switch_on_eof ) + if ( ! (yy_did_buffer_switch_on_eof) ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: - yy_c_buf_p = - yytext_ptr + yy_amount_of_matched_text; + (yy_c_buf_p) = + (yytext_ptr) + yy_amount_of_matched_text; - yy_current_state = yy_get_previous_state(); + yy_current_state = yy_get_previous_state( ); - yy_cp = yy_c_buf_p; - yy_bp = yytext_ptr + YY_MORE_ADJ; + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: - yy_c_buf_p = - &yy_current_buffer->yy_ch_buf[yy_n_chars]; + (yy_c_buf_p) = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; - yy_current_state = yy_get_previous_state(); + yy_current_state = yy_get_previous_state( ); - yy_cp = yy_c_buf_p; - yy_bp = yytext_ptr + YY_MORE_ADJ; + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; goto yy_find_action; } break; @@ -1753,8 +1895,7 @@ ECHO; "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ - } /* end of yylex */ - +} /* end of ldlex */ /* yy_get_next_buffer - try to read in a new buffer * @@ -1763,21 +1904,20 @@ ECHO; * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ - -static int yy_get_next_buffer() - { - register char *dest = yy_current_buffer->yy_ch_buf; - register char *source = yytext_ptr; +static int yy_get_next_buffer (void) +{ + register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + register char *source = (yytext_ptr); register int number_to_move, i; int ret_val; - if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] ) + if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); - if ( yy_current_buffer->yy_fill_buffer == 0 ) + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ - if ( yy_c_buf_p - yytext_ptr - YY_MORE_ADJ == 1 ) + if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) { /* We matched a single character, the EOB, so * treat this as a final EOF. @@ -1797,34 +1937,30 @@ static int yy_get_next_buffer() /* Try to read more data. */ /* First move last chars to start of buffer. */ - number_to_move = (int) (yy_c_buf_p - yytext_ptr) - 1; + number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); - if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ - yy_current_buffer->yy_n_chars = yy_n_chars = 0; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; else { - int num_to_read = - yy_current_buffer->yy_buf_size - number_to_move - 1; + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ -#ifdef YY_USES_REJECT - YY_FATAL_ERROR( -"input buffer overflow, can't enlarge buffer because scanner uses REJECT" ); -#else /* just a shorter name for the current buffer */ - YY_BUFFER_STATE b = yy_current_buffer; + YY_BUFFER_STATE b = YY_CURRENT_BUFFER; int yy_c_buf_p_offset = - (int) (yy_c_buf_p - b->yy_ch_buf); + (int) ((yy_c_buf_p) - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { @@ -1837,8 +1973,7 @@ static int yy_get_next_buffer() b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ - yy_flex_realloc( (void *) b->yy_ch_buf, - b->yy_buf_size + 2 ); + ldrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); } else /* Can't grow it, we don't own it. */ @@ -1848,35 +1983,35 @@ static int yy_get_next_buffer() YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); - yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; - num_to_read = yy_current_buffer->yy_buf_size - + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; -#endif + } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ - YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]), - yy_n_chars, num_to_read ); + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + (yy_n_chars), (size_t) num_to_read ); - yy_current_buffer->yy_n_chars = yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } - if ( yy_n_chars == 0 ) + if ( (yy_n_chars) == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; - yyrestart( yyin ); + ldrestart(ldin ); } else { ret_val = EOB_ACT_LAST_MATCH; - yy_current_buffer->yy_buffer_status = + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } @@ -1884,150 +2019,150 @@ static int yy_get_next_buffer() else ret_val = EOB_ACT_CONTINUE_SCAN; - yy_n_chars += number_to_move; - yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR; - yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) ldrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + } - yytext_ptr = &yy_current_buffer->yy_ch_buf[0]; + (yy_n_chars) += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; - return ret_val; - } + (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + return ret_val; +} /* yy_get_previous_state - get the state just before the EOB char was reached */ -static yy_state_type yy_get_previous_state() - { + static yy_state_type yy_get_previous_state (void) +{ register yy_state_type yy_current_state; register char *yy_cp; - - yy_current_state = yy_start; + + yy_current_state = (yy_start); yy_current_state += YY_AT_BOL(); - yy_state_ptr = yy_state_buf; - *yy_state_ptr++ = yy_current_state; - for ( yy_cp = yytext_ptr + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp ) + for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) { register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 211 ) + if ( yy_current_state >= 219 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; - *yy_state_ptr++ = yy_current_state; } return yy_current_state; - } - +} /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ - -#ifdef YY_USE_PROTOS -static yy_state_type yy_try_NUL_trans( yy_state_type yy_current_state ) -#else -static yy_state_type yy_try_NUL_trans( yy_current_state ) -yy_state_type yy_current_state; -#endif - { + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) +{ register int yy_is_jam; + register char *yy_cp = (yy_c_buf_p); register YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 211 ) + if ( yy_current_state >= 219 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; - yy_is_jam = (yy_current_state == 210); - if ( ! yy_is_jam ) - *yy_state_ptr++ = yy_current_state; + yy_is_jam = (yy_current_state == 218); return yy_is_jam ? 0 : yy_current_state; - } - +} -#ifndef YY_NO_UNPUT -#ifdef YY_USE_PROTOS -static void yyunput( int c, register char *yy_bp ) -#else -static void yyunput( c, yy_bp ) -int c; -register char *yy_bp; -#endif - { - register char *yy_cp = yy_c_buf_p; + static void yyunput (int c, register char * yy_bp ) +{ + register char *yy_cp; + + yy_cp = (yy_c_buf_p); - /* undo effects of setting up yytext */ - *yy_cp = yy_hold_char; + /* undo effects of setting up ldtext */ + *yy_cp = (yy_hold_char); - if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) { /* need to shift things up to make room */ /* +2 for EOB chars. */ - register int number_to_move = yy_n_chars + 2; - register char *dest = &yy_current_buffer->yy_ch_buf[ - yy_current_buffer->yy_buf_size + 2]; + register int number_to_move = (yy_n_chars) + 2; + register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; register char *source = - &yy_current_buffer->yy_ch_buf[number_to_move]; + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; - while ( source > yy_current_buffer->yy_ch_buf ) + while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) *--dest = *--source; yy_cp += (int) (dest - source); yy_bp += (int) (dest - source); - yy_current_buffer->yy_n_chars = - yy_n_chars = yy_current_buffer->yy_buf_size; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size; - if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) YY_FATAL_ERROR( "flex scanner push-back overflow" ); } *--yy_cp = (char) c; - if ( c == '\n' ) - --yylineno; - - yytext_ptr = yy_bp; - yy_hold_char = *yy_cp; - yy_c_buf_p = yy_cp; - } -#endif /* ifndef YY_NO_UNPUT */ + if ( c == '\n' ){ + --ldlineno; + } + (yytext_ptr) = yy_bp; + (yy_hold_char) = *yy_cp; + (yy_c_buf_p) = yy_cp; +} #ifndef YY_NO_INPUT #ifdef __cplusplus -static int yyinput() + static int yyinput (void) #else -static int input() + static int input (void) #endif - { - int c; - *yy_c_buf_p = yy_hold_char; +{ + int c; + + *(yy_c_buf_p) = (yy_hold_char); - if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ - if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) /* This was really a NUL. */ - *yy_c_buf_p = '\0'; + *(yy_c_buf_p) = '\0'; else { /* need more input */ - int offset = yy_c_buf_p - yytext_ptr; - ++yy_c_buf_p; + int offset = (yy_c_buf_p) - (yytext_ptr); + ++(yy_c_buf_p); - switch ( yy_get_next_buffer() ) + switch ( yy_get_next_buffer( ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() @@ -2041,16 +2176,16 @@ static int input() */ /* Reset buffer status. */ - yyrestart( yyin ); + ldrestart(ldin ); - /* fall through */ + /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { - if ( yywrap() ) + if ( ldwrap( ) ) return EOF; - if ( ! yy_did_buffer_switch_on_eof ) + if ( ! (yy_did_buffer_switch_on_eof) ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(); @@ -2060,170 +2195,171 @@ static int input() } case EOB_ACT_CONTINUE_SCAN: - yy_c_buf_p = yytext_ptr + offset; + (yy_c_buf_p) = (yytext_ptr) + offset; break; } } } - c = *(unsigned char *) yy_c_buf_p; /* cast for 8-bit char's */ - *yy_c_buf_p = '\0'; /* preserve yytext */ - yy_hold_char = *++yy_c_buf_p; + c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ + *(yy_c_buf_p) = '\0'; /* preserve ldtext */ + (yy_hold_char) = *++(yy_c_buf_p); - yy_current_buffer->yy_at_bol = (c == '\n'); - if ( yy_current_buffer->yy_at_bol ) - ++yylineno; + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = (c == '\n'); + if ( YY_CURRENT_BUFFER_LVALUE->yy_at_bol ) + + ldlineno++; +; return c; - } -#endif /* YY_NO_INPUT */ - -#ifdef YY_USE_PROTOS -void yyrestart( FILE *input_file ) -#else -void yyrestart( input_file ) -FILE *input_file; -#endif - { - if ( ! yy_current_buffer ) - yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); +} +#endif /* ifndef YY_NO_INPUT */ - yy_init_buffer( yy_current_buffer, input_file ); - yy_load_buffer_state(); +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * + * @note This function does not reset the start condition to @c INITIAL . + */ + void ldrestart (FILE * input_file ) +{ + + if ( ! YY_CURRENT_BUFFER ){ + ldensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + ld_create_buffer(ldin,YY_BUF_SIZE ); } + ld_init_buffer(YY_CURRENT_BUFFER,input_file ); + ld_load_buffer_state( ); +} -#ifdef YY_USE_PROTOS -void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer ) -#else -void yy_switch_to_buffer( new_buffer ) -YY_BUFFER_STATE new_buffer; -#endif - { - if ( yy_current_buffer == new_buffer ) +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * + */ + void ld_switch_to_buffer (YY_BUFFER_STATE new_buffer ) +{ + + /* TODO. We should be able to replace this entire function body + * with + * ldpop_buffer_state(); + * ldpush_buffer_state(new_buffer); + */ + ldensure_buffer_stack (); + if ( YY_CURRENT_BUFFER == new_buffer ) return; - if ( yy_current_buffer ) + if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ - *yy_c_buf_p = yy_hold_char; - yy_current_buffer->yy_buf_pos = yy_c_buf_p; - yy_current_buffer->yy_n_chars = yy_n_chars; + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } - yy_current_buffer = new_buffer; - yy_load_buffer_state(); + YY_CURRENT_BUFFER_LVALUE = new_buffer; + ld_load_buffer_state( ); /* We don't actually know whether we did this switch during - * EOF (yywrap()) processing, but the only time this flag - * is looked at is after yywrap() is called, so it's safe + * EOF (ldwrap()) processing, but the only time this flag + * is looked at is after ldwrap() is called, so it's safe * to go ahead and always set it. */ - yy_did_buffer_switch_on_eof = 1; - } - - -#ifdef YY_USE_PROTOS -void yy_load_buffer_state( void ) -#else -void yy_load_buffer_state() -#endif - { - yy_n_chars = yy_current_buffer->yy_n_chars; - yytext_ptr = yy_c_buf_p = yy_current_buffer->yy_buf_pos; - yyin = yy_current_buffer->yy_input_file; - yy_hold_char = *yy_c_buf_p; - } + (yy_did_buffer_switch_on_eof) = 1; +} +static void ld_load_buffer_state (void) +{ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + ldin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + (yy_hold_char) = *(yy_c_buf_p); +} -#ifdef YY_USE_PROTOS -YY_BUFFER_STATE yy_create_buffer( FILE *file, int size ) -#else -YY_BUFFER_STATE yy_create_buffer( file, size ) -FILE *file; -int size; -#endif - { +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * + * @return the allocated buffer state. + */ + YY_BUFFER_STATE ld_create_buffer (FILE * file, int size ) +{ YY_BUFFER_STATE b; - - b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) ); + + b = (YY_BUFFER_STATE) ldalloc(sizeof( struct yy_buffer_state ) ); if ( ! b ) - YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + YY_FATAL_ERROR( "out of dynamic memory in ld_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ - b->yy_ch_buf = (char *) yy_flex_alloc( b->yy_buf_size + 2 ); + b->yy_ch_buf = (char *) ldalloc(b->yy_buf_size + 2 ); if ( ! b->yy_ch_buf ) - YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + YY_FATAL_ERROR( "out of dynamic memory in ld_create_buffer()" ); b->yy_is_our_buffer = 1; - yy_init_buffer( b, file ); + ld_init_buffer(b,file ); return b; - } - +} -#ifdef YY_USE_PROTOS -void yy_delete_buffer( YY_BUFFER_STATE b ) -#else -void yy_delete_buffer( b ) -YY_BUFFER_STATE b; -#endif - { +/** Destroy the buffer. + * @param b a buffer created with ld_create_buffer() + * + */ + void ld_delete_buffer (YY_BUFFER_STATE b ) +{ + if ( ! b ) return; - if ( b == yy_current_buffer ) - yy_current_buffer = (YY_BUFFER_STATE) 0; + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) - yy_flex_free( (void *) b->yy_ch_buf ); + ldfree((void *) b->yy_ch_buf ); - yy_flex_free( (void *) b ); - } - - - -#ifdef YY_USE_PROTOS -void yy_init_buffer( YY_BUFFER_STATE b, FILE *file ) -#else -void yy_init_buffer( b, file ) -YY_BUFFER_STATE b; -FILE *file; -#endif + ldfree((void *) b ); +} +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a ldrestart() or at EOF. + */ + static void ld_init_buffer (YY_BUFFER_STATE b, FILE * file ) - { - yy_flush_buffer( b ); +{ + int oerrno = errno; + + ld_flush_buffer(b ); b->yy_input_file = file; b->yy_fill_buffer = 1; -#if YY_ALWAYS_INTERACTIVE - b->yy_is_interactive = 1; -#else -#if YY_NEVER_INTERACTIVE - b->yy_is_interactive = 0; -#else - b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; -#endif -#endif - } - + /* If b is the current buffer, then ld_init_buffer was _probably_ + * called from ldrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } -#ifdef YY_USE_PROTOS -void yy_flush_buffer( YY_BUFFER_STATE b ) -#else -void yy_flush_buffer( b ) -YY_BUFFER_STATE b; -#endif + b->yy_is_interactive = 0; + + errno = oerrno; +} - { - if ( ! b ) +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * + */ + void ld_flush_buffer (YY_BUFFER_STATE b ) +{ + if ( ! b ) return; b->yy_n_chars = 0; @@ -2240,31 +2376,127 @@ YY_BUFFER_STATE b; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; - if ( b == yy_current_buffer ) - yy_load_buffer_state(); + if ( b == YY_CURRENT_BUFFER ) + ld_load_buffer_state( ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * + */ +void ldpush_buffer_state (YY_BUFFER_STATE new_buffer ) +{ + if (new_buffer == NULL) + return; + + ldensure_buffer_stack(); + + /* This block is copied from ld_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + (yy_buffer_stack_top)++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from ld_switch_to_buffer. */ + ld_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * + */ +void ldpop_buffer_state (void) +{ + if (!YY_CURRENT_BUFFER) + return; + + ld_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + if ((yy_buffer_stack_top) > 0) + --(yy_buffer_stack_top); + + if (YY_CURRENT_BUFFER) { + ld_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void ldensure_buffer_stack (void) +{ + int num_to_alloc; + + if (!(yy_buffer_stack)) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; + (yy_buffer_stack) = (struct yy_buffer_state**)ldalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in ldensure_buffer_stack()" ); + + memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + (yy_buffer_stack_max) = num_to_alloc; + (yy_buffer_stack_top) = 0; + return; } + if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ -#ifndef YY_NO_SCAN_BUFFER -#ifdef YY_USE_PROTOS -YY_BUFFER_STATE yy_scan_buffer( char *base, yy_size_t size ) -#else -YY_BUFFER_STATE yy_scan_buffer( base, size ) -char *base; -yy_size_t size; -#endif - { - YY_BUFFER_STATE b; + /* Increase the buffer to prepare for a possible push. */ + int grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = (yy_buffer_stack_max) + grow_size; + (yy_buffer_stack) = (struct yy_buffer_state**)ldrealloc + ((yy_buffer_stack), + num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in ldensure_buffer_stack()" ); + /* zero only the new slots.*/ + memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); + (yy_buffer_stack_max) = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE ld_scan_buffer (char * base, yy_size_t size ) +{ + YY_BUFFER_STATE b; + if ( size < 2 || base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ return 0; - b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) ); + b = (YY_BUFFER_STATE) ldalloc(sizeof( struct yy_buffer_state ) ); if ( ! b ) - YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + YY_FATAL_ERROR( "out of dynamic memory in ld_scan_buffer()" ); b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; @@ -2276,58 +2508,53 @@ yy_size_t size; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; - yy_switch_to_buffer( b ); + ld_switch_to_buffer(b ); return b; - } -#endif - - -#ifndef YY_NO_SCAN_STRING -#ifdef YY_USE_PROTOS -YY_BUFFER_STATE yy_scan_string( yyconst char *yy_str ) -#else -YY_BUFFER_STATE yy_scan_string( yy_str ) -yyconst char *yy_str; -#endif - { - int len; - for ( len = 0; yy_str[len]; ++len ) - ; - - return yy_scan_bytes( yy_str, len ); - } -#endif +} +/** Setup the input buffer state to scan a string. The next call to ldlex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * ld_scan_bytes() instead. + */ +YY_BUFFER_STATE ld_scan_string (yyconst char * yystr ) +{ + + return ld_scan_bytes(yystr,strlen(yystr) ); +} -#ifndef YY_NO_SCAN_BYTES -#ifdef YY_USE_PROTOS -YY_BUFFER_STATE yy_scan_bytes( yyconst char *bytes, int len ) -#else -YY_BUFFER_STATE yy_scan_bytes( bytes, len ) -yyconst char *bytes; -int len; -#endif - { +/** Setup the input buffer state to scan the given bytes. The next call to ldlex() will + * scan from a @e copy of @a bytes. + * @param bytes the byte buffer to scan + * @param len the number of bytes in the buffer pointed to by @a bytes. + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE ld_scan_bytes (yyconst char * yybytes, int _yybytes_len ) +{ YY_BUFFER_STATE b; char *buf; yy_size_t n; int i; - + /* Get memory for full buffer, including space for trailing EOB's. */ - n = len + 2; - buf = (char *) yy_flex_alloc( n ); + n = _yybytes_len + 2; + buf = (char *) ldalloc(n ); if ( ! buf ) - YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + YY_FATAL_ERROR( "out of dynamic memory in ld_scan_bytes()" ); - for ( i = 0; i < len; ++i ) - buf[i] = bytes[i]; + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; - buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR; + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; - b = yy_scan_buffer( buf, n ); + b = ld_scan_buffer(buf,n ); if ( ! b ) - YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + YY_FATAL_ERROR( "bad buffer in ld_scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. @@ -2335,148 +2562,199 @@ int len; b->yy_is_our_buffer = 1; return b; - } +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 #endif +static void yy_fatal_error (yyconst char* msg ) +{ + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} -#ifndef YY_NO_PUSH_STATE -#ifdef YY_USE_PROTOS -static void yy_push_state( int new_state ) -#else -static void yy_push_state( new_state ) -int new_state; -#endif - { - if ( yy_start_stack_ptr >= yy_start_stack_depth ) - { - yy_size_t new_size; +/* Redefine yyless() so it works in section 3 code. */ - yy_start_stack_depth += YY_START_STACK_INCR; - new_size = yy_start_stack_depth * sizeof( int ); +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up ldtext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + ldtext[ldleng] = (yy_hold_char); \ + (yy_c_buf_p) = ldtext + yyless_macro_arg; \ + (yy_hold_char) = *(yy_c_buf_p); \ + *(yy_c_buf_p) = '\0'; \ + ldleng = yyless_macro_arg; \ + } \ + while ( 0 ) - if ( ! yy_start_stack ) - yy_start_stack = (int *) yy_flex_alloc( new_size ); +/* Accessor methods (get/set functions) to struct members. */ - else - yy_start_stack = (int *) yy_flex_realloc( - (void *) yy_start_stack, new_size ); +/** Get the current line number. + * + */ +int ldget_lineno (void) +{ + + return ldlineno; +} - if ( ! yy_start_stack ) - YY_FATAL_ERROR( - "out of memory expanding start-condition stack" ); - } +/** Get the input stream. + * + */ +FILE *ldget_in (void) +{ + return ldin; +} - yy_start_stack[yy_start_stack_ptr++] = YY_START; +/** Get the output stream. + * + */ +FILE *ldget_out (void) +{ + return ldout; +} - BEGIN(new_state); - } -#endif +/** Get the length of the current token. + * + */ +int ldget_leng (void) +{ + return ldleng; +} +/** Get the current token. + * + */ -#ifndef YY_NO_POP_STATE -static void yy_pop_state() - { - if ( --yy_start_stack_ptr < 0 ) - YY_FATAL_ERROR( "start-condition stack underflow" ); +char *ldget_text (void) +{ + return ldtext; +} - BEGIN(yy_start_stack[yy_start_stack_ptr]); - } -#endif +/** Set the current line number. + * @param line_number + * + */ +void ldset_lineno (int line_number ) +{ + + ldlineno = line_number; +} +/** Set the input stream. This does not discard the current + * input buffer. + * @param in_str A readable stream. + * + * @see ld_switch_to_buffer + */ +void ldset_in (FILE * in_str ) +{ + ldin = in_str ; +} -#ifndef YY_NO_TOP_STATE -static int yy_top_state() - { - return yy_start_stack[yy_start_stack_ptr - 1]; - } -#endif +void ldset_out (FILE * out_str ) +{ + ldout = out_str ; +} -#ifndef YY_EXIT_FAILURE -#define YY_EXIT_FAILURE 2 -#endif +int ldget_debug (void) +{ + return ld_flex_debug; +} + +void ldset_debug (int bdebug ) +{ + ld_flex_debug = bdebug ; +} -#ifdef YY_USE_PROTOS -static void yy_fatal_error( yyconst char msg[] ) +static int yy_init_globals (void) +{ + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from ldlex_destroy(), so don't allocate here. + */ + + /* We do not touch ldlineno unless the option is enabled. */ + ldlineno = 1; + + (yy_buffer_stack) = 0; + (yy_buffer_stack_top) = 0; + (yy_buffer_stack_max) = 0; + (yy_c_buf_p) = (char *) 0; + (yy_init) = 0; + (yy_start) = 0; + +/* Defined in main.c */ +#ifdef YY_STDINIT + ldin = stdin; + ldout = stdout; #else -static void yy_fatal_error( msg ) -char msg[]; + ldin = (FILE *) 0; + ldout = (FILE *) 0; #endif - { - (void) fprintf( stderr, "%s\n", msg ); - exit( YY_EXIT_FAILURE ); - } + /* For future reference: Set errno on error, since we are called by + * ldlex_init() + */ + return 0; +} +/* ldlex_destroy is for both reentrant and non-reentrant scanners. */ +int ldlex_destroy (void) +{ + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + ld_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + ldpop_buffer_state(); + } -/* Redefine yyless() so it works in section 3 code. */ + /* Destroy the stack itself. */ + ldfree((yy_buffer_stack) ); + (yy_buffer_stack) = NULL; -#undef yyless -#define yyless(n) \ - do \ - { \ - /* Undo effects of setting up yytext. */ \ - yytext[yyleng] = yy_hold_char; \ - yy_c_buf_p = yytext + n; \ - yy_hold_char = *yy_c_buf_p; \ - *yy_c_buf_p = '\0'; \ - yyleng = n; \ - } \ - while ( 0 ) + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * ldlex() is called, initialization will occur. */ + yy_init_globals( ); + return 0; +} -/* Internal utility routines. */ +/* + * Internal utility routines. + */ #ifndef yytext_ptr -#ifdef YY_USE_PROTOS -static void yy_flex_strncpy( char *s1, yyconst char *s2, int n ) -#else -static void yy_flex_strncpy( s1, s2, n ) -char *s1; -yyconst char *s2; -int n; -#endif - { +static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) +{ register int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; - } +} #endif #ifdef YY_NEED_STRLEN -#ifdef YY_USE_PROTOS -static int yy_flex_strlen( yyconst char *s ) -#else -static int yy_flex_strlen( s ) -yyconst char *s; -#endif - { +static int yy_flex_strlen (yyconst char * s ) +{ register int n; for ( n = 0; s[n]; ++n ) ; return n; - } +} #endif - -#ifdef YY_USE_PROTOS -static void *yy_flex_alloc( yy_size_t size ) -#else -static void *yy_flex_alloc( size ) -yy_size_t size; -#endif - { +void *ldalloc (yy_size_t size ) +{ return (void *) malloc( size ); - } +} -#ifdef YY_USE_PROTOS -static void *yy_flex_realloc( void *ptr, yy_size_t size ) -#else -static void *yy_flex_realloc( ptr, size ) -void *ptr; -yy_size_t size; -#endif - { +void *ldrealloc (void * ptr, yy_size_t size ) +{ /* The cast to (char *) in the following accommodates both * implementations that use char* generic pointers, and those * that use void* generic pointers. It works with the latter @@ -2485,26 +2763,17 @@ yy_size_t size; * as though doing an assignment. */ return (void *) realloc( (char *) ptr, size ); - } +} -#ifdef YY_USE_PROTOS -static void yy_flex_free( void *ptr ) -#else -static void yy_flex_free( ptr ) -void *ptr; -#endif - { - free( ptr ); - } +void ldfree (void * ptr ) +{ + free( (char *) ptr ); /* see ldrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 206 "ldlex.l" -#if YY_MAIN -int main() - { - yylex(); - return 0; - } -#endif -#line 188 "/home/drepper/gnu/elfutils/src/ldlex.l" static void @@ -2549,13 +2818,13 @@ eat_to_eol (bool empty) break; if (c == '\n') { - ++yylineno; + ++ldlineno; break; } if (empty && ! isspace (c) && ! warned) { - error (0, 0, gettext ("%d: garbage at end of line"), yylineno); + error (0, 0, gettext ("%d: garbage at end of line"), ldlineno); warned = true; } } @@ -2579,7 +2848,7 @@ push_state (enum prepstate state) { if (prepdepth >= MAX_PREPDEPTH) error (EXIT_FAILURE, 0, gettext ("%d: conditionals nested too deep"), - yylineno); + ldlineno); prepstate[prepdepth++] = state; } @@ -2589,7 +2858,7 @@ static int pop_state (void) { if (prepdepth == 0) - error (0, 0, gettext ("%d: unexpected #endif"), yylineno); + error (0, 0, gettext ("%d: unexpected #endif"), ldlineno); else --prepdepth; @@ -2654,10 +2923,11 @@ invalid_char (int ch) error (0, 0, (isascii (ch) ? gettext ("invalid character '%c' at line %d; ignored") : gettext ("invalid character '\\%o' at line %d; ignored")), - ch, yylineno); + ch, ldlineno); } // Local Variables: // mode: C // End: + diff --git a/src/ldlex.l b/src/ldlex.l index 06ea6237..eb15c7be 100644 --- a/src/ldlex.l +++ b/src/ldlex.l @@ -1,16 +1,28 @@ %{ -/* Copyright (C) 2001, 2002, 2003, 2004 Red Hat, Inc. +/* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2008 Red Hat, Inc. + This file is part of Red Hat elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 2001. - This program is Open Source software; you can redistribute it and/or - modify it under the terms of the Open Software License version 1.0 as - published by the Open Source Initiative. + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. - You should have received a copy of the Open Software License along - with this program; if not, you may obtain a copy of the Open Software - License version 1.0 from http://www.opensource.org/licenses/osl.php or - by writing the Open Source Initiative c/o Lawrence Rosen, Esq., - 3001 King Ranch Road, Ukiah, CA 95482. */ + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ #ifdef HAVE_CONFIG_H # include <config.h> @@ -41,6 +53,11 @@ #undef fwrite #define fwrite(b, m, n, s) fwrite_unlocked (b, m, n, s) +/* ECHO must be redefined since the default implementation ignores + the return value of fwrite_unlocked. */ +#define ECHO do { size_t n__ __attribute__ ((unused)) \ + = fwrite (yytext, yyleng, 1, yyout); } while (0) + /* Defined in ld.c. */ extern int ld_scan_version_script; @@ -62,7 +79,7 @@ static int handle_ifdef (void); static void invalid_char (int ch); %} -ID [a-zA-Z0-9_.*?]+ +ID [a-zA-Z0-9_.*?][a-zA-Z0-9_.*?-]* FILENAMECHAR1 [a-zA-Z0-9_/.\\~] FILENAMECHAR [^][{}[:space:]():;]+ HEX 0[xX][0-9a-fA-F]+[kKmM]? @@ -119,6 +136,7 @@ WHITE [[:space:]]+ "/*" { eat_comment (); } ALIGN { return kALIGN; } +AS_NEEDED { return kAS_NEEDED; } ENTRY { return kENTRY; } EXCLUDE_FILE { return kEXCLUDE_FILE; } "global:" { return kGLOBAL; } diff --git a/src/ldscript.c b/src/ldscript.c index 0f70ca95..da72f29c 100644 --- a/src/ldscript.c +++ b/src/ldscript.c @@ -1,7 +1,9 @@ -/* A Bison parser, made by GNU Bison 1.875c. */ +/* A Bison parser, made by GNU Bison 2.3. */ -/* Skeleton parser for Yacc-like parsing with Bison, - Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. +/* Skeleton implementation for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -15,16 +17,24 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. -/* As a special exception, when this file is copied by Bison into a - Bison output file, you may use that output file without restriction. - This special exception was added by the Free Software Foundation - in version 1.24 of Bison. */ + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ -/* Written by Richard Stallman by simplifying the original so called - ``semantic'' parser. */ +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ /* All symbols defined below should begin with yy or YY, to avoid infringing on user name space. This should be done even for local @@ -36,6 +46,9 @@ /* Identify Bison output. */ #define YYBISON 1 +/* Bison version. */ +#define YYBISON_VERSION "2.3" + /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" @@ -45,8 +58,7 @@ /* Using locations. */ #define YYLSP_NEEDED 0 -/* If NAME_PREFIX is specified substitute the variables and functions - names. */ +/* Substitute the variable and function names. */ #define yyparse ldparse #define yylex ldlex #define yyerror lderror @@ -64,78 +76,93 @@ enum yytokentype { kADD_OP = 258, kALIGN = 259, - kENTRY = 260, - kEXCLUDE_FILE = 261, - kFILENAME = 262, - kGLOBAL = 263, - kGROUP = 264, - kID = 265, - kINPUT = 266, - kINTERP = 267, - kKEEP = 268, - kLOCAL = 269, - kMODE = 270, - kMUL_OP = 271, - kNUM = 272, - kOUTPUT_FORMAT = 273, - kPAGESIZE = 274, - kPROVIDE = 275, - kSEARCH_DIR = 276, - kSEGMENT = 277, - kSIZEOF_HEADERS = 278, - kSORT = 279, - kVERSION = 280, - kVERSION_SCRIPT = 281, - ADD_OP = 282, - MUL_OP = 283 + kAS_NEEDED = 260, + kENTRY = 261, + kEXCLUDE_FILE = 262, + kFILENAME = 263, + kGLOBAL = 264, + kGROUP = 265, + kID = 266, + kINPUT = 267, + kINTERP = 268, + kKEEP = 269, + kLOCAL = 270, + kMODE = 271, + kMUL_OP = 272, + kNUM = 273, + kOUTPUT_FORMAT = 274, + kPAGESIZE = 275, + kPROVIDE = 276, + kSEARCH_DIR = 277, + kSEGMENT = 278, + kSIZEOF_HEADERS = 279, + kSORT = 280, + kVERSION = 281, + kVERSION_SCRIPT = 282, + ADD_OP = 283, + MUL_OP = 284 }; #endif +/* Tokens. */ #define kADD_OP 258 #define kALIGN 259 -#define kENTRY 260 -#define kEXCLUDE_FILE 261 -#define kFILENAME 262 -#define kGLOBAL 263 -#define kGROUP 264 -#define kID 265 -#define kINPUT 266 -#define kINTERP 267 -#define kKEEP 268 -#define kLOCAL 269 -#define kMODE 270 -#define kMUL_OP 271 -#define kNUM 272 -#define kOUTPUT_FORMAT 273 -#define kPAGESIZE 274 -#define kPROVIDE 275 -#define kSEARCH_DIR 276 -#define kSEGMENT 277 -#define kSIZEOF_HEADERS 278 -#define kSORT 279 -#define kVERSION 280 -#define kVERSION_SCRIPT 281 -#define ADD_OP 282 -#define MUL_OP 283 +#define kAS_NEEDED 260 +#define kENTRY 261 +#define kEXCLUDE_FILE 262 +#define kFILENAME 263 +#define kGLOBAL 264 +#define kGROUP 265 +#define kID 266 +#define kINPUT 267 +#define kINTERP 268 +#define kKEEP 269 +#define kLOCAL 270 +#define kMODE 271 +#define kMUL_OP 272 +#define kNUM 273 +#define kOUTPUT_FORMAT 274 +#define kPAGESIZE 275 +#define kPROVIDE 276 +#define kSEARCH_DIR 277 +#define kSEGMENT 278 +#define kSIZEOF_HEADERS 279 +#define kSORT 280 +#define kVERSION 281 +#define kVERSION_SCRIPT 282 +#define ADD_OP 283 +#define MUL_OP 284 /* Copy the first part of user declarations. */ -#line 1 "/home/drepper/gnu/elfutils/src/ldscript.y" +#line 1 "ldscript.y" /* Parser for linker scripts. - Copyright (C) 2001, 2002, 2003, 2004 Red Hat, Inc. + Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 Red Hat, Inc. + This file is part of Red Hat elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 2001. - This program is Open Source software; you can redistribute it and/or - modify it under the terms of the Open Software License version 1.0 as - published by the Open Source Initiative. + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. - You should have received a copy of the Open Software License along - with this program; if not, you may obtain a copy of the Open Software - License version 1.0 from http://www.opensource.org/licenses/osl.php or - by writing the Open Source Initiative c/o Lawrence Rosen, Esq., - 3001 King Ranch Road, Ukiah, CA 95482. */ + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ #ifdef HAVE_CONFIG_H # include <config.h> @@ -170,6 +197,7 @@ static void new_segment (int mode, struct output_rule *output_rule); static struct filename_list *new_filename_listelem (const char *string); static void add_inputfiles (struct filename_list *fnames); static struct id_list *new_id_listelem (const char *str); + static struct filename_list *mark_as_needed (struct filename_list *listp); static struct version *new_version (struct id_list *local, struct id_list *global); static struct version *merge_versions (struct version *one, @@ -192,9 +220,15 @@ extern int yylex (void); # define YYERROR_VERBOSE 0 #endif -#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED) -#line 58 "/home/drepper/gnu/elfutils/src/ldscript.y" -typedef union YYSTYPE { +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +#line 71 "ldscript.y" +{ uintmax_t num; enum expression_tag op; char *str; @@ -207,9 +241,10 @@ typedef union YYSTYPE { struct filename_list *filename_list; struct version *version; struct id_list *id_list; -} YYSTYPE; -/* Line 191 of yacc.c. */ -#line 213 "ldscript.c" +} +/* Line 187 of yacc.c. */ +#line 247 "ldscript.c" + YYSTYPE; # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 # define YYSTYPE_IS_TRIVIAL 1 @@ -220,56 +255,171 @@ typedef union YYSTYPE { /* Copy the second part of user declarations. */ -/* Line 214 of yacc.c. */ -#line 225 "ldscript.c" +/* Line 216 of yacc.c. */ +#line 260 "ldscript.c" + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif -#if ! defined (yyoverflow) || YYERROR_VERBOSE +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif -# ifndef YYFREE -# define YYFREE free +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int # endif -# ifndef YYMALLOC -# define YYMALLOC malloc +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if YYENABLE_NLS +# if ENABLE_NLS +# include <libintl.h> /* INFRINGES ON USER NAME SPACE */ +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif # endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(e) ((void) (e)) +#else +# define YYUSE(e) /* empty */ +#endif + +/* Identity function, used to suppress warnings about constant conditions. */ +#ifndef lint +# define YYID(n) (n) +#else +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static int +YYID (int i) +#else +static int +YYID (i) + int i; +#endif +{ + return i; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE /* The parser invokes alloca or malloc; define the necessary symbols. */ # ifdef YYSTACK_USE_ALLOCA # if YYSTACK_USE_ALLOCA -# define YYSTACK_ALLOC alloca -# endif -# else -# if defined (alloca) || defined (_ALLOCA_H) -# define YYSTACK_ALLOC alloca -# else # ifdef __GNUC__ # define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include <alloca.h> /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include <malloc.h> /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif # endif # endif # endif # ifdef YYSTACK_ALLOC - /* Pacify GCC's `empty if-body' warning. */ -# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) -# else -# if defined (__STDC__) || defined (__cplusplus) -# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ -# define YYSIZE_T size_t + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ # endif +# else # define YYSTACK_ALLOC YYMALLOC # define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined _STDLIB_H \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif # endif -#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */ +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ -#if (! defined (yyoverflow) \ - && (! defined (__cplusplus) \ - || (defined (YYSTYPE_IS_TRIVIAL) && YYSTYPE_IS_TRIVIAL))) +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) /* A type that is properly aligned for any stack member. */ union yyalloc { - short yyss; + yytype_int16 yyss; YYSTYPE yyvs; }; @@ -279,24 +429,24 @@ union yyalloc /* The size of an array large to enough to hold all stacks, each with N elements. */ # define YYSTACK_BYTES(N) \ - ((N) * (sizeof (short) + sizeof (YYSTYPE)) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + YYSTACK_GAP_MAXIMUM) /* Copy COUNT objects from FROM to TO. The source and destination do not overlap. */ # ifndef YYCOPY -# if defined (__GNUC__) && 1 < __GNUC__ +# if defined __GNUC__ && 1 < __GNUC__ # define YYCOPY(To, From, Count) \ __builtin_memcpy (To, From, (Count) * sizeof (*(From))) # else # define YYCOPY(To, From, Count) \ do \ { \ - register YYSIZE_T yyi; \ + YYSIZE_T yyi; \ for (yyi = 0; yyi < (Count); yyi++) \ (To)[yyi] = (From)[yyi]; \ } \ - while (0) + while (YYID (0)) # endif # endif @@ -314,53 +464,47 @@ union yyalloc yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ yyptr += yynewbytes / sizeof (*yyptr); \ } \ - while (0) + while (YYID (0)) #endif -#if defined (__STDC__) || defined (__cplusplus) - typedef signed char yysigned_char; -#else - typedef short yysigned_char; -#endif - -/* YYFINAL -- State number of the termination state. */ -#define YYFINAL 30 +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 32 /* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 198 +#define YYLAST 226 -/* YYNTOKENS -- Number of terminals. */ -#define YYNTOKENS 39 -/* YYNNTS -- Number of nonterminals. */ -#define YYNNTS 22 -/* YYNRULES -- Number of rules. */ -#define YYNRULES 62 -/* YYNRULES -- Number of states. */ -#define YYNSTATES 146 +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 40 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 23 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 66 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 159 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 -#define YYMAXUTOK 283 +#define YYMAXUTOK 284 -#define YYTRANSLATE(YYX) \ +#define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) /* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ -static const unsigned char yytranslate[] = +static const yytype_uint8 yytranslate[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 28, 2, - 32, 33, 30, 2, 38, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 34, - 2, 37, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 29, 2, + 33, 34, 31, 2, 39, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 35, + 2, 38, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 35, 27, 36, 2, 2, 2, 2, + 2, 2, 2, 36, 28, 37, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, @@ -376,80 +520,82 @@ static const unsigned char yytranslate[] = 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, 29, 31 + 25, 26, 27, 30, 32 }; #if YYDEBUG /* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in YYRHS. */ -static const unsigned char yyprhs[] = +static const yytype_uint8 yyprhs[] = { 0, 0, 3, 5, 8, 11, 13, 19, 25, 31, - 37, 43, 49, 54, 59, 64, 69, 72, 74, 77, - 82, 85, 89, 96, 99, 101, 103, 108, 111, 117, - 119, 124, 129, 130, 135, 139, 143, 147, 151, 155, - 159, 161, 163, 165, 167, 171, 173, 175, 176, 179, - 181, 186, 192, 199, 202, 204, 207, 210, 214, 217, - 219, 221, 223 + 37, 43, 49, 54, 59, 64, 69, 74, 77, 79, + 82, 87, 90, 94, 101, 104, 106, 108, 113, 116, + 122, 124, 129, 134, 135, 140, 144, 148, 152, 156, + 160, 164, 166, 168, 170, 172, 176, 178, 180, 181, + 186, 191, 193, 196, 198, 203, 209, 216, 219, 221, + 224, 227, 231, 234, 236, 238, 240 }; -/* YYRHS -- A `-1'-separated list of the rules' RHS. */ -static const yysigned_char yyrhs[] = +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yytype_int8 yyrhs[] = { - 40, 0, -1, 41, -1, 26, 54, -1, 41, 42, - -1, 42, -1, 5, 32, 10, 33, 34, -1, 21, - 32, 59, 33, 34, -1, 19, 32, 17, 33, 34, - -1, 12, 32, 59, 33, 34, -1, 22, 15, 35, - 43, 36, -1, 22, 1, 35, 43, 36, -1, 9, - 32, 52, 33, -1, 11, 32, 52, 33, -1, 25, - 35, 54, 36, -1, 18, 32, 59, 33, -1, 43, - 44, -1, 44, -1, 45, 34, -1, 10, 35, 46, - 36, -1, 10, 34, -1, 10, 37, 51, -1, 20, - 32, 10, 37, 51, 33, -1, 46, 47, -1, 47, - -1, 48, -1, 13, 32, 48, 33, -1, 45, 34, - -1, 60, 32, 50, 49, 33, -1, 10, -1, 24, - 32, 10, 33, -1, 6, 32, 59, 33, -1, -1, - 4, 32, 51, 33, -1, 32, 51, 33, -1, 51, - 30, 51, -1, 51, 16, 51, -1, 51, 3, 51, - -1, 51, 28, 51, -1, 51, 27, 51, -1, 17, - -1, 10, -1, 23, -1, 19, -1, 52, 53, 59, - -1, 59, -1, 38, -1, -1, 54, 55, -1, 55, - -1, 35, 56, 36, 34, -1, 59, 35, 56, 36, - 34, -1, 59, 35, 56, 36, 59, 34, -1, 56, - 57, -1, 57, -1, 8, 58, -1, 14, 58, -1, - 58, 60, 34, -1, 60, 34, -1, 7, -1, 10, - -1, 59, -1, 30, -1 + 41, 0, -1, 42, -1, 27, 56, -1, 42, 43, + -1, 43, -1, 6, 33, 11, 34, 35, -1, 22, + 33, 61, 34, 35, -1, 20, 33, 18, 34, 35, + -1, 13, 33, 61, 34, 35, -1, 23, 16, 36, + 44, 37, -1, 23, 1, 36, 44, 37, -1, 10, + 33, 53, 34, -1, 12, 33, 53, 34, -1, 5, + 33, 53, 34, -1, 26, 36, 56, 37, -1, 19, + 33, 61, 34, -1, 44, 45, -1, 45, -1, 46, + 35, -1, 11, 36, 47, 37, -1, 11, 35, -1, + 11, 38, 52, -1, 21, 33, 11, 38, 52, 34, + -1, 47, 48, -1, 48, -1, 49, -1, 14, 33, + 49, 34, -1, 46, 35, -1, 62, 33, 51, 50, + 34, -1, 11, -1, 25, 33, 11, 34, -1, 7, + 33, 61, 34, -1, -1, 4, 33, 52, 34, -1, + 33, 52, 34, -1, 52, 31, 52, -1, 52, 17, + 52, -1, 52, 3, 52, -1, 52, 29, 52, -1, + 52, 28, 52, -1, 18, -1, 11, -1, 24, -1, + 20, -1, 53, 54, 55, -1, 55, -1, 39, -1, + -1, 10, 33, 53, 34, -1, 5, 33, 53, 34, + -1, 61, -1, 56, 57, -1, 57, -1, 36, 58, + 37, 35, -1, 61, 36, 58, 37, 35, -1, 61, + 36, 58, 37, 61, 35, -1, 58, 59, -1, 59, + -1, 9, 60, -1, 15, 60, -1, 60, 62, 35, + -1, 62, 35, -1, 8, -1, 11, -1, 61, -1, + 31, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ -static const unsigned short yyrline[] = +static const yytype_uint16 yyrline[] = { - 0, 128, 128, 129, 133, 134, 137, 142, 146, 151, - 156, 160, 166, 177, 179, 181, 185, 190, 194, 199, - 211, 235, 237, 241, 246, 250, 255, 262, 269, 280, - 282, 286, 289, 292, 297, 299, 305, 311, 317, 323, - 329, 334, 339, 341, 345, 351, 355, 356, 359, 364, - 368, 374, 380, 389, 391, 395, 397, 402, 408, 412, - 414, 418, 420 + 0, 143, 143, 144, 148, 149, 152, 157, 161, 166, + 172, 176, 182, 193, 195, 197, 199, 203, 208, 212, + 217, 229, 253, 255, 259, 264, 268, 273, 280, 287, + 298, 300, 304, 307, 310, 315, 317, 323, 329, 335, + 341, 347, 352, 357, 359, 363, 368, 372, 373, 376, + 387, 389, 394, 399, 403, 409, 415, 424, 426, 430, + 432, 437, 443, 447, 449, 453, 455 }; #endif -#if YYDEBUG || YYERROR_VERBOSE -/* YYTNME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. - First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = { - "$end", "error", "$undefined", "kADD_OP", "kALIGN", "kENTRY", - "kEXCLUDE_FILE", "kFILENAME", "kGLOBAL", "kGROUP", "kID", "kINPUT", - "kINTERP", "kKEEP", "kLOCAL", "kMODE", "kMUL_OP", "kNUM", + "$end", "error", "$undefined", "kADD_OP", "kALIGN", "kAS_NEEDED", + "kENTRY", "kEXCLUDE_FILE", "kFILENAME", "kGLOBAL", "kGROUP", "kID", + "kINPUT", "kINTERP", "kKEEP", "kLOCAL", "kMODE", "kMUL_OP", "kNUM", "kOUTPUT_FORMAT", "kPAGESIZE", "kPROVIDE", "kSEARCH_DIR", "kSEGMENT", "kSIZEOF_HEADERS", "kSORT", "kVERSION", "kVERSION_SCRIPT", "'|'", "'&'", "ADD_OP", "'*'", "MUL_OP", "'('", "')'", "';'", "'{'", "'}'", "'='", "','", "$accept", "script_or_version", "file", "content", "outputsections", "outputsection", "assignment", "inputsections", "inputsection", "sectionname", "sort_opt_name", "exclude_opt", "expr", - "filename_id_list", "comma_opt", "versionlist", "version", - "version_stmt_list", "version_stmt", "filename_id_star_list", + "filename_id_list", "comma_opt", "filename_id_listelem", "versionlist", + "version", "version_stmt_list", "version_stmt", "filename_id_star_list", "filename_id", "filename_id_star", 0 }; #endif @@ -457,97 +603,99 @@ static const char *const yytname[] = # ifdef YYPRINT /* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to token YYLEX-NUM. */ -static const unsigned short yytoknum[] = +static const yytype_uint16 yytoknum[] = { 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, - 275, 276, 277, 278, 279, 280, 281, 124, 38, 282, - 42, 283, 40, 41, 59, 123, 125, 61, 44 + 275, 276, 277, 278, 279, 280, 281, 282, 124, 38, + 283, 42, 284, 40, 41, 59, 123, 125, 61, 44 }; # endif /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ -static const unsigned char yyr1[] = +static const yytype_uint8 yyr1[] = { - 0, 39, 40, 40, 41, 41, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 43, 43, 44, 44, - 44, 45, 45, 46, 46, 47, 47, 47, 48, 49, - 49, 50, 50, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 52, 52, 53, 53, 54, 54, - 55, 55, 55, 56, 56, 57, 57, 58, 58, 59, - 59, 60, 60 + 0, 40, 41, 41, 42, 42, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 44, 44, 45, + 45, 45, 46, 46, 47, 47, 48, 48, 48, 49, + 50, 50, 51, 51, 52, 52, 52, 52, 52, 52, + 52, 52, 52, 52, 52, 53, 53, 54, 54, 55, + 55, 55, 56, 56, 57, 57, 57, 58, 58, 59, + 59, 60, 60, 61, 61, 62, 62 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ -static const unsigned char yyr2[] = +static const yytype_uint8 yyr2[] = { 0, 2, 1, 2, 2, 1, 5, 5, 5, 5, - 5, 5, 4, 4, 4, 4, 2, 1, 2, 4, - 2, 3, 6, 2, 1, 1, 4, 2, 5, 1, - 4, 4, 0, 4, 3, 3, 3, 3, 3, 3, - 1, 1, 1, 1, 3, 1, 1, 0, 2, 1, - 4, 5, 6, 2, 1, 2, 2, 3, 2, 1, - 1, 1, 1 + 5, 5, 4, 4, 4, 4, 4, 2, 1, 2, + 4, 2, 3, 6, 2, 1, 1, 4, 2, 5, + 1, 4, 4, 0, 4, 3, 3, 3, 3, 3, + 3, 1, 1, 1, 1, 3, 1, 1, 0, 4, + 4, 1, 2, 1, 4, 5, 6, 2, 1, 2, + 2, 3, 2, 1, 1, 1, 1 }; /* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state STATE-NUM when YYTABLE doesn't specify something else to do. Zero means the default is an error. */ -static const unsigned char yydefact[] = +static const yytype_uint8 yydefact[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 2, 5, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 59, 60, 0, 3, 49, 0, - 1, 4, 0, 47, 45, 47, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 54, 48, 0, 0, - 12, 46, 0, 13, 0, 15, 0, 0, 0, 0, - 0, 17, 0, 0, 14, 62, 55, 61, 0, 56, - 0, 53, 0, 6, 44, 9, 8, 7, 20, 0, - 0, 0, 11, 16, 18, 10, 0, 58, 50, 0, - 60, 0, 0, 0, 24, 25, 0, 0, 41, 40, - 43, 42, 0, 21, 0, 57, 51, 0, 0, 27, - 19, 23, 32, 0, 0, 0, 0, 0, 0, 0, - 0, 52, 0, 0, 0, 0, 34, 37, 36, 39, - 38, 35, 0, 26, 0, 29, 0, 0, 33, 22, - 0, 0, 28, 31, 0, 30 + 0, 0, 0, 2, 5, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 63, 64, 0, 3, + 53, 0, 1, 4, 0, 0, 48, 46, 51, 0, + 48, 48, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 58, 52, 0, 0, 0, 14, 47, 0, + 0, 12, 13, 0, 16, 0, 0, 0, 0, 0, + 18, 0, 0, 15, 66, 59, 65, 0, 60, 0, + 57, 0, 48, 48, 45, 6, 9, 8, 7, 21, + 0, 0, 0, 11, 17, 19, 10, 0, 62, 54, + 0, 50, 49, 64, 0, 0, 0, 25, 26, 0, + 0, 42, 41, 44, 43, 0, 22, 0, 61, 55, + 0, 0, 28, 20, 24, 33, 0, 0, 0, 0, + 0, 0, 0, 0, 56, 0, 0, 0, 0, 35, + 38, 37, 40, 39, 36, 0, 27, 0, 30, 0, + 0, 34, 23, 0, 0, 29, 32, 0, 31 }; -/* YYDEFGOTO[NTERM-NUM]. */ -static const short yydefgoto[] = +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int16 yydefgoto[] = { - -1, 11, 12, 13, 60, 61, 62, 93, 94, 95, - 137, 124, 103, 33, 52, 27, 28, 45, 46, 66, - 67, 96 + -1, 12, 13, 14, 69, 70, 71, 106, 107, 108, + 150, 137, 116, 36, 59, 37, 29, 30, 51, 52, + 75, 76, 109 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ -#define YYPACT_NINF -41 -static const short yypact[] = +#define YYPACT_NINF -86 +static const yytype_int16 yypact[] = { - 107, -28, -20, -13, 34, 77, 85, 88, 91, 33, - 38, 123, 125, -41, 117, 52, 52, 52, 52, 114, - 52, 100, 103, 38, -41, -41, 96, 38, -41, 110, - -41, -41, 115, 64, -41, 67, 116, 118, 120, 127, - 1, 1, 28, 84, 84, 36, -41, -41, 96, 128, - -41, -41, 52, -41, 129, -41, 130, 131, 105, 134, - 75, -41, 133, 79, -41, -41, 84, -41, 135, 84, - 136, -41, 41, -41, -41, -41, -41, -41, -41, 83, - 48, 151, -41, -41, -41, -41, 137, -41, -41, 44, - 138, 140, 139, 17, -41, -41, 142, 144, -41, -41, - -41, -41, 48, 54, 141, -41, -41, 143, 84, -41, - -41, -41, 162, 48, -2, 48, 48, 48, 48, 48, - 48, -41, 146, 148, 97, 6, -41, 54, 54, 58, - 53, -1, 13, -41, 52, -41, 149, 150, -41, -41, - 152, 172, -41, -41, 153, -41 + 111, -18, -14, 23, 45, 70, 75, 85, 92, 97, + 91, 19, 128, 134, -86, 162, 96, 162, 162, 5, + 5, 123, 5, 93, 99, 19, -86, -86, 117, 19, + -86, 115, -86, -86, 125, 144, 71, -86, -86, 145, + 116, 135, 147, 148, 149, 150, 101, 101, 14, 83, + 83, 55, -86, -86, 117, 162, 162, -86, -86, 162, + 133, -86, -86, 143, -86, 151, 152, 107, 155, 63, + -86, 154, 74, -86, -86, 83, -86, 156, 83, 157, + -86, 56, 137, 141, -86, -86, -86, -86, -86, -86, + 88, 48, 174, -86, -86, -86, -86, 158, -86, -86, + 69, -86, -86, 159, 161, 160, 12, -86, -86, 163, + 165, -86, -86, -86, -86, 48, 59, 164, -86, -86, + 166, 83, -86, -86, -86, 183, 48, 0, 48, 48, + 48, 48, 48, 48, -86, 169, 167, 90, 7, -86, + 59, 59, 44, 66, 103, 29, -86, 5, -86, 171, + 172, -86, -86, 173, 188, -86, -86, 175, -86 }; /* YYPGOTO[NTERM-NUM]. */ -static const short yypgoto[] = +static const yytype_int16 yypgoto[] = { - -41, -41, -41, 175, 147, -40, 29, -41, 98, 76, - -41, -41, 39, 173, -41, 167, -24, 145, 15, 154, - -10, 32 + -86, -86, -86, 192, 168, 80, -85, -86, 102, 89, + -86, -86, 33, -16, -86, 153, 186, 38, 170, -39, + 176, -11, 4 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If @@ -555,91 +703,82 @@ static const short yypgoto[] = number is the opposite. If zero, do what YYDEFACT says. If YYTABLE_NINF, syntax error. */ #define YYTABLE_NINF -1 -static const unsigned char yytable[] = +static const yytype_uint8 yytable[] = { - 29, 115, 115, 47, 14, 34, 34, 36, 37, 115, - 39, 58, 15, 29, 116, 116, 115, 29, 47, 16, - 83, 59, 116, 83, 24, 117, 118, 90, 119, 116, - 91, 126, 29, 117, 118, 24, 119, 59, 25, 138, - 117, 118, 74, 119, 43, 24, 139, 65, 25, 43, - 44, 24, 97, 110, 25, 44, 115, 115, 98, 24, - 71, 115, 25, 26, 64, 99, 17, 100, 23, 116, - 116, 101, 70, 26, 116, 68, 68, 89, 106, 107, - 102, 117, 118, 119, 119, 58, 118, 71, 119, 58, - 24, 24, 21, 90, 25, 59, 91, 50, 86, 59, - 53, 86, 51, 59, 43, 51, 22, 135, 92, 18, - 44, 82, 1, 65, 65, 85, 2, 19, 3, 4, - 20, 136, 92, 30, 140, 5, 6, 32, 7, 8, - 1, 38, 9, 10, 2, 40, 3, 4, 41, 78, - 79, 114, 80, 5, 6, 48, 7, 8, 49, 54, - 9, 55, 125, 56, 127, 128, 129, 130, 131, 132, - 57, 104, 73, 75, 76, 77, 81, 84, 123, 87, - 88, 105, 108, 109, 112, 80, 113, 121, 120, 133, - 134, 141, 144, 142, 122, 143, 145, 31, 63, 35, - 42, 111, 0, 72, 0, 0, 0, 0, 69 + 31, 40, 41, 128, 38, 105, 38, 38, 42, 43, + 128, 45, 80, 26, 31, 15, 27, 129, 31, 16, + 26, 105, 26, 103, 129, 27, 104, 26, 130, 131, + 27, 132, 128, 68, 139, 130, 131, 31, 132, 82, + 83, 151, 80, 74, 38, 38, 129, 128, 38, 123, + 28, 73, 110, 77, 77, 28, 17, 130, 131, 111, + 132, 129, 128, 152, 49, 49, 112, 53, 113, 128, + 50, 50, 114, 131, 67, 132, 129, 26, 18, 97, + 27, 115, 97, 129, 68, 67, 53, 130, 131, 120, + 132, 26, 79, 100, 27, 68, 26, 132, 23, 103, + 93, 148, 104, 19, 119, 57, 128, 39, 20, 68, + 58, 96, 67, 24, 74, 149, 1, 2, 21, 74, + 129, 3, 68, 4, 5, 22, 49, 25, 32, 46, + 6, 7, 50, 8, 9, 47, 153, 10, 11, 1, + 2, 44, 89, 90, 3, 91, 4, 5, 127, 94, + 61, 54, 94, 6, 7, 58, 8, 9, 55, 138, + 10, 140, 141, 142, 143, 144, 145, 34, 85, 62, + 26, 101, 35, 27, 58, 102, 58, 56, 86, 60, + 58, 63, 64, 65, 66, 117, 87, 88, 92, 95, + 136, 98, 99, 118, 121, 122, 125, 91, 126, 157, + 147, 134, 133, 146, 154, 33, 155, 156, 124, 158, + 135, 48, 84, 0, 0, 72, 0, 0, 0, 0, + 0, 0, 0, 0, 81, 0, 78 }; -static const short yycheck[] = +static const yytype_int16 yycheck[] = { - 10, 3, 3, 27, 32, 15, 16, 17, 18, 3, - 20, 10, 32, 23, 16, 16, 3, 27, 42, 32, - 60, 20, 16, 63, 7, 27, 28, 10, 30, 16, - 13, 33, 42, 27, 28, 7, 30, 20, 10, 33, - 27, 28, 52, 30, 8, 7, 33, 30, 10, 8, - 14, 7, 4, 36, 10, 14, 3, 3, 10, 7, - 45, 3, 10, 35, 36, 17, 32, 19, 35, 16, - 16, 23, 36, 35, 16, 43, 44, 36, 34, 89, - 32, 27, 28, 30, 30, 10, 28, 72, 30, 10, - 7, 7, 1, 10, 10, 20, 13, 33, 66, 20, - 33, 69, 38, 20, 8, 38, 15, 10, 79, 32, - 14, 36, 5, 30, 30, 36, 9, 32, 11, 12, - 32, 24, 93, 0, 134, 18, 19, 10, 21, 22, - 5, 17, 25, 26, 9, 35, 11, 12, 35, 34, - 35, 102, 37, 18, 19, 35, 21, 22, 33, 33, - 25, 33, 113, 33, 115, 116, 117, 118, 119, 120, - 33, 10, 34, 34, 34, 34, 32, 34, 6, 34, - 34, 34, 32, 34, 32, 37, 32, 34, 37, 33, - 32, 32, 10, 33, 108, 33, 33, 12, 41, 16, - 23, 93, -1, 48, -1, -1, -1, -1, 44 + 11, 17, 18, 3, 15, 90, 17, 18, 19, 20, + 3, 22, 51, 8, 25, 33, 11, 17, 29, 33, + 8, 106, 8, 11, 17, 11, 14, 8, 28, 29, + 11, 31, 3, 21, 34, 28, 29, 48, 31, 55, + 56, 34, 81, 31, 55, 56, 17, 3, 59, 37, + 36, 37, 4, 49, 50, 36, 33, 28, 29, 11, + 31, 17, 3, 34, 9, 9, 18, 29, 20, 3, + 15, 15, 24, 29, 11, 31, 17, 8, 33, 75, + 11, 33, 78, 17, 21, 11, 48, 28, 29, 100, + 31, 8, 37, 37, 11, 21, 8, 31, 1, 11, + 37, 11, 14, 33, 35, 34, 3, 11, 33, 21, + 39, 37, 11, 16, 31, 25, 5, 6, 33, 31, + 17, 10, 21, 12, 13, 33, 9, 36, 0, 36, + 19, 20, 15, 22, 23, 36, 147, 26, 27, 5, + 6, 18, 35, 36, 10, 38, 12, 13, 115, 69, + 34, 36, 72, 19, 20, 39, 22, 23, 33, 126, + 26, 128, 129, 130, 131, 132, 133, 5, 35, 34, + 8, 34, 10, 11, 39, 34, 39, 33, 35, 34, + 39, 34, 34, 34, 34, 11, 35, 35, 33, 35, + 7, 35, 35, 35, 33, 35, 33, 38, 33, 11, + 33, 35, 38, 34, 33, 13, 34, 34, 106, 34, + 121, 25, 59, -1, -1, 47, -1, -1, -1, -1, + -1, -1, -1, -1, 54, -1, 50 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ -static const unsigned char yystos[] = +static const yytype_uint8 yystos[] = { - 0, 5, 9, 11, 12, 18, 19, 21, 22, 25, - 26, 40, 41, 42, 32, 32, 32, 32, 32, 32, - 32, 1, 15, 35, 7, 10, 35, 54, 55, 59, - 0, 42, 10, 52, 59, 52, 59, 59, 17, 59, - 35, 35, 54, 8, 14, 56, 57, 55, 35, 33, - 33, 38, 53, 33, 33, 33, 33, 33, 10, 20, - 43, 44, 45, 43, 36, 30, 58, 59, 60, 58, - 36, 57, 56, 34, 59, 34, 34, 34, 34, 35, - 37, 32, 36, 44, 34, 36, 60, 34, 34, 36, - 10, 13, 45, 46, 47, 48, 60, 4, 10, 17, - 19, 23, 32, 51, 10, 34, 34, 59, 32, 34, - 36, 47, 32, 32, 51, 3, 16, 27, 28, 30, - 37, 34, 48, 6, 50, 51, 33, 51, 51, 51, - 51, 51, 51, 33, 32, 10, 24, 49, 33, 33, - 59, 32, 33, 33, 10, 33 + 0, 5, 6, 10, 12, 13, 19, 20, 22, 23, + 26, 27, 41, 42, 43, 33, 33, 33, 33, 33, + 33, 33, 33, 1, 16, 36, 8, 11, 36, 56, + 57, 61, 0, 43, 5, 10, 53, 55, 61, 11, + 53, 53, 61, 61, 18, 61, 36, 36, 56, 9, + 15, 58, 59, 57, 36, 33, 33, 34, 39, 54, + 34, 34, 34, 34, 34, 34, 34, 11, 21, 44, + 45, 46, 44, 37, 31, 60, 61, 62, 60, 37, + 59, 58, 53, 53, 55, 35, 35, 35, 35, 35, + 36, 38, 33, 37, 45, 35, 37, 62, 35, 35, + 37, 34, 34, 11, 14, 46, 47, 48, 49, 62, + 4, 11, 18, 20, 24, 33, 52, 11, 35, 35, + 61, 33, 35, 37, 48, 33, 33, 52, 3, 17, + 28, 29, 31, 38, 35, 49, 7, 51, 52, 34, + 52, 52, 52, 52, 52, 52, 34, 33, 11, 25, + 50, 34, 34, 61, 33, 34, 34, 11, 34 }; -#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__) -# define YYSIZE_T __SIZE_TYPE__ -#endif -#if ! defined (YYSIZE_T) && defined (size_t) -# define YYSIZE_T size_t -#endif -#if ! defined (YYSIZE_T) -# if defined (__STDC__) || defined (__cplusplus) -# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ -# define YYSIZE_T size_t -# endif -#endif -#if ! defined (YYSIZE_T) -# define YYSIZE_T unsigned int -#endif - #define yyerrok (yyerrstatus = 0) #define yyclearin (yychar = YYEMPTY) #define YYEMPTY (-2) @@ -665,30 +804,63 @@ do \ yychar = (Token); \ yylval = (Value); \ yytoken = YYTRANSLATE (yychar); \ - YYPOPSTACK; \ + YYPOPSTACK (1); \ goto yybackup; \ } \ else \ - { \ - yyerror ("syntax error: cannot back up");\ + { \ + yyerror (YY_("syntax error: cannot back up")); \ YYERROR; \ } \ -while (0) +while (YYID (0)) + #define YYTERROR 1 #define YYERRCODE 256 -/* YYLLOC_DEFAULT -- Compute the default location (before the actions - are run). */ +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) #ifndef YYLLOC_DEFAULT -# define YYLLOC_DEFAULT(Current, Rhs, N) \ - ((Current).first_line = (Rhs)[1].first_line, \ - (Current).first_column = (Rhs)[1].first_column, \ - (Current).last_line = (Rhs)[N].last_line, \ - (Current).last_column = (Rhs)[N].last_column) +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (YYID (N)) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (YYID (0)) #endif + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if YYLTYPE_IS_TRIVIAL +# define YY_LOCATION_PRINT(File, Loc) \ + fprintf (File, "%d.%d-%d.%d", \ + (Loc).first_line, (Loc).first_column, \ + (Loc).last_line, (Loc).last_column) +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + /* YYLEX -- calling `yylex' with the right arguments. */ #ifdef YYLEX_PARAM @@ -709,42 +881,96 @@ while (0) do { \ if (yydebug) \ YYFPRINTF Args; \ -} while (0) +} while (YYID (0)) -# define YYDSYMPRINT(Args) \ -do { \ - if (yydebug) \ - yysymprint Args; \ -} while (0) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (YYID (0)) -# define YYDSYMPRINTF(Title, Token, Value, Location) \ -do { \ - if (yydebug) \ - { \ - YYFPRINTF (stderr, "%s ", Title); \ - yysymprint (stderr, \ - Token, Value); \ - YYFPRINTF (stderr, "\n"); \ - } \ -} while (0) + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + switch (yytype) + { + default: + break; + } +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + yy_symbol_value_print (yyoutput, yytype, yyvaluep); + YYFPRINTF (yyoutput, ")"); +} /*------------------------------------------------------------------. | yy_stack_print -- Print the state stack from its BOTTOM up to its | | TOP (included). | `------------------------------------------------------------------*/ -#if defined (__STDC__) || defined (__cplusplus) +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) static void -yy_stack_print (short *bottom, short *top) +yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) #else static void yy_stack_print (bottom, top) - short *bottom; - short *top; + yytype_int16 *bottom; + yytype_int16 *top; #endif { YYFPRINTF (stderr, "Stack now"); - for (/* Nothing. */; bottom <= top; ++bottom) + for (; bottom <= top; ++bottom) YYFPRINTF (stderr, " %d", *bottom); YYFPRINTF (stderr, "\n"); } @@ -753,45 +979,52 @@ yy_stack_print (bottom, top) do { \ if (yydebug) \ yy_stack_print ((Bottom), (Top)); \ -} while (0) +} while (YYID (0)) /*------------------------------------------------. | Report that the YYRULE is going to be reduced. | `------------------------------------------------*/ -#if defined (__STDC__) || defined (__cplusplus) +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) static void -yy_reduce_print (int yyrule) +yy_reduce_print (YYSTYPE *yyvsp, int yyrule) #else static void -yy_reduce_print (yyrule) +yy_reduce_print (yyvsp, yyrule) + YYSTYPE *yyvsp; int yyrule; #endif { + int yynrhs = yyr2[yyrule]; int yyi; - unsigned int yylno = yyrline[yyrule]; - YYFPRINTF (stderr, "Reducing stack by rule %d (line %u), ", - yyrule - 1, yylno); - /* Print the symbols being reduced, and their result. */ - for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++) - YYFPRINTF (stderr, "%s ", yytname [yyrhs[yyi]]); - YYFPRINTF (stderr, "-> %s\n", yytname [yyr1[yyrule]]); + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + fprintf (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + ); + fprintf (stderr, "\n"); + } } # define YY_REDUCE_PRINT(Rule) \ do { \ if (yydebug) \ - yy_reduce_print (Rule); \ -} while (0) + yy_reduce_print (yyvsp, Rule); \ +} while (YYID (0)) /* Nonzero means print parse trace. It is left uninitialized so that multiple parsers can coexist. */ int yydebug; #else /* !YYDEBUG */ # define YYDPRINTF(Args) -# define YYDSYMPRINT(Args) -# define YYDSYMPRINTF(Title, Token, Value, Location) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) # define YY_STACK_PRINT(Bottom, Top) # define YY_REDUCE_PRINT(Rule) #endif /* !YYDEBUG */ @@ -806,13 +1039,9 @@ int yydebug; if the built-in stack extension method is used). Do not make this value too large; the results are undefined if - SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH) + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) evaluated with infinite-precision integer arithmetic. */ -#if defined (YYMAXDEPTH) && YYMAXDEPTH == 0 -# undef YYMAXDEPTH -#endif - #ifndef YYMAXDEPTH # define YYMAXDEPTH 10000 #endif @@ -822,45 +1051,47 @@ int yydebug; #if YYERROR_VERBOSE # ifndef yystrlen -# if defined (__GLIBC__) && defined (_STRING_H) +# if defined __GLIBC__ && defined _STRING_H # define yystrlen strlen # else /* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) static YYSIZE_T -# if defined (__STDC__) || defined (__cplusplus) yystrlen (const char *yystr) -# else +#else +static YYSIZE_T yystrlen (yystr) - const char *yystr; -# endif + const char *yystr; +#endif { - register const char *yys = yystr; - - while (*yys++ != '\0') + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) continue; - - return yys - yystr - 1; + return yylen; } # endif # endif # ifndef yystpcpy -# if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE) +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE # define yystpcpy stpcpy # else /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) static char * -# if defined (__STDC__) || defined (__cplusplus) yystpcpy (char *yydest, const char *yysrc) -# else +#else +static char * yystpcpy (yydest, yysrc) - char *yydest; - const char *yysrc; -# endif + char *yydest; + const char *yysrc; +#endif { - register char *yyd = yydest; - register const char *yys = yysrc; + char *yyd = yydest; + const char *yys = yysrc; while ((*yyd++ = *yys++) != '\0') continue; @@ -870,70 +1101,192 @@ yystpcpy (yydest, yysrc) # endif # endif -#endif /* !YYERROR_VERBOSE */ +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; - + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } -#if YYDEBUG -/*--------------------------------. -| Print this symbol on YYOUTPUT. | -`--------------------------------*/ + if (! yyres) + return yystrlen (yystr); -#if defined (__STDC__) || defined (__cplusplus) -static void -yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep) -#else -static void -yysymprint (yyoutput, yytype, yyvaluep) - FILE *yyoutput; - int yytype; - YYSTYPE *yyvaluep; -#endif + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into YYRESULT an error message about the unexpected token + YYCHAR while in state YYSTATE. Return the number of bytes copied, + including the terminating null byte. If YYRESULT is null, do not + copy anything; just return the number of bytes that would be + copied. As a special case, return 0 if an ordinary "syntax error" + message will do. Return YYSIZE_MAXIMUM if overflow occurs during + size calculation. */ +static YYSIZE_T +yysyntax_error (char *yyresult, int yystate, int yychar) { - /* Pacify ``unused variable'' warnings. */ - (void) yyvaluep; + int yyn = yypact[yystate]; - if (yytype < YYNTOKENS) + if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) + return 0; + else { - YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); -# ifdef YYPRINT - YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); + int yytype = YYTRANSLATE (yychar); + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + int yysize_overflow = 0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + int yyx; + +# if 0 + /* This is so xgettext sees the translatable formats that are + constructed on the fly. */ + YY_("syntax error, unexpected %s"); + YY_("syntax error, unexpected %s, expecting %s"); + YY_("syntax error, unexpected %s, expecting %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); # endif - } - else - YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + char *yyfmt; + char const *yyf; + static char const yyunexpected[] = "syntax error, unexpected %s"; + static char const yyexpecting[] = ", expecting %s"; + static char const yyor[] = " or %s"; + char yyformat[sizeof yyunexpected + + sizeof yyexpecting - 1 + + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) + * (sizeof yyor - 1))]; + char const *yyprefix = yyexpecting; + + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yycount = 1; + + yyarg[0] = yytname[yytype]; + yyfmt = yystpcpy (yyformat, yyunexpected); + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + yyformat[sizeof yyunexpected - 1] = '\0'; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + yyfmt = yystpcpy (yyfmt, yyprefix); + yyprefix = yyor; + } - switch (yytype) - { - default: - break; + yyf = YY_(yyformat); + yysize1 = yysize + yystrlen (yyf); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + + if (yysize_overflow) + return YYSIZE_MAXIMUM; + + if (yyresult) + { + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + char *yyp = yyresult; + int yyi = 0; + while ((*yyp = *yyf) != '\0') + { + if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyf += 2; + } + else + { + yyp++; + yyf++; + } + } + } + return yysize; } - YYFPRINTF (yyoutput, ")"); } +#endif /* YYERROR_VERBOSE */ + -#endif /* ! YYDEBUG */ /*-----------------------------------------------. | Release the memory associated to this symbol. | `-----------------------------------------------*/ -#if defined (__STDC__) || defined (__cplusplus) +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) static void -yydestruct (int yytype, YYSTYPE *yyvaluep) +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) #else static void -yydestruct (yytype, yyvaluep) +yydestruct (yymsg, yytype, yyvaluep) + const char *yymsg; int yytype; YYSTYPE *yyvaluep; #endif { - /* Pacify ``unused variable'' warnings. */ - (void) yyvaluep; + YYUSE (yyvaluep); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); switch (yytype) { default: - break; + break; } } @@ -941,13 +1294,13 @@ yydestruct (yytype, yyvaluep) /* Prevent warnings from -Wmissing-prototypes. */ #ifdef YYPARSE_PARAM -# if defined (__STDC__) || defined (__cplusplus) +#if defined __STDC__ || defined __cplusplus int yyparse (void *YYPARSE_PARAM); -# else +#else int yyparse (); -# endif +#endif #else /* ! YYPARSE_PARAM */ -#if defined (__STDC__) || defined (__cplusplus) +#if defined __STDC__ || defined __cplusplus int yyparse (void); #else int yyparse (); @@ -956,10 +1309,10 @@ int yyparse (); -/* The lookahead symbol. */ +/* The look-ahead symbol. */ int yychar; -/* The semantic value of the lookahead symbol. */ +/* The semantic value of the look-ahead symbol. */ YYSTYPE yylval; /* Number of syntax errors so far. */ @@ -972,14 +1325,18 @@ int yynerrs; `----------*/ #ifdef YYPARSE_PARAM -# if defined (__STDC__) || defined (__cplusplus) -int yyparse (void *YYPARSE_PARAM) -# else -int yyparse (YYPARSE_PARAM) - void *YYPARSE_PARAM; -# endif +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif #else /* ! YYPARSE_PARAM */ -#if defined (__STDC__) || defined (__cplusplus) +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) int yyparse (void) #else @@ -990,13 +1347,19 @@ yyparse () #endif { - register int yystate; - register int yyn; + int yystate; + int yyn; int yyresult; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; - /* Lookahead token as an internal (translated) token number. */ + /* Look-ahead token as an internal (translated) token number. */ int yytoken = 0; +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif /* Three stacks and their tools: `yyss': related to states, @@ -1007,18 +1370,18 @@ yyparse () to reallocate them elsewhere. */ /* The state stack. */ - short yyssa[YYINITDEPTH]; - short *yyss = yyssa; - register short *yyssp; + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss = yyssa; + yytype_int16 *yyssp; /* The semantic value stack. */ YYSTYPE yyvsa[YYINITDEPTH]; YYSTYPE *yyvs = yyvsa; - register YYSTYPE *yyvsp; + YYSTYPE *yyvsp; -#define YYPOPSTACK (yyvsp--, yyssp--) +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) YYSIZE_T yystacksize = YYINITDEPTH; @@ -1027,9 +1390,9 @@ yyparse () YYSTYPE yyval; - /* When reducing, the number of symbols on the RHS of the reduced - rule. */ - int yylen; + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; YYDPRINTF ((stderr, "Starting parse\n")); @@ -1053,8 +1416,7 @@ yyparse () `------------------------------------------------------------*/ yynewstate: /* In all cases, when you get here, the value and location stacks - have just been pushed. so pushing a state here evens the stacks. - */ + have just been pushed. So pushing a state here evens the stacks. */ yyssp++; yysetstate: @@ -1067,18 +1429,18 @@ yyparse () #ifdef yyoverflow { - /* Give user a chance to reallocate the stack. Use copies of + /* Give user a chance to reallocate the stack. Use copies of these so that the &'s don't force the real ones into memory. */ YYSTYPE *yyvs1 = yyvs; - short *yyss1 = yyss; + yytype_int16 *yyss1 = yyss; /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might be undefined if yyoverflow is a macro. */ - yyoverflow ("parser stack overflow", + yyoverflow (YY_("memory exhausted"), &yyss1, yysize * sizeof (*yyssp), &yyvs1, yysize * sizeof (*yyvsp), @@ -1089,21 +1451,21 @@ yyparse () } #else /* no yyoverflow */ # ifndef YYSTACK_RELOCATE - goto yyoverflowlab; + goto yyexhaustedlab; # else /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) - goto yyoverflowlab; + goto yyexhaustedlab; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) yystacksize = YYMAXDEPTH; { - short *yyss1 = yyss; + yytype_int16 *yyss1 = yyss; union yyalloc *yyptr = (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); if (! yyptr) - goto yyoverflowlab; + goto yyexhaustedlab; YYSTACK_RELOCATE (yyss); YYSTACK_RELOCATE (yyvs); @@ -1134,19 +1496,17 @@ yyparse () `-----------*/ yybackup: -/* Do appropriate processing given the current state. */ -/* Read a lookahead token if we need one and don't already have one. */ -/* yyresume: */ - - /* First try to decide what to do without reference to lookahead token. */ + /* Do appropriate processing given the current state. Read a + look-ahead token if we need one and don't already have one. */ + /* First try to decide what to do without reference to look-ahead token. */ yyn = yypact[yystate]; if (yyn == YYPACT_NINF) goto yydefault; - /* Not known => get a lookahead token if don't already have one. */ + /* Not known => get a look-ahead token if don't already have one. */ - /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); @@ -1161,7 +1521,7 @@ yybackup: else { yytoken = YYTRANSLATE (yychar); - YYDSYMPRINTF ("Next token is", yytoken, &yylval, &yylloc); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); } /* If the proper action on seeing token YYTOKEN is to reduce or to @@ -1181,22 +1541,21 @@ yybackup: if (yyn == YYFINAL) YYACCEPT; - /* Shift the lookahead token. */ - YYDPRINTF ((stderr, "Shifting token %s, ", yytname[yytoken])); - - /* Discard the token being shifted unless it is eof. */ - if (yychar != YYEOF) - yychar = YYEMPTY; - - *++yyvsp = yylval; - - /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; + /* Shift the look-ahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + yystate = yyn; + *++yyvsp = yylval; + goto yynewstate; @@ -1232,430 +1591,458 @@ yyreduce: switch (yyn) { case 3: -#line 130 "/home/drepper/gnu/elfutils/src/ldscript.y" - { add_versions (yyvsp[0].version); } +#line 145 "ldscript.y" + { add_versions ((yyvsp[(2) - (2)].version)); } break; case 6: -#line 138 "/home/drepper/gnu/elfutils/src/ldscript.y" +#line 153 "ldscript.y" { if (likely (ld_state.entry == NULL)) - ld_state.entry = yyvsp[-2].str; + ld_state.entry = (yyvsp[(3) - (5)].str); } break; case 7: -#line 143 "/home/drepper/gnu/elfutils/src/ldscript.y" +#line 158 "ldscript.y" { - ld_new_searchdir (yyvsp[-2].str); + ld_new_searchdir ((yyvsp[(3) - (5)].str)); } break; case 8: -#line 147 "/home/drepper/gnu/elfutils/src/ldscript.y" +#line 162 "ldscript.y" { if (likely (ld_state.pagesize == 0)) - ld_state.pagesize = yyvsp[-2].num; + ld_state.pagesize = (yyvsp[(3) - (5)].num); } break; case 9: -#line 152 "/home/drepper/gnu/elfutils/src/ldscript.y" +#line 167 "ldscript.y" { - if (likely (ld_state.interp == NULL)) - ld_state.interp = yyvsp[-2].str; + if (likely (ld_state.interp == NULL) + && ld_state.file_type != dso_file_type) + ld_state.interp = (yyvsp[(3) - (5)].str); } break; case 10: -#line 157 "/home/drepper/gnu/elfutils/src/ldscript.y" +#line 173 "ldscript.y" { - new_segment (yyvsp[-3].num, yyvsp[-1].output_rule); + new_segment ((yyvsp[(2) - (5)].num), (yyvsp[(4) - (5)].output_rule)); } break; case 11: -#line 161 "/home/drepper/gnu/elfutils/src/ldscript.y" +#line 177 "ldscript.y" { fputs_unlocked (gettext ("mode for segment invalid\n"), stderr); - new_segment (0, yyvsp[-1].output_rule); + new_segment (0, (yyvsp[(4) - (5)].output_rule)); } break; case 12: -#line 167 "/home/drepper/gnu/elfutils/src/ldscript.y" +#line 183 "ldscript.y" { /* First little optimization. If there is only one file in the group don't do anything. */ - if (yyvsp[-1].filename_list != yyvsp[-1].filename_list->next) + if ((yyvsp[(3) - (4)].filename_list) != (yyvsp[(3) - (4)].filename_list)->next) { - yyvsp[-1].filename_list->next->group_start = 1; - yyvsp[-1].filename_list->group_end = 1; + (yyvsp[(3) - (4)].filename_list)->next->group_start = 1; + (yyvsp[(3) - (4)].filename_list)->group_end = 1; } - add_inputfiles (yyvsp[-1].filename_list); + add_inputfiles ((yyvsp[(3) - (4)].filename_list)); } break; case 13: -#line 178 "/home/drepper/gnu/elfutils/src/ldscript.y" - { add_inputfiles (yyvsp[-1].filename_list); } +#line 194 "ldscript.y" + { add_inputfiles ((yyvsp[(3) - (4)].filename_list)); } break; case 14: -#line 180 "/home/drepper/gnu/elfutils/src/ldscript.y" - { add_versions (yyvsp[-1].version); } +#line 196 "ldscript.y" + { add_inputfiles (mark_as_needed ((yyvsp[(3) - (4)].filename_list))); } break; case 15: -#line 182 "/home/drepper/gnu/elfutils/src/ldscript.y" - { /* XXX TODO */ } +#line 198 "ldscript.y" + { add_versions ((yyvsp[(3) - (4)].version)); } break; case 16: -#line 186 "/home/drepper/gnu/elfutils/src/ldscript.y" - { - yyvsp[0].output_rule->next = yyvsp[-1].output_rule->next; - yyval.output_rule = yyvsp[-1].output_rule->next = yyvsp[0].output_rule; - } +#line 200 "ldscript.y" + { /* XXX TODO */ } break; case 17: -#line 191 "/home/drepper/gnu/elfutils/src/ldscript.y" - { yyval.output_rule = yyvsp[0].output_rule; } +#line 204 "ldscript.y" + { + (yyvsp[(2) - (2)].output_rule)->next = (yyvsp[(1) - (2)].output_rule)->next; + (yyval.output_rule) = (yyvsp[(1) - (2)].output_rule)->next = (yyvsp[(2) - (2)].output_rule); + } break; case 18: -#line 195 "/home/drepper/gnu/elfutils/src/ldscript.y" +#line 209 "ldscript.y" + { (yyval.output_rule) = (yyvsp[(1) - (1)].output_rule); } + break; + + case 19: +#line 213 "ldscript.y" { - yyval.output_rule = new_output_rule (output_assignment); - yyval.output_rule->val.assignment = yyvsp[-1].assignment; + (yyval.output_rule) = new_output_rule (output_assignment); + (yyval.output_rule)->val.assignment = (yyvsp[(1) - (2)].assignment); } break; - case 19: -#line 200 "/home/drepper/gnu/elfutils/src/ldscript.y" + case 20: +#line 218 "ldscript.y" { - yyval.output_rule = new_output_rule (output_section); - yyval.output_rule->val.section.name = yyvsp[-3].str; - yyval.output_rule->val.section.input = yyvsp[-1].input_rule->next; + (yyval.output_rule) = new_output_rule (output_section); + (yyval.output_rule)->val.section.name = (yyvsp[(1) - (4)].str); + (yyval.output_rule)->val.section.input = (yyvsp[(3) - (4)].input_rule)->next; if (ld_state.strip == strip_debug - && ebl_debugscn_p (ld_state.ebl, yyvsp[-3].str)) - yyval.output_rule->val.section.ignored = true; + && ebl_debugscn_p (ld_state.ebl, (yyvsp[(1) - (4)].str))) + (yyval.output_rule)->val.section.ignored = true; else - yyval.output_rule->val.section.ignored = false; - yyvsp[-1].input_rule->next = NULL; + (yyval.output_rule)->val.section.ignored = false; + (yyvsp[(3) - (4)].input_rule)->next = NULL; } break; - case 20: -#line 212 "/home/drepper/gnu/elfutils/src/ldscript.y" + case 21: +#line 230 "ldscript.y" { /* This is a short cut for "ID { *(ID) }". */ - yyval.output_rule = new_output_rule (output_section); - yyval.output_rule->val.section.name = yyvsp[-1].str; - yyval.output_rule->val.section.input = new_input_rule (input_section); - yyval.output_rule->val.section.input->next = NULL; - yyval.output_rule->val.section.input->val.section = + (yyval.output_rule) = new_output_rule (output_section); + (yyval.output_rule)->val.section.name = (yyvsp[(1) - (2)].str); + (yyval.output_rule)->val.section.input = new_input_rule (input_section); + (yyval.output_rule)->val.section.input->next = NULL; + (yyval.output_rule)->val.section.input->val.section = (struct filemask_section_name *) obstack_alloc (&ld_state.smem, sizeof (struct filemask_section_name)); - yyval.output_rule->val.section.input->val.section->filemask = NULL; - yyval.output_rule->val.section.input->val.section->excludemask = NULL; - yyval.output_rule->val.section.input->val.section->section_name = - new_input_section_name (yyvsp[-1].str, false); - yyval.output_rule->val.section.input->val.section->keep_flag = false; + (yyval.output_rule)->val.section.input->val.section->filemask = NULL; + (yyval.output_rule)->val.section.input->val.section->excludemask = NULL; + (yyval.output_rule)->val.section.input->val.section->section_name = + new_input_section_name ((yyvsp[(1) - (2)].str), false); + (yyval.output_rule)->val.section.input->val.section->keep_flag = false; if (ld_state.strip == strip_debug - && ebl_debugscn_p (ld_state.ebl, yyvsp[-1].str)) - yyval.output_rule->val.section.ignored = true; + && ebl_debugscn_p (ld_state.ebl, (yyvsp[(1) - (2)].str))) + (yyval.output_rule)->val.section.ignored = true; else - yyval.output_rule->val.section.ignored = false; + (yyval.output_rule)->val.section.ignored = false; } break; - case 21: -#line 236 "/home/drepper/gnu/elfutils/src/ldscript.y" - { yyval.assignment = new_assignment (yyvsp[-2].str, yyvsp[0].expr, false); } - break; - case 22: -#line 238 "/home/drepper/gnu/elfutils/src/ldscript.y" - { yyval.assignment = new_assignment (yyvsp[-3].str, yyvsp[-1].expr, true); } +#line 254 "ldscript.y" + { (yyval.assignment) = new_assignment ((yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].expr), false); } break; case 23: -#line 242 "/home/drepper/gnu/elfutils/src/ldscript.y" - { - yyvsp[0].input_rule->next = yyvsp[-1].input_rule->next; - yyval.input_rule = yyvsp[-1].input_rule->next = yyvsp[0].input_rule; - } +#line 256 "ldscript.y" + { (yyval.assignment) = new_assignment ((yyvsp[(3) - (6)].str), (yyvsp[(5) - (6)].expr), true); } break; case 24: -#line 247 "/home/drepper/gnu/elfutils/src/ldscript.y" - { yyval.input_rule = yyvsp[0].input_rule; } +#line 260 "ldscript.y" + { + (yyvsp[(2) - (2)].input_rule)->next = (yyvsp[(1) - (2)].input_rule)->next; + (yyval.input_rule) = (yyvsp[(1) - (2)].input_rule)->next = (yyvsp[(2) - (2)].input_rule); + } break; case 25: -#line 251 "/home/drepper/gnu/elfutils/src/ldscript.y" - { - yyval.input_rule = new_input_rule (input_section); - yyval.input_rule->val.section = yyvsp[0].filemask_section_name; - } +#line 265 "ldscript.y" + { (yyval.input_rule) = (yyvsp[(1) - (1)].input_rule); } break; case 26: -#line 256 "/home/drepper/gnu/elfutils/src/ldscript.y" +#line 269 "ldscript.y" { - yyvsp[-1].filemask_section_name->keep_flag = true; - - yyval.input_rule = new_input_rule (input_section); - yyval.input_rule->val.section = yyvsp[-1].filemask_section_name; + (yyval.input_rule) = new_input_rule (input_section); + (yyval.input_rule)->val.section = (yyvsp[(1) - (1)].filemask_section_name); } break; case 27: -#line 263 "/home/drepper/gnu/elfutils/src/ldscript.y" +#line 274 "ldscript.y" { - yyval.input_rule = new_input_rule (input_assignment); - yyval.input_rule->val.assignment = yyvsp[-1].assignment; + (yyvsp[(3) - (4)].filemask_section_name)->keep_flag = true; + + (yyval.input_rule) = new_input_rule (input_section); + (yyval.input_rule)->val.section = (yyvsp[(3) - (4)].filemask_section_name); } break; case 28: -#line 270 "/home/drepper/gnu/elfutils/src/ldscript.y" +#line 281 "ldscript.y" { - yyval.filemask_section_name = (struct filemask_section_name *) - obstack_alloc (&ld_state.smem, sizeof (*yyval.filemask_section_name)); - yyval.filemask_section_name->filemask = yyvsp[-4].str; - yyval.filemask_section_name->excludemask = yyvsp[-2].str; - yyval.filemask_section_name->section_name = yyvsp[-1].sectionname; - yyval.filemask_section_name->keep_flag = false; + (yyval.input_rule) = new_input_rule (input_assignment); + (yyval.input_rule)->val.assignment = (yyvsp[(1) - (2)].assignment); } break; case 29: -#line 281 "/home/drepper/gnu/elfutils/src/ldscript.y" - { yyval.sectionname = new_input_section_name (yyvsp[0].str, false); } +#line 288 "ldscript.y" + { + (yyval.filemask_section_name) = (struct filemask_section_name *) + obstack_alloc (&ld_state.smem, sizeof (*(yyval.filemask_section_name))); + (yyval.filemask_section_name)->filemask = (yyvsp[(1) - (5)].str); + (yyval.filemask_section_name)->excludemask = (yyvsp[(3) - (5)].str); + (yyval.filemask_section_name)->section_name = (yyvsp[(4) - (5)].sectionname); + (yyval.filemask_section_name)->keep_flag = false; + } break; case 30: -#line 283 "/home/drepper/gnu/elfutils/src/ldscript.y" - { yyval.sectionname = new_input_section_name (yyvsp[-1].str, true); } +#line 299 "ldscript.y" + { (yyval.sectionname) = new_input_section_name ((yyvsp[(1) - (1)].str), false); } break; case 31: -#line 287 "/home/drepper/gnu/elfutils/src/ldscript.y" - { yyval.str = yyvsp[-1].str; } +#line 301 "ldscript.y" + { (yyval.sectionname) = new_input_section_name ((yyvsp[(3) - (4)].str), true); } break; case 32: -#line 289 "/home/drepper/gnu/elfutils/src/ldscript.y" - { yyval.str = NULL; } +#line 305 "ldscript.y" + { (yyval.str) = (yyvsp[(3) - (4)].str); } break; case 33: -#line 293 "/home/drepper/gnu/elfutils/src/ldscript.y" - { - yyval.expr = new_expr (exp_align); - yyval.expr->val.child = yyvsp[-1].expr; - } +#line 307 "ldscript.y" + { (yyval.str) = NULL; } break; case 34: -#line 298 "/home/drepper/gnu/elfutils/src/ldscript.y" - { yyval.expr = yyvsp[-1].expr; } +#line 311 "ldscript.y" + { + (yyval.expr) = new_expr (exp_align); + (yyval.expr)->val.child = (yyvsp[(3) - (4)].expr); + } break; case 35: -#line 300 "/home/drepper/gnu/elfutils/src/ldscript.y" - { - yyval.expr = new_expr (exp_mult); - yyval.expr->val.binary.left = yyvsp[-2].expr; - yyval.expr->val.binary.right = yyvsp[0].expr; - } +#line 316 "ldscript.y" + { (yyval.expr) = (yyvsp[(2) - (3)].expr); } break; case 36: -#line 306 "/home/drepper/gnu/elfutils/src/ldscript.y" +#line 318 "ldscript.y" { - yyval.expr = new_expr (yyvsp[-1].op); - yyval.expr->val.binary.left = yyvsp[-2].expr; - yyval.expr->val.binary.right = yyvsp[0].expr; + (yyval.expr) = new_expr (exp_mult); + (yyval.expr)->val.binary.left = (yyvsp[(1) - (3)].expr); + (yyval.expr)->val.binary.right = (yyvsp[(3) - (3)].expr); } break; case 37: -#line 312 "/home/drepper/gnu/elfutils/src/ldscript.y" +#line 324 "ldscript.y" { - yyval.expr = new_expr (yyvsp[-1].op); - yyval.expr->val.binary.left = yyvsp[-2].expr; - yyval.expr->val.binary.right = yyvsp[0].expr; + (yyval.expr) = new_expr ((yyvsp[(2) - (3)].op)); + (yyval.expr)->val.binary.left = (yyvsp[(1) - (3)].expr); + (yyval.expr)->val.binary.right = (yyvsp[(3) - (3)].expr); } break; case 38: -#line 318 "/home/drepper/gnu/elfutils/src/ldscript.y" +#line 330 "ldscript.y" { - yyval.expr = new_expr (exp_and); - yyval.expr->val.binary.left = yyvsp[-2].expr; - yyval.expr->val.binary.right = yyvsp[0].expr; + (yyval.expr) = new_expr ((yyvsp[(2) - (3)].op)); + (yyval.expr)->val.binary.left = (yyvsp[(1) - (3)].expr); + (yyval.expr)->val.binary.right = (yyvsp[(3) - (3)].expr); } break; case 39: -#line 324 "/home/drepper/gnu/elfutils/src/ldscript.y" +#line 336 "ldscript.y" { - yyval.expr = new_expr (exp_or); - yyval.expr->val.binary.left = yyvsp[-2].expr; - yyval.expr->val.binary.right = yyvsp[0].expr; + (yyval.expr) = new_expr (exp_and); + (yyval.expr)->val.binary.left = (yyvsp[(1) - (3)].expr); + (yyval.expr)->val.binary.right = (yyvsp[(3) - (3)].expr); } break; case 40: -#line 330 "/home/drepper/gnu/elfutils/src/ldscript.y" +#line 342 "ldscript.y" { - yyval.expr = new_expr (exp_num); - yyval.expr->val.num = yyvsp[0].num; + (yyval.expr) = new_expr (exp_or); + (yyval.expr)->val.binary.left = (yyvsp[(1) - (3)].expr); + (yyval.expr)->val.binary.right = (yyvsp[(3) - (3)].expr); } break; case 41: -#line 335 "/home/drepper/gnu/elfutils/src/ldscript.y" +#line 348 "ldscript.y" { - yyval.expr = new_expr (exp_id); - yyval.expr->val.str = yyvsp[0].str; + (yyval.expr) = new_expr (exp_num); + (yyval.expr)->val.num = (yyvsp[(1) - (1)].num); } break; case 42: -#line 340 "/home/drepper/gnu/elfutils/src/ldscript.y" - { yyval.expr = new_expr (exp_sizeof_headers); } +#line 353 "ldscript.y" + { + (yyval.expr) = new_expr (exp_id); + (yyval.expr)->val.str = (yyvsp[(1) - (1)].str); + } break; case 43: -#line 342 "/home/drepper/gnu/elfutils/src/ldscript.y" - { yyval.expr = new_expr (exp_pagesize); } +#line 358 "ldscript.y" + { (yyval.expr) = new_expr (exp_sizeof_headers); } break; case 44: -#line 346 "/home/drepper/gnu/elfutils/src/ldscript.y" - { - struct filename_list *newp = new_filename_listelem (yyvsp[0].str); - newp->next = yyvsp[-2].filename_list->next; - yyval.filename_list = yyvsp[-2].filename_list->next = newp; - } +#line 360 "ldscript.y" + { (yyval.expr) = new_expr (exp_pagesize); } break; case 45: -#line 352 "/home/drepper/gnu/elfutils/src/ldscript.y" - { yyval.filename_list = new_filename_listelem (yyvsp[0].str); } - break; - - case 48: -#line 360 "/home/drepper/gnu/elfutils/src/ldscript.y" +#line 364 "ldscript.y" { - yyvsp[0].version->next = yyvsp[-1].version->next; - yyval.version = yyvsp[-1].version->next = yyvsp[0].version; + (yyvsp[(3) - (3)].filename_list)->next = (yyvsp[(1) - (3)].filename_list)->next; + (yyval.filename_list) = (yyvsp[(1) - (3)].filename_list)->next = (yyvsp[(3) - (3)].filename_list); } break; - case 49: -#line 365 "/home/drepper/gnu/elfutils/src/ldscript.y" - { yyval.version = yyvsp[0].version; } + case 46: +#line 369 "ldscript.y" + { (yyval.filename_list) = (yyvsp[(1) - (1)].filename_list); } break; - case 50: -#line 369 "/home/drepper/gnu/elfutils/src/ldscript.y" + case 49: +#line 377 "ldscript.y" { - yyvsp[-2].version->versionname = ""; - yyvsp[-2].version->parentname = NULL; - yyval.version = yyvsp[-2].version; + /* First little optimization. If there is only one + file in the group don't do anything. */ + if ((yyvsp[(3) - (4)].filename_list) != (yyvsp[(3) - (4)].filename_list)->next) + { + (yyvsp[(3) - (4)].filename_list)->next->group_start = 1; + (yyvsp[(3) - (4)].filename_list)->group_end = 1; + } + (yyval.filename_list) = (yyvsp[(3) - (4)].filename_list); } break; + case 50: +#line 388 "ldscript.y" + { (yyval.filename_list) = mark_as_needed ((yyvsp[(3) - (4)].filename_list)); } + break; + case 51: -#line 375 "/home/drepper/gnu/elfutils/src/ldscript.y" - { - yyvsp[-2].version->versionname = yyvsp[-4].str; - yyvsp[-2].version->parentname = NULL; - yyval.version = yyvsp[-2].version; - } +#line 390 "ldscript.y" + { (yyval.filename_list) = new_filename_listelem ((yyvsp[(1) - (1)].str)); } break; case 52: -#line 381 "/home/drepper/gnu/elfutils/src/ldscript.y" +#line 395 "ldscript.y" { - yyvsp[-3].version->versionname = yyvsp[-5].str; - yyvsp[-3].version->parentname = yyvsp[-1].str; - yyval.version = yyvsp[-3].version; + (yyvsp[(2) - (2)].version)->next = (yyvsp[(1) - (2)].version)->next; + (yyval.version) = (yyvsp[(1) - (2)].version)->next = (yyvsp[(2) - (2)].version); } break; case 53: -#line 390 "/home/drepper/gnu/elfutils/src/ldscript.y" - { yyval.version = merge_versions (yyvsp[-1].version, yyvsp[0].version); } +#line 400 "ldscript.y" + { (yyval.version) = (yyvsp[(1) - (1)].version); } break; case 54: -#line 392 "/home/drepper/gnu/elfutils/src/ldscript.y" - { yyval.version = yyvsp[0].version; } +#line 404 "ldscript.y" + { + (yyvsp[(2) - (4)].version)->versionname = ""; + (yyvsp[(2) - (4)].version)->parentname = NULL; + (yyval.version) = (yyvsp[(2) - (4)].version); + } break; case 55: -#line 396 "/home/drepper/gnu/elfutils/src/ldscript.y" - { yyval.version = new_version (NULL, yyvsp[0].id_list); } +#line 410 "ldscript.y" + { + (yyvsp[(3) - (5)].version)->versionname = (yyvsp[(1) - (5)].str); + (yyvsp[(3) - (5)].version)->parentname = NULL; + (yyval.version) = (yyvsp[(3) - (5)].version); + } break; case 56: -#line 398 "/home/drepper/gnu/elfutils/src/ldscript.y" - { yyval.version = new_version (yyvsp[0].id_list, NULL); } +#line 416 "ldscript.y" + { + (yyvsp[(3) - (6)].version)->versionname = (yyvsp[(1) - (6)].str); + (yyvsp[(3) - (6)].version)->parentname = (yyvsp[(5) - (6)].str); + (yyval.version) = (yyvsp[(3) - (6)].version); + } break; case 57: -#line 403 "/home/drepper/gnu/elfutils/src/ldscript.y" - { - struct id_list *newp = new_id_listelem (yyvsp[-1].str); - newp->next = yyvsp[-2].id_list->next; - yyval.id_list = yyvsp[-2].id_list->next = newp; - } +#line 425 "ldscript.y" + { (yyval.version) = merge_versions ((yyvsp[(1) - (2)].version), (yyvsp[(2) - (2)].version)); } break; case 58: -#line 409 "/home/drepper/gnu/elfutils/src/ldscript.y" - { yyval.id_list = new_id_listelem (yyvsp[-1].str); } +#line 427 "ldscript.y" + { (yyval.version) = (yyvsp[(1) - (1)].version); } break; case 59: -#line 413 "/home/drepper/gnu/elfutils/src/ldscript.y" - { yyval.str = yyvsp[0].str; } +#line 431 "ldscript.y" + { (yyval.version) = new_version (NULL, (yyvsp[(2) - (2)].id_list)); } break; case 60: -#line 415 "/home/drepper/gnu/elfutils/src/ldscript.y" - { yyval.str = yyvsp[0].str; } +#line 433 "ldscript.y" + { (yyval.version) = new_version ((yyvsp[(2) - (2)].id_list), NULL); } break; case 61: -#line 419 "/home/drepper/gnu/elfutils/src/ldscript.y" - { yyval.str = yyvsp[0].str; } +#line 438 "ldscript.y" + { + struct id_list *newp = new_id_listelem ((yyvsp[(2) - (3)].str)); + newp->next = (yyvsp[(1) - (3)].id_list)->next; + (yyval.id_list) = (yyvsp[(1) - (3)].id_list)->next = newp; + } break; case 62: -#line 421 "/home/drepper/gnu/elfutils/src/ldscript.y" - { yyval.str = NULL; } +#line 444 "ldscript.y" + { (yyval.id_list) = new_id_listelem ((yyvsp[(1) - (2)].str)); } break; + case 63: +#line 448 "ldscript.y" + { (yyval.str) = (yyvsp[(1) - (1)].str); } + break; - } + case 64: +#line 450 "ldscript.y" + { (yyval.str) = (yyvsp[(1) - (1)].str); } + break; -/* Line 1000 of yacc.c. */ -#line 1654 "ldscript.c" - - yyvsp -= yylen; - yyssp -= yylen; + case 65: +#line 454 "ldscript.y" + { (yyval.str) = (yyvsp[(1) - (1)].str); } + break; + case 66: +#line 456 "ldscript.y" + { (yyval.str) = NULL; } + break; + +/* Line 1267 of yacc.c. */ +#line 2040 "ldscript.c" + default: break; + } + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; YY_STACK_PRINT (yyss, yyssp); *++yyvsp = yyval; @@ -1684,99 +2071,65 @@ yyerrlab: if (!yyerrstatus) { ++yynerrs; -#if YYERROR_VERBOSE - yyn = yypact[yystate]; - - if (YYPACT_NINF < yyn && yyn < YYLAST) - { - YYSIZE_T yysize = 0; - int yytype = YYTRANSLATE (yychar); - const char* yyprefix; - char *yymsg; - int yyx; - - /* Start YYX at -YYN if negative to avoid negative indexes in - YYCHECK. */ - int yyxbegin = yyn < 0 ? -yyn : 0; - - /* Stay within bounds of both yycheck and yytname. */ - int yychecklim = YYLAST - yyn; - int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; - int yycount = 0; - - yyprefix = ", expecting "; - for (yyx = yyxbegin; yyx < yyxend; ++yyx) - if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) +#if ! YYERROR_VERBOSE + yyerror (YY_("syntax error")); +#else + { + YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); + if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) + { + YYSIZE_T yyalloc = 2 * yysize; + if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) + yyalloc = YYSTACK_ALLOC_MAXIMUM; + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yyalloc); + if (yymsg) + yymsg_alloc = yyalloc; + else { - yysize += yystrlen (yyprefix) + yystrlen (yytname [yyx]); - yycount += 1; - if (yycount == 5) - { - yysize = 0; - break; - } + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; } - yysize += (sizeof ("syntax error, unexpected ") - + yystrlen (yytname[yytype])); - yymsg = (char *) YYSTACK_ALLOC (yysize); - if (yymsg != 0) - { - char *yyp = yystpcpy (yymsg, "syntax error, unexpected "); - yyp = yystpcpy (yyp, yytname[yytype]); + } - if (yycount < 5) - { - yyprefix = ", expecting "; - for (yyx = yyxbegin; yyx < yyxend; ++yyx) - if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) - { - yyp = yystpcpy (yyp, yyprefix); - yyp = yystpcpy (yyp, yytname[yyx]); - yyprefix = " or "; - } - } - yyerror (yymsg); - YYSTACK_FREE (yymsg); - } - else - yyerror ("syntax error; also virtual memory exhausted"); - } - else -#endif /* YYERROR_VERBOSE */ - yyerror ("syntax error"); + if (0 < yysize && yysize <= yymsg_alloc) + { + (void) yysyntax_error (yymsg, yystate, yychar); + yyerror (yymsg); + } + else + { + yyerror (YY_("syntax error")); + if (yysize != 0) + goto yyexhaustedlab; + } + } +#endif } if (yyerrstatus == 3) { - /* If just tried and failed to reuse lookahead token after an + /* If just tried and failed to reuse look-ahead token after an error, discard it. */ if (yychar <= YYEOF) - { - /* If at end of input, pop the error token, - then the rest of the stack, then return failure. */ + { + /* Return failure if at end of input. */ if (yychar == YYEOF) - for (;;) - { - YYPOPSTACK; - if (yyssp == yyss) - YYABORT; - YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp); - yydestruct (yystos[*yyssp], yyvsp); - } - } + YYABORT; + } else { - YYDSYMPRINTF ("Error: discarding", yytoken, &yylval, &yylloc); - yydestruct (yytoken, &yylval); + yydestruct ("Error: discarding", + yytoken, &yylval); yychar = YYEMPTY; - } } - /* Else will try to reuse lookahead token after shifting the error + /* Else will try to reuse look-ahead token after shifting the error token. */ goto yyerrlab1; @@ -1786,15 +2139,17 @@ yyerrlab: `---------------------------------------------------*/ yyerrorlab: -#ifdef __GNUC__ - /* Pacify GCC when the user code never invokes YYERROR and the label - yyerrorlab therefore never appears in user code. */ - if (0) + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) goto yyerrorlab; -#endif - yyvsp -= yylen; - yyssp -= yylen; + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); yystate = *yyssp; goto yyerrlab1; @@ -1823,9 +2178,10 @@ yyerrlab1: if (yyssp == yyss) YYABORT; - YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp); - yydestruct (yystos[yystate], yyvsp); - YYPOPSTACK; + + yydestruct ("Error: popping", + yystos[yystate], yyvsp); + YYPOPSTACK (1); yystate = *yyssp; YY_STACK_PRINT (yyss, yyssp); } @@ -1833,11 +2189,12 @@ yyerrlab1: if (yyn == YYFINAL) YYACCEPT; - YYDPRINTF ((stderr, "Shifting error token, ")); - *++yyvsp = yylval; + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + yystate = yyn; goto yynewstate; @@ -1857,25 +2214,43 @@ yyabortlab: goto yyreturn; #ifndef yyoverflow -/*----------------------------------------------. -| yyoverflowlab -- parser overflow comes here. | -`----------------------------------------------*/ -yyoverflowlab: - yyerror ("parser stack overflow"); +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); yyresult = 2; /* Fall through. */ #endif yyreturn: + if (yychar != YYEOF && yychar != YYEMPTY) + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + /* Do not reclaim the symbols of the rule which action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp); + YYPOPSTACK (1); + } #ifndef yyoverflow if (yyss != yyssa) YYSTACK_FREE (yyss); #endif - return yyresult; +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); } -#line 424 "/home/drepper/gnu/elfutils/src/ldscript.y" +#line 459 "ldscript.y" static void @@ -2001,6 +2376,21 @@ new_filename_listelem (const char *string) } +static struct filename_list * +mark_as_needed (struct filename_list *listp) +{ + struct filename_list *runp = listp; + do + { + runp->as_needed = true; + runp = runp->next; + } + while (runp != listp); + + return listp; +} + + static void add_inputfiles (struct filename_list *fnames) { diff --git a/src/ldscript.h b/src/ldscript.h index 6464d8da..946ef4c6 100644 --- a/src/ldscript.h +++ b/src/ldscript.h @@ -1,7 +1,9 @@ -/* A Bison parser, made by GNU Bison 1.875c. */ +/* A Bison parser, made by GNU Bison 2.3. */ -/* Skeleton parser for Yacc-like parsing with Bison, - Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. +/* Skeleton interface for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -15,13 +17,21 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. -/* As a special exception, when this file is copied by Bison into a - Bison output file, you may use that output file without restriction. - This special exception was added by the Free Software Foundation - in version 1.24 of Bison. */ + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ /* Tokens. */ #ifndef YYTOKENTYPE @@ -31,65 +41,69 @@ enum yytokentype { kADD_OP = 258, kALIGN = 259, - kENTRY = 260, - kEXCLUDE_FILE = 261, - kFILENAME = 262, - kGLOBAL = 263, - kGROUP = 264, - kID = 265, - kINPUT = 266, - kINTERP = 267, - kKEEP = 268, - kLOCAL = 269, - kMODE = 270, - kMUL_OP = 271, - kNUM = 272, - kOUTPUT_FORMAT = 273, - kPAGESIZE = 274, - kPROVIDE = 275, - kSEARCH_DIR = 276, - kSEGMENT = 277, - kSIZEOF_HEADERS = 278, - kSORT = 279, - kVERSION = 280, - kVERSION_SCRIPT = 281, - ADD_OP = 282, - MUL_OP = 283 + kAS_NEEDED = 260, + kENTRY = 261, + kEXCLUDE_FILE = 262, + kFILENAME = 263, + kGLOBAL = 264, + kGROUP = 265, + kID = 266, + kINPUT = 267, + kINTERP = 268, + kKEEP = 269, + kLOCAL = 270, + kMODE = 271, + kMUL_OP = 272, + kNUM = 273, + kOUTPUT_FORMAT = 274, + kPAGESIZE = 275, + kPROVIDE = 276, + kSEARCH_DIR = 277, + kSEGMENT = 278, + kSIZEOF_HEADERS = 279, + kSORT = 280, + kVERSION = 281, + kVERSION_SCRIPT = 282, + ADD_OP = 283, + MUL_OP = 284 }; #endif +/* Tokens. */ #define kADD_OP 258 #define kALIGN 259 -#define kENTRY 260 -#define kEXCLUDE_FILE 261 -#define kFILENAME 262 -#define kGLOBAL 263 -#define kGROUP 264 -#define kID 265 -#define kINPUT 266 -#define kINTERP 267 -#define kKEEP 268 -#define kLOCAL 269 -#define kMODE 270 -#define kMUL_OP 271 -#define kNUM 272 -#define kOUTPUT_FORMAT 273 -#define kPAGESIZE 274 -#define kPROVIDE 275 -#define kSEARCH_DIR 276 -#define kSEGMENT 277 -#define kSIZEOF_HEADERS 278 -#define kSORT 279 -#define kVERSION 280 -#define kVERSION_SCRIPT 281 -#define ADD_OP 282 -#define MUL_OP 283 +#define kAS_NEEDED 260 +#define kENTRY 261 +#define kEXCLUDE_FILE 262 +#define kFILENAME 263 +#define kGLOBAL 264 +#define kGROUP 265 +#define kID 266 +#define kINPUT 267 +#define kINTERP 268 +#define kKEEP 269 +#define kLOCAL 270 +#define kMODE 271 +#define kMUL_OP 272 +#define kNUM 273 +#define kOUTPUT_FORMAT 274 +#define kPAGESIZE 275 +#define kPROVIDE 276 +#define kSEARCH_DIR 277 +#define kSEGMENT 278 +#define kSIZEOF_HEADERS 279 +#define kSORT 280 +#define kVERSION 281 +#define kVERSION_SCRIPT 282 +#define ADD_OP 283 +#define MUL_OP 284 -#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED) -#line 58 "/home/drepper/gnu/elfutils/src/ldscript.y" -typedef union YYSTYPE { +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +#line 71 "ldscript.y" +{ uintmax_t num; enum expression_tag op; char *str; @@ -102,9 +116,10 @@ typedef union YYSTYPE { struct filename_list *filename_list; struct version *version; struct id_list *id_list; -} YYSTYPE; -/* Line 1275 of yacc.c. */ -#line 108 "ldscript.h" +} +/* Line 1489 of yacc.c. */ +#line 122 "ldscript.h" + YYSTYPE; # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 # define YYSTYPE_IS_TRIVIAL 1 @@ -112,5 +127,3 @@ typedef union YYSTYPE { extern YYSTYPE ldlval; - - diff --git a/src/ldscript.y b/src/ldscript.y index 8f68078d..252f9d4b 100644 --- a/src/ldscript.y +++ b/src/ldscript.y @@ -1,17 +1,29 @@ %{ /* Parser for linker scripts. - Copyright (C) 2001, 2002, 2003, 2004 Red Hat, Inc. + Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 Red Hat, Inc. + This file is part of Red Hat elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 2001. - This program is Open Source software; you can redistribute it and/or - modify it under the terms of the Open Software License version 1.0 as - published by the Open Source Initiative. + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. - You should have received a copy of the Open Software License along - with this program; if not, you may obtain a copy of the Open Software - License version 1.0 from http://www.opensource.org/licenses/osl.php or - by writing the Open Source Initiative c/o Lawrence Rosen, Esq., - 3001 King Ranch Road, Ukiah, CA 95482. */ + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ #ifdef HAVE_CONFIG_H # include <config.h> @@ -46,6 +58,7 @@ static void new_segment (int mode, struct output_rule *output_rule); static struct filename_list *new_filename_listelem (const char *string); static void add_inputfiles (struct filename_list *fnames); static struct id_list *new_id_listelem (const char *str); + static struct filename_list *mark_as_needed (struct filename_list *listp); static struct version *new_version (struct id_list *local, struct id_list *global); static struct version *merge_versions (struct version *one, @@ -72,6 +85,7 @@ extern int yylex (void); %token kADD_OP %token kALIGN +%token kAS_NEEDED %token kENTRY %token kEXCLUDE_FILE %token <str> kFILENAME @@ -114,6 +128,7 @@ extern int yylex (void); %type <output_rule> outputsections %type <assignment> assignment %type <filename_list> filename_id_list +%type <filename_list> filename_id_listelem %type <version> versionlist %type <version> version %type <version> version_stmt_list @@ -150,7 +165,8 @@ content: kENTRY '(' kID ')' ';' } | kINTERP '(' filename_id ')' ';' { - if (likely (ld_state.interp == NULL)) + if (likely (ld_state.interp == NULL) + && ld_state.file_type != dso_file_type) ld_state.interp = $3; } | kSEGMENT kMODE '{' outputsections '}' @@ -176,6 +192,8 @@ content: kENTRY '(' kID ')' ';' } | kINPUT '(' filename_id_list ')' { add_inputfiles ($3); } + | kAS_NEEDED '(' filename_id_list ')' + { add_inputfiles (mark_as_needed ($3)); } | kVERSION '{' versionlist '}' { add_versions ($3); } | kOUTPUT_FORMAT '(' filename_id ')' @@ -342,20 +360,37 @@ expr: kALIGN '(' expr ')' { $$ = new_expr (exp_pagesize); } ; -filename_id_list: filename_id_list comma_opt filename_id +filename_id_list: filename_id_list comma_opt filename_id_listelem { - struct filename_list *newp = new_filename_listelem ($3); - newp->next = $1->next; - $$ = $1->next = newp; + $3->next = $1->next; + $$ = $1->next = $3; } - | filename_id - { $$ = new_filename_listelem ($1); } + | filename_id_listelem + { $$ = $1; } ; comma_opt: ',' | ; +filename_id_listelem: kGROUP '(' filename_id_list ')' + { + /* First little optimization. If there is only one + file in the group don't do anything. */ + if ($3 != $3->next) + { + $3->next->group_start = 1; + $3->group_end = 1; + } + $$ = $3; + } + | kAS_NEEDED '(' filename_id_list ')' + { $$ = mark_as_needed ($3); } + | filename_id + { $$ = new_filename_listelem ($1); } + ; + + versionlist: versionlist version { $2->next = $1->next; @@ -546,6 +581,21 @@ new_filename_listelem (const char *string) } +static struct filename_list * +mark_as_needed (struct filename_list *listp) +{ + struct filename_list *runp = listp; + do + { + runp->as_needed = true; + runp = runp->next; + } + while (runp != listp); + + return listp; +} + + static void add_inputfiles (struct filename_list *fnames) { diff --git a/src/make-debug-archive.in b/src/make-debug-archive.in new file mode 100644 index 00000000..c3fcbce4 --- /dev/null +++ b/src/make-debug-archive.in @@ -0,0 +1,132 @@ +#!/bin/sh +# +# Script to make an offline archive for debugging with libdwfl-based tools. +# +# make-debug-archive ARCHIVE {options} +# make-debug-archive --kernel [--force] [RELEASE] +# +# Valid options are those listed under 'Input selection options' +# by running @UNSTRIP@ --help. +# +# The archive installed by --kernel be used automatically by -K. +# An offline archive can be used via -e in any tool that accepts those options. +# + +UNSTRIP=${UNSTRIP:-@UNSTRIP@} +AR=${AR:-@AR@} +SUDO=${SUDO:-/usr/bin/sudo} + +LS=/bin/ls +RM=/bin/rm +MV=/bin/mv +MKDIR=/bin/mkdir +XARGS=/usr/bin/xargs + +outdir=${TMPDIR:-/tmp}/debugar$$ + +usage() +{ + echo "Usage: $0 ARCHIVE {options}" + echo " or: $0 --kernel [--sudo] [--force] [RELEASE]" + echo + echo "Valid options are listed under 'Input selection options'" + echo "when running: $UNSTRIP --help" + echo + echo "The --kernel form updates the file used by -K if the" + echo "kernel installation has changed, or always with --force." + echo "With --sudo, touches the installed file via $SUDO." +} + +fatal_usage() +{ + usage >&2 + exit 2 +} + +script_version() +{ + echo "`basename $0` (@PACKAGE_NAME@) @PACKAGE_VERSION@" + echo "Copyright (C) 2007 Red Hat, Inc." + echo "This is free software; see the source for copying conditions." + echo "There is NO warranty; not even for MERCHANTABILITY or" + echo "FITNESS FOR A PARTICULAR PURPOSE." + echo "Written by Roland McGrath." +} + +sudo= +kernel=no +force_kernel=no +while [ $# -gt 0 ]; do + case "x$1" in + x--help) usage; exit 0 ;; + x--version) script_version; exit 0 ;; + x--kernel) kernel=yes ;; + x--force) force_kernel=yes ;; + x--sudo) sudo=$SUDO ;; + *) break ;; + esac + shift +done + +if [ $kernel = no ] && [ $force_kernel = yes -o -n "$sudo" ]; then + usage +fi + +if [ $kernel = yes ]; then + if [ $# -eq 0 ]; then + release=`uname -r` + elif [ $# -eq 1 ]; then + release=$1 + else + fatal_usage + fi + + dir=/usr/lib/debug/lib/modules/$release + archive=$dir/debug.a + dep=/lib/modules/$release/modules.dep + + if [ ! -d $dir ]; then + echo >&2 "$0: $dir not installed" + exit 1 + fi + + # Without --force, bail if the kernel installation is not newer. + # This file is normally touched by installing new kernels or modules. + if [ $force_kernel = no -a "$archive" -nt "$dep" ]; then + exit 0 + fi + + # We have to kill the old one first, because our own -K would use it. + [ ! -e "$archive" ] || $sudo $RM -f "$archive" || exit + + set "$archive" "-K$release" +fi + +if [ $# -lt 2 ]; then + fatal_usage +fi + +archive="$1" +shift + +case "$archive" in +/*) ;; +*) archive="`/bin/pwd`/$archive" ;; +esac + +if [ -z "$sudo" ]; then + new_archive="$archive.new" +else + new_archive="$outdir.a" +fi + +$RM -f "$new_archive" || exit + +trap '$RM -rf "$outdir" "$new_archive"' 0 1 2 15 + +$MKDIR "$outdir" && +$UNSTRIP -d "$outdir" -m -a -R "$@" && +(cd "$outdir" && $LS | $XARGS $AR cq "$new_archive") && +$sudo $MV -f "$new_archive" "$archive" + +exit @@ -1,16 +1,28 @@ -/* Print information from ELF file in human-readable form. - Copyright (C) 2000, 2001, 2002, 2003, 2004 Red Hat, Inc. +/* Print symbol information from ELF file in human-readable form. + Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007,2008 Red Hat, Inc. + This file is part of Red Hat elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 2000. - This program is Open Source software; you can redistribute it and/or - modify it under the terms of the Open Software License version 1.0 as - published by the Open Source Initiative. + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. - You should have received a copy of the Open Software License along - with this program; if not, you may obtain a copy of the Open Software - License version 1.0 from http://www.opensource.org/licenses/osl.php or - by writing the Open Source Initiative c/o Lawrence Rosen, Esq., - 3001 King Ranch Road, Ukiah, CA 95482. */ + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ #ifdef HAVE_CONFIG_H # include <config.h> @@ -27,7 +39,6 @@ #include <gelf.h> #include <inttypes.h> #include <libdw.h> -#include <libebl.h> #include <libintl.h> #include <locale.h> #include <mcheck.h> @@ -42,12 +53,16 @@ #include <sys/param.h> #include <system.h> +#include "../libebl/libeblP.h" /* Name and version of program. */ static void print_version (FILE *stream, struct argp_state *state); void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; +/* Bug report address. */ +const char *argp_program_bug_address = PACKAGE_BUGREPORT; + /* Values for the parameters which have no short form. */ #define OPT_DEFINED 0x100 @@ -98,13 +113,10 @@ static const char args_doc[] = N_("[FILE...]"); /* Prototype for option handler. */ static error_t parse_opt (int key, char *arg, struct argp_state *state); -/* Function to print some extra text in the help message. */ -static char *more_help (int key, const char *text, void *input); - /* Data structure to communicate with argp functions. */ static struct argp argp = { - options, parse_opt, args_doc, doc, NULL, more_help, NULL + options, parse_opt, args_doc, doc, NULL, NULL, NULL }; @@ -122,7 +134,7 @@ static int handle_elf (Elf *elf, const char *prefix, const char *fname, #define INTERNAL_ERROR(fname) \ error (EXIT_FAILURE, 0, gettext ("%s: INTERNAL ERROR %d (%s-%s): %s"), \ - fname, __LINE__, VERSION, __DATE__, elf_errmsg (-1)) + fname, __LINE__, PACKAGE_VERSION, __DATE__, elf_errmsg (-1)) /* Internal representation of symbols. */ @@ -205,10 +217,10 @@ main (int argc, char *argv[]) (void) setlocale (LC_ALL, ""); /* Make sure the message catalog can be found. */ - (void) bindtextdomain (PACKAGE, LOCALEDIR); + (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR); /* Initialize the message catalog. */ - (void) textdomain (PACKAGE); + (void) textdomain (PACKAGE_TARNAME); /* Parse and process arguments. */ (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL); @@ -235,21 +247,22 @@ main (int argc, char *argv[]) /* Print the version information. */ static void -print_version (FILE *stream, /*@unused@*/ struct argp_state *state) +print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) { - fprintf (stream, "nm (%s) %s\n", PACKAGE_NAME, VERSION); + fprintf (stream, "nm (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION); fprintf (stream, gettext ("\ Copyright (C) %s Red Hat, Inc.\n\ This is free software; see the source for copying conditions. There is NO\n\ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ -"), "2004"); +"), "2008"); fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); } /* Handle program arguments. */ static error_t -parse_opt (int key, char *arg, /*@unused@*/ struct argp_state *state) +parse_opt (int key, char *arg, + struct argp_state *state __attribute__ ((unused))) { switch (key) { @@ -339,27 +352,6 @@ parse_opt (int key, char *arg, /*@unused@*/ struct argp_state *state) } -static char * -more_help (int key, const char *text, /*@unused@*/ void *input) -{ - char *buf; - - switch (key) - { - case ARGP_KEY_HELP_EXTRA: - /* We print some extra information. */ - if (asprintf (&buf, gettext ("Please report bugs to %s.\n"), - PACKAGE_BUGREPORT) < 0) - buf = NULL; - return buf; - - default: - break; - } - return (char *) text; -} - - /* Open the file and determine the type. */ static int process_file (const char *fname, bool more_than_one) @@ -368,7 +360,7 @@ process_file (const char *fname, bool more_than_one) int fd = open (fname, O_RDONLY); if (fd == -1) { - error (0, errno, fname); + error (0, errno, gettext ("cannot open '%s'"), fname); return 1; } @@ -385,7 +377,7 @@ process_file (const char *fname, bool more_than_one) INTERNAL_ERROR (fname); if (close (fd) != 0) - error (EXIT_FAILURE, errno, gettext ("while close `%s'"), fname); + error (EXIT_FAILURE, errno, gettext ("while closing '%s'"), fname); return result; } @@ -397,7 +389,7 @@ process_file (const char *fname, bool more_than_one) INTERNAL_ERROR (fname); if (close (fd) != 0) - error (EXIT_FAILURE, errno, gettext ("while close `%s'"), fname); + error (EXIT_FAILURE, errno, gettext ("while closing '%s'"), fname); return result; } @@ -527,13 +519,6 @@ static const int length_map[2][3] = }; -struct global_name -{ - Dwarf_Global global; - const char *name; -}; - - static int global_compare (const void *p1, const void *p2) { @@ -548,7 +533,8 @@ static void *global_root; static int -get_global (Dwarf *dbg, Dwarf_Global *global, void *arg) +get_global (Dwarf *dbg __attribute__ ((unused)), Dwarf_Global *global, + void *arg __attribute__ ((unused))) { tsearch (memcpy (xmalloc (sizeof (Dwarf_Global)), global, sizeof (Dwarf_Global)), @@ -608,9 +594,9 @@ get_var_range (Dwarf_Die *die, Dwarf_Word *lowpc, Dwarf_Word *highpc) if (locattr == NULL) return 1; - Dwarf_Loc *loc; + Dwarf_Op *loc; size_t nloc; - if (dwarf_getloclist (locattr, &loc, &nloc) != 0) + if (dwarf_getlocation (locattr, &loc, &nloc) != 0) return 1; /* Interpret the location expressions. */ @@ -630,7 +616,7 @@ static void *local_root; static void -get_local_names (Ebl *ebl, Dwarf *dbg) +get_local_names (Dwarf *dbg) { Dwarf_Off offset = 0; Dwarf_Off old_offset; @@ -717,6 +703,18 @@ get_local_names (Ebl *ebl, Dwarf *dbg) } } +/* Do elf_strptr, but return a backup string and never NULL. */ +static const char * +sym_name (Elf *elf, GElf_Word strndx, GElf_Word st_name, char buf[], size_t n) +{ + const char *symstr = elf_strptr (elf, strndx, st_name); + if (symstr == NULL) + { + snprintf (buf, n, "[invalid st_name %#" PRIx32 "]", st_name); + symstr = buf; + } + return symstr; +} /* Show symbols in SysV format. */ static void @@ -750,9 +748,15 @@ show_symbols_sysv (Ebl *ebl, GElf_Word strndx, assert (elf_ndxscn (scn) == cnt++); - scnnames[elf_ndxscn (scn)] - = elf_strptr (ebl->elf, shstrndx, - gelf_getshdr (scn, &shdr_mem)->sh_name); + char *name = elf_strptr (ebl->elf, shstrndx, + gelf_getshdr (scn, &shdr_mem)->sh_name); + if (unlikely (name == NULL)) + { + name = alloca (sizeof "[invalid sh_name 0x12345678]"); + snprintf (name, sizeof name, "[invalid sh_name %#" PRIx32 "]", + gelf_getshdr (scn, &shdr_mem)->sh_name); + } + scnnames[elf_ndxscn (scn)] = name; } int digits = length_map[gelf_getclass (ebl->elf) - 1][radix]; @@ -786,8 +790,10 @@ show_symbols_sysv (Ebl *ebl, GElf_Word strndx, /* Iterate over all symbols. */ for (cnt = 0; cnt < nsyms; ++cnt) { - const char *symstr = elf_strptr (ebl->elf, strndx, - syms[cnt].sym.st_name); + char symstrbuf[50]; + const char *symstr = sym_name (ebl->elf, strndx, syms[cnt].sym.st_name, + symstrbuf, sizeof symstrbuf); + char symbindbuf[50]; char symtypebuf[50]; char secnamebuf[1024]; @@ -839,7 +845,7 @@ class_type_char (GElf_Sym *sym) static void -show_symbols_bsd (Elf *elf, GElf_Ehdr *ehdr, GElf_Word strndx, +show_symbols_bsd (Elf *elf, GElf_Word strndx, const char *prefix, const char *fname, const char *fullname, GElf_SymX *syms, size_t nsyms) { @@ -864,7 +870,9 @@ show_symbols_bsd (Elf *elf, GElf_Ehdr *ehdr, GElf_Word strndx, /* Iterate over all symbols. */ for (size_t cnt = 0; cnt < nsyms; ++cnt) { - const char *symstr = elf_strptr (elf, strndx, syms[cnt].sym.st_name); + char symstrbuf[50]; + const char *symstr = sym_name (elf, strndx, syms[cnt].sym.st_name, + symstrbuf, sizeof symstrbuf); /* Printing entries with a zero-length name makes the output not very well parseable. Since these entries don't carry @@ -886,7 +894,7 @@ show_symbols_bsd (Elf *elf, GElf_Ehdr *ehdr, GElf_Word strndx, ? (GELF_ST_BIND (syms[cnt].sym.st_info) == STB_WEAK ? "*" : " ") : "", - elf_strptr (elf, strndx, syms[cnt].sym.st_name)); + symstr); else printf (print_size ? sfmtstrs[radix] : fmtstrs[radix], digits, syms[cnt].sym.st_value, @@ -895,15 +903,14 @@ show_symbols_bsd (Elf *elf, GElf_Ehdr *ehdr, GElf_Word strndx, ? (GELF_ST_BIND (syms[cnt].sym.st_info) == STB_WEAK ? "*" : " ") : "", - elf_strptr (elf, strndx, syms[cnt].sym.st_name), + symstr, digits, (uint64_t) syms[cnt].sym.st_size); } } static void -show_symbols_posix (Elf *elf, GElf_Ehdr *ehdr, GElf_Word strndx, - const char *prefix, const char *fname, +show_symbols_posix (Elf *elf, GElf_Word strndx, const char *prefix, const char *fullname, GElf_SymX *syms, size_t nsyms) { if (prefix != NULL && ! print_file_name) @@ -922,7 +929,9 @@ show_symbols_posix (Elf *elf, GElf_Ehdr *ehdr, GElf_Word strndx, /* Iterate over all symbols. */ for (size_t cnt = 0; cnt < nsyms; ++cnt) { - const char *symstr = elf_strptr (elf, strndx, syms[cnt].sym.st_name); + char symstrbuf[50]; + const char *symstr = sym_name (elf, strndx, syms[cnt].sym.st_name, + symstrbuf, sizeof symstrbuf); /* Printing entries with a zero-length name makes the output not very well parseable. Since these entries don't carry @@ -953,34 +962,39 @@ show_symbols_posix (Elf *elf, GElf_Ehdr *ehdr, GElf_Word strndx, /* Maximum size of memory we allocate on the stack. */ #define MAX_STACK_ALLOC 65536 -static void -show_symbols (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, Elf_Scn *xndxscn, - GElf_Shdr *shdr, const char *prefix, const char *fname, - const char *fullname) +static int +sort_by_address (const void *p1, const void *p2) { - int sort_by_name (const void *p1, const void *p2) - { - GElf_SymX *s1 = (GElf_SymX *) p1; - GElf_SymX *s2 = (GElf_SymX *) p2; - int result; + GElf_SymX *s1 = (GElf_SymX *) p1; + GElf_SymX *s2 = (GElf_SymX *) p2; - result = strcmp (elf_strptr (ebl->elf, shdr->sh_link, s1->sym.st_name), - elf_strptr (ebl->elf, shdr->sh_link, s2->sym.st_name)); + int result = (s1->sym.st_value < s2->sym.st_value + ? -1 : (s1->sym.st_value == s2->sym.st_value ? 0 : 1)); - return reverse_sort ? -result : result; - } + return reverse_sort ? -result : result; +} - int sort_by_address (const void *p1, const void *p2) - { - GElf_SymX *s1 = (GElf_SymX *) p1; - GElf_SymX *s2 = (GElf_SymX *) p2; +static Elf_Data *sort_by_name_strtab; - int result = (s1->sym.st_value < s2->sym.st_value - ? -1 : (s1->sym.st_value == s2->sym.st_value ? 0 : 1)); +static int +sort_by_name (const void *p1, const void *p2) +{ + GElf_SymX *s1 = (GElf_SymX *) p1; + GElf_SymX *s2 = (GElf_SymX *) p2; - return reverse_sort ? -result : result; - } + const char *n1 = sort_by_name_strtab->d_buf + s1->sym.st_name; + const char *n2 = sort_by_name_strtab->d_buf + s2->sym.st_name; + int result = strcmp (n1, n2); + + return reverse_sort ? -result : result; +} + +static void +show_symbols (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, Elf_Scn *xndxscn, + GElf_Shdr *shdr, const char *prefix, const char *fname, + const char *fullname) +{ /* Get the section header string table index. */ size_t shstrndx; if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) @@ -1021,7 +1035,7 @@ show_symbols (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, Elf_Scn *xndxscn, { (void) dwarf_getpubnames (dbg, get_global, NULL, 0); - get_local_names (ebl, dbg); + get_local_names (dbg); } } @@ -1066,6 +1080,8 @@ show_symbols (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, Elf_Scn *xndxscn, { const char *symstr = elf_strptr (ebl->elf, shdr->sh_link, sym->st_name); + if (symstr == NULL) + continue; longest_name = MAX ((size_t) longest_name, strlen (symstr)); @@ -1157,7 +1173,11 @@ show_symbols (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, Elf_Scn *xndxscn, /* Sort the entries according to the users wishes. */ if (sort == sort_name) - qsort (sym_mem, nentries, sizeof (GElf_SymX), sort_by_name); + { + sort_by_name_strtab = elf_getdata (elf_getscn (ebl->elf, shdr->sh_link), + NULL); + qsort (sym_mem, nentries, sizeof (GElf_SymX), sort_by_name); + } else if (sort == sort_numeric) qsort (sym_mem, nentries, sizeof (GElf_SymX), sort_by_address); @@ -1171,15 +1191,15 @@ show_symbols (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, Elf_Scn *xndxscn, break; case format_bsd: - show_symbols_bsd (ebl->elf, ehdr, shdr->sh_link, prefix, fname, - fullname, sym_mem, nentries); + show_symbols_bsd (ebl->elf, shdr->sh_link, prefix, fname, fullname, + sym_mem, nentries); break; case format_posix: default: assert (format == format_posix); - show_symbols_posix (ebl->elf, ehdr, shdr->sh_link, prefix, fname, - fullname, sym_mem, nentries); + show_symbols_posix (ebl->elf, shdr->sh_link, prefix, fullname, sym_mem, + nentries); break; } @@ -1300,3 +1320,6 @@ handle_elf (Elf *elf, const char *prefix, const char *fname, return result; } + + +#include "debugpred.h" diff --git a/src/objdump.c b/src/objdump.c new file mode 100644 index 00000000..91fa8750 --- /dev/null +++ b/src/objdump.c @@ -0,0 +1,815 @@ +/* Print information from ELF file in human-readable form. + Copyright (C) 2005, 2006, 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + Written by Ulrich Drepper <drepper@redhat.com>, 2005. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <argp.h> +#include <error.h> +#include <fcntl.h> +#include <inttypes.h> +#include <libintl.h> +#include <locale.h> +#include <mcheck.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdio_ext.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/param.h> + +#include <system.h> +#include "../libebl/libeblP.h" + + +/* Name and version of program. */ +static void print_version (FILE *stream, struct argp_state *state); +void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; + +/* Bug report address. */ +const char *argp_program_bug_address = PACKAGE_BUGREPORT; + + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + { NULL, 0, NULL, 0, N_("Mode selection:"), 0 }, + { "reloc", 'r', NULL, 0, N_("Display relocation information."), 0 }, + { "full-contents", 's', NULL, 0, + N_("Display the full contents of all sections requested"), 0 }, + { "disassemble", 'd', NULL, 0, + N_("Display assembler code of executable sections"), 0 }, + + { NULL, 0, NULL, 0, N_("Output option selection:"), 0 }, + { "section", 'j', "NAME", 0, + N_("Only display information for section NAME."), 0 }, + + { NULL, 0, NULL, 0, NULL, 0 } +}; + +/* Short description of program. */ +static const char doc[] = N_("\ +Show information from FILEs (a.out by default)."); + +/* Strings for arguments in help texts. */ +static const char args_doc[] = N_("[FILE...]"); + +/* Prototype for option handler. */ +static error_t parse_opt (int key, char *arg, struct argp_state *state); + +/* Data structure to communicate with argp functions. */ +static struct argp argp = +{ + options, parse_opt, args_doc, doc, NULL, NULL, NULL +}; + + +/* Print symbols in file named FNAME. */ +static int process_file (const char *fname, bool more_than_one); + +/* Handle content of archive. */ +static int handle_ar (int fd, Elf *elf, const char *prefix, const char *fname, + const char *suffix); + +/* Handle ELF file. */ +static int handle_elf (Elf *elf, const char *prefix, const char *fname, + const char *suffix); + + +#define INTERNAL_ERROR(fname) \ + error (EXIT_FAILURE, 0, gettext ("%s: INTERNAL ERROR %d (%s-%s): %s"), \ + fname, __LINE__, PACKAGE_VERSION, __DATE__, elf_errmsg (-1)) + + +/* List of sections which should be used. */ +static struct section_list +{ + bool is_name; + union + { + const char *name; + uint32_t scnndx; + }; + struct section_list *next; +} *section_list; + + +/* If true print archive index. */ +static bool print_relocs; + +/* If true print full contents of requested sections. */ +static bool print_full_content; + +/* If true print disassembled output.. */ +static bool print_disasm; + +int +main (int argc, char *argv[]) +{ + /* Make memory leak detection possible. */ + mtrace (); + + /* We use no threads here which can interfere with handling a stream. */ + (void) __fsetlocking (stdin, FSETLOCKING_BYCALLER); + (void) __fsetlocking (stdout, FSETLOCKING_BYCALLER); + (void) __fsetlocking (stderr, FSETLOCKING_BYCALLER); + + /* Set locale. */ + (void) setlocale (LC_ALL, ""); + + /* Make sure the message catalog can be found. */ + (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR); + + /* Initialize the message catalog. */ + (void) textdomain (PACKAGE_TARNAME); + + /* Parse and process arguments. */ + int remaining; + (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL); + + /* Tell the library which version we are expecting. */ + (void) elf_version (EV_CURRENT); + + int result = 0; + if (remaining == argc) + /* The user didn't specify a name so we use a.out. */ + result = process_file ("a.out", false); + else + { + /* Process all the remaining files. */ + const bool more_than_one = remaining + 1 < argc; + + do + result |= process_file (argv[remaining], more_than_one); + while (++remaining < argc); + } + + return result; +} + + +/* Print the version information. */ +static void +print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) +{ + fprintf (stream, "objdump (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION); + fprintf (stream, gettext ("\ +Copyright (C) %s Red Hat, Inc.\n\ +This is free software; see the source for copying conditions. There is NO\n\ +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ +"), "2008"); + fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); +} + + +/* Handle program arguments. */ +static error_t +parse_opt (int key, char *arg, + struct argp_state *state __attribute__ ((unused))) +{ + /* True if any of the control options is set. */ + static bool any_control_option; + + switch (key) + { + case 'j': + { + struct section_list *newp = xmalloc (sizeof (*newp)); + char *endp; + newp->scnndx = strtoul (arg, &endp, 0); + if (*endp == 0) + newp->is_name = false; + else + { + newp->name = arg; + newp->is_name = true; + } + newp->next = section_list; + section_list = newp; + } + any_control_option = true; + break; + + case 'd': + print_disasm = true; + any_control_option = true; + break; + + case 'r': + print_relocs = true; + any_control_option = true; + break; + + case 's': + print_full_content = true; + any_control_option = true; + break; + + case ARGP_KEY_FINI: + if (! any_control_option) + { + fputs (gettext ("No operation specified.\n"), stderr); + argp_help (&argp, stderr, ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR, + program_invocation_short_name); + exit (1); + } + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + + +/* Open the file and determine the type. */ +static int +process_file (const char *fname, bool more_than_one) +{ + /* Open the file. */ + int fd = open (fname, O_RDONLY); + if (fd == -1) + { + error (0, errno, gettext ("cannot open %s"), fname); + return 1; + } + + /* Now get the ELF descriptor. */ + Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL); + if (elf != NULL) + { + if (elf_kind (elf) == ELF_K_ELF) + { + int result = handle_elf (elf, more_than_one ? "" : NULL, + fname, NULL); + + if (elf_end (elf) != 0) + INTERNAL_ERROR (fname); + + if (close (fd) != 0) + error (EXIT_FAILURE, errno, gettext ("while close `%s'"), fname); + + return result; + } + else if (elf_kind (elf) == ELF_K_AR) + { + int result = handle_ar (fd, elf, NULL, fname, NULL); + + if (elf_end (elf) != 0) + INTERNAL_ERROR (fname); + + if (close (fd) != 0) + error (EXIT_FAILURE, errno, gettext ("while close `%s'"), fname); + + return result; + } + + /* We cannot handle this type. Close the descriptor anyway. */ + if (elf_end (elf) != 0) + INTERNAL_ERROR (fname); + } + + error (0, 0, gettext ("%s: File format not recognized"), fname); + + return 1; +} + + +static int +handle_ar (int fd, Elf *elf, const char *prefix, const char *fname, + const char *suffix) +{ + size_t fname_len = strlen (fname) + 1; + size_t prefix_len = prefix != NULL ? strlen (prefix) : 0; + char new_prefix[prefix_len + fname_len + 2]; + size_t suffix_len = suffix != NULL ? strlen (suffix) : 0; + char new_suffix[suffix_len + 2]; + Elf *subelf; + Elf_Cmd cmd = ELF_C_READ_MMAP; + int result = 0; + + char *cp = new_prefix; + if (prefix != NULL) + cp = stpcpy (cp, prefix); + cp = stpcpy (cp, fname); + stpcpy (cp, "["); + + cp = new_suffix; + if (suffix != NULL) + cp = stpcpy (cp, suffix); + stpcpy (cp, "]"); + + /* Process all the files contained in the archive. */ + while ((subelf = elf_begin (fd, cmd, elf)) != NULL) + { + /* The the header for this element. */ + Elf_Arhdr *arhdr = elf_getarhdr (subelf); + + /* Skip over the index entries. */ + if (strcmp (arhdr->ar_name, "/") != 0 + && strcmp (arhdr->ar_name, "//") != 0) + { + if (elf_kind (subelf) == ELF_K_ELF) + result |= handle_elf (subelf, new_prefix, arhdr->ar_name, + new_suffix); + else if (elf_kind (subelf) == ELF_K_AR) + result |= handle_ar (fd, subelf, new_prefix, arhdr->ar_name, + new_suffix); + else + { + error (0, 0, gettext ("%s%s%s: file format not recognized"), + new_prefix, arhdr->ar_name, new_suffix); + result = 1; + } + } + + /* Get next archive element. */ + cmd = elf_next (subelf); + if (elf_end (subelf) != 0) + INTERNAL_ERROR (fname); + } + + return result; +} + + +static void +show_relocs_rel (Ebl *ebl, GElf_Shdr *shdr, Elf_Data *data, + Elf_Data *symdata, Elf_Data *xndxdata, size_t symstrndx, + size_t shstrndx) +{ + int elfclass = gelf_getclass (ebl->elf); + int nentries = shdr->sh_size / shdr->sh_entsize; + + for (int cnt = 0; cnt < nentries; ++cnt) + { + GElf_Rel relmem; + GElf_Rel *rel; + + rel = gelf_getrel (data, cnt, &relmem); + if (rel != NULL) + { + char buf[128]; + GElf_Sym symmem; + GElf_Sym *sym; + Elf32_Word xndx; + + sym = gelf_getsymshndx (symdata, xndxdata, GELF_R_SYM (rel->r_info), + &symmem, &xndx); + if (sym == NULL) + printf ("%0*" PRIx64 " %-20s <%s %ld>\n", + elfclass == ELFCLASS32 ? 8 : 16, rel->r_offset, + ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info)) + ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info), + buf, sizeof (buf)) + : gettext ("<INVALID RELOC>"), + gettext ("INVALID SYMBOL"), + (long int) GELF_R_SYM (rel->r_info)); + else if (GELF_ST_TYPE (sym->st_info) != STT_SECTION) + printf ("%0*" PRIx64 " %-20s %s\n", + elfclass == ELFCLASS32 ? 8 : 16, rel->r_offset, + ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info)) + ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info), + buf, sizeof (buf)) + : gettext ("<INVALID RELOC>"), + elf_strptr (ebl->elf, symstrndx, sym->st_name)); + else + { + GElf_Shdr destshdr_mem; + GElf_Shdr *destshdr; + destshdr = gelf_getshdr (elf_getscn (ebl->elf, + sym->st_shndx == SHN_XINDEX + ? xndx : sym->st_shndx), + &destshdr_mem); + + if (shdr == NULL) + printf ("%0*" PRIx64 " %-20s <%s %ld>\n", + elfclass == ELFCLASS32 ? 8 : 16, rel->r_offset, + ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info)) + ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info), + buf, sizeof (buf)) + : gettext ("<INVALID RELOC>"), + gettext ("INVALID SECTION"), + (long int) (sym->st_shndx == SHN_XINDEX + ? xndx : sym->st_shndx)); + else + printf ("%0*" PRIx64 " %-20s %s\n", + elfclass == ELFCLASS32 ? 8 : 16, rel->r_offset, + ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info)) + ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info), + buf, sizeof (buf)) + : gettext ("<INVALID RELOC>"), + elf_strptr (ebl->elf, shstrndx, destshdr->sh_name)); + } + } + } +} + + +static void +show_relocs_rela (Ebl *ebl, GElf_Shdr *shdr, Elf_Data *data, + Elf_Data *symdata, Elf_Data *xndxdata, size_t symstrndx, + size_t shstrndx) +{ + int elfclass = gelf_getclass (ebl->elf); + int nentries = shdr->sh_size / shdr->sh_entsize; + + for (int cnt = 0; cnt < nentries; ++cnt) + { + GElf_Rela relmem; + GElf_Rela *rel; + + rel = gelf_getrela (data, cnt, &relmem); + if (rel != NULL) + { + char buf[128]; + GElf_Sym symmem; + GElf_Sym *sym; + Elf32_Word xndx; + + sym = gelf_getsymshndx (symdata, xndxdata, GELF_R_SYM (rel->r_info), + &symmem, &xndx); + if (sym == NULL) + printf ("%0*" PRIx64 " %-20s <%s %ld>", + elfclass == ELFCLASS32 ? 8 : 16, rel->r_offset, + ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info)) + ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info), + buf, sizeof (buf)) + : gettext ("<INVALID RELOC>"), + gettext ("INVALID SYMBOL"), + (long int) GELF_R_SYM (rel->r_info)); + else if (GELF_ST_TYPE (sym->st_info) != STT_SECTION) + printf ("%0*" PRIx64 " %-20s %s", + elfclass == ELFCLASS32 ? 8 : 16, rel->r_offset, + ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info)) + ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info), + buf, sizeof (buf)) + : gettext ("<INVALID RELOC>"), + elf_strptr (ebl->elf, symstrndx, sym->st_name)); + else + { + GElf_Shdr destshdr_mem; + GElf_Shdr *destshdr; + destshdr = gelf_getshdr (elf_getscn (ebl->elf, + sym->st_shndx == SHN_XINDEX + ? xndx : sym->st_shndx), + &destshdr_mem); + + if (shdr == NULL) + printf ("%0*" PRIx64 " %-20s <%s %ld>", + elfclass == ELFCLASS32 ? 8 : 16, rel->r_offset, + ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info)) + ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info), + buf, sizeof (buf)) + : gettext ("<INVALID RELOC>"), + gettext ("INVALID SECTION"), + (long int) (sym->st_shndx == SHN_XINDEX + ? xndx : sym->st_shndx)); + else + printf ("%0*" PRIx64 " %-20s %s", + elfclass == ELFCLASS32 ? 8 : 16, rel->r_offset, + ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info)) + ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info), + buf, sizeof (buf)) + : gettext ("<INVALID RELOC>"), + elf_strptr (ebl->elf, shstrndx, destshdr->sh_name)); + } + + if (rel->r_addend != 0) + printf ("+%#" PRIx64, rel->r_addend); + putchar ('\n'); + } + } +} + + +static bool +section_match (Elf *elf, uint32_t scnndx, GElf_Shdr *shdr, size_t shstrndx) +{ + if (section_list == NULL) + return true; + + struct section_list *runp = section_list; + + do + { + if (runp->is_name) + { + if (strcmp (runp->name, + elf_strptr (elf, shstrndx, shdr->sh_name)) == 0) + return true; + } + else + { + if (runp->scnndx == scnndx) + return true; + } + + runp = runp->next; + } + while (runp != NULL); + + return false; +} + + +static int +show_relocs (Ebl *ebl, const char *fname, uint32_t shstrndx) +{ + int elfclass = gelf_getclass (ebl->elf); + + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + + if (shdr == NULL) + INTERNAL_ERROR (fname); + + if (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA) + { + if (! section_match (ebl->elf, elf_ndxscn (scn), shdr, shstrndx)) + continue; + + GElf_Shdr destshdr_mem; + GElf_Shdr *destshdr = gelf_getshdr (elf_getscn (ebl->elf, + shdr->sh_info), + &destshdr_mem); + + printf (gettext ("RELOCATION RECORDS FOR [%s]:\n" + "%-*s TYPE VALUE\n"), + elf_strptr (ebl->elf, shstrndx, destshdr->sh_name), + elfclass == ELFCLASS32 ? 8 : 16, gettext ("OFFSET")); + + /* Get the data of the section. */ + Elf_Data *data = elf_getdata (scn, NULL); + if (data == NULL) + continue; + + /* Get the symbol table information. */ + Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link); + GElf_Shdr symshdr_mem; + GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem); + Elf_Data *symdata = elf_getdata (symscn, NULL); + + /* Search for the optional extended section index table. */ + Elf_Data *xndxdata = NULL; + Elf_Scn *xndxscn = NULL; + while ((xndxscn = elf_nextscn (ebl->elf, xndxscn)) != NULL) + { + GElf_Shdr xndxshdr_mem; + GElf_Shdr *xndxshdr; + + xndxshdr = gelf_getshdr (xndxscn, &xndxshdr_mem); + if (xndxshdr != NULL && xndxshdr->sh_type == SHT_SYMTAB_SHNDX + && xndxshdr->sh_link == elf_ndxscn (symscn)) + { + /* Found it. */ + xndxdata = elf_getdata (xndxscn, NULL); + break; + } + } + + if (shdr->sh_type == SHT_REL) + show_relocs_rel (ebl, shdr, data, symdata, xndxdata, + symshdr->sh_link, shstrndx); + else + show_relocs_rela (ebl, shdr, data, symdata, xndxdata, + symshdr->sh_link, shstrndx); + } + } + + fputs_unlocked ("\n\n", stdout); + + return 0; +} + + +static int +show_full_content (Ebl *ebl, const char *fname, uint32_t shstrndx) +{ + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + + if (shdr == NULL) + INTERNAL_ERROR (fname); + + if (shdr->sh_type == SHT_PROGBITS && shdr->sh_size > 0) + { + if (! section_match (ebl->elf, elf_ndxscn (scn), shdr, shstrndx)) + continue; + + printf (gettext ("Contents of section %s:\n"), + elf_strptr (ebl->elf, shstrndx, shdr->sh_name)); + + /* Get the data of the section. */ + Elf_Data *data = elf_getdata (scn, NULL); + if (data == NULL) + continue; + + unsigned char *cp = data->d_buf; + size_t cnt; + for (cnt = 0; cnt + 16 < data->d_size; cp += 16, cnt += 16) + { + printf (" %04zx ", cnt); + + for (size_t inner = 0; inner < 16; inner += 4) + printf ("%02hhx%02hhx%02hhx%02hhx ", + cp[inner], cp[inner + 1], cp[inner + 2], + cp[inner + 3]); + fputc_unlocked (' ', stdout); + + for (size_t inner = 0; inner < 16; ++inner) + fputc_unlocked (isascii (cp[inner]) && isprint (cp[inner]) + ? cp[inner] : '.', stdout); + fputc_unlocked ('\n', stdout); + } + + printf (" %04zx ", cnt); + + size_t remaining = data->d_size - cnt; + size_t inner; + for (inner = 0; inner + 4 <= remaining; inner += 4) + printf ("%02hhx%02hhx%02hhx%02hhx ", + cp[inner], cp[inner + 1], cp[inner + 2], cp[inner + 3]); + + for (; inner < remaining; ++inner) + printf ("%02hhx", cp[inner]); + + for (inner = 2 * (16 - inner) + (16 - inner + 3) / 4 + 1; inner > 0; + --inner) + fputc_unlocked (' ', stdout); + + for (inner = 0; inner < remaining; ++inner) + fputc_unlocked (isascii (cp[inner]) && isprint (cp[inner]) + ? cp[inner] : '.', stdout); + fputc_unlocked ('\n', stdout); + + fputc_unlocked ('\n', stdout); + } + } + + return 0; +} + + +struct disasm_info +{ + GElf_Addr addr; + const uint8_t *cur; + const uint8_t *last_end; +}; + + +// XXX This is not the preferred output for all architectures. Needs +// XXX customization, too. +static int +disasm_output (char *buf, size_t buflen, void *arg) +{ + struct disasm_info *info = (struct disasm_info *) arg; + + printf ("%8" PRIx64 ": ", (uint64_t) info->addr); + size_t cnt; + for (cnt = 0; cnt < (size_t) MIN (info->cur - info->last_end, 8); ++cnt) + printf (" %02" PRIx8, info->last_end[cnt]); + printf ("%*s %.*s\n", + (int) (8 - cnt) * 3 + 1, "", (int) buflen, buf); + + info->addr += cnt; + + /* We limit the number of bytes printed before the mnemonic to 8. + Print the rest on a separate, following line. */ + if (info->cur - info->last_end > 8) + { + printf ("%8" PRIx64 ": ", (uint64_t) info->addr); + for (; cnt < (size_t) (info->cur - info->last_end); ++cnt) + printf (" %02" PRIx8, info->last_end[cnt]); + putchar_unlocked ('\n'); + info->addr += info->cur - info->last_end - 8; + } + + info->last_end = info->cur; + + return 0; +} + + +static int +show_disasm (Ebl *ebl, const char *fname, uint32_t shstrndx) +{ + DisasmCtx_t *ctx = disasm_begin (ebl, ebl->elf, NULL /* XXX TODO */); + if (ctx == NULL) + error (EXIT_FAILURE, 0, gettext ("cannot disassemble")); + + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + + if (shdr == NULL) + INTERNAL_ERROR (fname); + + if (shdr->sh_type == SHT_PROGBITS && shdr->sh_size > 0 + && (shdr->sh_flags & SHF_EXECINSTR) != 0) + { + if (! section_match (ebl->elf, elf_ndxscn (scn), shdr, shstrndx)) + continue; + + Elf_Data *data = elf_getdata (scn, NULL); + if (data == NULL) + continue; + + printf ("Disassembly of section %s:\n\n", + elf_strptr (ebl->elf, shstrndx, shdr->sh_name)); + + struct disasm_info info; + info.addr = shdr->sh_addr; + info.last_end = info.cur = data->d_buf; + + disasm_cb (ctx, &info.cur, info.cur + data->d_size, info.addr, + "%7m %.1o,%.2o,%.3o%34a %l", disasm_output, &info, + NULL /* XXX */); + } + } + + (void) disasm_end (ctx); + + return 0; +} + + +static int +handle_elf (Elf *elf, const char *prefix, const char *fname, + const char *suffix) +{ + + /* Get the backend for this object file type. */ + Ebl *ebl = ebl_openbackend (elf); + + printf ("%s: elf%d-%s\n\n", + fname, gelf_getclass (elf) == ELFCLASS32 ? 32 : 64, + ebl_backend_name (ebl)); + + /* Create the full name of the file. */ + size_t prefix_len = prefix == NULL ? 0 : strlen (prefix); + size_t suffix_len = suffix == NULL ? 0 : strlen (suffix); + size_t fname_len = strlen (fname) + 1; + char fullname[prefix_len + 1 + fname_len + suffix_len]; + char *cp = fullname; + if (prefix != NULL) + cp = mempcpy (cp, prefix, prefix_len); + cp = mempcpy (cp, fname, fname_len); + if (suffix != NULL) + memcpy (cp - 1, suffix, suffix_len + 1); + + /* Get the section header string table index. */ + size_t shstrndx; + if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + int result = 0; + if (print_disasm) + result = show_disasm (ebl, fullname, shstrndx); + if (print_relocs && !print_disasm) + result = show_relocs (ebl, fullname, shstrndx); + if (print_full_content) + result = show_full_content (ebl, fullname, shstrndx); + + /* Close the ELF backend library descriptor. */ + ebl_closebackend (ebl); + + return result; +} + + +#include "debugpred.h" diff --git a/src/ranlib.c b/src/ranlib.c new file mode 100644 index 00000000..a915e558 --- /dev/null +++ b/src/ranlib.c @@ -0,0 +1,309 @@ +/* Generate an index to speed access to archives. + Copyright (C) 2005, 2006, 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + Written by Ulrich Drepper <drepper@redhat.com>, 2005. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <ar.h> +#include <argp.h> +#include <assert.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <gelf.h> +#include <libintl.h> +#include <locale.h> +#include <mcheck.h> +#include <obstack.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdio_ext.h> +#include <unistd.h> +#include <sys/mman.h> +#include <sys/param.h> +#include <sys/stat.h> + +#include <system.h> + +#include "arlib.h" + + +/* Prototypes for local functions. */ +static int handle_file (const char *fname); + + +/* Name and version of program. */ +static void print_version (FILE *stream, struct argp_state *state); +void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; + +/* Bug report address. */ +const char *argp_program_bug_address = PACKAGE_BUGREPORT; + + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + { NULL, 0, NULL, 0, NULL, 0 } +}; + +/* Short description of program. */ +static const char doc[] = N_("Generate an index to speed access to archives."); + +/* Strings for arguments in help texts. */ +static const char args_doc[] = N_("ARCHIVE"); + +/* Data structure to communicate with argp functions. */ +static const struct argp argp = +{ + options, NULL, args_doc, doc, NULL, NULL, NULL +}; + + +int +main (int argc, char *argv[]) +{ + /* Make memory leak detection possible. */ + mtrace (); + + /* We use no threads here which can interfere with handling a stream. */ + (void) __fsetlocking (stdin, FSETLOCKING_BYCALLER); + (void) __fsetlocking (stdout, FSETLOCKING_BYCALLER); + (void) __fsetlocking (stderr, FSETLOCKING_BYCALLER); + + /* Set locale. */ + (void) setlocale (LC_ALL, ""); + + /* Make sure the message catalog can be found. */ + (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR); + + /* Initialize the message catalog. */ + (void) textdomain (PACKAGE_TARNAME); + + /* Parse and process arguments. */ + int remaining; + (void) argp_parse (&argp, argc, argv, ARGP_IN_ORDER, &remaining, NULL); + + /* Tell the library which version we are expecting. */ + (void) elf_version (EV_CURRENT); + + /* There must at least be one more parameter specifying the archive. */ + if (remaining == argc) + { + error (0, 0, gettext ("Archive name required")); + argp_help (&argp, stderr, ARGP_HELP_SEE, "ranlib"); + exit (EXIT_FAILURE); + } + + /* We accept the names of multiple archives. */ + int status = 0; + do + status |= handle_file (argv[remaining]); + while (++remaining < argc); + + return status; +} + + +/* Print the version information. */ +static void +print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) +{ + fprintf (stream, "ranlib (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION); + fprintf (stream, gettext ("\ +Copyright (C) %s Red Hat, Inc.\n\ +This is free software; see the source for copying conditions. There is NO\n\ +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ +"), "2008"); + fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); +} + + +static int +copy_content (Elf *elf, int newfd, off_t off, size_t n) +{ + size_t len; + char *rawfile = elf_rawfile (elf, &len); + + assert (off + n <= len); + + /* Tell the kernel we will read all the pages sequentially. */ + size_t ps = sysconf (_SC_PAGESIZE); + if (n > 2 * ps) + posix_madvise (rawfile + (off & ~(ps - 1)), n, POSIX_MADV_SEQUENTIAL); + + return write_retry (newfd, rawfile + off, n) != (ssize_t) n; +} + + +/* Handle a file given on the command line. */ +static int +handle_file (const char *fname) +{ + int fd = open (fname, O_RDONLY); + if (fd == -1) + { + error (0, errno, gettext ("cannot open '%s'"), fname); + return 1; + } + + struct stat st; + if (fstat (fd, &st) != 0) + { + error (0, errno, gettext ("cannot stat '%s'"), fname); + close (fd); + return 1; + } + + /* First we walk through the file, looking for all ELF files to + collect symbols from. */ + Elf *arelf = elf_begin (fd, ELF_C_READ_MMAP, NULL); + if (arelf == NULL) + { + error (0, 0, gettext ("cannot create ELF descriptor for '%s': %s"), + fname, elf_errmsg (-1)); + close (fd); + return 1; + } + + if (elf_kind (arelf) != ELF_K_AR) + { + error (0, 0, gettext ("'%s' is no archive"), fname); + elf_end (arelf); + close (fd); + return 1; + } + + arlib_init (); + + /* Iterate over the content of the archive. */ + off_t index_off = -1; + size_t index_size = 0; + off_t cur_off = SARMAG; + Elf *elf; + Elf_Cmd cmd = ELF_C_READ_MMAP; + while ((elf = elf_begin (fd, cmd, arelf)) != NULL) + { + Elf_Arhdr *arhdr = elf_getarhdr (elf); + assert (arhdr != NULL); + + /* If this is the index, remember the location. */ + if (strcmp (arhdr->ar_name, "/") == 0) + { + index_off = elf_getaroff (elf); + index_size = arhdr->ar_size; + } + else + { + arlib_add_symbols (elf, fname, arhdr->ar_name, cur_off); + cur_off += (((arhdr->ar_size + 1) & ~((off_t) 1)) + + sizeof (struct ar_hdr)); + } + + /* Get next archive element. */ + cmd = elf_next (elf); + if (elf_end (elf) != 0) + error (0, 0, gettext ("error while freeing sub-ELF descriptor: %s"), + elf_errmsg (-1)); + } + + arlib_finalize (); + + /* If the file contains no symbols we need not do anything. */ + int status = 0; + if (symtab.symsnamelen != 0 + /* We have to rewrite the file also if it initially had an index + but now does not need one anymore. */ + || (symtab.symsnamelen == 0 && index_size != 0)) + { + /* Create a new, temporary file in the same directory as the + original file. */ + char tmpfname[strlen (fname) + 7]; + strcpy (stpcpy (tmpfname, fname), "XXXXXX"); + int newfd = mkstemp (tmpfname); + if (unlikely (newfd == -1)) + { + nonew: + error (0, errno, gettext ("cannot create new file")); + status = 1; + } + else + { + /* Create the header. */ + if (unlikely (write_retry (newfd, ARMAG, SARMAG) != SARMAG)) + { + // XXX Use /prof/self/fd/%d ??? + nonew_unlink: + unlink (tmpfname); + if (newfd != -1) + close (newfd); + goto nonew; + } + + /* Create the new file. There are three parts as far we are + concerned: 1. original context before the index, 2. the + new index, 3. everything after the new index. */ + off_t rest_off; + if (index_off != -1) + rest_off = (index_off + sizeof (struct ar_hdr) + + ((index_size + 1) & ~1ul)); + else + rest_off = SARMAG; + + if ((symtab.symsnamelen != 0 + && ((write_retry (newfd, symtab.symsoff, + symtab.symsofflen) + != (ssize_t) symtab.symsofflen) + || (write_retry (newfd, symtab.symsname, + symtab.symsnamelen) + != (ssize_t) symtab.symsnamelen))) + /* Even if the original file had content before the + symbol table, we write it in the correct order. */ + || (index_off > SARMAG + && copy_content (arelf, newfd, SARMAG, index_off - SARMAG)) + || copy_content (arelf, newfd, rest_off, st.st_size - rest_off) + /* Set the mode of the new file to the same values the + original file has. */ + || fchmod (newfd, st.st_mode & ALLPERMS) != 0 + /* Never complain about fchown failing. */ + || (({asm ("" :: "r" (fchown (newfd, st.st_uid, st.st_gid))); }), + close (newfd) != 0) + || (newfd = -1, rename (tmpfname, fname) != 0)) + goto nonew_unlink; + } + } + + elf_end (arelf); + + arlib_fini (); + + close (fd); + + return status; +} + + +#include "debugpred.h" diff --git a/src/readelf.c b/src/readelf.c index 6129e29d..2797a849 100644 --- a/src/readelf.c +++ b/src/readelf.c @@ -1,16 +1,28 @@ /* Print information from ELF file in human-readable form. - Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 Red Hat, Inc. + Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008 Red Hat, Inc. + This file is part of Red Hat elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 1999. - This program is Open Source software; you can redistribute it and/or - modify it under the terms of the Open Software License version 1.0 as - published by the Open Source Initiative. + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. - You should have received a copy of the Open Software License along - with this program; if not, you may obtain a copy of the Open Software - License version 1.0 from http://www.opensource.org/licenses/osl.php or - by writing the Open Source Initiative c/o Lawrence Rosen, Esq., - 3001 King Ranch Road, Ukiah, CA 95482. */ + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ #ifdef HAVE_CONFIG_H # include <config.h> @@ -18,6 +30,7 @@ #include <argp.h> #include <assert.h> +#include <ctype.h> #include <dwarf.h> #include <errno.h> #include <error.h> @@ -26,9 +39,10 @@ #include <inttypes.h> #include <langinfo.h> #include <libdw.h> -#include <libebl.h> +#include <libdwfl.h> #include <libintl.h> #include <locale.h> +#include <stdarg.h> #include <stdbool.h> #include <stdlib.h> #include <string.h> @@ -37,7 +51,11 @@ #include <sys/param.h> #include <system.h> +#include "../libelf/libelfP.h" +#include "../libelf/common.h" +#include "../libebl/libeblP.h" #include "../libdw/libdwP.h" +#include "../libdwfl/libdwflP.h" #include "../libdw/memory-access.h" @@ -45,30 +63,43 @@ static void print_version (FILE *stream, struct argp_state *state); void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; +/* Bug report address. */ +const char *argp_program_bug_address = PACKAGE_BUGREPORT; + /* Definitions of arguments for argp functions. */ static const struct argp_option options[] = { - { NULL, 0, NULL, 0, N_("Output selection:") }, - { "all", 'a', NULL, 0, N_("Equivalent to: -h -l") }, - { "dynamic", 'd', NULL, 0, N_("Display the dynamic segment") }, - { "file-header", 'h', NULL, 0, N_("Display the ELF file header") }, + { NULL, 0, NULL, 0, N_("Output selection:"), 0 }, + { "all", 'a', NULL, 0, N_("Equivalent to: -h -l"), 0 }, + { "dynamic", 'd', NULL, 0, N_("Display the dynamic segment"), 0 }, + { "file-header", 'h', NULL, 0, N_("Display the ELF file header"), 0 }, { "histogram", 'I', NULL, 0, - N_("Display histogram of bucket list lengths") }, - { "program-headers", 'l', NULL, 0, N_("Display the program headers") }, - { "relocs", 'r', NULL, 0, N_("Display relocations") }, - { "section-headers", 'S', NULL, 0, N_("Display the sections' header") }, - { "symbols", 's', NULL, 0, N_("Display the symbol table") }, - { "version-info", 'V', NULL, 0, N_("Display versioning information") }, + N_("Display histogram of bucket list lengths"), 0 }, + { "program-headers", 'l', NULL, 0, N_("Display the program headers"), 0 }, + { "segments", 'l', NULL, OPTION_ALIAS | OPTION_HIDDEN, NULL, 0 }, + { "relocs", 'r', NULL, 0, N_("Display relocations"), 0 }, + { "section-headers", 'S', NULL, 0, N_("Display the sections' header"), 0 }, + { "sections", 'S', NULL, OPTION_ALIAS | OPTION_HIDDEN, NULL, 0 }, + { "symbols", 's', NULL, 0, N_("Display the symbol table"), 0 }, + { "version-info", 'V', NULL, 0, N_("Display versioning information"), 0 }, { "debug-dump", 'w', "SECTION", OPTION_ARG_OPTIONAL, N_("Display DWARF section content. SECTION can be one of abbrev, " - "aranges, frame, info, loc, line, pubnames, str, or macinfo.") }, - { "notes", 'n', NULL, 0, N_("Display the core notes") }, + "aranges, frame, info, loc, line, ranges, pubnames, str, or macinfo."), + 0 }, + { "notes", 'n', NULL, 0, N_("Display the core notes"), 0 }, { "arch-specific", 'A', NULL, 0, - N_("Display architecture specific information (if any)") }, - - { NULL, 0, NULL, 0, N_("Output control:") }, - - { NULL, 0, NULL, 0, NULL } + N_("Display architecture specific information (if any)"), 0 }, + { "hex-dump", 'x', "SECTION", 0, + N_("Dump the uninterpreted contents of SECTION, by number or name"), 0 }, + { "strings", 'p', "SECTION", OPTION_ARG_OPTIONAL, + N_("Print string contents of sections"), 0 }, + { "string-dump", 'p', NULL, OPTION_ALIAS | OPTION_HIDDEN, NULL, 0 }, + { "archive-index", 'c', NULL, 0, + N_("Display the symbol index of an archive"), 0 }, + + { NULL, 0, NULL, 0, N_("Output control:"), 0 }, + + { NULL, 0, NULL, 0, NULL, 0 } }; /* Short description of program. */ @@ -81,21 +112,15 @@ static const char args_doc[] = N_("FILE..."); /* Prototype for option handler. */ static error_t parse_opt (int key, char *arg, struct argp_state *state); -/* Function to print some extra text in the help message. */ -static char *more_help (int key, const char *text, void *input); - /* Data structure to communicate with argp functions. */ static struct argp argp = { - options, parse_opt, args_doc, doc, NULL, more_help + options, parse_opt, args_doc, doc, NULL, NULL, NULL }; /* Flags set by the option controlling the output. */ -/* True if any of the control options is set. */ -static bool any_control_option; - /* True if dynamic segment should be printed. */ static bool print_dynamic_table; @@ -129,6 +154,15 @@ static bool print_arch; /* True if note section content should be printed. */ static bool print_notes; +/* True if SHF_STRINGS section content should be printed. */ +static bool print_string_sections; + +/* True if archive index should be printed. */ +static bool print_archive_index; + +/* True if any of the control options except print_archive_index is set. */ +static bool any_control_option; + /* Select printing of debugging sections. */ static enum section_e { @@ -141,102 +175,89 @@ static enum section_e section_pubnames = 64,/* .debug_pubnames */ section_str = 128, /* .debug_str */ section_macinfo = 256,/* .debug_macinfo */ + section_ranges = 512, /* .debug_ranges */ section_all = (section_abbrev | section_aranges | section_frame | section_info | section_line | section_loc - | section_pubnames | section_str | section_macinfo) + | section_pubnames | section_str | section_macinfo + | section_ranges) } print_debug_sections; +/* Select hex dumping of sections. */ +static struct section_argument *dump_data_sections; +static struct section_argument **dump_data_sections_tail = &dump_data_sections; + +/* Select string dumping of sections. */ +static struct section_argument *string_sections; +static struct section_argument **string_sections_tail = &string_sections; + +struct section_argument +{ + struct section_argument *next; + const char *arg; +}; + /* Number of sections in the file. */ static size_t shnum; /* Declarations of local functions. */ -static void process_file (int fd, Elf *elf, const char *prefix, - const char *fname, bool only_one); -static void process_elf_file (Elf *elf, const char *prefix, const char *fname, - bool only_one); +static void process_file (int fd, const char *fname, bool only_one); +static void process_elf_file (Dwfl_Module *dwflmod, int fd); static void print_ehdr (Ebl *ebl, GElf_Ehdr *ehdr); static void print_shdr (Ebl *ebl, GElf_Ehdr *ehdr); static void print_phdr (Ebl *ebl, GElf_Ehdr *ehdr); -static void print_scngrp (Ebl *ebl, GElf_Ehdr *ehdr); +static void print_scngrp (Ebl *ebl); static void print_dynamic (Ebl *ebl, GElf_Ehdr *ehdr); -static void print_relocs (Ebl *ebl, GElf_Ehdr *ehdr); -static void handle_relocs_rel (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, - GElf_Shdr *shdr); -static void handle_relocs_rela (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, - GElf_Shdr *shdr); -static void print_symtab (Ebl *ebl, GElf_Ehdr *ehdr, int type); -static void handle_symtab (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, - GElf_Shdr *shdr); -static void print_verinfo (Ebl *ebl, GElf_Ehdr *ehdr); -static void handle_verneed (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, - GElf_Shdr *shdr); -static void handle_verdef (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, - GElf_Shdr *shdr); -static void handle_versym (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, +static void print_relocs (Ebl *ebl); +static void handle_relocs_rel (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr); +static void handle_relocs_rela (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr); +static void print_symtab (Ebl *ebl, int type); +static void handle_symtab (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr); +static void print_verinfo (Ebl *ebl); +static void handle_verneed (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr); +static void handle_verdef (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr); +static void handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr); -static void print_debug (Ebl *ebl, GElf_Ehdr *ehdr); -static void handle_hash (Ebl *ebl, GElf_Ehdr *ehdr); +static void print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr); +static void handle_hash (Ebl *ebl); static void handle_notes (Ebl *ebl, GElf_Ehdr *ehdr); -static void print_liblist (Ebl *ebl, GElf_Ehdr *ehdr); +static void print_liblist (Ebl *ebl); +static void print_attributes (Ebl *ebl, const GElf_Ehdr *ehdr); +static void dump_data (Ebl *ebl); +static void dump_strings (Ebl *ebl); +static void print_strings (Ebl *ebl); +static void dump_archive_index (Elf *, const char *); int main (int argc, char *argv[]) { - int remaining; - bool only_one; - /* Set locale. */ setlocale (LC_ALL, ""); /* Initialize the message catalog. */ - textdomain (PACKAGE); + textdomain (PACKAGE_TARNAME); /* Parse and process arguments. */ + int remaining; argp_parse (&argp, argc, argv, 0, &remaining, NULL); - /* If no control option or no ELF file is given punt. */ - if ((any_control_option == 0 && print_debug_sections == 0) - || remaining >= argc) - { - argp_help (&argp, stdout, ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR, - program_invocation_short_name); - exit (1); - } - /* Before we start tell the ELF library which version we are using. */ elf_version (EV_CURRENT); /* Now process all the files given at the command line. */ - only_one = remaining + 1 == argc; + bool only_one = remaining + 1 == argc; do { - int fd; - Elf *elf; - /* Open the file. */ - fd = open (argv[remaining], O_RDONLY); + int fd = open (argv[remaining], O_RDONLY); if (fd == -1) { error (0, errno, gettext ("cannot open input file")); continue; } - /* Create an `Elf' descriptor. */ - elf = elf_begin (fd, ELF_C_READ_MMAP, NULL); - if (elf == NULL) - error (0, 0, gettext ("cannot generate Elf descriptor: %s\n"), - elf_errmsg (-1)); - else - { - process_file (fd, elf, NULL, argv[remaining], only_one); - - /* Now we can close the descriptor. */ - if (elf_end (elf) != 0) - error (0, 0, gettext ("error while closing Elf descriptor: %s"), - elf_errmsg (-1)); - } + process_file (fd, argv[remaining], only_one); close (fd); } @@ -248,7 +269,8 @@ main (int argc, char *argv[]) /* Handle program arguments. */ static error_t -parse_opt (int key, char *arg, struct argp_state *state) +parse_opt (int key, char *arg, + struct argp_state *state __attribute__ ((unused))) { switch (key) { @@ -310,6 +332,9 @@ parse_opt (int key, char *arg, struct argp_state *state) print_version_info = true; any_control_option = true; break; + case 'c': + print_archive_index = true; + break; case 'w': if (arg == NULL) print_debug_sections = section_all; @@ -317,6 +342,8 @@ parse_opt (int key, char *arg, struct argp_state *state) print_debug_sections |= section_abbrev; else if (strcmp (arg, "aranges") == 0) print_debug_sections |= section_aranges; + else if (strcmp (arg, "ranges") == 0) + print_debug_sections |= section_ranges; else if (strcmp (arg, "frame") == 0) print_debug_sections |= section_frame; else if (strcmp (arg, "info") == 0) @@ -339,6 +366,40 @@ parse_opt (int key, char *arg, struct argp_state *state) program_invocation_short_name); exit (1); } + any_control_option = true; + break; + case 'p': + any_control_option = true; + if (arg == NULL) + { + print_string_sections = true; + break; + } + /* Fall through. */ + case 'x': + { + struct section_argument *a = xmalloc (sizeof *a); + a->arg = arg; + a->next = NULL; + struct section_argument ***tailp + = key == 'x' ? &dump_data_sections_tail : &string_sections_tail; + **tailp = a; + *tailp = &a->next; + } + any_control_option = true; + break; + case ARGP_KEY_NO_ARGS: + fputs (gettext ("Missing file name.\n"), stderr); + goto do_argp_help; + case ARGP_KEY_FINI: + if (! any_control_option && ! print_archive_index) + { + fputs (gettext ("No operation specified.\n"), stderr); + do_argp_help: + argp_help (&argp, stderr, ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR, + program_invocation_short_name); + exit (1); + } break; default: return ARGP_ERR_UNKNOWN; @@ -347,177 +408,265 @@ parse_opt (int key, char *arg, struct argp_state *state) } -static char * -more_help (int key, const char *text, void *input) -{ - char *buf; - - switch (key) - { - case ARGP_KEY_HELP_EXTRA: - /* We print some extra information. */ - if (asprintf (&buf, gettext ("Please report bugs to %s.\n"), - PACKAGE_BUGREPORT) < 0) - buf = NULL; - return buf; - - default: - break; - } - return (char *) text; -} - - /* Print the version information. */ static void -print_version (FILE *stream, struct argp_state *state) +print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) { - fprintf (stream, "readelf (%s) %s\n", PACKAGE_NAME, VERSION); + fprintf (stream, "readelf (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION); fprintf (stream, gettext ("\ Copyright (C) %s Red Hat, Inc.\n\ This is free software; see the source for copying conditions. There is NO\n\ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ -"), "2004"); +"), "2008"); fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); } -/* Process one file. */ +/* Check if the file is an archive, and if so dump its index. */ static void -process_file (int fd, Elf *elf, const char *prefix, const char *fname, - bool only_one) +check_archive_index (int fd, const char *fname, bool only_one) +{ + /* Create an `Elf' descriptor. */ + Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL); + if (elf == NULL) + error (0, 0, gettext ("cannot generate Elf descriptor: %s"), + elf_errmsg (-1)); + else + { + if (elf_kind (elf) == ELF_K_AR) + { + if (!only_one) + printf ("\n%s:\n\n", fname); + dump_archive_index (elf, fname); + } + else + error (0, 0, + gettext ("'%s' is not an archive, cannot print archive index"), + fname); + + /* Now we can close the descriptor. */ + if (elf_end (elf) != 0) + error (0, 0, gettext ("error while closing Elf descriptor: %s"), + elf_errmsg (-1)); + } +} + +/* Trivial callback used for checking if we opened an archive. */ +static int +count_dwflmod (Dwfl_Module *dwflmod __attribute__ ((unused)), + void **userdata __attribute__ ((unused)), + const char *name __attribute__ ((unused)), + Dwarf_Addr base __attribute__ ((unused)), + void *arg) +{ + if (*(bool *) arg) + return DWARF_CB_ABORT; + *(bool *) arg = true; + return DWARF_CB_OK; +} + +struct process_dwflmod_args { - /* We can handle two types of files: ELF files and archives. */ - Elf_Kind kind = elf_kind (elf); - struct stat64 st; + int fd; + bool only_one; +}; - switch (kind) +static int +process_dwflmod (Dwfl_Module *dwflmod, + void **userdata __attribute__ ((unused)), + const char *name __attribute__ ((unused)), + Dwarf_Addr base __attribute__ ((unused)), + void *arg) +{ + const struct process_dwflmod_args *a = arg; + + /* Print the file name. */ + if (!a->only_one) { - case ELF_K_ELF: - /* Yes! It's an ELF file. */ - process_elf_file (elf, prefix, fname, only_one); - break; + const char *fname; + dwfl_module_info (dwflmod, NULL, NULL, NULL, NULL, NULL, &fname, NULL); - case ELF_K_AR: - { - Elf *subelf; - Elf_Cmd cmd = ELF_C_READ_MMAP; - size_t prefix_len = prefix == NULL ? 0 : strlen (prefix); - size_t fname_len = strlen (fname) + 1; - char new_prefix[prefix_len + 1 + fname_len]; - char *cp = new_prefix; - - /* Create the full name of the file. */ - if (prefix != NULL) - { - cp = mempcpy (cp, prefix, prefix_len); - *cp++ = ':'; - } - memcpy (cp, fname, fname_len); + printf ("\n%s:\n\n", fname); + } - /* It's an archive. We process each file in it. */ - while ((subelf = elf_begin (fd, cmd, elf)) != NULL) - { - kind = elf_kind (subelf); + process_elf_file (dwflmod, a->fd); - /* Call this function recursively. */ - if (kind == ELF_K_ELF || kind == ELF_K_AR) - { - Elf_Arhdr *arhdr = elf_getarhdr (subelf); - assert (arhdr != NULL); + return DWARF_CB_OK; +} - process_file (fd, subelf, new_prefix, arhdr->ar_name, false); - } +/* Stub libdwfl callback, only the ELF handle already open is ever used. */ +static int +find_no_debuginfo (Dwfl_Module *mod __attribute__ ((unused)), + void **userdata __attribute__ ((unused)), + const char *modname __attribute__ ((unused)), + Dwarf_Addr base __attribute__ ((unused)), + const char *file_name __attribute__ ((unused)), + const char *debuglink_file __attribute__ ((unused)), + GElf_Word debuglink_crc __attribute__ ((unused)), + char **debuginfo_file_name __attribute__ ((unused))) +{ + return -1; +} - /* Get next archive element. */ - cmd = elf_next (subelf); - if (elf_end (subelf) != 0) - error (0, 0, - gettext (" error while freeing sub-ELF descriptor: %s\n"), - elf_errmsg (-1)); - } - } - break; +/* Process one input file. */ +static void +process_file (int fd, const char *fname, bool only_one) +{ + if (print_archive_index) + check_archive_index (fd, fname, only_one); - default: + if (!any_control_option) + return; + + /* Duplicate an fd for dwfl_report_offline to swallow. */ + int dwfl_fd = dup (fd); + if (unlikely (dwfl_fd < 0)) + error (EXIT_FAILURE, errno, "dup"); + + /* Use libdwfl in a trivial way to open the libdw handle for us. + This takes care of applying relocations to DWARF data in ET_REL files. */ + static const Dwfl_Callbacks callbacks = + { + .section_address = dwfl_offline_section_address, + .find_debuginfo = find_no_debuginfo + }; + Dwfl *dwfl = dwfl_begin (&callbacks); + if (likely (dwfl != NULL)) + /* Let 0 be the logical address of the file (or first in archive). */ + dwfl->offline_next_address = 0; + if (dwfl_report_offline (dwfl, fname, fname, dwfl_fd) == NULL) + { + struct stat64 st; if (fstat64 (fd, &st) != 0) error (0, errno, gettext ("cannot stat input file")); - else if (st.st_size == 0) + else if (unlikely (st.st_size == 0)) error (0, 0, gettext ("input file is empty")); else - /* We cannot do anything. */ - error (0, 0, gettext ("\ -Not an ELF file - it has the wrong magic bytes at the start")); - break; + error (0, 0, gettext ("failed reading '%s': %s"), + fname, dwfl_errmsg (-1)); } + else + { + dwfl_report_end (dwfl, NULL, NULL); + + if (only_one) + { + /* Clear ONLY_ONE if we have multiple modules, from an archive. */ + bool seen = false; + only_one = dwfl_getmodules (dwfl, &count_dwflmod, &seen, 0) == 0; + } + + /* Process the one or more modules gleaned from this file. */ + struct process_dwflmod_args a = { .fd = fd, .only_one = only_one }; + dwfl_getmodules (dwfl, &process_dwflmod, &a, 0); + } + dwfl_end (dwfl); } -/* Process one file. */ +/* Process one ELF file. */ static void -process_elf_file (Elf *elf, const char *prefix, const char *fname, - bool only_one) +process_elf_file (Dwfl_Module *dwflmod, int fd) { + GElf_Addr dwflbias; + Elf *elf = dwfl_module_getelf (dwflmod, &dwflbias); + GElf_Ehdr ehdr_mem; GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); - Ebl *ebl; - - /* Print the file name. */ - if (!only_one) - { - if (prefix != NULL) - printf ("\n%s(%s):\n\n", prefix, fname); - else - printf ("\n%s:\n\n", fname); - } if (ehdr == NULL) { + elf_error: error (0, 0, gettext ("cannot read ELF header: %s"), elf_errmsg (-1)); return; } - ebl = ebl_openbackend (elf); - if (ebl == NULL) + Ebl *ebl = ebl_openbackend (elf); + if (unlikely (ebl == NULL)) { + ebl_error: error (0, errno, gettext ("cannot create EBL handle")); return; } /* Determine the number of sections. */ - if (elf_getshnum (ebl->elf, &shnum) < 0) + if (unlikely (elf_getshnum (ebl->elf, &shnum) < 0)) error (EXIT_FAILURE, 0, gettext ("cannot determine number of sections: %s"), elf_errmsg (-1)); + /* For an ET_REL file, libdwfl has adjusted the in-core shdrs + and may have applied relocation to some sections. + So we need to get a fresh Elf handle on the file to display those. */ + bool print_unrelocated = (print_section_header + || print_relocations + || dump_data_sections != NULL + || print_notes); + + Elf *pure_elf = NULL; + Ebl *pure_ebl = ebl; + if (ehdr->e_type == ET_REL && print_unrelocated) + { + /* Read the file afresh. */ + off64_t aroff = elf_getaroff (elf); + pure_elf = elf_begin (fd, ELF_C_READ_MMAP, NULL); + if (aroff > 0) + { + /* Archive member. */ + (void) elf_rand (pure_elf, aroff); + Elf *armem = elf_begin (-1, ELF_C_READ_MMAP, pure_elf); + elf_end (pure_elf); + pure_elf = armem; + } + if (pure_elf == NULL) + goto elf_error; + pure_ebl = ebl_openbackend (pure_elf); + if (pure_ebl == NULL) + goto ebl_error; + } + if (print_file_header) print_ehdr (ebl, ehdr); if (print_section_header) - print_shdr (ebl, ehdr); + print_shdr (pure_ebl, ehdr); if (print_program_header) print_phdr (ebl, ehdr); if (print_section_groups) - print_scngrp (ebl, ehdr); + print_scngrp (ebl); if (print_dynamic_table) print_dynamic (ebl, ehdr); if (print_relocations) - print_relocs (ebl, ehdr); + print_relocs (pure_ebl); if (print_histogram) - handle_hash (ebl, ehdr); + handle_hash (ebl); if (print_symbol_table) - print_symtab (ebl, ehdr, SHT_DYNSYM); + print_symtab (ebl, SHT_DYNSYM); if (print_version_info) - print_verinfo (ebl, ehdr); + print_verinfo (ebl); if (print_symbol_table) - print_symtab (ebl, ehdr, SHT_SYMTAB); + print_symtab (ebl, SHT_SYMTAB); + if (print_arch) + print_liblist (ebl); if (print_arch) - print_liblist (ebl, ehdr); + print_attributes (ebl, ehdr); + if (dump_data_sections != NULL) + dump_data (pure_ebl); + if (string_sections != NULL) + dump_strings (ebl); if (print_debug_sections != 0) - print_debug (ebl, ehdr); + print_debug (dwflmod, ebl, ehdr); if (print_notes) - handle_notes (ebl, ehdr); + handle_notes (pure_ebl, ehdr); + if (print_string_sections) + print_strings (ebl); ebl_closebackend (ebl); + + if (pure_ebl != ebl) + { + ebl_closebackend (pure_ebl); + elf_end (pure_elf); + } } @@ -525,9 +674,9 @@ process_elf_file (Elf *elf, const char *prefix, const char *fname, static void print_file_type (unsigned short int e_type) { - if (e_type <= ET_CORE) + if (likely (e_type <= ET_CORE)) { - static const char *knowntypes[] = + static const char *const knowntypes[] = { N_("NONE (None)"), N_("REL (Relocatable file)"), @@ -550,11 +699,8 @@ print_file_type (unsigned short int e_type) static void print_ehdr (Ebl *ebl, GElf_Ehdr *ehdr) { - char buf[512]; - size_t cnt; - fputs_unlocked (gettext ("ELF Header:\n Magic: "), stdout); - for (cnt = 0; cnt < EI_NIDENT; ++cnt) + for (size_t cnt = 0; cnt < EI_NIDENT; ++cnt) printf (" %02hhx", ehdr->e_ident[cnt]); printf (gettext ("\n Class: %s\n"), @@ -568,11 +714,12 @@ print_ehdr (Ebl *ebl, GElf_Ehdr *ehdr) : ehdr->e_ident[EI_DATA] == ELFDATA2MSB ? "2's complement, big endian" : "\?\?\?"); - printf (gettext (" Version: %hhd %s\n"), + printf (gettext (" Ident Version: %hhd %s\n"), ehdr->e_ident[EI_VERSION], ehdr->e_ident[EI_VERSION] == EV_CURRENT ? gettext ("(current)") : "(\?\?\?)"); + char buf[512]; printf (gettext (" OS/ABI: %s\n"), ebl_osabi_name (ebl, ehdr->e_ident[EI_OSABI], buf, sizeof (buf))); @@ -617,9 +764,7 @@ print_ehdr (Ebl *ebl, GElf_Ehdr *ehdr) if (ehdr->e_shnum == 0) { GElf_Shdr shdr_mem; - GElf_Shdr *shdr; - - shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem); + GElf_Shdr *shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem); if (shdr != NULL) printf (gettext (" (%" PRIu32 " in [0].sh_size)"), (uint32_t) shdr->sh_size); @@ -628,12 +773,10 @@ print_ehdr (Ebl *ebl, GElf_Ehdr *ehdr) } fputc_unlocked ('\n', stdout); - if (ehdr->e_shstrndx == SHN_XINDEX) + if (unlikely (ehdr->e_shstrndx == SHN_XINDEX)) { GElf_Shdr shdr_mem; - GElf_Shdr *shdr; - - shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem); + GElf_Shdr *shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem); if (shdr != NULL) /* We managed to get the zeroth section. */ snprintf (buf, sizeof (buf), gettext (" (%" PRIu32 " in [0].sh_link)"), @@ -686,7 +829,7 @@ There are %d section headers, starting at offset %#" PRIx64 ":\n\ ehdr->e_shnum, ehdr->e_shoff); /* Get the section header string table index. */ - if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + if (unlikely (elf_getshstrndx (ebl->elf, &shstrndx) < 0)) error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); @@ -699,24 +842,21 @@ There are %d section headers, starting at offset %#" PRIx64 ":\n\ for (cnt = 0; cnt < shnum; ++cnt) { - char buf[128]; - char flagbuf[20]; - char *cp; Elf_Scn *scn = elf_getscn (ebl->elf, cnt); - GElf_Shdr shdr_mem; - GElf_Shdr *shdr; - if (scn == NULL) + if (unlikely (scn == NULL)) error (EXIT_FAILURE, 0, gettext ("cannot get section: %s"), elf_errmsg (-1)); /* Get the section header. */ - shdr = gelf_getshdr (scn, &shdr_mem); - if (shdr == NULL) + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + if (unlikely (shdr == NULL)) error (EXIT_FAILURE, 0, gettext ("cannot get section header: %s"), elf_errmsg (-1)); - cp = flagbuf; + char flagbuf[20]; + char *cp = flagbuf; if (shdr->sh_flags & SHF_WRITE) *cp++ = 'W'; if (shdr->sh_flags & SHF_ALLOC) @@ -743,6 +883,7 @@ There are %d section headers, starting at offset %#" PRIx64 ":\n\ *cp++ = 'E'; *cp = '\0'; + char buf[128]; printf ("[%2zu] %-20s %-12s %0*" PRIx64 " %0*" PRIx64 " %0*" PRIx64 " %2" PRId64 " %-5s %2" PRId32 " %3" PRId32 " %2" PRId64 "\n", @@ -765,9 +906,6 @@ There are %d section headers, starting at offset %#" PRIx64 ":\n\ static void print_phdr (Ebl *ebl, GElf_Ehdr *ehdr) { - size_t cnt; - size_t shstrndx; - if (ehdr->e_phnum == 0) /* No program header, this is OK in relocatable objects. */ return; @@ -784,14 +922,14 @@ print_phdr (Ebl *ebl, GElf_Ehdr *ehdr) bool has_relro = false; GElf_Addr relro_from = 0; GElf_Addr relro_to = 0; - for (cnt = 0; cnt < ehdr->e_phnum; ++cnt) + for (size_t cnt = 0; cnt < ehdr->e_phnum; ++cnt) { char buf[128]; GElf_Phdr mem; GElf_Phdr *phdr = gelf_getphdr (ebl->elf, cnt, &mem); /* If for some reason the header cannot be returned show this. */ - if (phdr == NULL) + if (unlikely (phdr == NULL)) { puts (" ???"); continue; @@ -829,42 +967,40 @@ print_phdr (Ebl *ebl, GElf_Ehdr *ehdr) } /* Get the section header string table index. */ - if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + size_t shstrndx; + if (unlikely (elf_getshstrndx (ebl->elf, &shstrndx) < 0)) error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); puts (gettext ("\n Section to Segment mapping:\n Segment Sections...")); - for (cnt = 0; cnt < ehdr->e_phnum; ++cnt) + for (size_t cnt = 0; cnt < ehdr->e_phnum; ++cnt) { - GElf_Phdr phdr_mem; - GElf_Phdr *phdr = gelf_getphdr (ebl->elf, cnt, &phdr_mem); - size_t inner; - /* Print the segment number. */ printf (" %2.2zu ", cnt); + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = gelf_getphdr (ebl->elf, cnt, &phdr_mem); /* This must not happen. */ - if (phdr == NULL) + if (unlikely (phdr == NULL)) error (EXIT_FAILURE, 0, gettext ("cannot get program header: %s"), elf_errmsg (-1)); /* Iterate over the sections. */ bool in_relro = false; - for (inner = 1; inner < shnum; ++inner) + bool in_ro = false; + for (size_t inner = 1; inner < shnum; ++inner) { Elf_Scn *scn = elf_getscn (ebl->elf, inner); - GElf_Shdr shdr_mem; - GElf_Shdr *shdr; - - /* It should not happen. */ - if (scn == NULL) + /* This should not happen. */ + if (unlikely (scn == NULL)) error (EXIT_FAILURE, 0, gettext ("cannot get section: %s"), elf_errmsg (-1)); /* Get the section header. */ - shdr = gelf_getshdr (scn, &shdr_mem); - if (shdr == NULL) + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + if (unlikely (shdr == NULL)) error (EXIT_FAILURE, 0, gettext ("cannot get section header: %s"), elf_errmsg (-1)); @@ -895,6 +1031,45 @@ print_phdr (Ebl *ebl, GElf_Ehdr *ehdr) else if (has_relro && in_relro && shdr->sh_addr + shdr->sh_size > relro_to) fputs_unlocked ("] <RELRO:", stdout); + else if (phdr->p_type == PT_LOAD && (phdr->p_flags & PF_W) == 0) + { + if (!in_ro) + { + fputs_unlocked (" [RO:", stdout); + in_ro = true; + } + } + else + { + /* Determine the segment this section is part of. */ + size_t cnt2; + GElf_Phdr *phdr2 = NULL; + for (cnt2 = 0; cnt2 < ehdr->e_phnum; ++cnt2) + { + GElf_Phdr phdr2_mem; + phdr2 = gelf_getphdr (ebl->elf, cnt2, &phdr2_mem); + + if (phdr2 != NULL && phdr2->p_type == PT_LOAD + && shdr->sh_addr >= phdr2->p_vaddr + && (shdr->sh_addr + shdr->sh_size + <= phdr2->p_vaddr + phdr2->p_memsz)) + break; + } + + if (cnt2 < ehdr->e_phnum) + { + if ((phdr2->p_flags & PF_W) == 0 && !in_ro) + { + fputs_unlocked (" [RO:", stdout); + in_ro = true; + } + else if ((phdr2->p_flags & PF_W) != 0 && in_ro) + { + fputs_unlocked ("]", stdout); + in_ro = false; + } + } + } printf (" %s", elf_strptr (ebl->elf, shstrndx, shdr->sh_name)); @@ -908,7 +1083,7 @@ print_phdr (Ebl *ebl, GElf_Ehdr *ehdr) } } } - if (in_relro) + if (in_relro || in_ro) fputs_unlocked ("]", stdout); /* Finish the line. */ @@ -918,36 +1093,29 @@ print_phdr (Ebl *ebl, GElf_Ehdr *ehdr) static void -handle_scngrp (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) +handle_scngrp (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) { - Elf_Data *data; - Elf32_Word *grpref; - Elf_Scn *symscn; - GElf_Shdr symshdr_mem; - GElf_Shdr *symshdr; - Elf_Data *symdata; - GElf_Sym sym_mem; - size_t cnt; - size_t shstrndx; - /* Get the data of the section. */ - data = elf_getdata (scn, NULL); + Elf_Data *data = elf_getdata (scn, NULL); - symscn = elf_getscn (ebl->elf, shdr->sh_link); - symshdr = gelf_getshdr (symscn, &symshdr_mem); - symdata = elf_getdata (symscn, NULL); + Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link); + GElf_Shdr symshdr_mem; + GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem); + Elf_Data *symdata = elf_getdata (symscn, NULL); if (data == NULL || data->d_size < sizeof (Elf32_Word) || symshdr == NULL || symdata == NULL) return; /* Get the section header string table index. */ - if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + size_t shstrndx; + if (unlikely (elf_getshstrndx (ebl->elf, &shstrndx) < 0)) error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); - grpref = (Elf32_Word *) data->d_buf; + Elf32_Word *grpref = (Elf32_Word *) data->d_buf; + GElf_Sym sym_mem; printf ((grpref[0] & GRP_COMDAT) ? ngettext ("\ \nCOMDAT section group [%2zu] '%s' with signature '%s' contains %zu entry:\n", @@ -965,27 +1133,24 @@ handle_scngrp (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) ?: gettext ("<INVALID SYMBOL>"), data->d_size / sizeof (Elf32_Word) - 1); - for (cnt = 1; cnt < data->d_size / sizeof (Elf32_Word); ++cnt) + for (size_t cnt = 1; cnt < data->d_size / sizeof (Elf32_Word); ++cnt) { GElf_Shdr grpshdr_mem; - GElf_Shdr *grpshdr; - - grpshdr = gelf_getshdr (elf_getscn (ebl->elf, grpref[cnt]), - &grpshdr_mem); - - if (grpshdr == NULL) - printf (gettext (" [%2u] <INVALID SECTION>\n"), grpref[cnt]); - else - printf (" [%2u] %s\n", - grpref[cnt], - elf_strptr (ebl->elf, shstrndx, grpshdr->sh_name) - ?: gettext ("<INVALID SECTION>")); + GElf_Shdr *grpshdr = gelf_getshdr (elf_getscn (ebl->elf, grpref[cnt]), + &grpshdr_mem); + + const char *str; + printf (" [%2u] %s\n", + grpref[cnt], + grpshdr != NULL + && (str = elf_strptr (ebl->elf, shstrndx, grpshdr->sh_name)) + ? str : gettext ("<INVALID SECTION>")); } } static void -print_scngrp (Ebl *ebl, GElf_Ehdr *ehdr) +print_scngrp (Ebl *ebl) { /* Find all relocation sections and handle them. */ Elf_Scn *scn = NULL; @@ -997,7 +1162,7 @@ print_scngrp (Ebl *ebl, GElf_Ehdr *ehdr) GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); if (shdr != NULL && shdr->sh_type == SHT_GROUP) - handle_scngrp (ebl, ehdr, scn, shdr); + handle_scngrp (ebl, scn, shdr); } } @@ -1112,7 +1277,7 @@ print_dt_posflag_1 (int class, GElf_Xword d_val) static void -handle_dynamic (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) +handle_dynamic (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) { int class = gelf_getclass (ebl->elf); GElf_Shdr glink; @@ -1126,7 +1291,7 @@ handle_dynamic (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) return; /* Get the section header string table index. */ - if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + if (unlikely (elf_getshstrndx (ebl->elf, &shstrndx) < 0)) error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); @@ -1146,14 +1311,12 @@ handle_dynamic (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) for (cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt) { - char buf[64]; GElf_Dyn dynmem; - GElf_Dyn *dyn; - - dyn = gelf_getdyn (data, cnt, &dynmem); + GElf_Dyn *dyn = gelf_getdyn (data, cnt, &dynmem); if (dyn == NULL) break; + char buf[64]; printf (" %-17s ", ebl_dynamic_tag_name (ebl, dyn->d_tag, buf, sizeof (buf))); @@ -1164,7 +1327,7 @@ handle_dynamic (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) case DT_BIND_NOW: case DT_TEXTREL: /* No further output. */ - fputc ('\n', stdout); + fputc_unlocked ('\n', stdout); break; case DT_NEEDED: @@ -1246,18 +1409,18 @@ handle_dynamic (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) static void print_dynamic (Ebl *ebl, GElf_Ehdr *ehdr) { - /* Find all relocation sections and handle them. */ - Elf_Scn *scn = NULL; - - while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) + for (int i = 0; i < ehdr->e_phnum; ++i) { - /* Handle the section if it is a symbol table. */ - GElf_Shdr shdr_mem; - GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = gelf_getphdr (ebl->elf, i, &phdr_mem); - if (shdr != NULL && shdr->sh_type == SHT_DYNAMIC) + if (phdr != NULL && phdr->p_type == PT_DYNAMIC) { - handle_dynamic (ebl, ehdr, scn, shdr); + Elf_Scn *scn = gelf_offscn (ebl->elf, phdr->p_offset); + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr != NULL && shdr->sh_type == SHT_DYNAMIC) + handle_dynamic (ebl, scn, shdr); break; } } @@ -1266,7 +1429,7 @@ print_dynamic (Ebl *ebl, GElf_Ehdr *ehdr) /* Print relocations. */ static void -print_relocs (Ebl *ebl, GElf_Ehdr *ehdr) +print_relocs (Ebl *ebl) { /* Find all relocation sections and handle them. */ Elf_Scn *scn = NULL; @@ -1277,12 +1440,12 @@ print_relocs (Ebl *ebl, GElf_Ehdr *ehdr) GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); - if (shdr != NULL) + if (likely (shdr != NULL)) { if (shdr->sh_type == SHT_REL) - handle_relocs_rel (ebl, ehdr, scn, shdr); + handle_relocs_rel (ebl, scn, shdr); else if (shdr->sh_type == SHT_RELA) - handle_relocs_rela (ebl, ehdr, scn, shdr); + handle_relocs_rela (ebl, scn, shdr); } } } @@ -1290,37 +1453,28 @@ print_relocs (Ebl *ebl, GElf_Ehdr *ehdr) /* Handle a relocation section. */ static void -handle_relocs_rel (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) +handle_relocs_rel (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) { int class = gelf_getclass (ebl->elf); int nentries = shdr->sh_size / shdr->sh_entsize; - int cnt; - Elf_Data *data; - Elf_Scn *symscn; - GElf_Shdr symshdr_mem; - GElf_Shdr *symshdr; - Elf_Data *symdata; - GElf_Shdr destshdr_mem; - GElf_Shdr *destshdr; - Elf_Scn *xndxscn; - Elf_Data *xndxdata = NULL; - size_t shstrndx; /* Get the data of the section. */ - data = elf_getdata (scn, NULL); + Elf_Data *data = elf_getdata (scn, NULL); if (data == NULL) return; /* Get the symbol table information. */ - symscn = elf_getscn (ebl->elf, shdr->sh_link); - symshdr = gelf_getshdr (symscn, &symshdr_mem); - symdata = elf_getdata (symscn, NULL); + Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link); + GElf_Shdr symshdr_mem; + GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem); + Elf_Data *symdata = elf_getdata (symscn, NULL); /* Get the section header of the section the relocations are for. */ - destshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_info), - &destshdr_mem); + GElf_Shdr destshdr_mem; + GElf_Shdr *destshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_info), + &destshdr_mem); - if (symshdr == NULL || symdata == NULL || destshdr == NULL) + if (unlikely (symshdr == NULL || symdata == NULL || destshdr == NULL)) { printf (gettext ("\nInvalid symbol table at offset %#0" PRIx64 "\n"), shdr->sh_offset); @@ -1328,24 +1482,14 @@ handle_relocs_rel (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) } /* Search for the optional extended section index table. */ - xndxscn = NULL; - while ((xndxscn = elf_nextscn (ebl->elf, xndxscn)) != NULL) - { - GElf_Shdr xndxshdr_mem; - GElf_Shdr *xndxshdr; - - xndxshdr = gelf_getshdr (xndxscn, &xndxshdr_mem); - if (xndxshdr != NULL && xndxshdr->sh_type == SHT_SYMTAB_SHNDX - && xndxshdr->sh_link == elf_ndxscn (symscn)) - { - /* Found it. */ - xndxdata = elf_getdata (xndxscn, NULL); - break; - } - } + Elf_Data *xndxdata = NULL; + int xndxscnidx = elf_scnshndx (scn); + if (unlikely (xndxscnidx > 0)) + xndxdata = elf_getdata (elf_getscn (ebl->elf, xndxscnidx), NULL); /* Get the section header string table index. */ - if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + size_t shstrndx; + if (unlikely (elf_getshstrndx (ebl->elf, &shstrndx) < 0)) error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); @@ -1381,22 +1525,19 @@ handle_relocs_rel (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) Offset Type Value Name\n"), stdout); - for (cnt = 0; cnt < nentries; ++cnt) + for (int cnt = 0; cnt < nentries; ++cnt) { GElf_Rel relmem; - GElf_Rel *rel; - - rel = gelf_getrel (data, cnt, &relmem); - if (rel != NULL) + GElf_Rel *rel = gelf_getrel (data, cnt, &relmem); + if (likely (rel != NULL)) { char buf[128]; GElf_Sym symmem; - GElf_Sym *sym; Elf32_Word xndx; - - sym = gelf_getsymshndx (symdata, xndxdata, GELF_R_SYM (rel->r_info), - &symmem, &xndx); - if (sym == NULL) + GElf_Sym *sym = gelf_getsymshndx (symdata, xndxdata, + GELF_R_SYM (rel->r_info), + &symmem, &xndx); + if (unlikely (sym == NULL)) printf (" %#0*" PRIx64 " %-20s <%s %ld>\n", class == ELFCLASS32 ? 10 : 18, rel->r_offset, ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info)) @@ -1410,7 +1551,8 @@ handle_relocs_rel (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) else if (GELF_ST_TYPE (sym->st_info) != STT_SECTION) printf (" %#0*" PRIx64 " %-20s %#0*" PRIx64 " %s\n", class == ELFCLASS32 ? 10 : 18, rel->r_offset, - ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info)) + likely (ebl_reloc_type_check (ebl, + GELF_R_TYPE (rel->r_info))) /* Avoid the leading R_ which isn't carrying any information. */ ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info), @@ -1425,7 +1567,7 @@ handle_relocs_rel (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) ? xndx : sym->st_shndx), &destshdr_mem); - if (shdr == NULL) + if (unlikely (destshdr == NULL)) printf (" %#0*" PRIx64 " %-20s <%s %ld>\n", class == ELFCLASS32 ? 10 : 18, rel->r_offset, ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info)) @@ -1456,7 +1598,7 @@ handle_relocs_rel (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) /* Handle a relocation section. */ static void -handle_relocs_rela (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) +handle_relocs_rela (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) { int class = gelf_getclass (ebl->elf); int nentries = shdr->sh_size / shdr->sh_entsize; @@ -1477,7 +1619,7 @@ handle_relocs_rela (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) GElf_Shdr *destshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_info), &destshdr_mem); - if (symshdr == NULL || symdata == NULL || destshdr == NULL) + if (unlikely (symshdr == NULL || symdata == NULL || destshdr == NULL)) { printf (gettext ("\nInvalid symbol table at offset %#0" PRIx64 "\n"), shdr->sh_offset); @@ -1486,25 +1628,13 @@ handle_relocs_rela (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) /* Search for the optional extended section index table. */ Elf_Data *xndxdata = NULL; - Elf_Scn *xndxscn = NULL; - while ((xndxscn = elf_nextscn (ebl->elf, xndxscn)) != NULL) - { - GElf_Shdr xndxshdr_mem; - GElf_Shdr *xndxshdr; - - xndxshdr = gelf_getshdr (xndxscn, &xndxshdr_mem); - if (xndxshdr != NULL && xndxshdr->sh_type == SHT_SYMTAB_SHNDX - && xndxshdr->sh_link == elf_ndxscn (symscn)) - { - /* Found it. */ - xndxdata = elf_getdata (xndxscn, NULL); - break; - } - } + int xndxscnidx = elf_scnshndx (scn); + if (unlikely (xndxscnidx > 0)) + xndxdata = elf_getdata (elf_getscn (ebl->elf, xndxscnidx), NULL); /* Get the section header string table index. */ size_t shstrndx; - if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + if (unlikely (elf_getshstrndx (ebl->elf, &shstrndx) < 0)) error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); @@ -1530,17 +1660,16 @@ handle_relocs_rela (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) { GElf_Rela relmem; GElf_Rela *rel = gelf_getrela (data, cnt, &relmem); - if (rel != NULL) + if (likely (rel != NULL)) { char buf[64]; GElf_Sym symmem; - GElf_Sym *sym; Elf32_Word xndx; + GElf_Sym *sym = gelf_getsymshndx (symdata, xndxdata, + GELF_R_SYM (rel->r_info), + &symmem, &xndx); - sym = gelf_getsymshndx (symdata, xndxdata, GELF_R_SYM (rel->r_info), - &symmem, &xndx); - - if (sym == NULL) + if (unlikely (sym == NULL)) printf (" %#0*" PRIx64 " %-15s <%s %ld>\n", class == ELFCLASS32 ? 10 : 18, rel->r_offset, ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info)) @@ -1553,9 +1682,10 @@ handle_relocs_rela (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) (long int) GELF_R_SYM (rel->r_info)); else if (GELF_ST_TYPE (sym->st_info) != STT_SECTION) printf ("\ - %#0*" PRIx64 " %-15s %#0*" PRIx64 " +%5" PRId64 " %s\n", + %#0*" PRIx64 " %-15s %#0*" PRIx64 " %+6" PRId64 " %s\n", class == ELFCLASS32 ? 10 : 18, rel->r_offset, - ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info)) + likely (ebl_reloc_type_check (ebl, + GELF_R_TYPE (rel->r_info))) /* Avoid the leading R_ which isn't carrying any information. */ ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info), @@ -1571,7 +1701,7 @@ handle_relocs_rela (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) ? xndx : sym->st_shndx), &destshdr_mem); - if (shdr == NULL) + if (unlikely (shdr == NULL)) printf (" %#0*" PRIx64 " %-15s <%s %ld>\n", class == ELFCLASS32 ? 10 : 18, rel->r_offset, ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info)) @@ -1585,7 +1715,7 @@ handle_relocs_rela (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) ? xndx : sym->st_shndx)); else printf ("\ - %#0*" PRIx64 " %-15s %#0*" PRIx64 " +%5" PRId64 " %s\n", + %#0*" PRIx64 " %-15s %#0*" PRIx64 " %+6" PRId64 " %s\n", class == ELFCLASS32 ? 10 : 18, rel->r_offset, ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info)) /* Avoid the leading R_ which isn't carrying any @@ -1604,7 +1734,7 @@ handle_relocs_rela (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) /* Print the program header. */ static void -print_symtab (Ebl *ebl, GElf_Ehdr *ehdr, int type) +print_symtab (Ebl *ebl, int type) { /* Find the symbol table(s). For this we have to search through the section table. */ @@ -1617,41 +1747,35 @@ print_symtab (Ebl *ebl, GElf_Ehdr *ehdr, int type) GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); if (shdr != NULL && shdr->sh_type == (GElf_Word) type) - handle_symtab (ebl, ehdr, scn, shdr); + handle_symtab (ebl, scn, shdr); } } static void -handle_symtab (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) +handle_symtab (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) { Elf_Data *versym_data = NULL; Elf_Data *verneed_data = NULL; Elf_Data *verdef_data = NULL; Elf_Data *xndx_data = NULL; - Elf_Scn *runscn; - Elf_Data *data; int class = gelf_getclass (ebl->elf); - unsigned int nsyms; - unsigned int cnt; Elf32_Word verneed_stridx = 0; Elf32_Word verdef_stridx = 0; - GElf_Shdr glink; - size_t shstrndx; /* Get the data of the section. */ - data = elf_getdata (scn, NULL); + Elf_Data *data = elf_getdata (scn, NULL); if (data == NULL) return; /* Find out whether we have other sections we might need. */ - runscn = NULL; + Elf_Scn *runscn = NULL; while ((runscn = elf_nextscn (ebl->elf, runscn)) != NULL) { GElf_Shdr runshdr_mem; GElf_Shdr *runshdr = gelf_getshdr (runscn, &runshdr_mem); - if (runshdr != NULL) + if (likely (runshdr != NULL)) { if (runshdr->sh_type == SHT_GNU_versym && runshdr->sh_link == elf_ndxscn (scn)) @@ -1677,19 +1801,22 @@ handle_symtab (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) } /* Get the section header string table index. */ - if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + size_t shstrndx; + if (unlikely (elf_getshstrndx (ebl->elf, &shstrndx) < 0)) error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); /* Now we can compute the number of entries in the section. */ - nsyms = data->d_size / (class == ELFCLASS32 - ? sizeof (Elf32_Sym) : sizeof (Elf64_Sym)); + unsigned int nsyms = data->d_size / (class == ELFCLASS32 + ? sizeof (Elf32_Sym) + : sizeof (Elf64_Sym)); printf (ngettext ("\nSymbol table [%2u] '%s' contains %u entry:\n", "\nSymbol table [%2u] '%s' contains %u entries:\n", nsyms), (unsigned int) elf_ndxscn (scn), elf_strptr (ebl->elf, shstrndx, shdr->sh_name), nsyms); + GElf_Shdr glink; printf (ngettext (" %lu local symbol String table: [%2u] '%s'\n", " %lu local symbols String table: [%2u] '%s'\n", shdr->sh_info), @@ -1706,7 +1833,7 @@ handle_symtab (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) Num: Value Size Type Bind Vis Ndx Name\n"), stdout); - for (cnt = 0; cnt < nsyms; ++cnt) + for (unsigned int cnt = 0; cnt < nsyms; ++cnt) { char typebuf[64]; char bindbuf[64]; @@ -1715,11 +1842,11 @@ handle_symtab (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) GElf_Sym sym_mem; GElf_Sym *sym = gelf_getsymshndx (data, xndx_data, cnt, &sym_mem, &xndx); - if (sym == NULL) + if (unlikely (sym == NULL)) continue; /* Determine the real section index. */ - if (sym->st_shndx != SHN_XINDEX) + if (likely (sym->st_shndx != SHN_XINDEX)) xndx = sym->st_shndx; printf (gettext ("\ @@ -1741,9 +1868,7 @@ handle_symtab (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) { /* Get the version information. */ GElf_Versym versym_mem; - GElf_Versym *versym; - - versym = gelf_getversym (versym_data, cnt, &versym_mem); + GElf_Versym *versym = gelf_getversym (versym_data, cnt, &versym_mem); if (versym != NULL && ((*versym & 0x8000) != 0 || *versym > 1)) { @@ -1763,13 +1888,13 @@ handle_symtab (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) if (is_nobits || ! check_def) { /* We must test both. */ - GElf_Verneed verneed_mem; - GElf_Verneed *verneed; GElf_Vernaux vernaux_mem; GElf_Vernaux *vernaux = NULL; size_t vn_offset = 0; - verneed = gelf_getverneed (verneed_data, 0, &verneed_mem); + GElf_Verneed verneed_mem; + GElf_Verneed *verneed = gelf_getverneed (verneed_data, 0, + &verneed_mem); while (verneed != NULL) { size_t vna_offset = vn_offset; @@ -1811,7 +1936,7 @@ handle_symtab (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) (unsigned int) vernaux->vna_other); check_def = 0; } - else if (! is_nobits) + else if (unlikely (! is_nobits)) error (0, 0, gettext ("bad dynamic symbol")); else check_def = 1; @@ -1820,11 +1945,11 @@ handle_symtab (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) if (check_def && *versym != 0x8001) { /* We must test both. */ - GElf_Verdef verdef_mem; - GElf_Verdef *verdef; size_t vd_offset = 0; - verdef = gelf_getverdef (verdef_data, 0, &verdef_mem); + GElf_Verdef verdef_mem; + GElf_Verdef *verdef = gelf_getverdef (verdef_data, 0, + &verdef_mem); while (verdef != NULL) { if (verdef->vd_ndx == (*versym & 0x7fff)) @@ -1841,11 +1966,10 @@ handle_symtab (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) if (verdef != NULL) { GElf_Verdaux verdaux_mem; - GElf_Verdaux *verdaux; - - verdaux = gelf_getverdaux (verdef_data, - vd_offset + verdef->vd_aux, - &verdaux_mem); + GElf_Verdaux *verdaux + = gelf_getverdaux (verdef_data, + vd_offset + verdef->vd_aux, + &verdaux_mem); if (verdaux != NULL) printf ((*versym & 0x8000) ? "@%s" : "@@%s", @@ -1856,14 +1980,14 @@ handle_symtab (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) } } - putchar ('\n'); + putchar_unlocked ('\n'); } } /* Print version information. */ static void -print_verinfo (Ebl *ebl, GElf_Ehdr *ehdr) +print_verinfo (Ebl *ebl) { /* Find the version information sections. For this we have to search through the section table. */ @@ -1875,14 +1999,14 @@ print_verinfo (Ebl *ebl, GElf_Ehdr *ehdr) GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); - if (shdr != NULL) + if (likely (shdr != NULL)) { if (shdr->sh_type == SHT_GNU_verneed) - handle_verneed (ebl, ehdr, scn, shdr); + handle_verneed (ebl, scn, shdr); else if (shdr->sh_type == SHT_GNU_verdef) - handle_verdef (ebl, ehdr, scn, shdr); + handle_verdef (ebl, scn, shdr); else if (shdr->sh_type == SHT_GNU_versym) - handle_versym (ebl, ehdr, scn, shdr); + handle_versym (ebl, scn, shdr); } } } @@ -1910,7 +2034,7 @@ get_ver_flags (unsigned int flags) endp = stpcpy (endp, "WEAK "); } - if (flags & ~(VER_FLG_BASE | VER_FLG_WEAK)) + if (unlikely (flags & ~(VER_FLG_BASE | VER_FLG_WEAK))) { strncpy (endp, gettext ("| <unknown>"), buf + sizeof (buf) - endp); buf[sizeof (buf) - 1] = '\0'; @@ -1921,25 +2045,22 @@ get_ver_flags (unsigned int flags) static void -handle_verneed (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) +handle_verneed (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) { - Elf_Data *data; int class = gelf_getclass (ebl->elf); - GElf_Shdr glink; - int cnt; - unsigned int offset; - size_t shstrndx; /* Get the data of the section. */ - data = elf_getdata (scn, NULL); + Elf_Data *data = elf_getdata (scn, NULL); if (data == NULL) return; /* Get the section header string table index. */ - if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + size_t shstrndx; + if (unlikely (elf_getshstrndx (ebl->elf, &shstrndx) < 0)) error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); + GElf_Shdr glink; printf (ngettext ("\ \nVersion needs section [%2u] '%s' contains %d entry:\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'\n", "\ @@ -1954,17 +2075,13 @@ handle_verneed (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), &glink)->sh_name)); - offset = 0; - for (cnt = shdr->sh_info; --cnt >= 0; ) + unsigned int offset = 0; + for (int cnt = shdr->sh_info; --cnt >= 0; ) { - GElf_Verneed needmem; - GElf_Verneed *need; - unsigned int auxoffset; - int cnt2; - /* Get the data at the next offset. */ - need = gelf_getverneed (data, offset, &needmem); - if (need == NULL) + GElf_Verneed needmem; + GElf_Verneed *need = gelf_getverneed (data, offset, &needmem); + if (unlikely (need == NULL)) break; printf (gettext (" %#06x: Version: %hu File: %s Cnt: %hu\n"), @@ -1972,14 +2089,12 @@ handle_verneed (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) elf_strptr (ebl->elf, shdr->sh_link, need->vn_file), (unsigned short int) need->vn_cnt); - auxoffset = offset + need->vn_aux; - for (cnt2 = need->vn_cnt; --cnt2 >= 0; ) + unsigned int auxoffset = offset + need->vn_aux; + for (int cnt2 = need->vn_cnt; --cnt2 >= 0; ) { GElf_Vernaux auxmem; - GElf_Vernaux *aux; - - aux = gelf_getvernaux (data, auxoffset, &auxmem); - if (aux == NULL) + GElf_Vernaux *aux = gelf_getvernaux (data, auxoffset, &auxmem); + if (unlikely (aux == NULL)) break; printf (gettext (" %#06x: Name: %s Flags: %s Version: %hu\n"), @@ -1998,25 +2113,21 @@ handle_verneed (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) static void -handle_verdef (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) +handle_verdef (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) { - Elf_Data *data; - int class = gelf_getclass (ebl->elf); - GElf_Shdr glink; - int cnt; - unsigned int offset; - size_t shstrndx; - /* Get the data of the section. */ - data = elf_getdata (scn, NULL); + Elf_Data *data = elf_getdata (scn, NULL); if (data == NULL) return; /* Get the section header string table index. */ - if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + size_t shstrndx; + if (unlikely (elf_getshstrndx (ebl->elf, &shstrndx) < 0)) error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); + int class = gelf_getclass (ebl->elf); + GElf_Shdr glink; printf (ngettext ("\ \nVersion definition section [%2u] '%s' contains %d entry:\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'\n", "\ @@ -2032,24 +2143,19 @@ handle_verdef (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), &glink)->sh_name)); - offset = 0; - for (cnt = shdr->sh_info; --cnt >= 0; ) + unsigned int offset = 0; + for (int cnt = shdr->sh_info; --cnt >= 0; ) { - GElf_Verdef defmem; - GElf_Verdef *def; - GElf_Verdaux auxmem; - GElf_Verdaux *aux; - unsigned int auxoffset; - int cnt2; - /* Get the data at the next offset. */ - def = gelf_getverdef (data, offset, &defmem); - if (def == NULL) + GElf_Verdef defmem; + GElf_Verdef *def = gelf_getverdef (data, offset, &defmem); + if (unlikely (def == NULL)) break; - auxoffset = offset + def->vd_aux; - aux = gelf_getverdaux (data, auxoffset, &auxmem); - if (aux == NULL) + unsigned int auxoffset = offset + def->vd_aux; + GElf_Verdaux auxmem; + GElf_Verdaux *aux = gelf_getverdaux (data, auxoffset, &auxmem); + if (unlikely (aux == NULL)) break; printf (gettext ("\ @@ -2061,10 +2167,10 @@ handle_verdef (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) elf_strptr (ebl->elf, shdr->sh_link, aux->vda_name)); auxoffset += aux->vda_next; - for (cnt2 = 1; cnt2 < def->vd_cnt; ++cnt2) + for (int cnt2 = 1; cnt2 < def->vd_cnt; ++cnt2) { aux = gelf_getverdaux (data, auxoffset, &auxmem); - if (aux == NULL) + if (unlikely (aux == NULL)) break; printf (gettext (" %#06x: Parent %d: %s\n"), @@ -2081,42 +2187,35 @@ handle_verdef (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) static void -handle_versym (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) +handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) { - Elf_Data *data; int class = gelf_getclass (ebl->elf); - Elf_Scn *verscn; - GElf_Shdr glink; - Elf_Scn *defscn; - Elf_Scn *needscn; const char **vername; const char **filename; - size_t nvername; - unsigned int cnt; - size_t shstrndx; /* Get the data of the section. */ - data = elf_getdata (scn, NULL); + Elf_Data *data = elf_getdata (scn, NULL); if (data == NULL) return; /* Get the section header string table index. */ - if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + size_t shstrndx; + if (unlikely (elf_getshstrndx (ebl->elf, &shstrndx) < 0)) error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); /* We have to find the version definition section and extract the version names. */ - defscn = NULL; - needscn = NULL; + Elf_Scn *defscn = NULL; + Elf_Scn *needscn = NULL; - verscn = NULL; + Elf_Scn *verscn = NULL; while ((verscn = elf_nextscn (ebl->elf, verscn)) != NULL) { GElf_Shdr vershdr_mem; GElf_Shdr *vershdr = gelf_getshdr (verscn, &vershdr_mem); - if (vershdr != NULL) + if (likely (vershdr != NULL)) { if (vershdr->sh_type == SHT_GNU_verdef) defscn = verscn; @@ -2125,6 +2224,7 @@ handle_versym (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) } } + size_t nvername; if (defscn != NULL || needscn != NULL) { /* We have a version information (better should have). Now get @@ -2140,21 +2240,21 @@ handle_versym (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) GElf_Shdr *defshdr; defdata = elf_getdata (defscn, NULL); - if (defdata == NULL) + if (unlikely (defdata == NULL)) return; defshdr = gelf_getshdr (defscn, &defshdrmem); - if (defshdr == NULL) + if (unlikely (defshdr == NULL)) return; - for (cnt = 0; cnt < defshdr->sh_info; ++cnt) + for (unsigned int cnt = 0; cnt < defshdr->sh_info; ++cnt) { GElf_Verdef defmem; GElf_Verdef *def; /* Get the data at the next offset. */ def = gelf_getverdef (defdata, offset, &defmem); - if (def == NULL) + if (unlikely (def == NULL)) break; nvername = MAX (nvername, (size_t) (def->vd_ndx & 0x7fff)); @@ -2170,14 +2270,14 @@ handle_versym (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) GElf_Shdr *needshdr; needdata = elf_getdata (needscn, NULL); - if (needdata == NULL) + if (unlikely (needdata == NULL)) return; needshdr = gelf_getshdr (needscn, &needshdrmem); - if (needshdr == NULL) + if (unlikely (needshdr == NULL)) return; - for (cnt = 0; cnt < needshdr->sh_info; ++cnt) + for (unsigned int cnt = 0; cnt < needshdr->sh_info; ++cnt) { GElf_Verneed needmem; GElf_Verneed *need; @@ -2186,7 +2286,7 @@ handle_versym (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) /* Get the data at the next offset. */ need = gelf_getverneed (needdata, offset, &needmem); - if (need == NULL) + if (unlikely (need == NULL)) break; /* Run through the auxiliary entries. */ @@ -2197,7 +2297,7 @@ handle_versym (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) GElf_Vernaux *aux; aux = gelf_getvernaux (needdata, auxoffset, &auxmem); - if (aux == NULL) + if (unlikely (aux == NULL)) break; nvername = MAX (nvername, @@ -2228,27 +2328,24 @@ handle_versym (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) GElf_Shdr *defshdr; defdata = elf_getdata (defscn, NULL); - if (defdata == NULL) + if (unlikely (defdata == NULL)) return; defshdr = gelf_getshdr (defscn, &defshdrmem); - if (defshdr == NULL) + if (unlikely (defshdr == NULL)) return; - for (cnt = 0; cnt < defshdr->sh_info; ++cnt) + for (unsigned int cnt = 0; cnt < defshdr->sh_info; ++cnt) { - GElf_Verdef defmem; - GElf_Verdef *def; - GElf_Verdaux auxmem; - GElf_Verdaux *aux; /* Get the data at the next offset. */ - def = gelf_getverdef (defdata, offset, &defmem); - if (def == NULL) - break; - - aux = gelf_getverdaux (defdata, offset + def->vd_aux, &auxmem); - if (aux == NULL) + GElf_Verdef defmem; + GElf_Verdef *def = gelf_getverdef (defdata, offset, &defmem); + GElf_Verdaux auxmem; + GElf_Verdaux *aux = gelf_getverdaux (defdata, + offset + def->vd_aux, + &auxmem); + if (unlikely (def == NULL || aux == NULL)) break; vername[def->vd_ndx & 0x7fff] @@ -2261,39 +2358,30 @@ handle_versym (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) if (needscn != NULL) { unsigned int offset = 0; - Elf_Data *needdata; - GElf_Shdr needshdrmem; - GElf_Shdr *needshdr; - needdata = elf_getdata (needscn, NULL); - if (needdata == NULL) - return; - - needshdr = gelf_getshdr (needscn, &needshdrmem); - if (needshdr == NULL) + Elf_Data *needdata = elf_getdata (needscn, NULL); + GElf_Shdr needshdrmem; + GElf_Shdr *needshdr = gelf_getshdr (needscn, &needshdrmem); + if (unlikely (needdata == NULL || needshdr == NULL)) return; - for (cnt = 0; cnt < needshdr->sh_info; ++cnt) + for (unsigned int cnt = 0; cnt < needshdr->sh_info; ++cnt) { - GElf_Verneed needmem; - GElf_Verneed *need; - unsigned int auxoffset; - int cnt2; - /* Get the data at the next offset. */ - need = gelf_getverneed (needdata, offset, &needmem); - if (need == NULL) + GElf_Verneed needmem; + GElf_Verneed *need = gelf_getverneed (needdata, offset, + &needmem); + if (unlikely (need == NULL)) break; /* Run through the auxiliary entries. */ - auxoffset = offset + need->vn_aux; - for (cnt2 = need->vn_cnt; --cnt2 >= 0; ) + unsigned int auxoffset = offset + need->vn_aux; + for (int cnt2 = need->vn_cnt; --cnt2 >= 0; ) { GElf_Vernaux auxmem; - GElf_Vernaux *aux; - - aux = gelf_getvernaux (needdata, auxoffset, &auxmem); - if (aux == NULL) + GElf_Vernaux *aux = gelf_getvernaux (needdata, auxoffset, + &auxmem); + if (unlikely (aux == NULL)) break; vername[aux->vna_other & 0x7fff] @@ -2316,6 +2404,7 @@ handle_versym (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) } /* Print the header. */ + GElf_Shdr glink; printf (ngettext ("\ \nVersion symbols section [%2u] '%s' contains %d entry:\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'", "\ @@ -2332,21 +2421,19 @@ handle_versym (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) &glink)->sh_name)); /* Now we can finally look at the actual contents of this section. */ - for (cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt) + for (unsigned int cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt) { - GElf_Versym symmem; - GElf_Versym *sym; - ssize_t n; - if (cnt % 2 == 0) printf ("\n %4d:", cnt); - sym = gelf_getversym (data, cnt, &symmem); + GElf_Versym symmem; + GElf_Versym *sym = gelf_getversym (data, cnt, &symmem); if (sym == NULL) break; switch (*sym) { + ssize_t n; case 0: fputs_unlocked (gettext (" 0 *local* "), stdout); @@ -2369,138 +2456,276 @@ handle_versym (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) break; } } - putchar ('\n'); + putchar_unlocked ('\n'); } static void -handle_hash (Ebl *ebl, GElf_Ehdr *ehdr) +print_hash_info (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr, size_t shstrndx, + uint_fast32_t maxlength, Elf32_Word nbucket, + uint_fast32_t nsyms, uint32_t *lengths, const char *extrastr) { - /* Find the symbol table(s). For this we have to search through the - section table. */ - Elf_Scn *scn = NULL; - size_t shstrndx; + uint32_t *counts = (uint32_t *) xcalloc (maxlength + 1, sizeof (uint32_t)); - /* Get the section header string table index. */ - if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) - error (EXIT_FAILURE, 0, - gettext ("cannot get section header string table index")); + for (Elf32_Word cnt = 0; cnt < nbucket; ++cnt) + ++counts[lengths[cnt]]; - while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) + GElf_Shdr glink; + printf (ngettext ("\ +\nHistogram for bucket list length in section [%2u] '%s' (total of %d bucket):\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'\n", + "\ +\nHistogram for bucket list length in section [%2u] '%s' (total of %d buckets):\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'\n", + nbucket), + (unsigned int) elf_ndxscn (scn), + elf_strptr (ebl->elf, shstrndx, shdr->sh_name), + (int) nbucket, + gelf_getclass (ebl->elf) == ELFCLASS32 ? 10 : 18, + shdr->sh_addr, + shdr->sh_offset, + (unsigned int) shdr->sh_link, + elf_strptr (ebl->elf, shstrndx, + gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), + &glink)->sh_name)); + + if (extrastr != NULL) + fputs (extrastr, stdout); + + if (likely (nbucket > 0)) { - /* Handle the section if it is a symbol table. */ - GElf_Shdr shdr_mem; - GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + uint64_t success = 0; + + fputs_unlocked (gettext ("\ + Length Number % of total Coverage\n"), stdout); + printf (gettext (" 0 %6" PRIu32 " %5.1f%%\n"), + counts[0], (counts[0] * 100.0) / nbucket); - if (shdr != NULL && shdr->sh_type == SHT_HASH) + uint64_t nzero_counts = 0; + for (Elf32_Word cnt = 1; cnt <= maxlength; ++cnt) { - Elf_Data *data = elf_getdata (scn, NULL); - Elf32_Word nbucket; - Elf32_Word nchain; - Elf32_Word *bucket; - Elf32_Word *chain; - uint32_t *lengths; - uint32_t *counts; - Elf32_Word cnt; - Elf32_Word maxlength = 0; - Elf32_Word nsyms = 0; - uint64_t nzero_counts = 0; - GElf_Shdr glink; + nzero_counts += counts[cnt] * cnt; + printf (gettext ("\ +%7d %6" PRIu32 " %5.1f%% %5.1f%%\n"), + (int) cnt, counts[cnt], (counts[cnt] * 100.0) / nbucket, + (nzero_counts * 100.0) / nsyms); + } - if (data == NULL) - { - error (0, 0, gettext ("cannot get data for section %d: %s"), - (int) elf_ndxscn (scn), elf_errmsg (-1)); - continue; - } + Elf32_Word acc = 0; + for (Elf32_Word cnt = 1; cnt <= maxlength; ++cnt) + { + acc += cnt; + success += counts[cnt] * acc; + } + + printf (gettext ("\ + Average number of tests: successful lookup: %f\n\ + unsuccessful lookup: %f\n"), + (double) success / (double) nzero_counts, + (double) nzero_counts / (double) nbucket); + } - nbucket = ((Elf32_Word *) data->d_buf)[0]; - nchain = ((Elf32_Word *) data->d_buf)[1]; - bucket = &((Elf32_Word *) data->d_buf)[2]; - chain = &((Elf32_Word *) data->d_buf)[2 + nbucket]; + free (counts); +} - printf (ngettext ("\ -\nHistogram for bucket list length in section [%2u] '%s' (total of %d bucket):\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'\n", - "\ -\nHistogram for bucket list length in section [%2u] '%s' (total of %d buckets):\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'\n", - nbucket), - (unsigned int) elf_ndxscn (scn), - elf_strptr (ebl->elf, shstrndx, shdr->sh_name), - (int) nbucket, - gelf_getclass (ebl->elf) == ELFCLASS32 ? 10 : 18, - shdr->sh_addr, - shdr->sh_offset, - (unsigned int) shdr->sh_link, - elf_strptr (ebl->elf, shstrndx, - gelf_getshdr (elf_getscn (ebl->elf, - shdr->sh_link), - &glink)->sh_name)); - lengths = (uint32_t *) xcalloc (nbucket, sizeof (uint32_t)); +/* This function handles the traditional System V-style hash table format. */ +static void +handle_sysv_hash (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr, size_t shstrndx) +{ + Elf_Data *data = elf_getdata (scn, NULL); + if (unlikely (data == NULL)) + { + error (0, 0, gettext ("cannot get data for section %d: %s"), + (int) elf_ndxscn (scn), elf_errmsg (-1)); + return; + } - for (cnt = 0; cnt < nbucket; ++cnt) - if (bucket[cnt] != 0) - { - Elf32_Word inner; + Elf32_Word nbucket = ((Elf32_Word *) data->d_buf)[0]; + Elf32_Word nchain = ((Elf32_Word *) data->d_buf)[1]; + Elf32_Word *bucket = &((Elf32_Word *) data->d_buf)[2]; + Elf32_Word *chain = &((Elf32_Word *) data->d_buf)[2 + nbucket]; - inner = bucket[cnt]; - while (inner > 0 && inner < nchain) - { - ++nsyms; - if (maxlength < ++lengths[cnt]) - ++maxlength; + uint32_t *lengths = (uint32_t *) xcalloc (nbucket, sizeof (uint32_t)); - inner = chain[inner]; - } - } + uint_fast32_t maxlength = 0; + uint_fast32_t nsyms = 0; + for (Elf32_Word cnt = 0; cnt < nbucket; ++cnt) + { + Elf32_Word inner = bucket[cnt]; + while (inner > 0 && inner < nchain) + { + ++nsyms; + if (maxlength < ++lengths[cnt]) + ++maxlength; - counts = (uint32_t *) xcalloc (maxlength + 1, sizeof (uint32_t)); + inner = chain[inner]; + } + } - for (cnt = 0; cnt < nbucket; ++cnt) - ++counts[lengths[cnt]]; + print_hash_info (ebl, scn, shdr, shstrndx, maxlength, nbucket, nsyms, + lengths, NULL); - if (nbucket > 0) - { - uint64_t success = 0; - Elf32_Word acc; + free (lengths); +} - puts (gettext (" Length Number % of total Coverage")); - printf (gettext (" 0 %6" PRIu32 " %5.1f%%\n"), - counts[0], (counts[0] * 100.0) / nbucket); - for (cnt = 1; cnt <= maxlength; ++cnt) - { - nzero_counts += counts[cnt] * cnt; - printf (gettext ("\ -%7d %6" PRIu32 " %5.1f%% %5.1f%%\n"), - (int) cnt, - counts[cnt], (counts[cnt] * 100.0) / nbucket, - (nzero_counts * 100.0) / nsyms); - } +/* This function handles the incorrect, System V-style hash table + format some 64-bit architectures use. */ +static void +handle_sysv_hash64 (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr, size_t shstrndx) +{ + Elf_Data *data = elf_getdata (scn, NULL); + if (unlikely (data == NULL)) + { + error (0, 0, gettext ("cannot get data for section %d: %s"), + (int) elf_ndxscn (scn), elf_errmsg (-1)); + return; + } - acc = 0; - for (cnt = 1; cnt <= maxlength; ++cnt) - { - acc += cnt; - success += counts[cnt] * acc; - } + Elf64_Xword nbucket = ((Elf64_Xword *) data->d_buf)[0]; + Elf64_Xword nchain = ((Elf64_Xword *) data->d_buf)[1]; + Elf64_Xword *bucket = &((Elf64_Xword *) data->d_buf)[2]; + Elf64_Xword *chain = &((Elf64_Xword *) data->d_buf)[2 + nbucket]; - printf (gettext ("\ - Average number of tests: successful lookup: %f\n\ - unsuccessful lookup: %f\n"), - (double) success / (double) nzero_counts, - (double) nzero_counts / (double) nbucket); - } + uint32_t *lengths = (uint32_t *) xcalloc (nbucket, sizeof (uint32_t)); - free (counts); - free (lengths); + uint_fast32_t maxlength = 0; + uint_fast32_t nsyms = 0; + for (Elf64_Xword cnt = 0; cnt < nbucket; ++cnt) + { + Elf64_Xword inner = bucket[cnt]; + while (inner > 0 && inner < nchain) + { + ++nsyms; + if (maxlength < ++lengths[cnt]) + ++maxlength; + + inner = chain[inner]; } } + + print_hash_info (ebl, scn, shdr, shstrndx, maxlength, nbucket, nsyms, + lengths, NULL); + + free (lengths); +} + + +/* This function handles the GNU-style hash table format. */ +static void +handle_gnu_hash (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr, size_t shstrndx) +{ + Elf_Data *data = elf_getdata (scn, NULL); + if (unlikely (data == NULL)) + { + error (0, 0, gettext ("cannot get data for section %d: %s"), + (int) elf_ndxscn (scn), elf_errmsg (-1)); + return; + } + + Elf32_Word nbucket = ((Elf32_Word *) data->d_buf)[0]; + Elf32_Word symbias = ((Elf32_Word *) data->d_buf)[1]; + + /* Next comes the size of the bitmap. It's measured in words for + the architecture. It's 32 bits for 32 bit archs, and 64 bits for + 64 bit archs. */ + Elf32_Word bitmask_words = ((Elf32_Word *) data->d_buf)[2]; + if (gelf_getclass (ebl->elf) == ELFCLASS64) + bitmask_words *= 2; + + Elf32_Word shift = ((Elf32_Word *) data->d_buf)[3]; + + uint32_t *lengths = (uint32_t *) xcalloc (nbucket, sizeof (uint32_t)); + + Elf32_Word *bitmask = &((Elf32_Word *) data->d_buf)[4]; + Elf32_Word *bucket = &((Elf32_Word *) data->d_buf)[4 + bitmask_words]; + Elf32_Word *chain = &((Elf32_Word *) data->d_buf)[4 + bitmask_words + + nbucket]; + + /* Compute distribution of chain lengths. */ + uint_fast32_t maxlength = 0; + uint_fast32_t nsyms = 0; + for (Elf32_Word cnt = 0; cnt < nbucket; ++cnt) + if (bucket[cnt] != 0) + { + Elf32_Word inner = bucket[cnt] - symbias; + do + { + ++nsyms; + if (maxlength < ++lengths[cnt]) + ++maxlength; + } + while ((chain[inner++] & 1) == 0); + } + + /* Count bits in bitmask. */ + uint_fast32_t nbits = 0; + for (Elf32_Word cnt = 0; cnt < bitmask_words; ++cnt) + { + uint_fast32_t word = bitmask[cnt]; + + word = (word & 0x55555555) + ((word >> 1) & 0x55555555); + word = (word & 0x33333333) + ((word >> 2) & 0x33333333); + word = (word & 0x0f0f0f0f) + ((word >> 4) & 0x0f0f0f0f); + word = (word & 0x00ff00ff) + ((word >> 8) & 0x00ff00ff); + nbits += (word & 0x0000ffff) + ((word >> 16) & 0x0000ffff); + } + + char *str; + if (unlikely (asprintf (&str, gettext ("\ + Symbol Bias: %u\n\ + Bitmask Size: %zu bytes %" PRIuFAST32 "%% bits set 2nd hash shift: %u\n"), + (unsigned int) symbias, + bitmask_words * sizeof (Elf32_Word), + ((nbits * 100 + 50) + / (uint_fast32_t) (bitmask_words + * sizeof (Elf32_Word) * 8)), + (unsigned int) shift) == -1)) + error (EXIT_FAILURE, 0, gettext ("memory exhausted")); + + print_hash_info (ebl, scn, shdr, shstrndx, maxlength, nbucket, nsyms, + lengths, str); + + free (str); + free (lengths); } +/* Find the symbol table(s). For this we have to search through the + section table. */ static void -print_liblist (Ebl *ebl, GElf_Ehdr *ehdr) +handle_hash (Ebl *ebl) +{ + /* Get the section header string table index. */ + size_t shstrndx; + if (unlikely (elf_getshstrndx (ebl->elf, &shstrndx) < 0)) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) + { + /* Handle the section if it is a symbol table. */ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + + if (likely (shdr != NULL)) + { + if (shdr->sh_type == SHT_HASH) + { + if (ebl_sysvhash_entrysize (ebl) == sizeof (Elf64_Xword)) + handle_sysv_hash64 (ebl, scn, shdr, shstrndx); + else + handle_sysv_hash (ebl, scn, shdr, shstrndx); + } + else if (shdr->sh_type == SHT_GNU_HASH) + handle_gnu_hash (ebl, scn, shdr, shstrndx); + } + } +} + + +static void +print_liblist (Ebl *ebl) { /* Find the library list sections. For this we have to search through the section table. */ @@ -2508,7 +2733,7 @@ print_liblist (Ebl *ebl, GElf_Ehdr *ehdr) /* Get the section header string table index. */ size_t shstrndx; - if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + if (unlikely (elf_getshstrndx (ebl->elf, &shstrndx) < 0)) error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); @@ -2541,12 +2766,12 @@ print_liblist (Ebl *ebl, GElf_Ehdr *ehdr) { GElf_Lib lib_mem; GElf_Lib *lib = gelf_getlib (data, cnt, &lib_mem); - if (lib == NULL) + if (unlikely (lib == NULL)) continue; time_t t = (time_t) lib->l_time_stamp; struct tm *tm = gmtime (&t); - if (tm == NULL) + if (unlikely (tm == NULL)) continue; printf (" [%2d] %-29s %04u-%02u-%02uT%02u:%02u:%02u %08x %-7u %u\n", @@ -2561,11 +2786,244 @@ print_liblist (Ebl *ebl, GElf_Ehdr *ehdr) } } +static void +print_attributes (Ebl *ebl, const GElf_Ehdr *ehdr) +{ + /* Find the object attributes sections. For this we have to search + through the section table. */ + Elf_Scn *scn = NULL; + + /* Get the section header string table index. */ + size_t shstrndx; + if (unlikely (elf_getshstrndx (ebl->elf, &shstrndx) < 0)) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + + if (shdr == NULL || shdr->sh_type != SHT_GNU_ATTRIBUTES) + continue; + + printf (gettext ("\ +\nObject attributes section [%2zu] '%s' of %" PRIu64 + " bytes at offset %#0" PRIx64 ":\n"), + elf_ndxscn (scn), + elf_strptr (ebl->elf, shstrndx, shdr->sh_name), + shdr->sh_size, shdr->sh_offset); + + Elf_Data *data = elf_rawdata (scn, NULL); + if (data == NULL) + return; + + const unsigned char *p = data->d_buf; + + if (unlikely (*p++ != 'A')) + return; + + fputs_unlocked (gettext (" Owner Size\n"), stdout); + + inline size_t left (void) + { + return (const unsigned char *) data->d_buf + data->d_size - p; + } + + while (left () >= 4) + { + uint32_t len; + memcpy (&len, p, sizeof len); + + if (MY_ELFDATA != ehdr->e_ident[EI_DATA]) + CONVERT (len); + + if (unlikely (len > left ())) + break; + + const unsigned char *name = p + sizeof len; + p += len; + + unsigned const char *q = memchr (name, '\0', len); + if (unlikely (q == NULL)) + continue; + ++q; + + printf (gettext (" %-13s %4" PRIu32 "\n"), name, len); + + if (q - name == sizeof "gnu" + && !memcmp (name, "gnu", sizeof "gnu")) + while (q < p) + { + const unsigned char *const sub = q; + + unsigned int subsection_tag; + get_uleb128 (subsection_tag, q); + if (unlikely (q >= p)) + break; + + uint32_t subsection_len; + if (unlikely (p - sub < (ptrdiff_t) sizeof subsection_len)) + break; + + memcpy (&subsection_len, q, sizeof subsection_len); + + if (MY_ELFDATA != ehdr->e_ident[EI_DATA]) + CONVERT (subsection_len); + + if (unlikely (p - sub < (ptrdiff_t) subsection_len)) + break; + + const unsigned char *r = q + sizeof subsection_len; + q = sub + subsection_len; + + switch (subsection_tag) + { + default: + printf (gettext (" %-4u %12" PRIu32 "\n"), + subsection_tag, subsection_len); + break; + + case 1: /* Tag_File */ + printf (gettext (" File: %11" PRIu32 "\n"), + subsection_len); + + while (r < q) + { + unsigned int tag; + get_uleb128 (tag, r); + if (unlikely (r >= q)) + break; + + uint64_t value = 0; + const char *string = NULL; + if (tag == 32 || (tag & 1) == 0) + { + get_uleb128 (value, r); + if (r > q) + break; + } + if (tag == 32 || (tag & 1) != 0) + { + r = memchr (r, '\0', q - r); + if (r == NULL) + break; + ++r; + } + + const char *tag_name = NULL; + const char *value_name = NULL; + ebl_check_object_attribute (ebl, (const char *) name, + tag, value, + &tag_name, &value_name); + + if (tag_name != NULL) + { + if (tag == 32) + printf (gettext (" %s: %" PRId64 ", %s\n"), + tag_name, value, string); + else if (string == NULL && value_name == NULL) + printf (gettext (" %s: %" PRId64 "\n"), + tag_name, value); + else + printf (gettext (" %s: %s\n"), + tag_name, string ?: value_name); + } + else + { + assert (tag != 32); + if (string == NULL) + printf (gettext (" %u: %" PRId64 "\n"), + tag, value); + else + printf (gettext (" %u: %s\n"), + tag, string); + } + } + } + } + } + } +} + + +static char * +format_dwarf_addr (Dwfl_Module *dwflmod, + int address_size, Dwarf_Addr address) +{ + /* See if there is a name we can give for this address. */ + GElf_Sym sym; + const char *name = dwfl_module_addrsym (dwflmod, address, &sym, NULL); + if (name != NULL) + sym.st_value = address - sym.st_value; + + /* Relativize the address. */ + int n = dwfl_module_relocations (dwflmod); + int i = n < 1 ? -1 : dwfl_module_relocate_address (dwflmod, &address); + + /* In an ET_REL file there is a section name to refer to. */ + const char *scn = (i < 0 ? NULL + : dwfl_module_relocation_info (dwflmod, i, NULL)); + + char *result; + if ((name != NULL + ? (sym.st_value != 0 + ? (scn != NULL + ? (address_size == 0 + ? asprintf (&result, + gettext ("%s+%#" PRIx64 " <%s+%#" PRIx64 ">"), + scn, address, name, sym.st_value) + : asprintf (&result, + gettext ("%s+%#0*" PRIx64 " <%s+%#" PRIx64 ">"), + scn, 2 + address_size * 2, address, + name, sym.st_value)) + : (address_size == 0 + ? asprintf (&result, + gettext ("%#" PRIx64 " <%s+%#" PRIx64 ">"), + address, name, sym.st_value) + : asprintf (&result, + gettext ("%#0*" PRIx64 " <%s+%#" PRIx64 ">"), + 2 + address_size * 2, address, + name, sym.st_value))) + : (scn != NULL + ? (address_size == 0 + ? asprintf (&result, + gettext ("%s+%#" PRIx64 " <%s>"), + scn, address, name) + : asprintf (&result, + gettext ("%s+%#0*" PRIx64 " <%s>"), + scn, 2 + address_size * 2, address, name)) + : (address_size == 0 + ? asprintf (&result, + gettext ("%#" PRIx64 " <%s>"), + address, name) + : asprintf (&result, + gettext ("%#0*" PRIx64 " <%s>"), + 2 + address_size * 2, address, name)))) + : (scn != NULL + ? (address_size == 0 + ? asprintf (&result, + gettext ("%s+%#" PRIx64), + scn, address) + : asprintf (&result, + gettext ("%s+%#0*" PRIx64), + scn, 2 + address_size * 2, address)) + : (address_size == 0 + ? asprintf (&result, + "%#" PRIx64, + address) + : asprintf (&result, + "%#0*" PRIx64, + 2 + address_size * 2, address)))) < 0) + error (EXIT_FAILURE, 0, _("memory exhausted")); + + return result; +} static const char * dwarf_tag_string (unsigned int tag) { - static const char *known_tags[] = + static const char *const known_tags[] = { [DW_TAG_array_type] = "array_type", [DW_TAG_class_type] = "class_type", @@ -2607,23 +3065,34 @@ dwarf_tag_string (unsigned int tag) [DW_TAG_namelist_item] = "namelist_item", [DW_TAG_packed_type] = "packed_type", [DW_TAG_subprogram] = "subprogram", - [DW_TAG_template_type_param] = "template_type_param", - [DW_TAG_template_value_param] = "template_value_param", + [DW_TAG_template_type_parameter] = "template_type_parameter", + [DW_TAG_template_value_parameter] = "template_value_parameter", [DW_TAG_thrown_type] = "thrown_type", [DW_TAG_try_block] = "try_block", [DW_TAG_variant_part] = "variant_part", [DW_TAG_variable] = "variable", [DW_TAG_volatile_type] = "volatile_type", + [DW_TAG_dwarf_procedure] = "dwarf_procedure", + [DW_TAG_restrict_type] = "restrict_type", + [DW_TAG_interface_type] = "interface_type", + [DW_TAG_namespace] = "namespace", + [DW_TAG_imported_module] = "imported_module", + [DW_TAG_unspecified_type] = "unspecified_type", + [DW_TAG_partial_unit] = "partial_unit", + [DW_TAG_imported_unit] = "imported_unit", + [DW_TAG_mutable_type] = "mutable_type", + [DW_TAG_condition] = "condition", + [DW_TAG_shared_type] = "shared_type", }; const unsigned int nknown_tags = (sizeof (known_tags) / sizeof (known_tags[0])); static char buf[40]; const char *result = NULL; - if (tag < nknown_tags) + if (likely (tag < nknown_tags)) result = known_tags[tag]; - if (result == NULL) + if (unlikely (result == NULL)) /* There are a few known extensions. */ switch (tag) { @@ -2659,7 +3128,7 @@ dwarf_tag_string (unsigned int tag) static const char * dwarf_attr_string (unsigned int attrnum) { - static const char *known_attrs[] = + static const char *const known_attrs[] = { [DW_AT_sibling] = "sibling", [DW_AT_location] = "location", @@ -2692,7 +3161,7 @@ dwarf_attr_string (unsigned int attrnum) [DW_AT_prototyped] = "prototyped", [DW_AT_return_addr] = "return_addr", [DW_AT_start_scope] = "start_scope", - [DW_AT_stride_size] = "stride_size", + [DW_AT_bit_stride] = "bit_stride", [DW_AT_upper_bound] = "upper_bound", [DW_AT_abstract_origin] = "abstract_origin", [DW_AT_accessibility] = "accessibility", @@ -2713,7 +3182,7 @@ dwarf_attr_string (unsigned int attrnum) [DW_AT_friend] = "friend", [DW_AT_identifier_case] = "identifier_case", [DW_AT_macro_info] = "macro_info", - [DW_AT_namelist_items] = "namelist_items", + [DW_AT_namelist_item] = "namelist_item", [DW_AT_priority] = "priority", [DW_AT_segment] = "segment", [DW_AT_specification] = "specification", @@ -2722,17 +3191,44 @@ dwarf_attr_string (unsigned int attrnum) [DW_AT_use_location] = "use_location", [DW_AT_variable_parameter] = "variable_parameter", [DW_AT_virtuality] = "virtuality", - [DW_AT_vtable_elem_location] = "vtable_elem_location" + [DW_AT_vtable_elem_location] = "vtable_elem_location", + [DW_AT_allocated] = "allocated", + [DW_AT_associated] = "associated", + [DW_AT_data_location] = "data_location", + [DW_AT_byte_stride] = "byte_stride", + [DW_AT_entry_pc] = "entry_pc", + [DW_AT_use_UTF8] = "use_UTF8", + [DW_AT_extension] = "extension", + [DW_AT_ranges] = "ranges", + [DW_AT_trampoline] = "trampoline", + [DW_AT_call_column] = "call_column", + [DW_AT_call_file] = "call_file", + [DW_AT_call_line] = "call_line", + [DW_AT_description] = "description", + [DW_AT_binary_scale] = "binary_scale", + [DW_AT_decimal_scale] = "decimal_scale", + [DW_AT_small] = "small", + [DW_AT_decimal_sign] = "decimal_sign", + [DW_AT_digit_count] = "digit_count", + [DW_AT_picture_string] = "picture_string", + [DW_AT_mutable] = "mutable", + [DW_AT_threads_scaled] = "threads_scaled", + [DW_AT_explicit] = "explicit", + [DW_AT_object_pointer] = "object_pointer", + [DW_AT_endianity] = "endianity", + [DW_AT_elemental] = "elemental", + [DW_AT_pure] = "pure", + [DW_AT_recursive] = "recursive", }; const unsigned int nknown_attrs = (sizeof (known_attrs) / sizeof (known_attrs[0])); static char buf[40]; const char *result = NULL; - if (attrnum < nknown_attrs) + if (likely (attrnum < nknown_attrs)) result = known_attrs[attrnum]; - if (result == NULL) + if (unlikely (result == NULL)) /* There are a few known extensions. */ switch (attrnum) { @@ -2846,7 +3342,7 @@ dwarf_attr_string (unsigned int attrnum) static const char * dwarf_form_string (unsigned int form) { - static const char *known_forms[] = + static const char *const known_forms[] = { [DW_FORM_addr] = "addr", [DW_FORM_block2] = "block2", @@ -2875,10 +3371,10 @@ dwarf_form_string (unsigned int form) static char buf[40]; const char *result = NULL; - if (form < nknown_forms) + if (likely (form < nknown_forms)) result = known_forms[form]; - if (result == NULL) + if (unlikely (result == NULL)) snprintf (buf, sizeof buf, gettext ("unknown form %" PRIx64), (uint64_t) form); @@ -2889,12 +3385,12 @@ dwarf_form_string (unsigned int form) static const char * dwarf_lang_string (unsigned int lang) { - static const char *known[] = + static const char *const known[] = { [DW_LANG_C89] = "ISO C89", [DW_LANG_C] = "C", [DW_LANG_Ada83] = "Ada83", - [DW_LANG_C_plus_plus ] = "C++", + [DW_LANG_C_plus_plus] = "C++", [DW_LANG_Cobol74] = "Cobol74", [DW_LANG_Cobol85] = "Cobol85", [DW_LANG_Fortran77] = "Fortran77", @@ -2905,10 +3401,14 @@ dwarf_lang_string (unsigned int lang) [DW_LANG_C99] = "ISO C99", [DW_LANG_Ada95] = "Ada95", [DW_LANG_Fortran95] = "Fortran95", - [DW_LANG_PL1] = "PL1" + [DW_LANG_PL1] = "PL1", + [DW_LANG_Objc] = "Objective C", + [DW_LANG_ObjC_plus_plus] = "Objective C++", + [DW_LANG_UPC] = "UPC", + [DW_LANG_D] = "D", }; - if (lang < sizeof (known) / sizeof (known[0])) + if (likely (lang < sizeof (known) / sizeof (known[0]))) return known[lang]; else if (lang == DW_LANG_Mips_Assembler) /* This language tag is used for assembler in general. */ @@ -2916,7 +3416,7 @@ dwarf_lang_string (unsigned int lang) if (lang >= DW_LANG_lo_user && lang <= DW_LANG_hi_user) { - static char buf[100]; + static char buf[30]; snprintf (buf, sizeof (buf), "lo_user+%u", lang - DW_LANG_lo_user); return buf; } @@ -2925,11 +3425,191 @@ dwarf_lang_string (unsigned int lang) } +static const char * +dwarf_inline_string (unsigned int code) +{ + static const char *const known[] = + { + [DW_INL_not_inlined] = "not_inlined", + [DW_INL_inlined] = "inlined", + [DW_INL_declared_not_inlined] = "declared_not_inlined", + [DW_INL_declared_inlined] = "declared_inlined" + }; + + if (likely (code < sizeof (known) / sizeof (known[0]))) + return known[code]; + + return "???"; +} + + +static const char * +dwarf_encoding_string (unsigned int code) +{ + static const char *const known[] = + { + [DW_ATE_void] = "void", + [DW_ATE_address] = "address", + [DW_ATE_boolean] = "boolean", + [DW_ATE_complex_float] = "complex_float", + [DW_ATE_float] = "float", + [DW_ATE_signed] = "signed", + [DW_ATE_signed_char] = "signed_char", + [DW_ATE_unsigned] = "unsigned", + [DW_ATE_unsigned_char] = "unsigned_char", + [DW_ATE_imaginary_float] = "imaginary_float", + [DW_ATE_packed_decimal] = "packed_decimal", + [DW_ATE_numeric_string] = "numeric_string", + [DW_ATE_edited] = "edited", + [DW_ATE_signed_fixed] = "signed_fixed", + [DW_ATE_unsigned_fixed] = "unsigned_fixed", + [DW_ATE_decimal_float] = "decimal_float", + }; + + if (likely (code < sizeof (known) / sizeof (known[0]))) + return known[code]; + + if (code >= DW_ATE_lo_user && code <= DW_ATE_hi_user) + { + static char buf[30]; + snprintf (buf, sizeof (buf), "lo_user+%u", code - DW_ATE_lo_user); + return buf; + } + + return "???"; +} + + +static const char * +dwarf_access_string (unsigned int code) +{ + static const char *const known[] = + { + [DW_ACCESS_public] = "public", + [DW_ACCESS_protected] = "protected", + [DW_ACCESS_private] = "private" + }; + + if (likely (code < sizeof (known) / sizeof (known[0]))) + return known[code]; + + return "???"; +} + + +static const char * +dwarf_visibility_string (unsigned int code) +{ + static const char *const known[] = + { + [DW_VIS_local] = "local", + [DW_VIS_exported] = "exported", + [DW_VIS_qualified] = "qualified" + }; + + if (likely (code < sizeof (known) / sizeof (known[0]))) + return known[code]; + + return "???"; +} + + +static const char * +dwarf_virtuality_string (unsigned int code) +{ + static const char *const known[] = + { + [DW_VIRTUALITY_none] = "none", + [DW_VIRTUALITY_virtual] = "virtual", + [DW_VIRTUALITY_pure_virtual] = "pure_virtual" + }; + + if (likely (code < sizeof (known) / sizeof (known[0]))) + return known[code]; + + return "???"; +} + + +static const char * +dwarf_identifier_case_string (unsigned int code) +{ + static const char *const known[] = + { + [DW_ID_case_sensitive] = "sensitive", + [DW_ID_up_case] = "up_case", + [DW_ID_down_case] = "down_case", + [DW_ID_case_insensitive] = "insensitive" + }; + + if (likely (code < sizeof (known) / sizeof (known[0]))) + return known[code]; + + return "???"; +} + + +static const char * +dwarf_calling_convention_string (unsigned int code) +{ + static const char *const known[] = + { + [DW_CC_normal] = "normal", + [DW_CC_program] = "program", + [DW_CC_nocall] = "nocall", + }; + + if (likely (code < sizeof (known) / sizeof (known[0]))) + return known[code]; + + if (code >= DW_CC_lo_user && code <= DW_CC_hi_user) + { + static char buf[30]; + snprintf (buf, sizeof (buf), "lo_user+%u", code - DW_CC_lo_user); + return buf; + } + + return "???"; +} + + +static const char * +dwarf_ordering_string (unsigned int code) +{ + static const char *const known[] = + { + [DW_ORD_row_major] = "row_major", + [DW_ORD_col_major] = "col_major" + }; + + if (likely (code < sizeof (known) / sizeof (known[0]))) + return known[code]; + + return "???"; +} + + +static const char * +dwarf_discr_list_string (unsigned int code) +{ + static const char *const known[] = + { + [DW_DSC_label] = "label", + [DW_DSC_range] = "range" + }; + + if (likely (code < sizeof (known) / sizeof (known[0]))) + return known[code]; + + return "???"; +} + + static void -print_ops (Dwarf *dbg, int level, unsigned int addrsize, Dwarf_Word len, - unsigned char *data) +print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, + unsigned int addrsize, Dwarf_Word len, const unsigned char *data) { - static const char *known[] = + static const char *const known[] = { [DW_OP_addr] = "addr", [DW_OP_deref] = "deref", @@ -3080,13 +3760,15 @@ print_ops (Dwarf *dbg, int level, unsigned int addrsize, Dwarf_Word len, [DW_OP_call2] = "call2", [DW_OP_call4] = "call4", [DW_OP_call_ref] = "call_ref", + [DW_OP_form_tls_address] = "form_tls_address", + [DW_OP_call_frame_cfa] = "call_frame_cfa", + [DW_OP_bit_piece] = "bit_piece", }; Dwarf_Word offset = 0; while (len-- > 0) { - size_t op = *((unsigned char *) data); - ++data; + uint_fast8_t op = *data++; switch (op) { @@ -3104,18 +3786,27 @@ print_ops (Dwarf *dbg, int level, unsigned int addrsize, Dwarf_Word len, data += addrsize; len -= addrsize; - printf (" %*s [%4" PRIuMAX "] %s %" PRIuMAX "\n", - (int) (20 + level * 2), "", (uintmax_t) offset, - known[op] ?: "???", (uintmax_t) addr); + if (op == DW_OP_addr) + { + char *a = format_dwarf_addr (dwflmod, 0, addr); + printf ("%*s[%4" PRIuMAX "] %s %s\n", + indent, "", (uintmax_t) offset, + known[op] ?: "???", a); + free (a); + } + else + printf ("%*s[%4" PRIuMAX "] %s %#" PRIxMAX "\n", + indent, "", (uintmax_t) offset, + known[op] ?: "???", (uintmax_t) addr); offset += 1 + addrsize; break; - case DW_OP_deref_size: /* XXX Correct? */ - case DW_OP_xderef_size: /* XXX Correct? */ + case DW_OP_deref_size: + case DW_OP_xderef_size: case DW_OP_pick: case DW_OP_const1u: - printf (" %*s [%4" PRIuMAX "] %s %" PRIu8 "\n", - (int) (20 + level * 2), "", (uintmax_t) offset, + printf ("%*s[%4" PRIuMAX "] %s %" PRIu8 "\n", + indent, "", (uintmax_t) offset, known[op] ?: "???", *((uint8_t *) data)); ++data; --len; @@ -3123,8 +3814,8 @@ print_ops (Dwarf *dbg, int level, unsigned int addrsize, Dwarf_Word len, break; case DW_OP_const2u: - printf (" %*s [%4" PRIuMAX "] %s %" PRIu16 "\n", - (int) (20 + level * 2), "", (uintmax_t) offset, + printf ("%*s[%4" PRIuMAX "] %s %" PRIu16 "\n", + indent, "", (uintmax_t) offset, known[op] ?: "???", read_2ubyte_unaligned (dbg, data)); len -= 2; data += 2; @@ -3132,8 +3823,8 @@ print_ops (Dwarf *dbg, int level, unsigned int addrsize, Dwarf_Word len, break; case DW_OP_const4u: - printf (" %*s [%4" PRIuMAX "] %s %" PRIu32 "\n", - (int) (20 + level * 2), "", (uintmax_t) offset, + printf ("%*s[%4" PRIuMAX "] %s %" PRIu32 "\n", + indent, "", (uintmax_t) offset, known[op] ?: "???", read_4ubyte_unaligned (dbg, data)); len -= 4; data += 4; @@ -3141,8 +3832,8 @@ print_ops (Dwarf *dbg, int level, unsigned int addrsize, Dwarf_Word len, break; case DW_OP_const8u: - printf (" %*s [%4" PRIuMAX "] %s %" PRIu64 "\n", - (int) (20 + level * 2), "", (uintmax_t) offset, + printf ("%*s[%4" PRIuMAX "] %s %" PRIu64 "\n", + indent, "", (uintmax_t) offset, known[op] ?: "???", read_8ubyte_unaligned (dbg, data)); len -= 8; data += 8; @@ -3150,8 +3841,8 @@ print_ops (Dwarf *dbg, int level, unsigned int addrsize, Dwarf_Word len, break; case DW_OP_const1s: - printf (" %*s [%4" PRIuMAX "] %s %" PRId8 "\n", - (int) (20 + level * 2), "", (uintmax_t) offset, + printf ("%*s[%4" PRIuMAX "] %s %" PRId8 "\n", + indent, "", (uintmax_t) offset, known[op] ?: "???", *((int8_t *) data)); ++data; --len; @@ -3159,8 +3850,8 @@ print_ops (Dwarf *dbg, int level, unsigned int addrsize, Dwarf_Word len, break; case DW_OP_const2s: - printf (" %*s [%4" PRIuMAX "] %s %" PRId16 "\n", - (int) (20 + level * 2), "", (uintmax_t) offset, + printf ("%*s[%4" PRIuMAX "] %s %" PRId16 "\n", + indent, "", (uintmax_t) offset, known[op] ?: "???", read_2sbyte_unaligned (dbg, data)); len -= 2; data += 2; @@ -3168,8 +3859,8 @@ print_ops (Dwarf *dbg, int level, unsigned int addrsize, Dwarf_Word len, break; case DW_OP_const4s: - printf (" %*s [%4" PRIuMAX "] %s %" PRId32 "\n", - (int) (20 + level * 2), "", (uintmax_t) offset, + printf ("%*s[%4" PRIuMAX "] %s %" PRId32 "\n", + indent, "", (uintmax_t) offset, known[op] ?: "???", read_4sbyte_unaligned (dbg, data)); len -= 4; data += 4; @@ -3177,36 +3868,48 @@ print_ops (Dwarf *dbg, int level, unsigned int addrsize, Dwarf_Word len, break; case DW_OP_const8s: - printf (" %*s [%4" PRIuMAX "] %s %" PRId64 "\n", - (int) (20 + level * 2), "", (uintmax_t) offset, + printf ("%*s[%4" PRIuMAX "] %s %" PRId64 "\n", + indent, "", (uintmax_t) offset, known[op] ?: "???", read_8sbyte_unaligned (dbg, data)); len -= 8; data += 8; offset += 9; break; - case DW_OP_piece: /* XXX Correct? */ + case DW_OP_piece: case DW_OP_regx: case DW_OP_plus_uconst: case DW_OP_constu:; - unsigned char *start = data; + const unsigned char *start = data; unsigned int uleb; get_uleb128 (uleb, data); - printf (" %*s [%4" PRIuMAX "] %s %u\n", - (int) (20 + level * 2), "", (uintmax_t) offset, + printf ("%*s[%4" PRIuMAX "] %s %u\n", + indent, "", (uintmax_t) offset, known[op] ?: "???", uleb); len -= data - start; offset += 1 + (data - start); break; + case DW_OP_bit_piece: + start = data; + unsigned int uleb2; + get_uleb128 (uleb, data); + get_uleb128 (uleb2, data); + printf ("%*s[%4" PRIuMAX "] %s %u, %u\n", + indent, "", (uintmax_t) offset, + known[op] ?: "???", uleb, uleb2); + len -= data - start; + offset += 1 + (data - start); + break; + case DW_OP_fbreg: case DW_OP_breg0 ... DW_OP_breg31: case DW_OP_consts: start = data; unsigned int sleb; get_sleb128 (sleb, data); - printf (" %*s [%4" PRIuMAX "] %s %d\n", - (int) (20 + level * 2), "", (uintmax_t) offset, + printf ("%*s[%4" PRIuMAX "] %s %d\n", + indent, "", (uintmax_t) offset, known[op] ?: "???", sleb); len -= data - start; offset += 1 + (data - start); @@ -3216,8 +3919,8 @@ print_ops (Dwarf *dbg, int level, unsigned int addrsize, Dwarf_Word len, start = data; get_uleb128 (uleb, data); get_sleb128 (sleb, data); - printf (" %*s [%4" PRIuMAX "] %s %u %d\n", - (int) (20 + level * 2), "", (uintmax_t) offset, + printf ("%*s[%4" PRIuMAX "] %s %u %d\n", + indent, "", (uintmax_t) offset, known[op] ?: "???", uleb, sleb); len -= data - start; offset += 1 + (data - start); @@ -3227,8 +3930,8 @@ print_ops (Dwarf *dbg, int level, unsigned int addrsize, Dwarf_Word len, case DW_OP_call4: case DW_OP_skip: case DW_OP_bra: - printf (" %*s [%4" PRIuMAX "] %s %" PRIuMAX "\n", - (int) (20 + level * 2), "", (uintmax_t) offset, + printf ("%*s[%4" PRIuMAX "] %s %" PRIuMAX "\n", + indent, "", (uintmax_t) offset, known[op] ?: "???", (uintmax_t) (offset + read_2sbyte_unaligned (dbg, data))); len -= 2; @@ -3238,18 +3941,26 @@ print_ops (Dwarf *dbg, int level, unsigned int addrsize, Dwarf_Word len, default: /* No Operand. */ - printf (" %*s [%4" PRIuMAX "] %s\n", - (int) (20 + level * 2), "", (uintmax_t) offset, - known[op] ?: "???"); + if (op < sizeof known / sizeof known[0] && known[op] != NULL) + printf ("%*s[%4" PRIuMAX "] %s\n", + indent, "", (uintmax_t) offset, known[op]); + else + printf ("%*s[%4" PRIuMAX "] %#x\n", + indent, "", (uintmax_t) offset, op); ++offset; break; } + + indent = indentrest; } } static void -print_debug_abbrev_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, +print_debug_abbrev_section (Dwfl_Module *dwflmod __attribute__ ((unused)), + Ebl *ebl __attribute__ ((unused)), + GElf_Ehdr *ehdr __attribute__ ((unused)), + Elf_Scn *scn __attribute__ ((unused)), GElf_Shdr *shdr, Dwarf *dbg) { printf (gettext ("\nDWARF section '%s' at offset %#" PRIx64 ":\n" @@ -3257,47 +3968,59 @@ print_debug_abbrev_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, ".debug_abbrev", (uint64_t) shdr->sh_offset); Dwarf_Off offset = 0; - while (1) + while (offset < shdr->sh_size) { - size_t length; - Dwarf_Abbrev abbrev; + printf (gettext ("\nAbbreviation section at offset %" PRIu64 ":\n"), + offset); - if (dwarf_offabbrev (dbg, offset, &length, &abbrev) != 0) + while (1) { - printf (gettext (" *** error while reading abbreviation: %s\n"), - dwarf_errmsg (-1)); - break; - } + size_t length; + Dwarf_Abbrev abbrev; - if (length == 1) - /* This is the NUL byte at the end of the section. */ - break; + int res = dwarf_offabbrev (dbg, offset, &length, &abbrev); + if (res != 0) + { + if (unlikely (res < 0)) + { + printf (gettext ("\ + *** error while reading abbreviation: %s\n"), + dwarf_errmsg (-1)); + return; + } - /* We know these calls can never fail. */ - unsigned int code = dwarf_getabbrevcode (&abbrev); - unsigned int tag = dwarf_getabbrevtag (&abbrev); - int has_children = dwarf_abbrevhaschildren (&abbrev); + /* This is the NUL byte at the end of the section. */ + ++offset; + break; + } - printf (gettext (" [%5u] offset: %" PRId64 - ", children: %s, tag: %s\n"), - code, (int64_t) offset, - has_children ? gettext ("yes") : gettext ("no"), - dwarf_tag_string (tag)); + /* We know these calls can never fail. */ + unsigned int code = dwarf_getabbrevcode (&abbrev); + unsigned int tag = dwarf_getabbrevtag (&abbrev); + int has_children = dwarf_abbrevhaschildren (&abbrev); + + printf (gettext (" [%5u] offset: %" PRId64 + ", children: %s, tag: %s\n"), + code, (int64_t) offset, + has_children ? gettext ("yes") : gettext ("no"), + dwarf_tag_string (tag)); + + size_t cnt = 0; + unsigned int name; + unsigned int form; + Dwarf_Off enoffset; + while (dwarf_getabbrevattr (&abbrev, cnt, + &name, &form, &enoffset) == 0) + { + printf (" attr: %s, form: %s, offset: %#" PRIx64 "\n", + dwarf_attr_string (name), dwarf_form_string (form), + (uint64_t) enoffset); - size_t cnt = 0; - unsigned int name; - unsigned int form; - Dwarf_Off enoffset; - while (dwarf_getabbrevattr (&abbrev, cnt, &name, &form, &enoffset) == 0) - { - printf (" attr: %s, form: %s, offset: %#" PRIx64 "\n", - dwarf_attr_string (name), dwarf_form_string (form), - (uint64_t) enoffset); + ++cnt; + } - ++cnt; + offset += length; } - - offset += length; } } @@ -3306,12 +4029,15 @@ print_debug_abbrev_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, not have to know a bit about the structure of the section, libdwarf takes care of it. */ static void -print_debug_aranges_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, +print_debug_aranges_section (Dwfl_Module *dwflmod __attribute__ ((unused)), + Ebl *ebl __attribute__ ((unused)), + GElf_Ehdr *ehdr __attribute__ ((unused)), + Elf_Scn *scn __attribute__ ((unused)), GElf_Shdr *shdr, Dwarf *dbg) { Dwarf_Aranges *aranges; size_t cnt; - if (dwarf_getaranges (dbg, &aranges, &cnt) != 0) + if (unlikely (dwarf_getaranges (dbg, &aranges, &cnt) != 0)) { error (0, 0, gettext ("cannot get .debug_aranges content: %s"), dwarf_errmsg (-1)); @@ -3337,7 +4063,7 @@ print_debug_aranges_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, for (size_t n = 0; n < cnt; ++n) { Dwarf_Arange *runp = dwarf_onearange (aranges, n); - if (runp == NULL) + if (unlikely (runp == NULL)) { printf ("cannot get arange %zu: %s\n", n, dwarf_errmsg (-1)); return; @@ -3347,7 +4073,7 @@ print_debug_aranges_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, Dwarf_Word length; Dwarf_Off offset; - if (dwarf_getarangeinfo (runp, &start, &length, &offset) != 0) + if (unlikely (dwarf_getarangeinfo (runp, &start, &length, &offset) != 0)) printf (gettext (" [%*zu] ???\n"), digits, n); else printf (gettext (" [%*zu] start: %0#*" PRIx64 @@ -3358,16 +4084,95 @@ print_debug_aranges_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, } } +/* Print content of DWARF .debug_ranges section. */ +static void +print_debug_ranges_section (Dwfl_Module *dwflmod, + Ebl *ebl __attribute__ ((unused)), + GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr, + Dwarf *dbg) +{ + Elf_Data *data = elf_rawdata (scn, NULL); + + if (unlikely (data == NULL)) + { + error (0, 0, gettext ("cannot get .debug_ranges content: %s"), + elf_errmsg (-1)); + return; + } + + printf (gettext ("\ +\nDWARF section '%s' at offset %#" PRIx64 ":\n"), + ".debug_ranges", (uint64_t) shdr->sh_offset); + + size_t address_size = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8; + + bool first = true; + unsigned char *readp = data->d_buf; + while (readp < (unsigned char *) data->d_buf + data->d_size) + { + ptrdiff_t offset = readp - (unsigned char *) data->d_buf; + + if (unlikely (data->d_size - offset < address_size * 2)) + { + printf (gettext (" [%6tx] <INVALID DATA>\n"), offset); + break; + } + + Dwarf_Addr begin; + Dwarf_Addr end; + if (address_size == 8) + { + begin = read_8ubyte_unaligned_inc (dbg, readp); + end = read_8ubyte_unaligned_inc (dbg, readp); + } + else + { + begin = read_4ubyte_unaligned_inc (dbg, readp); + end = read_4ubyte_unaligned_inc (dbg, readp); + if (begin == (Dwarf_Addr) (uint32_t) -1) + begin = (Dwarf_Addr) -1l; + } + + if (begin == (Dwarf_Addr) -1l) /* Base address entry. */ + { + char *b = format_dwarf_addr (dwflmod, address_size, end); + printf (gettext (" [%6tx] base address %s\n"), offset, b); + free (b); + } + else if (begin == 0 && end == 0) /* End of list entry. */ + first = true; + else + { + char *b = format_dwarf_addr (dwflmod, address_size, begin); + char *e = format_dwarf_addr (dwflmod, address_size, end); + /* We have an address range entry. */ + if (first) /* First address range entry in a list. */ + printf (gettext (" [%6tx] %s..%s\n"), offset, b, e); + else + printf (gettext (" %s..%s\n"), b, e); + free (b); + free (e); + + first = false; + } + } +} + static void -print_debug_frame_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, - GElf_Shdr *shdr, Dwarf *dbg) +print_debug_frame_section (Dwfl_Module *dwflmod __attribute__ ((unused)), + Ebl *ebl __attribute__ ((unused)), + GElf_Ehdr *ehdr __attribute__ ((unused)), + Elf_Scn *scn __attribute__ ((unused)), + GElf_Shdr *shdr __attribute__ ((unused)), + Dwarf *dbg __attribute__ ((unused))) { } struct attrcb_args { + Dwfl_Module *dwflmod; Dwarf *dbg; int level; unsigned int addrsize; @@ -3399,18 +4204,21 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) switch (form) { - case DW_FORM_addr:; - Dwarf_Addr addr; - if (unlikely (dwarf_formaddr (attrp, &addr) != 0)) - { - attrval_out: - error (0, 0, gettext ("cannot get attribute value: %s"), - dwarf_errmsg (-1)); - return DWARF_CB_ABORT; - } - printf (" %*s%-20s %#0*" PRIxMAX "\n", - (int) (level * 2), "", dwarf_attr_string (attr), - (int) (cbargs->addrsize * 2), (uintmax_t) addr); + case DW_FORM_addr: + { + Dwarf_Addr addr; + if (unlikely (dwarf_formaddr (attrp, &addr) != 0)) + { + attrval_out: + error (0, 0, gettext ("cannot get attribute value: %s"), + dwarf_errmsg (-1)); + return DWARF_CB_ABORT; + } + char *a = format_dwarf_addr (cbargs->dwflmod, cbargs->addrsize, addr); + printf (" %*s%-20s %s\n", + (int) (level * 2), "", dwarf_attr_string (attr), a); + free (a); + } break; case DW_FORM_indirect: @@ -3429,13 +4237,13 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) case DW_FORM_ref4: case DW_FORM_ref2: case DW_FORM_ref1:; - Dwarf_Off ref; - if (unlikely (dwarf_formref (attrp, &ref) != 0)) + Dwarf_Die ref; + if (unlikely (dwarf_formref_die (attrp, &ref) == NULL)) goto attrval_out; printf (" %*s%-20s [%6" PRIxMAX "]\n", (int) (level * 2), "", dwarf_attr_string (attr), - (uintmax_t) (ref + cbargs->cu_offset)); + (uintmax_t) dwarf_dieoffset (&ref)); break; case DW_FORM_udata: @@ -3448,17 +4256,72 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) if (unlikely (dwarf_formudata (attrp, &num) != 0)) goto attrval_out; - if (attr == DW_AT_language) + const char *valuestr = NULL; + switch (attr) { - printf (" %*s%-20s %s (%d)\n", + case DW_AT_location: + case DW_AT_data_location: + case DW_AT_data_member_location: + case DW_AT_vtable_elem_location: + case DW_AT_string_length: + case DW_AT_use_location: + case DW_AT_frame_base: + case DW_AT_return_addr: + case DW_AT_static_link: + printf (" %*s%-20s location list [%6" PRIxMAX "]\n", (int) (level * 2), "", dwarf_attr_string (attr), - dwarf_lang_string (num), (int) num); + (uintmax_t) num); + return DWARF_CB_OK; + + case DW_AT_ranges: + printf (" %*s%-20s range list [%6" PRIxMAX "]\n", + (int) (level * 2), "", dwarf_attr_string (attr), + (uintmax_t) num); + return DWARF_CB_OK; + + case DW_AT_language: + valuestr = dwarf_lang_string (num); + break; + case DW_AT_encoding: + valuestr = dwarf_encoding_string (num); + break; + case DW_AT_accessibility: + valuestr = dwarf_access_string (num); + break; + case DW_AT_visibility: + valuestr = dwarf_visibility_string (num); + break; + case DW_AT_virtuality: + valuestr = dwarf_virtuality_string (num); + break; + case DW_AT_identifier_case: + valuestr = dwarf_identifier_case_string (num); + break; + case DW_AT_calling_convention: + valuestr = dwarf_calling_convention_string (num); + break; + case DW_AT_inline: + valuestr = dwarf_inline_string (num); + break; + case DW_AT_ordering: + valuestr = dwarf_ordering_string (num); + break; + case DW_AT_discr_list: + valuestr = dwarf_discr_list_string (num); + break; + default: + /* Nothing. */ break; } - printf (" %*s%-20s %" PRIuMAX "\n", - (int) (level * 2), "", dwarf_attr_string (attr), - (uintmax_t) num); + if (valuestr == NULL) + printf (" %*s%-20s %" PRIuMAX "\n", + (int) (level * 2), "", dwarf_attr_string (attr), + (uintmax_t) num); + else + printf (" %*s%-20s %s (%" PRIuMAX ")\n", + (int) (level * 2), "", dwarf_attr_string (attr), + valuestr, (uintmax_t) num); break; case DW_FORM_flag:; @@ -3483,9 +4346,32 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) (int) (level * 2), "", dwarf_attr_string (attr), (uintmax_t) block.length); - if (attr == DW_AT_data_member_location) - print_ops (cbargs->dbg, level, cbargs->addrsize, block.length, - block.data); + switch (attr) + { + case DW_AT_location: + case DW_AT_data_location: + case DW_AT_data_member_location: + case DW_AT_vtable_elem_location: + case DW_AT_string_length: + case DW_AT_use_location: + case DW_AT_frame_base: + case DW_AT_return_addr: + case DW_AT_static_link: + case DW_AT_allocated: + case DW_AT_associated: + case DW_AT_bit_size: + case DW_AT_bit_offset: + case DW_AT_bit_stride: + case DW_AT_byte_size: + case DW_AT_byte_stride: + case DW_AT_count: + case DW_AT_lower_bound: + case DW_AT_upper_bound: + print_ops (cbargs->dwflmod, cbargs->dbg, + 12 + level * 2, 12 + level * 2, + cbargs->addrsize, block.length, block.data); + break; + } break; default: @@ -3500,7 +4386,10 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) static void -print_debug_info_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, +print_debug_info_section (Dwfl_Module *dwflmod, + Ebl *ebl __attribute__ ((unused)), + GElf_Ehdr *ehdr __attribute__ ((unused)), + Elf_Scn *scn __attribute__ ((unused)), GElf_Shdr *shdr, Dwarf *dbg) { printf (gettext ("\ @@ -3511,7 +4400,7 @@ print_debug_info_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, if (shdr->sh_size == 0) return; - size_t maxdies = 20; + int maxdies = 20; Dwarf_Die *dies = (Dwarf_Die *) xmalloc (maxdies * sizeof (Dwarf_Die)); Dwarf_Off offset = 0; @@ -3535,6 +4424,7 @@ print_debug_info_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, struct attrcb_args args; + args.dwflmod = dwflmod; args.dbg = dbg; args.addrsize = addrsize; args.cu_offset = offset; @@ -3554,7 +4444,7 @@ print_debug_info_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, do { offset = dwarf_dieoffset (&dies[level]); - if (offset == -1l) + if (unlikely (offset == ~0ul)) { error (0, 0, gettext ("cannot get DIE offset: %s"), dwarf_errmsg (-1)); @@ -3562,7 +4452,7 @@ print_debug_info_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, } int tag = dwarf_tag (&dies[level]); - if (tag == DW_TAG_invalid) + if (unlikely (tag == DW_TAG_invalid)) { error (0, 0, gettext ("cannot get tag of DIE at offset %" PRIu64 " in section '%s': %s"), @@ -3570,93 +4460,9 @@ print_debug_info_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, goto do_return; } - static const char *const lowtags[] = - { - [DW_TAG_array_type] = "array_type", - [DW_TAG_class_type] = "class_type", - [DW_TAG_entry_point] = "entry_point", - [DW_TAG_enumeration_type] = "enumeration_type", - [DW_TAG_formal_parameter] = "formal_parameter", - [DW_TAG_imported_declaration] = "imported_declaration", - [DW_TAG_label] = "label", - [DW_TAG_lexical_block] = "lexical_block", - [DW_TAG_member] = "member", - [DW_TAG_pointer_type] = "pointer_type", - [DW_TAG_reference_type] = "reference_type", - [DW_TAG_compile_unit] = "compile_unit", - [DW_TAG_string_type] = "string_type", - [DW_TAG_structure_type] = "structure_type", - [DW_TAG_subroutine_type] = "subroutine_type", - [DW_TAG_typedef] = "typedef", - [DW_TAG_union_type] = "union_type", - [DW_TAG_unspecified_parameters] = "unspecified_parameters", - [DW_TAG_variant] = "variant", - [DW_TAG_common_block] = "common_block", - [DW_TAG_common_inclusion] = "common_inclusion", - [DW_TAG_inheritance] = "inheritance", - [DW_TAG_inlined_subroutine] = "inlined_subroutine", - [DW_TAG_module] = "module", - [DW_TAG_ptr_to_member_type] = "ptr_to_member_type", - [DW_TAG_set_type] = "set_type", - [DW_TAG_subrange_type] = "subrange_type", - [DW_TAG_with_stmt] = "with_stmt", - [DW_TAG_access_declaration] = "access_declaration", - [DW_TAG_base_type] = "base_type", - [DW_TAG_catch_block] = "catch_block", - [DW_TAG_const_type] = "const_type", - [DW_TAG_constant] = "constant", - [DW_TAG_enumerator] = "enumerator", - [DW_TAG_file_type] = "file_type", - [DW_TAG_friend] = "friend", - [DW_TAG_namelist] = "namelist", - [DW_TAG_namelist_item] = "namelist_item", - [DW_TAG_packed_type] = "packed_type", - [DW_TAG_subprogram] = "subprogram", - [DW_TAG_template_type_param] = "template_type_param", - [DW_TAG_template_value_param] = "template_value_param", - [DW_TAG_thrown_type] = "thrown_type", - [DW_TAG_try_block] = "try_block", - [DW_TAG_variant_part] = "variant_part", - [DW_TAG_variable] = "variable", - [DW_TAG_volatile_type] = "volatile_type" - }; - - const char *tagstr; - switch (tag) - { - case DW_TAG_lo_user: - tagstr = "lo_user"; - break; - - case DW_TAG_MIPS_loop: - tagstr = "MIPS_loop"; - break; - - case DW_TAG_format_label: - tagstr = "format_label"; - break; - - case DW_TAG_function_template: - tagstr = "function_template"; - break; - - case DW_TAG_class_template: - tagstr = "class_template"; - break; - case DW_TAG_hi_user: - tagstr = "hi_user"; - break; - - default: - if (tag < sizeof (lowtags) / sizeof (lowtags[0])) - tagstr = lowtags[tag]; - else - tagstr = "???"; - break; - } - printf (" [%6" PRIx64 "] %*s%s\n", - (uint64_t) offset, (int) (level * 2), "", tagstr); + (uint64_t) offset, (int) (level * 2), "", + dwarf_tag_string (tag)); /* Print the attribute values. */ args.level = level; @@ -3675,7 +4481,7 @@ print_debug_info_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, if (level-- == 0) break; - if (res == -1) + if (unlikely (res == -1)) { error (0, 0, gettext ("cannot get next DIE: %s\n"), dwarf_errmsg (-1)); @@ -3703,8 +4509,9 @@ print_debug_info_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, static void -print_debug_line_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, - GElf_Shdr *shdr, Dwarf *dbg) +print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, + GElf_Ehdr *ehdr __attribute__ ((unused)), + Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) { printf (gettext ("\ \nDWARF section '%s' at offset %#" PRIx64 ":\n"), @@ -3716,7 +4523,7 @@ print_debug_line_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, /* There is no functionality in libdw to read the information in the way it is represented here. Hardcode the decoder. */ Elf_Data *data = elf_getdata (scn, NULL); - if (data == NULL || data->d_buf == NULL) + if (unlikely (data == NULL || data->d_buf == NULL)) { error (0, 0, gettext ("cannot get line data section data: %s"), elf_errmsg (-1)); @@ -3731,6 +4538,8 @@ print_debug_line_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, { size_t start_offset = linep - (const unsigned char *) data->d_buf; + printf (gettext ("\nTable at offset %Zu:\n"), start_offset); + Dwarf_Word unit_length = read_4ubyte_unaligned_inc (dbg, linep); unsigned int length = 4; if (unlikely (unit_length == 0xffffffff)) @@ -3797,7 +4606,15 @@ print_debug_line_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, line_range, opcode_base); if (unlikely (linep + opcode_base - 1 >= lineendp)) - goto invalid_data; + { + invalid_unit: + error (0, 0, + gettext ("invalid data at offset %tu in section [%zu] '%s'"), + linep - (const unsigned char *) data->d_buf, + elf_ndxscn (scn), ".debug_line"); + linep = lineendp; + continue; + } int opcode_base_l10 = 1; unsigned int tmp = opcode_base; while (tmp > 10) @@ -3813,14 +4630,14 @@ print_debug_line_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, opcode_base_l10, cnt, linep[cnt - 1]); linep += opcode_base - 1; if (unlikely (linep >= lineendp)) - goto invalid_data; + goto invalid_unit; puts (gettext ("\nDirectory table:")); while (*linep != 0) { unsigned char *endp = memchr (linep, '\0', lineendp - linep); - if (endp == NULL) - goto invalid_data; + if (unlikely (endp == NULL)) + goto invalid_unit; printf (" %s\n", (char *) linep); @@ -3830,7 +4647,7 @@ print_debug_line_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, ++linep; if (unlikely (linep >= lineendp)) - goto invalid_data; + goto invalid_unit; puts (gettext ("\nFile name table:\n" " Entry Dir Time Size Name")); for (unsigned int cnt = 1; *linep != 0; ++cnt) @@ -3838,8 +4655,8 @@ print_debug_line_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, /* First comes the file name. */ char *fname = (char *) linep; unsigned char *endp = memchr (fname, '\0', lineendp - linep); - if (endp == NULL) - goto invalid_data; + if (unlikely (endp == NULL)) + goto invalid_unit; linep = endp + 1; /* Then the index. */ @@ -3920,22 +4737,23 @@ print_debug_line_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, line += line_increment; address += address_increment; + char *a = format_dwarf_addr (dwflmod, 0, address); printf (gettext ("\ - special opcode %u: address+%u = %#" PRIx64 ", line%+d = %zu\n"), - opcode, address_increment, (uint64_t) address, - line_increment, line); + special opcode %u: address+%u = %s, line%+d = %zu\n"), + opcode, address_increment, a, line_increment, line); + free (a); } else if (opcode == 0) { /* This an extended opcode. */ if (unlikely (linep + 2 > lineendp)) - goto invalid_data; + goto invalid_unit; /* The length. */ unsigned int len = *linep++; if (unlikely (linep + len > lineendp)) - goto invalid_data; + goto invalid_unit; /* The sub-opcode. */ opcode = *linep++; @@ -3958,8 +4776,11 @@ print_debug_line_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, address = read_4ubyte_unaligned_inc (dbg, linep); else address = read_8ubyte_unaligned_inc (dbg, linep); - printf (gettext ("set address to %#" PRIx64 "\n"), - (uint64_t) address); + { + char *a = format_dwarf_addr (dwflmod, 0, address); + printf (gettext ("set address to %s\n"), a); + free (a); + } break; case DW_LNE_define_file: @@ -3967,8 +4788,8 @@ print_debug_line_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, char *fname = (char *) linep; unsigned char *endp = memchr (linep, '\0', lineendp - linep); - if (endp == NULL) - goto invalid_data; + if (unlikely (endp == NULL)) + goto invalid_unit; linep = endp + 1; unsigned int diridx; @@ -3992,7 +4813,7 @@ define new file: dir=%u, mtime=%" PRIu64 ", length=%" PRIu64 ", name=%s\n"), break; } } - else if (opcode <= DW_LNS_set_epilog_begin) + else if (opcode <= DW_LNS_set_epilogue_begin) { /* This is a known standard opcode. */ switch (opcode) @@ -4007,9 +4828,12 @@ define new file: dir=%u, mtime=%" PRIu64 ", length=%" PRIu64 ", name=%s\n"), address. */ get_uleb128 (u128, linep); address += minimum_instr_len * u128; - printf (gettext ("\ - advance address by %u to %#" PRIx64 "\n"), - u128, (uint64_t) address); + { + char *a = format_dwarf_addr (dwflmod, 0, address); + printf (gettext ("advance address by %u to %s\n"), + u128, a); + free (a); + } break; case DW_LNS_advance_line: @@ -4032,7 +4856,7 @@ define new file: dir=%u, mtime=%" PRIu64 ", length=%" PRIu64 ", name=%s\n"), case DW_LNS_set_column: /* Takes one uleb128 parameter which is stored in column. */ if (unlikely (standard_opcode_lengths[opcode] != 1)) - goto invalid_data; + goto invalid_unit; get_uleb128 (u128, linep); printf (gettext (" set column to %" PRIu64 "\n"), @@ -4056,22 +4880,29 @@ define new file: dir=%u, mtime=%" PRIu64 ", length=%" PRIu64 ", name=%s\n"), u128 = (minimum_instr_len * ((255 - opcode_base) / line_range)); address += u128; - printf (gettext ("\ - advance address by constant %u to %#" PRIx64 "\n"), - u128, (uint64_t) address); + { + char *a = format_dwarf_addr (dwflmod, 0, address); + printf (gettext ("advance address by constant %u to %s\n"), + u128, a); + free (a); + } break; case DW_LNS_fixed_advance_pc: /* Takes one 16 bit parameter which is added to the address. */ if (unlikely (standard_opcode_lengths[opcode] != 1)) - goto invalid_data; + goto invalid_unit; u128 = read_2ubyte_unaligned_inc (dbg, linep); address += u128; - printf (gettext ("\ - advance address by fixed value %u to %#" PRIx64 "\n"), - u128, (uint64_t) address); + { + char *a = format_dwarf_addr (dwflmod, 0, address); + printf (gettext ("\ +advance address by fixed value %u to %s\n"), + u128, a); + free (a); + } break; case DW_LNS_set_prologue_end: @@ -4079,7 +4910,7 @@ define new file: dir=%u, mtime=%" PRIu64 ", length=%" PRIu64 ", name=%s\n"), puts (gettext (" set prologue end flag")); break; - case DW_LNS_set_epilog_begin: + case DW_LNS_set_epilogue_begin: /* Takes no argument. */ puts (gettext (" set epilogue begin flag")); break; @@ -4114,16 +4945,87 @@ define new file: dir=%u, mtime=%" PRIu64 ", length=%" PRIu64 ", name=%s\n"), static void -print_debug_loc_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, - GElf_Shdr *shdr, Dwarf *dbg) +print_debug_loc_section (Dwfl_Module *dwflmod, + Ebl *ebl __attribute__ ((unused)), + GElf_Ehdr *ehdr __attribute__ ((unused)), + Elf_Scn *scn __attribute__ ((unused)), + GElf_Shdr *shdr, + Dwarf *dbg __attribute__ ((unused))) { + Elf_Data *data = elf_rawdata (scn, NULL); + + if (unlikely (data == NULL)) + { + error (0, 0, gettext ("cannot get .debug_loc content: %s"), + elf_errmsg (-1)); + return; + } + printf (gettext ("\ \nDWARF section '%s' at offset %#" PRIx64 ":\n"), ".debug_loc", (uint64_t) shdr->sh_offset); - // XXX add something -} + size_t address_size = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8; + + bool first = true; + unsigned char *readp = data->d_buf; + while (readp < (unsigned char *) data->d_buf + data->d_size) + { + ptrdiff_t offset = readp - (unsigned char *) data->d_buf; + if (unlikely (data->d_size - offset < address_size * 2)) + { + printf (gettext (" [%6tx] <INVALID DATA>\n"), offset); + break; + } + + Dwarf_Addr begin; + Dwarf_Addr end; + if (address_size == 8) + { + begin = read_8ubyte_unaligned_inc (dbg, readp); + end = read_8ubyte_unaligned_inc (dbg, readp); + } + else + { + begin = read_4ubyte_unaligned_inc (dbg, readp); + end = read_4ubyte_unaligned_inc (dbg, readp); + if (begin == (Dwarf_Addr) (uint32_t) -1) + begin = (Dwarf_Addr) -1l; + } + + if (begin == (Dwarf_Addr) -1l) /* Base address entry. */ + { + char *b = format_dwarf_addr (dwflmod, address_size, end); + printf (gettext (" [%6tx] base address %s\n"), offset, b); + free (b); + } + else if (begin == 0 && end == 0) /* End of list entry. */ + first = true; + else + { + /* We have a location expression entry. */ + uint_fast16_t len = read_2ubyte_unaligned_inc (dbg, readp); + + char *b = format_dwarf_addr (dwflmod, address_size, begin); + char *e = format_dwarf_addr (dwflmod, address_size, end); + + if (first) /* First entry in a list. */ + printf (gettext (" [%6tx] %s..%s"), offset, b, e); + else + printf (gettext (" %s..%s"), b, e); + + free (b); + free (e); + + print_ops (dwflmod, dbg, 1, 18 + (address_size * 4), + address_size, len, readp); + + first = false; + readp += len; + } + } +} struct mac_culist { @@ -4149,8 +5051,10 @@ mac_compare (const void *p1, const void *p2) static void -print_debug_macinfo_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, - GElf_Shdr *shdr, Dwarf *dbg) +print_debug_macinfo_section (Dwfl_Module *dwflmod __attribute__ ((unused)), + Ebl *ebl __attribute__ ((unused)), + GElf_Ehdr *ehdr __attribute__ ((unused)), + Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) { printf (gettext ("\ \nDWARF section '%s' at offset %#" PRIx64 ":\n"), @@ -4160,7 +5064,7 @@ print_debug_macinfo_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, /* There is no function in libdw to iterate over the raw content of the section but it is easy enough to do. */ Elf_Data *data = elf_getdata (scn, NULL); - if (data == NULL || data->d_buf == NULL) + if (unlikely (data == NULL || data->d_buf == NULL)) { error (0, 0, gettext ("cannot get macro information section data: %s"), elf_errmsg (-1)); @@ -4239,7 +5143,7 @@ print_debug_macinfo_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, get_uleb128 (u128, readp); endp = memchr (readp, '\0', readendp - readp); - if (endp == NULL) + if (unlikely (endp == NULL)) { printf (gettext ("\ %*s*** non-terminated string at end of section"), @@ -4265,7 +5169,7 @@ print_debug_macinfo_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, get_uleb128 (u128_2, readp); /* Find the CU DIE for this file. */ - ptrdiff_t macoff = readp - (const unsigned char *) data->d_buf; + size_t macoff = readp - (const unsigned char *) data->d_buf; const char *fname = "???"; if (macoff >= cus[0].offset) { @@ -4294,7 +5198,7 @@ print_debug_macinfo_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, default: // XXX gcc seems to generate files with a trailing zero. - if (opcode != 0 || readp != readendp) + if (unlikely (opcode != 0 || readp != readendp)) printf ("%*s*** invalid opcode %u\n", level, "", opcode); break; } @@ -4304,7 +5208,8 @@ print_debug_macinfo_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, /* Callback for printing global names. */ static int -print_pubnames (Dwarf *dbg, Dwarf_Global *global, void *arg) +print_pubnames (Dwarf *dbg __attribute__ ((unused)), Dwarf_Global *global, + void *arg) { int *np = (int *) arg; @@ -4318,7 +5223,10 @@ print_pubnames (Dwarf *dbg, Dwarf_Global *global, void *arg) /* Print the known exported symbols in the DWARF section '.debug_pubnames'. */ static void -print_debug_pubnames_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, +print_debug_pubnames_section (Dwfl_Module *dwflmod __attribute__ ((unused)), + Ebl *ebl __attribute__ ((unused)), + GElf_Ehdr *ehdr __attribute__ ((unused)), + Elf_Scn *scn __attribute__ ((unused)), GElf_Shdr *shdr, Dwarf *dbg) { printf (gettext ("\nDWARF section '%s' at offset %#" PRIx64 ":\n"), @@ -4330,7 +5238,10 @@ print_debug_pubnames_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, /* Print the content of the DWARF string section '.debug_str'. */ static void -print_debug_str_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, +print_debug_str_section (Dwfl_Module *dwflmod __attribute__ ((unused)), + Ebl *ebl __attribute__ ((unused)), + GElf_Ehdr *ehdr __attribute__ ((unused)), + Elf_Scn *scn __attribute__ ((unused)), GElf_Shdr *shdr, Dwarf *dbg) { /* Compute floor(log16(shdr->sh_size)). */ @@ -4354,7 +5265,7 @@ print_debug_str_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, { size_t len; const char *str = dwarf_getstring (dbg, offset, &len); - if (str == NULL) + if (unlikely (str == NULL)) { printf (gettext (" *** error while reading strings: %s\n"), dwarf_errmsg (-1)); @@ -4367,44 +5278,40 @@ print_debug_str_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, } } - static void -print_debug (Ebl *ebl, GElf_Ehdr *ehdr) +print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr) { - /* Find the version information sections. For this we have to - search through the section table. */ - Dwarf *dbg; - Elf_Scn *scn; - size_t shstrndx; - /* Before we start the real work get a debug context descriptor. */ - dbg = dwarf_begin_elf (ebl->elf, DWARF_C_READ, NULL); + Dwarf_Addr dwbias; + Dwarf *dbg = dwfl_module_getdwarf (dwflmod, &dwbias); if (dbg == NULL) { error (0, 0, gettext ("cannot get debug context descriptor: %s"), - dwarf_errmsg (-1)); + dwfl_errmsg (-1)); return; } /* Get the section header string table index. */ - if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + size_t shstrndx; + if (unlikely (elf_getshstrndx (ebl->elf, &shstrndx) < 0)) error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); - scn = NULL; + /* Look through all the sections for the debugging sections to print. */ + Elf_Scn *scn = NULL; while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) { - /* Handle the section if it is part of the versioning handling. */ GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); - if (shdr != NULL || shdr->sh_type != SHT_PROGBITS) + if (shdr != NULL && shdr->sh_type == SHT_PROGBITS) { static const struct { const char *name; enum section_e bitmask; - void (*fp) (Ebl *, GElf_Ehdr *, Elf_Scn *, GElf_Shdr *, Dwarf *); + void (*fp) (Dwfl_Module *, Ebl *, + GElf_Ehdr *, Elf_Scn *, GElf_Shdr *, Dwarf *); } debug_sections[] = { #define NEW_SECTION(name) \ @@ -4418,6 +5325,7 @@ print_debug (Ebl *ebl, GElf_Ehdr *ehdr) NEW_SECTION (pubnames), NEW_SECTION (str), NEW_SECTION (macinfo), + NEW_SECTION (ranges), { ".eh_frame", section_frame, print_debug_frame_section } }; const int ndebug_sections = (sizeof (debug_sections) @@ -4430,26 +5338,851 @@ print_debug (Ebl *ebl, GElf_Ehdr *ehdr) if (strcmp (name, debug_sections[n].name) == 0) { if (print_debug_sections & debug_sections[n].bitmask) - debug_sections[n].fp (ebl, ehdr, scn, shdr, dbg); + debug_sections[n].fp (dwflmod, ebl, ehdr, scn, shdr, dbg); break; } } } +} + + +#define ITEM_INDENT 4 +#define ITEM_WRAP_COLUMN 150 +#define REGISTER_WRAP_COLUMN 75 + +/* Print "NAME: FORMAT", wrapping when FORMAT_MAX chars of FORMAT would + make the line exceed ITEM_WRAP_COLUMN. Unpadded numbers look better + for the core items. But we do not want the line breaks to depend on + the particular values. */ +static unsigned int +__attribute__ ((format (printf, 7, 8))) +print_core_item (unsigned int colno, char sep, unsigned int wrap, + size_t name_width, const char *name, + size_t format_max, const char *format, ...) +{ + size_t len = strlen (name); + if (name_width < len) + name_width = len; + + size_t n = name_width + sizeof ": " - 1 + format_max; + + if (colno == 0) + { + printf ("%*s", ITEM_INDENT, ""); + colno = ITEM_INDENT + n; + } + else if (colno + 2 + n < wrap) + { + printf ("%c ", sep); + colno += 2 + n; + } + else + { + printf ("\n%*s", ITEM_INDENT, ""); + colno = ITEM_INDENT + n; + } + + printf ("%s: %*s", name, (int) (name_width - len), ""); + + va_list ap; + va_start (ap, format); + vprintf (format, ap); + va_end (ap); + + return colno; +} + +static const void * +convert (Elf *core, Elf_Type type, uint_fast16_t count, + void *value, const void *data, size_t size) +{ + Elf_Data valuedata = + { + .d_type = type, + .d_buf = value, + .d_size = size ?: gelf_fsize (core, type, count, EV_CURRENT), + .d_version = EV_CURRENT, + }; + Elf_Data indata = + { + .d_type = type, + .d_buf = (void *) data, + .d_size = valuedata.d_size, + .d_version = EV_CURRENT, + }; + + Elf_Data *d = (gelf_getclass (core) == ELFCLASS32 + ? elf32_xlatetom : elf64_xlatetom) + (&valuedata, &indata, elf_getident (core, NULL)[EI_DATA]); + if (d == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot convert core note data: %s"), elf_errmsg (-1)); + + return data + indata.d_size; +} + +typedef uint8_t GElf_Byte; + +static unsigned int +handle_core_item (Elf *core, const Ebl_Core_Item *item, const void *desc, + unsigned int colno, size_t *repeated_size) +{ + uint_fast16_t count = item->count ?: 1; + +#define TYPES \ + DO_TYPE (BYTE, Byte, "0x%.2" PRIx8, "%" PRId8, 4); \ + DO_TYPE (HALF, Half, "0x%.4" PRIx16, "%" PRId16, 6); \ + DO_TYPE (WORD, Word, "0x%.8" PRIx32, "%" PRId32, 11); \ + DO_TYPE (SWORD, Sword, "%" PRId32, "%" PRId32, 11); \ + DO_TYPE (XWORD, Xword, "0x%.16" PRIx64, "%" PRId64, 20); \ + DO_TYPE (SXWORD, Sxword, "%" PRId64, "%" PRId64, 20) + +#define DO_TYPE(NAME, Name, hex, dec, max) GElf_##Name Name[count] + union { TYPES; } value; +#undef DO_TYPE + + void *data = &value; + size_t size = gelf_fsize (core, item->type, count, EV_CURRENT); + size_t convsize = size; + if (repeated_size != NULL) + { + if (*repeated_size > size && (item->format == 'b' || item->format == 'B')) + { + data = alloca (*repeated_size); + count *= *repeated_size / size; + convsize = count * size; + *repeated_size -= convsize; + } + else + *repeated_size -= size; + } + + desc = convert (core, item->type, count, data, desc + item->offset, convsize); + + Elf_Type type = item->type; + if (type == ELF_T_ADDR) + type = gelf_getclass (core) == ELFCLASS32 ? ELF_T_WORD : ELF_T_XWORD; + + switch (item->format) + { + case 'd': + assert (count == 1); + switch (type) + { +#define DO_TYPE(NAME, Name, hex, dec, max) \ + case ELF_T_##NAME: \ + colno = print_core_item (colno, ',', ITEM_WRAP_COLUMN, \ + 0, item->name, max, dec, value.Name[0]); \ + break + TYPES; +#undef DO_TYPE + default: + abort (); + } + break; + + case 'x': + assert (count == 1); + switch (type) + { +#define DO_TYPE(NAME, Name, hex, dec, max) \ + case ELF_T_##NAME: \ + colno = print_core_item (colno, ',', ITEM_WRAP_COLUMN, \ + 0, item->name, max, hex, value.Name[0]); \ + break + TYPES; +#undef DO_TYPE + default: + abort (); + } + break; + + case 'b': + case 'B': + assert (size % sizeof (unsigned int) == 0); + unsigned int nbits = count * size * 8; + unsigned int pop = 0; + for (const unsigned int *i = data; (void *) i < data + count * size; ++i) + pop += __builtin_popcount (*i); + bool negate = pop > nbits / 2; + const unsigned int bias = item->format == 'b'; + + { + char printed[(negate ? nbits - pop : pop) * 16]; + char *p = printed; + *p = '\0'; + + if (BYTE_ORDER != LITTLE_ENDIAN && size > sizeof (unsigned int)) + { + assert (size == sizeof (unsigned int) * 2); + for (unsigned int *i = data; + (void *) i < data + count * size; i += 2) + { + unsigned int w = i[1]; + i[1] = i[0]; + i[0] = w; + } + } + + unsigned int lastbit = 0; + for (const unsigned int *i = data; + (void *) i < data + count * size; ++i) + { + unsigned int bit = ((void *) i - data) * 8; + unsigned int w = negate ? ~*i : *i; + while (w != 0) + { + int n = ffs (w); + w >>= n; + bit += n; + + if (lastbit + 1 != bit) + p += sprintf (p, "-%u,%u", lastbit - bias, bit - bias); + else if (lastbit == 0) + p += sprintf (p, "%u", bit - bias); + + lastbit = bit; + } + } + if (lastbit > 0 && lastbit + 1 != nbits) + p += sprintf (p, "-%u", nbits - bias); + + colno = print_core_item (colno, ',', ITEM_WRAP_COLUMN, 0, item->name, + 4 + nbits * 4, + negate ? "~<%s>" : "<%s>", printed); + } + break; + + case 'T': + case (char) ('T'|0x80): + assert (count == 2); + Dwarf_Word sec; + Dwarf_Word usec; + size_t maxfmt = 7; + switch (type) + { +#define DO_TYPE(NAME, Name, hex, dec, max) \ + case ELF_T_##NAME: \ + sec = value.Name[0]; \ + usec = value.Name[1]; \ + maxfmt += max; \ + break + TYPES; +#undef DO_TYPE + default: + abort (); + } + if (unlikely (item->format == (char) ('T'|0x80))) + { + /* This is a hack for an ill-considered 64-bit ABI where + tv_usec is actually a 32-bit field with 32 bits of padding + rounding out struct timeval. We've already converted it as + a 64-bit field. For little-endian, this just means the + high half is the padding; it's presumably zero, but should + be ignored anyway. For big-endian, it means the 32-bit + field went into the high half of USEC. */ + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr = gelf_getehdr (core, &ehdr_mem); + if (likely (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)) + usec >>= 32; + else + usec &= UINT32_MAX; + } + colno = print_core_item (colno, ',', ITEM_WRAP_COLUMN, 0, item->name, + maxfmt, "%" PRIu64 ".%.6" PRIu64, sec, usec); + break; + + case 'c': + assert (count == 1); + colno = print_core_item (colno, ',', ITEM_WRAP_COLUMN, 0, item->name, + 1, "%c", value.Byte[0]); + break; + + case 's': + colno = print_core_item (colno, ',', ITEM_WRAP_COLUMN, 0, item->name, + count, "%.*s", (int) count, value.Byte); + break; + + default: + error (0, 0, "XXX not handling format '%c' for %s", + item->format, item->name); + break; + } + +#undef TYPES + + return colno; +} + + +/* Sort items by group, and by layout offset within each group. */ +static int +compare_core_items (const void *a, const void *b) +{ + const Ebl_Core_Item *const *p1 = a; + const Ebl_Core_Item *const *p2 = b; + const Ebl_Core_Item *item1 = *p1; + const Ebl_Core_Item *item2 = *p2; + + return ((item1->group == item2->group ? 0 + : strcmp (item1->group, item2->group)) + ?: (int) item1->offset - (int) item2->offset); +} + +/* Sort item groups by layout offset of the first item in the group. */ +static int +compare_core_item_groups (const void *a, const void *b) +{ + const Ebl_Core_Item *const *const *p1 = a; + const Ebl_Core_Item *const *const *p2 = b; + const Ebl_Core_Item *const *group1 = *p1; + const Ebl_Core_Item *const *group2 = *p2; + const Ebl_Core_Item *item1 = *group1; + const Ebl_Core_Item *item2 = *group2; + + return (int) item1->offset - (int) item2->offset; +} + +static unsigned int +handle_core_items (Elf *core, const void *desc, size_t descsz, + const Ebl_Core_Item *items, size_t nitems) +{ + if (nitems == 0) + return 0; + + /* Sort to collect the groups together. */ + const Ebl_Core_Item *sorted_items[nitems]; + for (size_t i = 0; i < nitems; ++i) + sorted_items[i] = &items[i]; + qsort (sorted_items, nitems, sizeof sorted_items[0], &compare_core_items); + + /* Collect the unique groups and sort them. */ + const Ebl_Core_Item **groups[nitems]; + groups[0] = &sorted_items[0]; + size_t ngroups = 1; + for (size_t i = 1; i < nitems; ++i) + if (sorted_items[i]->group != sorted_items[i - 1]->group + && strcmp (sorted_items[i]->group, sorted_items[i - 1]->group)) + groups[ngroups++] = &sorted_items[i]; + qsort (groups, ngroups, sizeof groups[0], &compare_core_item_groups); + + /* Write out all the groups. */ + unsigned int colno = 0; + + const void *last = desc; + if (nitems == 1) + { + size_t size = descsz; + colno = handle_core_item (core, sorted_items[0], desc, colno, &size); + if (size == 0) + return colno; + desc += descsz - size; + descsz = size; + } + + do + { + for (size_t i = 0; i < ngroups; ++i) + { + for (const Ebl_Core_Item **item = groups[i]; + (item < &sorted_items[nitems] + && ((*item)->group == groups[i][0]->group + || !strcmp ((*item)->group, groups[i][0]->group))); + ++item) + colno = handle_core_item (core, *item, desc, colno, NULL); + + /* Force a line break at the end of the group. */ + colno = ITEM_WRAP_COLUMN; + } + + if (descsz == 0) + break; + + /* This set of items consumed a certain amount of the note's data. + If there is more data there, we have another unit of the same size. + Loop to print that out too. */ + const Ebl_Core_Item *item = &items[nitems - 1]; + size_t eltsz = item->offset + gelf_fsize (core, item->type, + item->count ?: 1, EV_CURRENT); + + int reps = -1; + do + { + ++reps; + desc += eltsz; + descsz -= eltsz; + } + while (descsz >= eltsz && !memcmp (desc, last, eltsz)); + + if (reps == 1) + { + /* For just one repeat, print it unabridged twice. */ + desc -= eltsz; + descsz += eltsz; + } + else if (reps > 1) + printf (gettext ("\n%*s... <repeats %u more times> ..."), + ITEM_INDENT, "", reps); + + last = desc; + } + while (descsz > 0); + + return colno; +} + +static unsigned int +handle_bit_registers (const Ebl_Register_Location *regloc, const void *desc, + unsigned int colno) +{ + desc += regloc->offset; + + abort (); /* XXX */ + return colno; +} + + +static unsigned int +handle_core_register (Ebl *ebl, Elf *core, int maxregname, + const Ebl_Register_Location *regloc, const void *desc, + unsigned int colno) +{ + if (regloc->bits % 8 != 0) + return handle_bit_registers (regloc, desc, colno); + + desc += regloc->offset; + + for (int reg = regloc->regno; reg < regloc->regno + regloc->count; ++reg) + { + const char *pfx; + const char *set; + char name[16]; + int bits; + int type; + ssize_t n = ebl_register_info (ebl, reg, name, sizeof name, + &pfx, &set, &bits, &type); + if (n <= 0) + error (EXIT_FAILURE, 0, + gettext ("unable to handle register number %d"), + regloc->regno); + +#define TYPES \ + BITS (8, BYTE, "%4" PRId8, "0x%.2" PRIx8, 4); \ + BITS (16, HALF, "%6" PRId16, "0x%.4" PRIx16, 6); \ + BITS (32, WORD, "%11" PRId32, " 0x%.8" PRIx32, 11); \ + BITS (64, XWORD, "%20" PRId64, " 0x%.16" PRIx64, 20) + +#define BITS(bits, xtype, sfmt, ufmt, max) \ + uint##bits##_t b##bits; int##bits##_t b##bits##s + union { TYPES; uint64_t b128[2]; } value; +#undef BITS + + switch (type) + { + case DW_ATE_unsigned: + case DW_ATE_signed: + case DW_ATE_address: + switch (bits) + { +#define BITS(bits, xtype, sfmt, ufmt, max) \ + case bits: \ + desc = convert (core, ELF_T_##xtype, 1, &value, desc, 0); \ + if (type == DW_ATE_signed) \ + colno = print_core_item (colno, ' ', REGISTER_WRAP_COLUMN, \ + maxregname, name, \ + max, sfmt, value.b##bits##s); \ + else \ + colno = print_core_item (colno, ' ', REGISTER_WRAP_COLUMN, \ + maxregname, name, \ + max, ufmt, value.b##bits); \ + break + + TYPES; + + case 128: + assert (type == DW_ATE_unsigned); + desc = convert (core, ELF_T_XWORD, 2, &value, desc, 0); + int be = elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB; + colno = print_core_item (colno, ' ', REGISTER_WRAP_COLUMN, + maxregname, name, + 34, "0x%.16" PRIx64 "%.16" PRIx64, + value.b128[!be], value.b128[be]); + break; + + default: + abort (); +#undef BITS + } + break; + + default: + /* Print each byte in hex, the whole thing in native byte order. */ + assert (bits % 8 == 0); + const uint8_t *bytes = desc; + desc += bits / 8; + char hex[bits / 4 + 1]; + hex[bits / 4] = '\0'; + int incr = 1; + if (elf_getident (core, NULL)[EI_DATA] == ELFDATA2LSB) + { + bytes += bits / 8 - 1; + incr = -1; + } + size_t idx = 0; + for (char *h = hex; bits > 0; bits -= 8, idx += incr) + { + *h++ = "0123456789abcdef"[bytes[idx] >> 4]; + *h++ = "0123456789abcdef"[bytes[idx] & 0xf]; + } + colno = print_core_item (colno, ' ', REGISTER_WRAP_COLUMN, + maxregname, name, + 2 + sizeof hex - 1, "0x%s", hex); + break; + } + desc += regloc->pad; + +#undef TYPES + } + + return colno; +} + + +struct register_info +{ + const Ebl_Register_Location *regloc; + const char *set; + char name[16]; + Dwarf_Half regno; + uint8_t bits; + uint8_t type; +}; + +static int +register_bitpos (const struct register_info *r) +{ + return (r->regloc->offset * 8 + + ((r->regno - r->regloc->regno) + * (r->regloc->bits + r->regloc->pad * 8))); +} + +static int +compare_sets_by_info (const struct register_info *r1, + const struct register_info *r2) +{ + return ((int) r2->bits - (int) r1->bits + ?: register_bitpos (r1) - register_bitpos (r2)); +} + +/* Sort registers by set, and by size and layout offset within each set. */ +static int +compare_registers (const void *a, const void *b) +{ + const struct register_info *r1 = a; + const struct register_info *r2 = b; + + /* Unused elements sort last. */ + if (r1->regloc == NULL) + return r2->regloc == NULL ? 0 : 1; + if (r2->regloc == NULL) + return -1; + + return ((r1->set == r2->set ? 0 : strcmp (r1->set, r2->set)) + ?: compare_sets_by_info (r1, r2)); +} + +/* Sort register sets by layout offset of the first register in the set. */ +static int +compare_register_sets (const void *a, const void *b) +{ + const struct register_info *const *p1 = a; + const struct register_info *const *p2 = b; + return compare_sets_by_info (*p1, *p2); +} + +static unsigned int +handle_core_registers (Ebl *ebl, Elf *core, const void *desc, + const Ebl_Register_Location *reglocs, size_t nregloc) +{ + if (nregloc == 0) + return 0; + + ssize_t maxnreg = ebl_register_info (ebl, 0, NULL, 0, NULL, NULL, NULL, NULL); + if (maxnreg <= 0) + error (EXIT_FAILURE, 0, + gettext ("cannot register info: %s"), elf_errmsg (-1)); + + struct register_info regs[maxnreg]; + memset (regs, 0, sizeof regs); + + /* Sort to collect the sets together. */ + int maxreg = 0; + for (size_t i = 0; i < nregloc; ++i) + for (int reg = reglocs[i].regno; + reg < reglocs[i].regno + reglocs[i].count; + ++reg) + { + assert (reg < maxnreg); + if (reg > maxreg) + maxreg = reg; + struct register_info *info = ®s[reg]; + + const char *pfx; + int bits; + int type; + ssize_t n = ebl_register_info (ebl, reg, info->name, sizeof info->name, + &pfx, &info->set, &bits, &type); + if (n <= 0) + error (EXIT_FAILURE, 0, + gettext ("cannot register info: %s"), elf_errmsg (-1)); + + info->regloc = ®locs[i]; + info->regno = reg; + info->bits = bits; + info->type = type; + } + qsort (regs, maxreg + 1, sizeof regs[0], &compare_registers); + + /* Collect the unique sets and sort them. */ + inline bool same_set (const struct register_info *a, + const struct register_info *b) + { + return (a < ®s[maxnreg] && a->regloc != NULL + && b < ®s[maxnreg] && b->regloc != NULL + && a->bits == b->bits + && (a->set == b->set || !strcmp (a->set, b->set))); + } + struct register_info *sets[maxreg + 1]; + sets[0] = ®s[0]; + size_t nsets = 1; + for (int i = 1; i <= maxreg; ++i) + if (regs[i].regloc != NULL && !same_set (®s[i], ®s[i - 1])) + sets[nsets++] = ®s[i]; + qsort (sets, nsets, sizeof sets[0], &compare_register_sets); + + /* Write out all the sets. */ + unsigned int colno = 0; + for (size_t i = 0; i < nsets; ++i) + { + /* Find the longest name of a register in this set. */ + size_t maxname = 0; + const struct register_info *end; + for (end = sets[i]; same_set (sets[i], end); ++end) + { + size_t len = strlen (end->name); + if (len > maxname) + maxname = len; + } + + for (const struct register_info *reg = sets[i]; + reg < end; + reg += reg->regloc->count ?: 1) + colno = handle_core_register (ebl, core, maxname, + reg->regloc, desc, colno); + + /* Force a line break at the end of the group. */ + colno = REGISTER_WRAP_COLUMN; + } - /* We are done with the DWARF handling. */ - dwarf_end (dbg); + return colno; } +static void +handle_auxv_note (Ebl *ebl, Elf *core, GElf_Word descsz, GElf_Off desc_pos) +{ + Elf_Data *data = elf_getdata_rawchunk (core, desc_pos, descsz, ELF_T_AUXV); + if (data == NULL) + elf_error: + error (EXIT_FAILURE, 0, + gettext ("cannot convert core note data: %s"), elf_errmsg (-1)); + + const size_t nauxv = descsz / gelf_fsize (core, ELF_T_AUXV, 1, EV_CURRENT); + for (size_t i = 0; i < nauxv; ++i) + { + GElf_auxv_t av_mem; + GElf_auxv_t *av = gelf_getauxv (data, i, &av_mem); + if (av == NULL) + goto elf_error; + + const char *name; + const char *fmt; + if (ebl_auxv_info (ebl, av->a_type, &name, &fmt) == 0) + { + /* Unknown type. */ + if (av->a_un.a_val == 0) + printf (" %" PRIu64 "\n", av->a_type); + else + printf (" %" PRIu64 ": %#" PRIx64 "\n", + av->a_type, av->a_un.a_val); + } + else + switch (fmt[0]) + { + case '\0': /* Normally zero. */ + if (av->a_un.a_val == 0) + { + printf (" %s\n", name); + break; + } + /* Fall through */ + case 'x': /* hex */ + case 'p': /* address */ + case 's': /* address of string */ + printf (" %s: %#" PRIx64 "\n", name, av->a_un.a_val); + break; + case 'u': + printf (" %s: %" PRIu64 "\n", name, av->a_un.a_val); + break; + case 'd': + printf (" %s: %" PRId64 "\n", name, av->a_un.a_val); + break; + + case 'b': + printf (" %s: %#" PRIx64 " ", name, av->a_un.a_val); + GElf_Xword bit = 1; + const char *pfx = "<"; + for (const char *p = fmt + 1; *p != 0; p = strchr (p, '\0') + 1) + { + if (av->a_un.a_val & bit) + { + printf ("%s%s", pfx, p); + pfx = " "; + } + bit <<= 1; + } + printf (">\n"); + break; + + default: + abort (); + } + } +} + +static void +handle_core_note (Ebl *ebl, const GElf_Nhdr *nhdr, const void *desc) +{ + GElf_Word regs_offset; + size_t nregloc; + const Ebl_Register_Location *reglocs; + size_t nitems; + const Ebl_Core_Item *items; + + if (! ebl_core_note (ebl, nhdr->n_type, nhdr->n_descsz, + ®s_offset, &nregloc, ®locs, &nitems, &items)) + return; + + /* Pass 0 for DESCSZ when there are registers in the note, + so that the ITEMS array does not describe the whole thing. + For non-register notes, the actual descsz might be a multiple + of the unit size, not just exactly the unit size. */ + unsigned int colno = handle_core_items (ebl->elf, desc, + nregloc == 0 ? nhdr->n_descsz : 0, + items, nitems); + if (colno != 0) + putchar_unlocked ('\n'); + + colno = handle_core_registers (ebl, ebl->elf, desc + regs_offset, + reglocs, nregloc); + if (colno != 0) + putchar_unlocked ('\n'); +} + +static void +handle_notes_data (Ebl *ebl, const GElf_Ehdr *ehdr, + GElf_Off start, Elf_Data *data) +{ + fputs_unlocked (gettext (" Owner Data size Type\n"), stdout); + + if (data == NULL) + goto bad_note; + + size_t offset = 0; + GElf_Nhdr nhdr; + size_t name_offset; + size_t desc_offset; + while (offset < data->d_size + && (offset = gelf_getnote (data, offset, + &nhdr, &name_offset, &desc_offset)) > 0) + { + const char *name = data->d_buf + name_offset; + const char *desc = data->d_buf + desc_offset; + + char buf[100]; + char buf2[100]; + printf (gettext (" %-13.*s %9" PRId32 " %s\n"), + (int) nhdr.n_namesz, name, nhdr.n_descsz, + ehdr->e_type == ET_CORE + ? ebl_core_note_type_name (ebl, nhdr.n_type, + buf, sizeof (buf)) + : ebl_object_note_type_name (ebl, nhdr.n_type, + buf2, sizeof (buf2))); + + /* Filter out invalid entries. */ + if (memchr (name, '\0', nhdr.n_namesz) != NULL + /* XXX For now help broken Linux kernels. */ + || 1) + { + if (ehdr->e_type == ET_CORE) + { + if (nhdr.n_type == NT_AUXV) + handle_auxv_note (ebl, ebl->elf, nhdr.n_descsz, + start + desc_offset); + else + handle_core_note (ebl, &nhdr, desc); + } + else + ebl_object_note (ebl, name, nhdr.n_type, nhdr.n_descsz, desc); + } + } + + if (offset == data->d_size) + return; + + bad_note: + error (EXIT_FAILURE, 0, + gettext ("cannot get content of note section: %s"), + elf_errmsg (-1)); +} static void handle_notes (Ebl *ebl, GElf_Ehdr *ehdr) { - int class = gelf_getclass (ebl->elf); - size_t cnt; + /* If we have section headers, just look for SHT_NOTE sections. + In a debuginfo file, the program headers are not reliable. */ + if (shnum != 0) + { + /* Get the section header string table index. */ + size_t shstrndx; + if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + + if (shdr == NULL || shdr->sh_type != SHT_NOTE) + /* Not what we are looking for. */ + continue; + + printf (gettext ("\ +\nNote section [%2zu] '%s' of %" PRIu64 " bytes at offset %#0" PRIx64 ":\n"), + elf_ndxscn (scn), + elf_strptr (ebl->elf, shstrndx, shdr->sh_name), + shdr->sh_size, shdr->sh_offset); + + handle_notes_data (ebl, ehdr, shdr->sh_offset, + elf_getdata (scn, NULL)); + } + return; + } /* We have to look through the program header to find the note sections. There can be more than one. */ - for (cnt = 0; cnt < ehdr->e_phnum; ++cnt) + for (size_t cnt = 0; cnt < ehdr->e_phnum; ++cnt) { GElf_Phdr mem; GElf_Phdr *phdr = gelf_getphdr (ebl->elf, cnt, &mem); @@ -4459,83 +6192,254 @@ handle_notes (Ebl *ebl, GElf_Ehdr *ehdr) continue; printf (gettext ("\ -\nNote segment of %" PRId64 " bytes at offset %#0" PRIx64 ":\n"), +\nNote segment of %" PRIu64 " bytes at offset %#0" PRIx64 ":\n"), phdr->p_filesz, phdr->p_offset); - char *notemem = gelf_rawchunk (ebl->elf, phdr->p_offset, phdr->p_filesz); - if (notemem == NULL) - error (EXIT_FAILURE, 0, - gettext ("cannot get content of note section: %s"), - elf_errmsg (-1)); + handle_notes_data (ebl, ehdr, phdr->p_offset, + elf_getdata_rawchunk (ebl->elf, + phdr->p_offset, phdr->p_filesz, + ELF_T_NHDR)); + } +} - fputs_unlocked (gettext (" Owner Data size Type\n"), stdout); +static void +hex_dump (const uint8_t *data, size_t len) +{ + size_t pos = 0; + while (pos < len) + { + printf (" 0x%08Zx ", pos); - /* Handle the note section content. It consists of one or more - entries each of which consists of five parts: + const size_t chunk = MIN (len - pos, 16); - - a 32-bit name length - - a 32-bit descriptor length - - a 32-bit type field - - the NUL-terminated name, length as specified in the first field - - the descriptor, length as specified in the second field + for (size_t i = 0; i < chunk; ++i) + if (i % 4 == 3) + printf ("%02x ", data[pos + i]); + else + printf ("%02x", data[pos + i]); - The variable sized fields are padded to 32- or 64-bits - depending on whether the file is a 32- or 64-bit ELF file. - */ - size_t align = class == ELFCLASS32 ? 4 : 8; -#define ALIGNED_LEN(len) (((len) + align - 1) & ~(align - 1)) + if (chunk < 16) + printf ("%*s", (int) ((16 - chunk) * 2 + (16 - chunk + 3) / 4), ""); - size_t idx = 0; - while (idx < phdr->p_filesz) + for (size_t i = 0; i < chunk; ++i) { - /* XXX Handle 64-bit note section entries correctly. */ - struct - { - uint32_t namesz; - uint32_t descsz; - uint32_t type; - char name[0]; - } *noteentry = (__typeof (noteentry)) (notemem + idx); - - if (idx + 12 > phdr->p_filesz - || (idx + 12 + ALIGNED_LEN (noteentry->namesz) - + ALIGNED_LEN (noteentry->descsz) > phdr->p_filesz)) - /* This entry isn't completely contained in the note - section. Ignore it. */ - break; + unsigned char b = data[pos + i]; + printf ("%c", isprint (b) ? b : '.'); + } + + putchar ('\n'); + pos += chunk; + } +} + +static void +dump_data_section (Elf_Scn *scn, const GElf_Shdr *shdr, const char *name) +{ + if (shdr->sh_size == 0 || shdr->sh_type == SHT_NOBITS) + printf (gettext ("\nSection [%Zu] '%s' has no data to dump.\n"), + elf_ndxscn (scn), name); + else + { + Elf_Data *data = elf_rawdata (scn, NULL); + if (data == NULL) + error (0, 0, gettext ("cannot get data for section [%Zu] '%s': %s"), + elf_ndxscn (scn), name, elf_errmsg (-1)); + else + { + printf (gettext ("\nHex dump of section [%Zu] '%s', %" PRIu64 + " bytes at offset %#0" PRIx64 ":\n"), + elf_ndxscn (scn), name, + shdr->sh_size, shdr->sh_offset); + hex_dump (data->d_buf, data->d_size); + } + } +} - char buf[100]; - char buf2[100]; - printf (gettext (" %-13.*s %9" PRId32 " %s\n"), - (int) noteentry->namesz, noteentry->name, - noteentry->descsz, - ehdr->e_type == ET_CORE - ? ebl_core_note_type_name (ebl, noteentry->type, - buf, sizeof (buf)) - : ebl_object_note_type_name (ebl, noteentry->type, - buf2, sizeof (buf2))); - - /* Filter out invalid entries. */ - if (memchr (noteentry->name, '\0', noteentry->namesz) != NULL - /* XXX For now help broken Linux kernels. */ - || 1) +static void +print_string_section (Elf_Scn *scn, const GElf_Shdr *shdr, const char *name) +{ + if (shdr->sh_size == 0) + printf (gettext ("\nSection [%Zu] '%s' is empty.\n"), + elf_ndxscn (scn), name); + + Elf_Data *data = elf_rawdata (scn, NULL); + if (data == NULL) + error (0, 0, gettext ("cannot get data for section [%Zu] '%s': %s"), + elf_ndxscn (scn), name, elf_errmsg (-1)); + else + { + printf (gettext ("\nString section [%Zu] '%s' contains %" PRIu64 + " bytes at offset %#0" PRIx64 ":\n"), + elf_ndxscn (scn), name, + shdr->sh_size, shdr->sh_offset); + + const char *start = data->d_buf; + const char *const limit = start + data->d_size; + do + { + const char *end = memchr (start, '\0', limit - start); + const size_t pos = start - (const char *) data->d_buf; + if (unlikely (end == NULL)) { - if (ehdr->e_type == ET_CORE) - ebl_core_note (ebl, noteentry->name, noteentry->type, - noteentry->descsz, - ¬eentry->name[ALIGNED_LEN (noteentry->namesz)]); - else - ebl_object_note (ebl, noteentry->name, noteentry->type, - noteentry->descsz, - ¬eentry->name[ALIGNED_LEN (noteentry->namesz)]); + printf (" [%6Zx]- %.*s\n", + pos, (int) (limit - start), start); + break; } + printf (" [%6Zx] %s\n", pos, start); + start = end + 1; + } while (start < limit); + } +} - /* Move to the next entry. */ - idx += (12 + ALIGNED_LEN (noteentry->namesz) - + ALIGNED_LEN (noteentry->descsz)); +static void +for_each_section_argument (Elf *elf, const struct section_argument *list, + void (*dump) (Elf_Scn *scn, const GElf_Shdr *shdr, + const char *name)) +{ + /* Get the section header string table index. */ + size_t shstrndx; + if (elf_getshstrndx (elf, &shstrndx) < 0) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + for (const struct section_argument *a = list; a != NULL; a = a->next) + { + Elf_Scn *scn; + GElf_Shdr shdr_mem; + const char *name = NULL; + + char *endp = NULL; + unsigned long int shndx = strtoul (a->arg, &endp, 0); + if (endp != a->arg && *endp == '\0') + { + scn = elf_getscn (elf, shndx); + if (scn == NULL) + { + error (0, 0, gettext ("\nsection [%lu] does not exist"), shndx); + continue; + } + + if (gelf_getshdr (scn, &shdr_mem) == NULL) + error (EXIT_FAILURE, 0, gettext ("cannot get section header: %s"), + elf_errmsg (-1)); + name = elf_strptr (elf, shstrndx, shdr_mem.sh_name); + } + else + { + /* Need to look up the section by name. */ + scn = NULL; + while ((scn = elf_nextscn (elf, scn)) != NULL) + { + if (gelf_getshdr (scn, &shdr_mem) == NULL) + continue; + name = elf_strptr (elf, shstrndx, shdr_mem.sh_name); + if (name == NULL) + continue; + if (!strcmp (name, a->arg)) + break; + } + + if (unlikely (scn == NULL)) + { + error (0, 0, gettext ("\nsection '%s' does not exist"), a->arg); + continue; + } + } + + (*dump) (scn, &shdr_mem, name); + } +} + +static void +dump_data (Ebl *ebl) +{ + for_each_section_argument (ebl->elf, dump_data_sections, &dump_data_section); +} + +static void +dump_strings (Ebl *ebl) +{ + for_each_section_argument (ebl->elf, string_sections, &print_string_section); +} + +static void +print_strings (Ebl *ebl) +{ + /* Get the section header string table index. */ + size_t shstrndx; + if (unlikely (elf_getshstrndx (ebl->elf, &shstrndx) < 0)) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + Elf_Scn *scn; + GElf_Shdr shdr_mem; + const char *name; + scn = NULL; + while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) + { + if (gelf_getshdr (scn, &shdr_mem) == NULL) + continue; + + if (shdr_mem.sh_type != SHT_PROGBITS + || !(shdr_mem.sh_flags & SHF_STRINGS)) + continue; + + name = elf_strptr (ebl->elf, shstrndx, shdr_mem.sh_name); + if (name == NULL) + continue; + + print_string_section (scn, &shdr_mem, name); + } +} + +static void +dump_archive_index (Elf *elf, const char *fname) +{ + size_t narsym; + const Elf_Arsym *arsym = elf_getarsym (elf, &narsym); + if (arsym == NULL) + { + int result = elf_errno (); + if (unlikely (result != ELF_E_NO_INDEX)) + error (EXIT_FAILURE, 0, + gettext ("cannot get symbol index of archive '%s': %s"), + fname, elf_errmsg (result)); + else + printf (gettext ("\nArchive '%s' has no symbol index\n"), fname); + return; + } + + printf (gettext ("\nIndex of archive '%s' has %Zu entries:\n"), + fname, narsym); + + size_t as_off = 0; + for (const Elf_Arsym *s = arsym; s < &arsym[narsym - 1]; ++s) + { + if (s->as_off != as_off) + { + as_off = s->as_off; + + Elf *subelf; + if (unlikely (elf_rand (elf, as_off) == 0) + || unlikely ((subelf = elf_begin (-1, ELF_C_READ_MMAP, elf)) + == NULL)) +#if __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 7) + while (1) +#endif + error (EXIT_FAILURE, 0, + gettext ("cannot extract member at offset %Zu in '%s': %s"), + as_off, fname, elf_errmsg (-1)); + + const Elf_Arhdr *h = elf_getarhdr (subelf); + + printf (gettext ("Archive member '%s' contains:\n"), h->ar_name); + + elf_end (subelf); } - gelf_freechunk (ebl->elf, notemem); + printf ("\t%s\n", s->as_name); } } + +#include "debugpred.h" diff --git a/src/sectionhash.c b/src/sectionhash.c index dc559409..68d734e1 100644 --- a/src/sectionhash.c +++ b/src/sectionhash.c @@ -1,16 +1,28 @@ /* Section hash table implementation. - Copyright (C) 2001, 2002 Red Hat, Inc. + Copyright (C) 2001, 2002, 2005 Red Hat, Inc. + This file is part of Red Hat elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 2001. - This program is Open Source software; you can redistribute it and/or - modify it under the terms of the Open Software License version 1.0 as - published by the Open Source Initiative. + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. - You should have received a copy of the Open Software License along - with this program; if not, you may obtain a copy of the Open Software - License version 1.0 from http://www.opensource.org/licenses/osl.php or - by writing the Open Source Initiative c/o Lawrence Rosen, Esq., - 3001 King Ranch Road, Ukiah, CA 95482. */ + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ #ifdef HAVE_CONFIG_H # include <config.h> @@ -34,7 +46,7 @@ scnhead_compare (struct scnhead *one, struct scnhead *two) if (result == 0) { - GElf_Xword diff = (SH_FLAGS_IMPORTANT (one->flags) + GElf_Sxword diff = (SH_FLAGS_IMPORTANT (one->flags) - SH_FLAGS_IMPORTANT (two->flags)); result = diff < 0 ? -1 : diff == 0 ? 0 : 1; diff --git a/src/sectionhash.h b/src/sectionhash.h index 50bcb9c8..ba41ee8c 100644 --- a/src/sectionhash.h +++ b/src/sectionhash.h @@ -1,15 +1,27 @@ /* Copyright (C) 2001, 2002 Red Hat, Inc. + This file is part of Red Hat elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 2001. - This program is Open Source software; you can redistribute it and/or - modify it under the terms of the Open Software License version 1.0 as - published by the Open Source Initiative. + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. - You should have received a copy of the Open Software License along - with this program; if not, you may obtain a copy of the Open Software - License version 1.0 from http://www.opensource.org/licenses/osl.php or - by writing the Open Source Initiative c/o Lawrence Rosen, Esq., - 3001 King Ranch Road, Ukiah, CA 95482. */ + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ #ifndef SECTIONHASH_H #define SECTIONHASH_H 1 @@ -1,16 +1,28 @@ /* Print size information from ELF file. - Copyright (C) 2000, 2001, 2002, 2003, 2004 Red Hat, Inc. + Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 2000. - This program is Open Source software; you can redistribute it and/or - modify it under the terms of the Open Software License version 1.0 as - published by the Open Source Initiative. + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. - You should have received a copy of the Open Software License along - with this program; if not, you may obtain a copy of the Open Software - License version 1.0 from http://www.opensource.org/licenses/osl.php or - by writing the Open Source Initiative c/o Lawrence Rosen, Esq., - 3001 King Ranch Road, Ukiah, CA 95482. */ + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ #ifdef HAVE_CONFIG_H # include <config.h> @@ -40,6 +52,9 @@ static void print_version (FILE *stream, struct argp_state *state); void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; +/* Bug report address. */ +const char *argp_program_bug_address = PACKAGE_BUGREPORT; + /* Values for the parameters which have no short form. */ #define OPT_FORMAT 0x100 @@ -48,20 +63,25 @@ void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; /* Definitions of arguments for argp functions. */ static const struct argp_option options[] = { - { NULL, 0, NULL, 0, N_("Output format:") }, - { "format", OPT_FORMAT, "FORMAT", 0, N_("Use the output format FORMAT. FORMAT can be `bsd' or `sysv'. The default is `bsd'") }, - { NULL, 'A', NULL, 0, N_("Same as `--format=sysv'") }, - { NULL, 'B', NULL, 0, N_("Same as `--format=bsd'") }, - { "radix", OPT_RADIX, "RADIX", 0, N_("Use RADIX for printing symbol values") }, - { NULL, 'd', NULL, 0, N_("Same as `--radix=10'") }, - { NULL, 'o', NULL, 0, N_("Same as `--radix=8'") }, - { NULL, 'x', NULL, 0, N_("Same as `--radix=16'") }, - { NULL, 'f', NULL, 0, N_("Similar to `--format=sysv' output but in one line") }, - - { NULL, 0, NULL, 0, N_("Output options:") }, - { NULL, 'F', NULL, 0, N_("Print size and permission flags for loadable segments") }, - { "totals", 't', NULL, 0, N_("Display the total sizes (bsd only)") }, - { NULL, 0, NULL, 0, NULL } + { NULL, 0, NULL, 0, N_("Output format:"), 0 }, + { "format", OPT_FORMAT, "FORMAT", 0, + N_("Use the output format FORMAT. FORMAT can be `bsd' or `sysv'. " + "The default is `bsd'"), 0 }, + { NULL, 'A', NULL, 0, N_("Same as `--format=sysv'"), 0 }, + { NULL, 'B', NULL, 0, N_("Same as `--format=bsd'"), 0 }, + { "radix", OPT_RADIX, "RADIX", 0, N_("Use RADIX for printing symbol values"), + 0}, + { NULL, 'd', NULL, 0, N_("Same as `--radix=10'"), 0 }, + { NULL, 'o', NULL, 0, N_("Same as `--radix=8'"), 0 }, + { NULL, 'x', NULL, 0, N_("Same as `--radix=16'"), 0 }, + { NULL, 'f', NULL, 0, + N_("Similar to `--format=sysv' output but in one line"), 0 }, + + { NULL, 0, NULL, 0, N_("Output options:"), 0 }, + { NULL, 'F', NULL, 0, + N_("Print size and permission flags for loadable segments"), 0 }, + { "totals", 't', NULL, 0, N_("Display the total sizes (bsd only)"), 0 }, + { NULL, 0, NULL, 0, NULL, 0 } }; /* Short description of program. */ @@ -74,13 +94,10 @@ static const char args_doc[] = N_("[FILE...]"); /* Prototype for option handler. */ static error_t parse_opt (int key, char *arg, struct argp_state *state); -/* Function to print some extra text in the help message. */ -static char *more_help (int key, const char *text, void *input); - /* Data structure to communicate with argp functions. */ static struct argp argp = { - options, parse_opt, args_doc, doc, NULL, more_help + options, parse_opt, args_doc, doc, NULL, NULL, NULL }; @@ -98,7 +115,7 @@ static void show_bsd_totals (void); #define INTERNAL_ERROR(fname) \ error (EXIT_FAILURE, 0, gettext ("%s: INTERNAL ERROR %d (%s-%s): %s"), \ - fname, __LINE__, VERSION, __DATE__, elf_errmsg (-1)) + fname, __LINE__, PACKAGE_VERSION, __DATE__, elf_errmsg (-1)) /* User-selectable options. */ @@ -163,10 +180,10 @@ main (int argc, char *argv[]) setlocale (LC_ALL, ""); /* Make sure the message catalog can be found. */ - bindtextdomain (PACKAGE, LOCALEDIR); + bindtextdomain (PACKAGE_TARNAME, LOCALEDIR); /* Initialize the message catalog. */ - textdomain (PACKAGE); + textdomain (PACKAGE_TARNAME); /* Parse and process arguments. */ argp_parse (&argp, argc, argv, 0, &remaining, NULL); @@ -196,21 +213,22 @@ main (int argc, char *argv[]) /* Print the version information. */ static void -print_version (FILE *stream, struct argp_state *state) +print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) { - fprintf (stream, "size (%s) %s\n", PACKAGE_NAME, VERSION); + fprintf (stream, "size (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION); fprintf (stream, gettext ("\ Copyright (C) %s Red Hat, Inc.\n\ This is free software; see the source for copying conditions. There is NO\n\ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ -"), "2004"); +"), "2008"); fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); } /* Handle program arguments. */ static error_t -parse_opt (int key, char *arg, struct argp_state *state) +parse_opt (int key, char *arg, + struct argp_state *state __attribute__ ((unused))) { switch (key) { @@ -245,7 +263,7 @@ parse_opt (int key, char *arg, struct argp_state *state) case OPT_FORMAT: if (strcmp (arg, "bsd") == 0 || strcmp (arg, "berkeley") == 0) format = format_bsd; - else if (strcmp (arg, "sysv") == 0) + else if (likely (strcmp (arg, "sysv") == 0)) format = format_sysv; else error (EXIT_FAILURE, 0, gettext ("Invalid format: %s"), arg); @@ -273,63 +291,38 @@ parse_opt (int key, char *arg, struct argp_state *state) } -static char * -more_help (int key, const char *text, void *input) -{ - char *buf; - - switch (key) - { - case ARGP_KEY_HELP_EXTRA: - /* We print some extra information. */ - if (asprintf (&buf, gettext ("Please report bugs to %s.\n"), - PACKAGE_BUGREPORT) < 0) - buf = NULL; - return buf; - - default: - break; - } - return (char *) text; -} - - +/* Open the file and determine the type. */ static int process_file (const char *fname) { - /* Open the file and determine the type. */ - int fd; - Elf *elf; - - /* Open the file. */ - fd = open (fname, O_RDONLY); - if (fd == -1) + int fd = open (fname, O_RDONLY); + if (unlikely (fd == -1)) { - error (0, errno, fname); + error (0, errno, gettext ("cannot open '%s'"), fname); return 1; } /* Now get the ELF descriptor. */ - elf = elf_begin (fd, ELF_C_READ_MMAP, NULL); - if (elf != NULL) + Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL); + if (likely (elf != NULL)) { if (elf_kind (elf) == ELF_K_ELF) { handle_elf (elf, NULL, fname); - if (elf_end (elf) != 0) + if (unlikely (elf_end (elf) != 0)) INTERNAL_ERROR (fname); - if (close (fd) != 0) - error (EXIT_FAILURE, errno, gettext ("while close `%s'"), fname); + if (unlikely (close (fd) != 0)) + error (EXIT_FAILURE, errno, gettext ("while closing '%s'"), fname); return 0; } - else + else if (likely (elf_kind (elf) == ELF_K_AR)) return handle_ar (fd, elf, NULL, fname); /* We cannot handle this type. Close the descriptor anyway. */ - if (elf_end (elf) != 0) + if (unlikely (elf_end (elf) != 0)) INTERNAL_ERROR (fname); } @@ -366,12 +359,9 @@ print_header (Elf *elf) static int handle_ar (int fd, Elf *elf, const char *prefix, const char *fname) { - Elf *subelf; - Elf_Cmd cmd = ELF_C_READ_MMAP; size_t prefix_len = prefix == NULL ? 0 : strlen (prefix); size_t fname_len = strlen (fname) + 1; char new_prefix[prefix_len + 1 + fname_len]; - int result = 0; char *cp = new_prefix; /* Create the full name of the file. */ @@ -383,6 +373,9 @@ handle_ar (int fd, Elf *elf, const char *prefix, const char *fname) memcpy (cp, fname, fname_len); /* Process all the files contained in the archive. */ + int result = 0; + Elf *subelf; + Elf_Cmd cmd = ELF_C_READ_MMAP; while ((subelf = elf_begin (fd, cmd, elf)) != NULL) { /* The the header for this element. */ @@ -390,21 +383,21 @@ handle_ar (int fd, Elf *elf, const char *prefix, const char *fname) if (elf_kind (subelf) == ELF_K_ELF) handle_elf (subelf, new_prefix, arhdr->ar_name); - else if (elf_kind (subelf) == ELF_K_AR) + else if (likely (elf_kind (subelf) == ELF_K_AR)) result |= handle_ar (fd, subelf, new_prefix, arhdr->ar_name); /* else signal error??? */ /* Get next archive element. */ cmd = elf_next (subelf); - if (elf_end (subelf) != 0) + if (unlikely (elf_end (subelf) != 0)) INTERNAL_ERROR (fname); } - if (elf_end (elf) != 0) + if (unlikely (elf_end (elf) != 0)) INTERNAL_ERROR (fname); - if (close (fd) != 0) - error (EXIT_FAILURE, errno, gettext ("while closing `%s'"), fname); + if (unlikely (close (fd) != 0)) + error (EXIT_FAILURE, errno, gettext ("while closing '%s'"), fname); return result; } @@ -415,22 +408,20 @@ static void show_sysv (Elf *elf, const char *prefix, const char *fname, const char *fullname) { - size_t shstrndx; - Elf_Scn *scn = NULL; - GElf_Shdr shdr_mem; int maxlen = 10; - int digits = length_map[gelf_getclass (elf) - 1][radix]; - const char *fmtstr; - GElf_Off total = 0; + const int digits = length_map[gelf_getclass (elf) - 1][radix]; /* Get the section header string table index. */ - if (elf_getshstrndx (elf, &shstrndx) < 0) + size_t shstrndx; + if (unlikely (elf_getshstrndx (elf, &shstrndx) < 0)) error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); /* First round over the sections: determine the longest section name. */ + Elf_Scn *scn = NULL; while ((scn = elf_nextscn (elf, scn)) != NULL) { + GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); if (shdr == NULL) @@ -439,7 +430,8 @@ show_sysv (Elf *elf, const char *prefix, const char *fname, /* Ignore all sections which are not used at runtime. */ if ((shdr->sh_flags & SHF_ALLOC) != 0) maxlen = MAX (maxlen, - strlen (elf_strptr (elf, shstrndx, shdr->sh_name))); + (int) strlen (elf_strptr (elf, shstrndx, + shdr->sh_name))); } fputs_unlocked (fname, stdout); @@ -450,6 +442,7 @@ show_sysv (Elf *elf, const char *prefix, const char *fname, digits - 2, sgettext ("sysv|size"), digits, sgettext ("sysv|addr")); + const char *fmtstr; if (radix == radix_hex) fmtstr = "%-*s %*" PRIx64 " %*" PRIx64 "\n"; else if (radix == radix_decimal) @@ -458,8 +451,10 @@ show_sysv (Elf *elf, const char *prefix, const char *fname, fmtstr = "%-*s %*" PRIo64 " %*" PRIo64 "\n"; /* Iterate over all sections. */ + GElf_Off total = 0; while ((scn = elf_nextscn (elf, scn)) != NULL) { + GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); /* Ignore all sections which are not used at runtime. */ @@ -488,21 +483,15 @@ show_sysv (Elf *elf, const char *prefix, const char *fname, /* Show sizes in SysV format in one line. */ static void -show_sysv_one_line (Elf *elf, const char *prefix, const char *fname, - const char *fullname) +show_sysv_one_line (Elf *elf) { - size_t shstrndx; - Elf_Scn *scn = NULL; - GElf_Shdr shdr_mem; - const char *fmtstr; - GElf_Off total = 0; - int first = 1; - /* Get the section header string table index. */ - if (elf_getshstrndx (elf, &shstrndx) < 0) + size_t shstrndx; + if (unlikely (elf_getshstrndx (elf, &shstrndx) < 0)) error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); + const char *fmtstr; if (radix == radix_hex) fmtstr = "%" PRIx64 "(%s)"; else if (radix == radix_decimal) @@ -511,8 +500,12 @@ show_sysv_one_line (Elf *elf, const char *prefix, const char *fname, fmtstr = "%" PRIo64 "(%s)"; /* Iterate over all sections. */ + GElf_Off total = 0; + bool first = true; + Elf_Scn *scn = NULL; while ((scn = elf_nextscn (elf, scn)) != NULL) { + GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); /* Ignore all sections which are not used at runtime. */ @@ -521,7 +514,7 @@ show_sysv_one_line (Elf *elf, const char *prefix, const char *fname, if (! first) fputs_unlocked (" + ", stdout); - first = 0; + first = false; printf (fmtstr, shdr->sh_size, elf_strptr (elf, shstrndx, shdr->sh_name)); @@ -549,17 +542,17 @@ static void show_bsd (Elf *elf, const char *prefix, const char *fname, const char *fullname) { - Elf_Scn *scn = NULL; - GElf_Shdr shdr_mem; GElf_Off textsize = 0; GElf_Off datasize = 0; GElf_Off bsssize = 0; - int ddigits = length_map[gelf_getclass (elf) - 1][radix_decimal]; - int xdigits = length_map[gelf_getclass (elf) - 1][radix_hex]; + const int ddigits = length_map[gelf_getclass (elf) - 1][radix_decimal]; + const int xdigits = length_map[gelf_getclass (elf) - 1][radix_hex]; /* Iterate over all sections. */ + Elf_Scn *scn = NULL; while ((scn = elf_nextscn (elf, scn)) != NULL) { + GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); if (shdr == NULL) @@ -617,20 +610,16 @@ show_bsd_totals (void) /* Show size and permission of loadable segments. */ static void -show_segments (Elf *elf, const char *prefix, const char *fname, - const char *fullname) +show_segments (Elf *elf, const char *fullname) { GElf_Ehdr ehdr_mem; - GElf_Ehdr *ehdr; - size_t cnt; - GElf_Off total = 0; - int first = 1; - - ehdr = gelf_getehdr (elf, &ehdr_mem); + GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); if (ehdr == NULL) INTERNAL_ERROR (fullname); - for (cnt = 0; cnt < ehdr->e_phnum; ++cnt) + GElf_Off total = 0; + bool first = true; + for (size_t cnt = 0; cnt < ehdr->e_phnum; ++cnt) { GElf_Phdr phdr_mem; GElf_Phdr *phdr; @@ -645,7 +634,7 @@ show_segments (Elf *elf, const char *prefix, const char *fname, if (! first) fputs_unlocked (" + ", stdout); - first = 0; + first = false; printf (radix == radix_hex ? "%" PRIx64 "(%c%c%c)" : (radix == radix_decimal ? "%" PRId64 "(%c%c%c)" @@ -686,9 +675,9 @@ handle_elf (Elf *elf, const char *prefix, const char *fname) if (format == format_sysv) show_sysv (elf, prefix, fname, fullname); else if (format == format_sysv_one_line) - show_sysv_one_line (elf, prefix, fname, fullname); + show_sysv_one_line (elf); else if (format == format_segments) - show_segments (elf, prefix, fname, fullname); + show_segments (elf, fullname); else { print_header (elf); @@ -696,3 +685,6 @@ handle_elf (Elf *elf, const char *prefix, const char *fname) show_bsd (elf, prefix, fname, fullname); } } + + +#include "debugpred.h" diff --git a/src/strings.c b/src/strings.c new file mode 100644 index 00000000..b2109961 --- /dev/null +++ b/src/strings.c @@ -0,0 +1,744 @@ +/* Print the strings of printable characters in files. + Copyright (C) 2005, 2006, 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + Written by Ulrich Drepper <drepper@redhat.com>, 2005. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <argp.h> +#include <assert.h> +#include <ctype.h> +#include <endian.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <gelf.h> +#include <inttypes.h> +#include <libintl.h> +#include <locale.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdio_ext.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/mman.h> +#include <sys/param.h> +#include <sys/stat.h> + +#include <system.h> + + +/* Prototypes of local functions. */ +static int read_fd (int fd, const char *fname, off64_t fdlen); +static int read_elf (Elf *elf, int fd, const char *fname, off64_t fdlen); + + +/* Name and version of program. */ +static void print_version (FILE *stream, struct argp_state *state); +void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; + +/* Bug report address. */ +const char *argp_program_bug_address = PACKAGE_BUGREPORT; + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + { NULL, 0, NULL, 0, N_("Output Selection:"), 0 }, + { "all", 'a', NULL, 0, N_("Scan entire file, not only loaded sections"), 0 }, + { "bytes", 'n', "MIN-LEN", 0, + N_("Only NUL-terminated sequences of MIN-LEN characters or more are printed"), 0 }, + { "encoding", 'e', "SELECTOR", 0, N_("\ +Select character size and endianess: s = 7-bit, S = 8-bit, {b,l} = 16-bit, {B,L} = 32-bit"), + 0}, + { "print-file-name", 'f', NULL, 0, + N_("Print name of the file before each string."), 0 }, + { "radix", 't', "{o,d,x}", 0, + N_("Print location of the string in base 8, 10, or 16 respectively."), 0 }, + { NULL, 'o', NULL, 0, N_("Alias for --radix=o"), 0 }, + + { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 }, + { NULL, 0, NULL, 0, NULL, 0 } +}; + +/* Short description of program. */ +static const char doc[] = N_("\ +Print the strings of printable characters in files."); + +/* Strings for arguments in help texts. */ +static const char args_doc[] = N_("[FILE...]"); + +/* Prototype for option handler. */ +static error_t parse_opt (int key, char *arg, struct argp_state *state); + +/* Data structure to communicate with argp functions. */ +static struct argp argp = +{ + options, parse_opt, args_doc, doc, NULL, NULL, NULL +}; + + +/* Global variables. */ + +/* True if whole file and not only loaded sections are looked at. */ +static bool entire_file; + +/* Minimum length of any sequence reported. */ +static size_t min_len = 4; + +/* Number of bytes per character. */ +static size_t bytes_per_char = 1; + +/* Minimum length of any sequence reported in bytes. */ +static size_t min_len_bytes; + +/* True if multibyte characters are in big-endian order. */ +static bool big_endian; + +/* True unless 7-bit ASCII are expected. */ +static bool char_7bit; + +/* True if file names should be printed before strings. */ +static bool print_file_name; + +/* Location print format string. */ +static const char *locfmt; + +/* Page size in use. */ +static size_t ps; + + +/* Mapped parts of the ELF file. */ +static unsigned char *elfmap; +static unsigned char *elfmap_base; +static size_t elfmap_size; +static off64_t elfmap_off; + + +int +main (int argc, char *argv[]) +{ + /* We use no threads. */ + __fsetlocking (stdin, FSETLOCKING_BYCALLER); + __fsetlocking (stdout, FSETLOCKING_BYCALLER); + + /* Set locale. */ + (void) setlocale (LC_ALL, ""); + + /* Make sure the message catalog can be found. */ + (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR); + + /* Initialize the message catalog. */ + (void) textdomain (PACKAGE_TARNAME); + + /* Parse and process arguments. */ + int remaining; + (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL); + + /* Tell the library which version we are expecting. */ + elf_version (EV_CURRENT); + + /* Determine the page size. We will likely need it a couple of times. */ + ps = sysconf (_SC_PAGESIZE); + + struct stat64 st; + int result = 0; + if (remaining == argc) + /* We read from standard input. This we cannot do for a + structured file. */ + result = read_fd (STDOUT_FILENO, + print_file_name ? "{standard input}" : NULL, + fstat64 (STDOUT_FILENO, &st) == 0 + ? st.st_size : INT64_C (0x7fffffffffffffff)); + else + do + { + int fd = (strcmp (argv[remaining], "-") == 0 + ? STDIN_FILENO : open (argv[remaining], O_RDONLY)); + if (unlikely (fd == -1)) + { + error (0, errno, gettext ("cannot open '%s'"), argv[remaining]); + result = 1; + } + else + { + const char *fname = print_file_name ? argv[remaining] : NULL; + int fstat_fail = fstat64 (fd, &st); + off64_t fdlen = (fstat_fail + ? INT64_C (0x7fffffffffffffff) : st.st_size); + if (fdlen > (off64_t) min_len_bytes) + { + Elf *elf = NULL; + if (entire_file + || fstat_fail + || !S_ISREG (st.st_mode) + || (elf = elf_begin (fd, ELF_C_READ, NULL)) == NULL + || elf_kind (elf) != ELF_K_ELF) + result |= read_fd (fd, fname, fdlen); + else + result |= read_elf (elf, fd, fname, fdlen); + + /* This call will succeed even if ELF is NULL. */ + elf_end (elf); + } + + if (strcmp (argv[remaining], "-") != 0) + close (fd); + } + + if (elfmap != NULL && elfmap != MAP_FAILED) + munmap (elfmap, elfmap_size); + elfmap = NULL; + } + while (++remaining < argc); + + return result; +} + + +/* Print the version information. */ +static void +print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) +{ + fprintf (stream, "strings (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION); + fprintf (stream, gettext ("\ +Copyright (C) %s Red Hat, Inc.\n\ +This is free software; see the source for copying conditions. There is NO\n\ +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ +"), "2008"); + fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); +} + + +/* Handle program arguments. */ +static error_t +parse_opt (int key, char *arg, + struct argp_state *state __attribute__ ((unused))) +{ + switch (key) + { + case 'a': + entire_file = true; + break; + + case 'e': + /* We expect a string of one character. */ + switch (arg[1] != '\0' ? '\0' : arg[0]) + { + case 's': + case 'S': + char_7bit = arg[0] == 's'; + bytes_per_char = 1; + break; + + case 'b': + case 'B': + big_endian = true; + /* FALLTHROUGH */ + + case 'l': + case 'L': + bytes_per_char = isupper (arg[0]) ? 4 : 2; + break; + + default: + error (0, 0, gettext ("invalid value '%s' for %s parameter"), + arg, "-e"); + argp_help (&argp, stderr, ARGP_HELP_SEE, "strings"); + return ARGP_ERR_UNKNOWN; + } + break; + + case 'f': + print_file_name = true; + break; + + case 'n': + min_len = atoi (arg); + break; + + case 'o': + goto octfmt; + + case 't': + switch (arg[0]) + { + case 'd': + locfmt = "%7" PRId64 " "; + break; + + case 'o': + octfmt: + locfmt = "%7" PRIo64 " "; + break; + + case 'x': + locfmt = "%7" PRIx64 " "; + break; + + default: + error (0, 0, gettext ("invalid value '%s' for %s parameter"), + arg, "-t"); + argp_help (&argp, stderr, ARGP_HELP_SEE, "strings"); + return ARGP_ERR_UNKNOWN; + } + break; + + case ARGP_KEY_FINI: + /* Compute the length in bytes of any match. */ + if (min_len <= 0 || min_len > INT_MAX / bytes_per_char) + error (EXIT_FAILURE, 0, + gettext ("invalid minimum length of matched string size")); + min_len_bytes = min_len * bytes_per_char; + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + + +static void +process_chunk_mb (const char *fname, const unsigned char *buf, off64_t to, + size_t len, char **unprinted) +{ + size_t curlen = *unprinted == NULL ? 0 : strlen (*unprinted); + const unsigned char *start = buf; + while (len >= bytes_per_char) + { + uint32_t ch; + + if (bytes_per_char == 2) + { + if (big_endian) + ch = buf[0] << 8 | buf[1]; + else + ch = buf[1] << 8 | buf[0]; + } + else + { + if (big_endian) + ch = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; + else + ch = buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0]; + } + + if (ch <= 255 && (isprint (ch) || ch == '\t')) + { + ++buf; + ++curlen; + } + else + { + if (curlen >= min_len) + { + /* We found a match. */ + if (unlikely (fname != NULL)) + { + fputs_unlocked (fname, stdout); + fputs_unlocked (": ", stdout); + } + + if (unlikely (locfmt != NULL)) + printf (locfmt, (int64_t) to - len - (buf - start)); + + if (unlikely (*unprinted != NULL)) + { + fputs_unlocked (*unprinted, stdout); + free (*unprinted); + *unprinted = NULL; + } + + /* There is no sane way of printing the string. If we + assume the file data is encoded in UCS-2/UTF-16 or + UCS-4/UTF-32 respectively we could covert the string. + But there is no such guarantee. */ + fwrite_unlocked (start, 1, buf - start, stdout); + putc_unlocked ('\n', stdout); + } + + start = ++buf; + curlen = 0; + + if (len <= min_len) + break; + } + + --len; + } + + if (curlen != 0) + *unprinted = xstrndup ((const char *) start, curlen); +} + + +static void +process_chunk (const char *fname, const unsigned char *buf, off64_t to, + size_t len, char **unprinted) +{ + /* We are not going to slow the check down for the 2- and 4-byte + encodings. Handle them special. */ + if (unlikely (bytes_per_char != 1)) + { + process_chunk_mb (fname, buf, to, len, unprinted); + return; + } + + size_t curlen = *unprinted == NULL ? 0 : strlen (*unprinted); + const unsigned char *start = buf; + while (len > 0) + { + if ((isprint (*buf) || *buf == '\t') && (! char_7bit || *buf <= 127)) + { + ++buf; + ++curlen; + } + else + { + if (curlen >= min_len) + { + /* We found a match. */ + if (likely (fname != NULL)) + { + fputs_unlocked (fname, stdout); + fputs_unlocked (": ", stdout); + } + + if (likely (locfmt != NULL)) + printf (locfmt, (int64_t) to - len - (buf - start)); + + if (unlikely (*unprinted != NULL)) + { + fputs_unlocked (*unprinted, stdout); + free (*unprinted); + *unprinted = NULL; + } + fwrite_unlocked (start, 1, buf - start, stdout); + putc_unlocked ('\n', stdout); + } + + start = ++buf; + curlen = 0; + + if (len <= min_len) + break; + } + + --len; + } + + if (curlen != 0) + *unprinted = xstrndup ((const char *) start, curlen); +} + + +/* Map a file in as large chunks as possible. */ +static void * +map_file (int fd, off64_t start_off, off64_t fdlen, size_t *map_sizep) +{ +#if _MUDFLAP + (void) fd; + (void) start_off; + (void) fdlen; + (void) map_sizep; + return MAP_FAILED; +#else + /* Maximum size we mmap. We use an #ifdef to avoid overflows on + 32-bit machines. 64-bit machines these days do not have usable + address spaces larger than about 43 bits. Not that any file + should be that large. */ +# if SIZE_MAX > 0xffffffff + const size_t mmap_max = 0x4000000000lu; +# else + const size_t mmap_max = 0x40000000lu; +# endif + + /* Try to mmap the file. */ + size_t map_size = MIN ((off64_t) mmap_max, fdlen); + const size_t map_size_min = MAX (MAX (SIZE_MAX / 16, 2 * ps), + roundup (2 * min_len_bytes + 1, ps)); + void *mem; + while (1) + { + /* We map the memory for reading only here. Since we will + always look at every byte of the file it makes sense to + use MAP_POPULATE. */ + mem = mmap64 (NULL, map_size, PROT_READ, MAP_PRIVATE | MAP_POPULATE, + fd, start_off); + if (mem != MAP_FAILED) + { + /* We will go through the mapping sequentially. */ + (void) posix_madvise (mem, map_size, POSIX_MADV_SEQUENTIAL); + break; + } + if (errno != EINVAL && errno != ENOMEM) + /* This is an error other than the lack of address space. */ + break; + + /* Maybe the size of the mapping is too big. Try again. */ + map_size /= 2; + if (map_size < map_size_min) + /* That size should have fit. */ + break; + } + + *map_sizep = map_size; + return mem; +#endif +} + + +/* Read the file without mapping. */ +static int +read_block_no_mmap (int fd, const char *fname, off64_t from, off64_t fdlen) +{ + char *unprinted = NULL; +#define CHUNKSIZE 65536 + unsigned char *buf = xmalloc (CHUNKSIZE + min_len_bytes + + bytes_per_char - 1); + size_t ntrailer = 0; + int result = 0; + while (fdlen > 0) + { + ssize_t n = TEMP_FAILURE_RETRY (read (fd, buf + ntrailer, + MIN (fdlen, CHUNKSIZE))); + if (n == 0) + { + /* There are less than MIN_LEN+1 bytes left so there cannot be + another match. */ + assert (unprinted == NULL || ntrailer == 0); + break; + } + if (unlikely (n < 0)) + { + /* Something went wrong. */ + result = 1; + break; + } + + /* Account for the number of bytes read in this round. */ + fdlen -= n; + + /* Do not use the signed N value. Note that the addition cannot + overflow. */ + size_t nb = (size_t) n + ntrailer; + if (nb >= min_len_bytes) + { + /* We only use complete charactesr. */ + nb &= ~(bytes_per_char - 1); + + process_chunk (fname, buf, from + nb, nb, &unprinted); + + /* If the last bytes of the buffer (module the character + size) have been printed we are not copying them. */ + size_t to_keep = unprinted != NULL ? 0 : min_len_bytes; + + memmove (buf, buf + nb - to_keep, to_keep + nb); + ntrailer = to_keep + nb; + from += nb; + } + else + ntrailer = nb; + } + + free (buf); + + /* Don't print anything we collected so far. There is no + terminating NUL byte. */ + free (unprinted); + + return result; +} + + +static int +read_block (int fd, const char *fname, off64_t fdlen, off64_t from, off64_t to) +{ + assert ((off64_t) min_len_bytes < fdlen); + + if (elfmap == NULL) + { + /* We need a completely new mapping. */ + elfmap_off = from & ~(ps - 1); + elfmap_base = elfmap = map_file (fd, elfmap_off, fdlen, &elfmap_size); + + if (unlikely (elfmap == MAP_FAILED)) + /* Let the kernel know we are going to read everything in sequence. */ + (void) posix_fadvise (fd, 0, 0, POSIX_FADV_SEQUENTIAL); + } + + if (unlikely (elfmap == MAP_FAILED)) + { + /* Read from the file descriptor. For this we must position the + read pointer. */ + // XXX Eventually add flag which avoids this if the position + // XXX is known to match. + if (lseek64 (fd, from, SEEK_SET) != from) + error (EXIT_FAILURE, errno, gettext ("lseek64 failed")); + + return read_block_no_mmap (fd, fname, from, to - from); + } + + if (to < (off64_t) elfmap_off || from > (off64_t) (elfmap_off + elfmap_size)) + { + /* The existing mapping cannot fit at all. Map the new area. + We always map the full range of ELFMAP_SIZE bytes even if + this extend beyond the end of the file. The Linux kernel + handles this OK if the access pages are not touched. */ + elfmap_off = from & ~(ps - 1); + if (mmap64 (elfmap, elfmap_size, PROT_READ, + MAP_PRIVATE | MAP_POPULATE | MAP_FIXED, fd, from) + == MAP_FAILED) + error (EXIT_FAILURE, errno, gettext ("re-mmap failed")); + elfmap_base = elfmap; + } + + char *unprinted = NULL; + + /* Use the existing mapping as much as possible. If necessary, map + new pages. */ + if (from >= (off64_t) elfmap_off + && from < (off64_t) (elfmap_off + elfmap_size)) + /* There are at least a few bytes in this mapping which we can + use. */ + process_chunk (fname, elfmap_base + (from - elfmap_off), + MIN (to, (off64_t) (elfmap_off + elfmap_size)), + MIN (to, (off64_t) (elfmap_off + elfmap_size)) - from, + &unprinted); + + if (to > (off64_t) (elfmap_off + elfmap_size)) + { + unsigned char *remap_base = elfmap_base; + size_t read_now = elfmap_size - (elfmap_base - elfmap); + + assert (from >= (off64_t) elfmap_off + && from < (off64_t) (elfmap_off + elfmap_size)); + off64_t handled_to = elfmap_off + elfmap_size; + assert (elfmap == elfmap_base + || (elfmap_base - elfmap + == (ptrdiff_t) ((min_len_bytes + ps - 1) & ~(ps - 1)))); + if (elfmap == elfmap_base) + { + size_t keep_area = (min_len_bytes + ps - 1) & ~(ps - 1); + assert (elfmap_size >= keep_area + ps); + /* The keep area is used for the content of the previous + buffer we have to keep. This means copying those bytes + and for this we have to make the data writable. */ + if (unlikely (mprotect (elfmap, keep_area, PROT_READ | PROT_WRITE) + != 0)) + error (EXIT_FAILURE, errno, gettext ("mprotect failed")); + + elfmap_base = elfmap + keep_area; + } + + while (1) + { + /* Map the rest of the file, eventually again in pieces. + We speed things up with a nice Linux feature. Note + that we have at least two pages mapped. */ + size_t to_keep = unprinted != NULL ? 0 : min_len_bytes; + + assert (read_now >= to_keep); + memmove (elfmap_base - to_keep, + remap_base + read_now - to_keep, to_keep); + remap_base = elfmap_base; + + assert ((elfmap_size - (elfmap_base - elfmap)) % bytes_per_char + == 0); + read_now = MIN (to - handled_to, + (ptrdiff_t) elfmap_size - (elfmap_base - elfmap)); + + assert (handled_to % ps == 0); + assert (handled_to % bytes_per_char == 0); + if (mmap64 (remap_base, read_now, PROT_READ, + MAP_PRIVATE | MAP_POPULATE | MAP_FIXED, fd, handled_to) + == MAP_FAILED) + error (EXIT_FAILURE, errno, gettext ("re=mmap failed")); + elfmap_off = handled_to; + + process_chunk (fname, remap_base - to_keep, + elfmap_off + (read_now & ~(bytes_per_char - 1)), + to_keep + (read_now & ~(bytes_per_char - 1)), + &unprinted); + handled_to += read_now; + if (handled_to >= to) + break; + } + } + + /* Don't print anything we collected so far. There is no + terminating NUL byte. */ + free (unprinted); + + return 0; +} + + +static int +read_fd (int fd, const char *fname, off64_t fdlen) +{ + return read_block (fd, fname, fdlen, 0, fdlen); +} + + +static int +read_elf (Elf *elf, int fd, const char *fname, off64_t fdlen) +{ + assert (fdlen >= 0); + + /* We will look at each section separately. The ELF file is not + mmapped. The libelf implementation will load the needed parts on + demand. Since we only interate over the section header table the + memory consumption at this stage is kept minimal. */ + Elf_Scn *scn = elf_nextscn (elf, NULL); + if (scn == NULL) + return read_fd (fd, fname, fdlen); + + int result = 0; + do + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + + /* Only look in sections which are loaded at runtime and + actually have content. */ + if (shdr != NULL && shdr->sh_type != SHT_NOBITS + && (shdr->sh_flags & SHF_ALLOC) != 0) + result |= read_block (fd, fname, fdlen, shdr->sh_offset, + shdr->sh_offset + shdr->sh_size); + } + while ((scn = elf_nextscn (elf, scn)) != NULL); + + if (elfmap != NULL && elfmap != MAP_FAILED) + munmap (elfmap, elfmap_size); + elfmap = NULL; + + return result; +} + + +#include "debugpred.h" diff --git a/src/strip.c b/src/strip.c index e5c4289c..0f83e61d 100644 --- a/src/strip.c +++ b/src/strip.c @@ -1,16 +1,28 @@ /* Discard section not used at runtime from object files. - Copyright (C) 2000, 2001, 2002, 2003, 2004 Red Hat, Inc. + Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007,2008 Red Hat, Inc. + This file is part of Red Hat elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 2000. - This program is Open Source software; you can redistribute it and/or - modify it under the terms of the Open Software License version 1.0 as - published by the Open Source Initiative. + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. - You should have received a copy of the Open Software License along - with this program; if not, you may obtain a copy of the Open Software - License version 1.0 from http://www.opensource.org/licenses/osl.php or - by writing the Open Source Initiative c/o Lawrence Rosen, Esq., - 3001 King Ranch Road, Ukiah, CA 95482. */ + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ #ifdef HAVE_CONFIG_H # include <config.h> @@ -45,6 +57,9 @@ static void print_version (FILE *stream, struct argp_state *state); void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; +/* Bug report address. */ +const char *argp_program_bug_address = PACKAGE_BUGREPORT; + /* Values for the parameters which have no short form. */ #define OPT_REMOVE_COMMENT 0x100 @@ -54,19 +69,24 @@ void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; /* Definitions of arguments for argp functions. */ static const struct argp_option options[] = { - { NULL, 0, NULL, 0, N_("Output selection:") }, - { NULL, 'o', "FILE", 0, N_("Place stripped output into FILE") }, - { NULL, 'f', "FILE", 0, N_("Extract the removed sections into FILE") }, - - { NULL, 0, NULL, 0, N_("Output options:") }, - { "strip-debug", 'g', NULL, 0, N_("Remove all debugging symbols") }, + { NULL, 0, NULL, 0, N_("Output selection:"), 0 }, + { "output", 'o', "FILE", 0, N_("Place stripped output into FILE"), 0 }, + { NULL, 'f', "FILE", 0, N_("Extract the removed sections into FILE"), 0 }, + { NULL, 'F', "FILE", 0, N_("Embed name FILE instead of -f argument"), 0 }, + + { NULL, 0, NULL, 0, N_("Output options:"), 0 }, + { "strip-all", 's', NULL, OPTION_HIDDEN, NULL, 0 }, + { "strip-debug", 'g', NULL, 0, N_("Remove all debugging symbols"), 0 }, + { NULL, 'd', NULL, OPTION_ALIAS, NULL, 0 }, + { NULL, 'S', NULL, OPTION_ALIAS, NULL, 0 }, { "preserve-dates", 'p', NULL, 0, - N_("Copy modified/access timestamps to the output") }, + N_("Copy modified/access timestamps to the output"), 0 }, { "remove-comment", OPT_REMOVE_COMMENT, NULL, 0, - N_("Remove .comment section") }, + N_("Remove .comment section"), 0 }, + { "remove-section", 'R', "SECTION", OPTION_HIDDEN, NULL, 0 }, { "permissive", OPT_PERMISSIVE, NULL, 0, - N_("Relax a few rules to handle slightly broken ELF files") }, - { NULL, 0, NULL, 0, NULL } + N_("Relax a few rules to handle slightly broken ELF files"), 0 }, + { NULL, 0, NULL, 0, NULL, 0 } }; /* Short description of program. */ @@ -78,13 +98,10 @@ static const char args_doc[] = N_("[FILE...]"); /* Prototype for option handler. */ static error_t parse_opt (int key, char *arg, struct argp_state *state); -/* Function to print some extra text in the help message. */ -static char *more_help (int key, const char *text, void *input); - /* Data structure to communicate with argp functions. */ static struct argp argp = { - options, parse_opt, args_doc, doc, NULL, more_help + options, parse_opt, args_doc, doc, NULL, NULL, NULL }; @@ -101,7 +118,7 @@ static int handle_ar (int fd, Elf *elf, const char *prefix, const char *fname, #define INTERNAL_ERROR(fname) \ error (EXIT_FAILURE, 0, gettext ("%s: INTERNAL ERROR %d (%s-%s): %s"), \ - fname, __LINE__, VERSION, __DATE__, elf_errmsg (-1)) + fname, __LINE__, PACKAGE_VERSION, __DATE__, elf_errmsg (-1)) /* Name of the output file. */ @@ -110,6 +127,9 @@ static const char *output_fname; /* Name of the debug output file. */ static const char *debug_fname; +/* Name to pretend the debug output file has. */ +static const char *debug_fname_embed; + /* If true output files shall have same date as the input file. */ static bool preserve_dates; @@ -141,13 +161,14 @@ main (int argc, char *argv[]) setlocale (LC_ALL, ""); /* Make sure the message catalog can be found. */ - bindtextdomain (PACKAGE, LOCALEDIR); + bindtextdomain (PACKAGE_TARNAME, LOCALEDIR); /* Initialize the message catalog. */ - textdomain (PACKAGE); + textdomain (PACKAGE_TARNAME); /* Parse and process arguments. */ - argp_parse (&argp, argc, argv, 0, &remaining, NULL); + if (argp_parse (&argp, argc, argv, 0, &remaining, NULL) != 0) + return EXIT_FAILURE; /* Tell the library which version we are expecting. */ elf_version (EV_CURRENT); @@ -157,7 +178,7 @@ main (int argc, char *argv[]) result = process_file ("a.out"); else { - /* If we have seen the `-o' or '-f' option there must be exactly one + /* If we have seen the '-o' or '-f' option there must be exactly one input file. */ if ((output_fname != NULL || debug_fname != NULL) && remaining + 1 < argc) @@ -176,14 +197,14 @@ Only one input file allowed together with '-o' and '-f'")); /* Print the version information. */ static void -print_version (FILE *stream, struct argp_state *state) +print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) { - fprintf (stream, "strip (%s) %s\n", PACKAGE_NAME, VERSION); + fprintf (stream, "strip (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION); fprintf (stream, gettext ("\ Copyright (C) %s Red Hat, Inc.\n\ This is free software; see the source for copying conditions. There is NO\n\ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ -"), "2004"); +"), "2008"); fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); } @@ -195,10 +216,29 @@ parse_opt (int key, char *arg, struct argp_state *state) switch (key) { case 'f': + if (debug_fname != NULL) + { + error (0, 0, gettext ("-f option specified twice")); + return EINVAL; + } debug_fname = arg; break; + case 'F': + if (debug_fname_embed != NULL) + { + error (0, 0, gettext ("-F option specified twice")); + return EINVAL; + } + debug_fname_embed = arg; + break; + case 'o': + if (output_fname != NULL) + { + error (0, 0, gettext ("-o option specified twice")); + return EINVAL; + } output_fname = arg; break; @@ -210,7 +250,20 @@ parse_opt (int key, char *arg, struct argp_state *state) remove_comment = true; break; + case 'R': + if (!strcmp (arg, ".comment")) + remove_comment = true; + else + { + argp_error (state, + gettext ("-R option supports only .comment section")); + return EINVAL; + } + break; + case 'g': + case 'd': + case 'S': remove_debug = true; break; @@ -218,6 +271,9 @@ parse_opt (int key, char *arg, struct argp_state *state) permissive = true; break; + case 's': /* Ignored for compatibility. */ + break; + default: return ARGP_ERR_UNKNOWN; } @@ -225,27 +281,6 @@ parse_opt (int key, char *arg, struct argp_state *state) } -static char * -more_help (int key, const char *text, void *input) -{ - char *buf; - - switch (key) - { - case ARGP_KEY_HELP_EXTRA: - /* We print some extra information. */ - if (asprintf (&buf, gettext ("Please report bugs to %s.\n"), - PACKAGE_BUGREPORT) < 0) - buf = NULL; - return buf; - - default: - break; - } - return (char *) text; -} - - static int process_file (const char *fname) { @@ -259,7 +294,7 @@ process_file (const char *fname) { if (stat64 (fname, &pre_st) != 0) { - error (0, errno, gettext ("cannot stat input file \"%s\""), fname); + error (0, errno, gettext ("cannot stat input file '%s'"), fname); return 1; } @@ -270,10 +305,10 @@ process_file (const char *fname) } /* Open the file. */ - int fd = open (fname, O_RDWR); + int fd = open (fname, output_fname == NULL ? O_RDWR : O_RDONLY); if (fd == -1) { - error (0, errno, gettext ("while opening \"%s\""), fname); + error (0, errno, gettext ("while opening '%s'"), fname); return 1; } @@ -283,7 +318,7 @@ process_file (const char *fname) struct stat64 st; if (fstat64 (fd, &st) != 0) { - error (0, errno, gettext ("cannot stat input file \"%s\""), fname); + error (0, errno, gettext ("cannot stat input file '%s'"), fname); return 1; } /* Paranoid mode on. */ @@ -296,7 +331,8 @@ process_file (const char *fname) } /* Now get the ELF descriptor. */ - Elf *elf = elf_begin (fd, ELF_C_RDWR, NULL); + Elf *elf = elf_begin (fd, output_fname == NULL ? ELF_C_RDWR : ELF_C_READ, + NULL); int result; switch (elf_kind (elf)) { @@ -308,9 +344,9 @@ process_file (const char *fname) case ELF_K_AR: /* It is not possible to strip the content of an archive direct the output to a specific file. */ - if (unlikely (output_fname != NULL)) + if (unlikely (output_fname != NULL || debug_fname != NULL)) { - error (0, 0, gettext ("%s: cannot use -o when stripping archive"), + error (0, 0, gettext ("%s: cannot use -o or -f when stripping archive"), fname); result = 1; } @@ -336,27 +372,6 @@ process_file (const char *fname) /* Maximum size of array allocated on stack. */ #define MAX_STACK_ALLOC (400 * 1024) - -static uint32_t -crc32_file (int fd, uint32_t *resp) -{ - unsigned char buffer[1024 * 8]; - uint32_t crc = 0; - ssize_t count; - - /* We have to rewind. */ - if (lseek (fd, 0, SEEK_SET) < 0) - return 1; - - while ((count = TEMP_FAILURE_RETRY (read (fd, buffer, sizeof (buffer)))) > 0) - crc = crc32 (crc, buffer, count); - - *resp = crc; - - return count != 0; -} - - static int handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, mode_t mode, struct timeval tvp[2]) @@ -365,19 +380,17 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, size_t fname_len = strlen (fname) + 1; char *fullname = alloca (prefix_len + 1 + fname_len); char *cp = fullname; - Elf *newelf; Elf *debugelf = NULL; char *tmp_debug_fname = NULL; int result = 0; - GElf_Ehdr ehdr_mem; - GElf_Ehdr *ehdr; + size_t shdridx = 0; size_t shstrndx; - size_t shnum; struct shdr_info { Elf_Scn *scn; GElf_Shdr shdr; Elf_Data *data; + Elf_Data *debug_data; const char *name; Elf32_Word idx; /* Index in new file. */ Elf32_Word old_sh_link; /* Original value of shdr.sh_link. */ @@ -398,7 +411,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, GElf_Ehdr debugehdr_mem; GElf_Ehdr *debugehdr; struct Ebl_Strtab *shst = NULL; - uint32_t debug_crc; + Elf_Data debuglink_crc_data; bool any_symtab_changes = false; Elf_Data *shstrtab_data = NULL; @@ -416,7 +429,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, fd = open (output_fname, O_RDWR | O_CREAT, mode); if (unlikely (fd == -1)) { - error (0, errno, gettext ("cannot open `%s'"), output_fname); + error (0, errno, gettext ("cannot open '%s'"), output_fname); return 1; } } @@ -451,14 +464,15 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, debug_fd = mkstemp (tmp_debug_fname); if (unlikely (debug_fd == -1)) { - error (0, errno, gettext ("cannot open `%s'"), debug_fname); + error (0, errno, gettext ("cannot open '%s'"), debug_fname); result = 1; goto fail; } } /* Get the information from the old file. */ - ehdr = gelf_getehdr (elf, &ehdr_mem); + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); if (ehdr == NULL) INTERNAL_ERROR (fname); @@ -470,6 +484,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, /* We now create a new ELF descriptor for the same file. We construct it almost exactly in the same way with some information dropped. */ + Elf *newelf; if (output_fname != NULL) newelf = elf_begin (fd, ELF_C_WRITE_MMAP, NULL); else @@ -479,7 +494,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, || (ehdr->e_type != ET_REL && unlikely (gelf_newphdr (newelf, ehdr->e_phnum) == 0))) { - error (0, 0, gettext ("cannot create new file `%s': %s"), + error (0, 0, gettext ("cannot create new file '%s': %s"), output_fname, elf_errmsg (-1)); goto fail; } @@ -489,9 +504,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, for (cnt = 0; cnt < ehdr->e_phnum; ++cnt) { GElf_Phdr phdr_mem; - GElf_Phdr *phdr; - - phdr = gelf_getphdr (elf, cnt, &phdr_mem); + GElf_Phdr *phdr = gelf_getphdr (elf, cnt, &phdr_mem); if (phdr == NULL || unlikely (gelf_update_phdr (newelf, cnt, phdr) == 0)) INTERNAL_ERROR (fname); @@ -505,7 +518,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, || (ehdr->e_type != ET_REL && unlikely (gelf_newphdr (debugelf, ehdr->e_phnum) == 0))) { - error (0, 0, gettext ("cannot create new file `%s': %s"), + error (0, 0, gettext ("cannot create new file '%s': %s"), debug_fname, elf_errmsg (-1)); goto fail_close; } @@ -515,9 +528,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, for (cnt = 0; cnt < ehdr->e_phnum; ++cnt) { GElf_Phdr phdr_mem; - GElf_Phdr *phdr; - - phdr = gelf_getphdr (elf, cnt, &phdr_mem); + GElf_Phdr *phdr = gelf_getphdr (elf, cnt, &phdr_mem); if (phdr == NULL || unlikely (gelf_update_phdr (debugelf, cnt, phdr) == 0)) INTERNAL_ERROR (fname); @@ -525,6 +536,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, } /* Number of sections. */ + size_t shnum; if (unlikely (elf_getshnum (elf, &shnum) < 0)) { error (0, 0, gettext ("cannot determine number of sections: %s"), @@ -566,7 +578,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, shdr_info[cnt].shdr.sh_name); if (shdr_info[cnt].name == NULL) { - error (0, 0, gettext ("illformed file `%s'"), fname); + error (0, 0, gettext ("illformed file '%s'"), fname); goto fail_close; } @@ -592,9 +604,6 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, } else if (unlikely (shdr_info[cnt].shdr.sh_type == SHT_GROUP)) { - Elf32_Word *grpref; - size_t inner; - /* Cross-reference the sections contained in the section group. */ shdr_info[cnt].data = elf_getdata (shdr_info[cnt].scn, NULL); @@ -602,7 +611,8 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, INTERNAL_ERROR (fname); /* XXX Fix for unaligned access. */ - grpref = (Elf32_Word *) shdr_info[cnt].data->d_buf; + Elf32_Word *grpref = (Elf32_Word *) shdr_info[cnt].data->d_buf; + size_t inner; for (inner = 1; inner < shdr_info[cnt].data->d_size / sizeof (Elf32_Word); ++inner) @@ -651,8 +661,9 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, */ for (cnt = 1; cnt < shnum; ++cnt) /* Check whether the section can be removed. */ - if (SECTION_STRIP_P (ebl, elf, ehdr, &shdr_info[cnt].shdr, - shdr_info[cnt].name, remove_comment, remove_debug)) + if (ebl_section_strip_p (ebl, ehdr, &shdr_info[cnt].shdr, + shdr_info[cnt].name, remove_comment, + remove_debug)) { /* For now assume this section will be removed. */ shdr_info[cnt].idx = 0; @@ -660,16 +671,14 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, idx = shdr_info[cnt].group_idx; while (idx != 0) { - /* If the references section group is a normal section - group and has one element remaining, or if it is an - empty COMDAT section group it is removed. */ - bool is_comdat; - /* The section group data is already loaded. */ assert (shdr_info[idx].data != NULL); - is_comdat = (((Elf32_Word *) shdr_info[idx].data->d_buf)[0] - & GRP_COMDAT) != 0; + /* If the references section group is a normal section + group and has one element remaining, or if it is an + empty COMDAT section group it is removed. */ + bool is_comdat = (((Elf32_Word *) shdr_info[idx].data->d_buf)[0] + & GRP_COMDAT) != 0; --shdr_info[idx].group_cnt; if ((!is_comdat && shdr_info[idx].group_cnt == 1) @@ -712,13 +721,11 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, /* The content of symbol tables we don't remove must not reference any section which we do remove. Otherwise we cannot remove the section. */ - if (shdr_info[cnt].shdr.sh_type == SHT_DYNSYM - || shdr_info[cnt].shdr.sh_type == SHT_SYMTAB) + if (debug_fname != NULL + && shdr_info[cnt].debug_data == NULL + && (shdr_info[cnt].shdr.sh_type == SHT_DYNSYM + || shdr_info[cnt].shdr.sh_type == SHT_SYMTAB)) { - Elf_Data *symdata; - Elf_Data *xndxdata; - size_t elsize; - /* Make sure the data is loaded. */ if (shdr_info[cnt].data == NULL) { @@ -727,7 +734,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, if (shdr_info[cnt].data == NULL) INTERNAL_ERROR (fname); } - symdata = shdr_info[cnt].data; + Elf_Data *symdata = shdr_info[cnt].data; /* If there is an extended section index table load it as well. */ @@ -742,11 +749,13 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, if (shdr_info[shdr_info[cnt].symtab_idx].data == NULL) INTERNAL_ERROR (fname); } - xndxdata = shdr_info[shdr_info[cnt].symtab_idx].data; + Elf_Data *xndxdata + = shdr_info[shdr_info[cnt].symtab_idx].data; /* Go through all symbols and make sure the section they reference is not removed. */ - elsize = gelf_fsize (elf, ELF_T_SYM, 1, ehdr->e_version); + size_t elsize = gelf_fsize (elf, ELF_T_SYM, 1, + ehdr->e_version); for (size_t inner = 0; inner < shdr_info[cnt].data->d_size / elsize; @@ -754,15 +763,13 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, { GElf_Sym sym_mem; Elf32_Word xndx; - GElf_Sym *sym; - size_t scnidx; - - sym = gelf_getsymshndx (symdata, xndxdata, inner, - &sym_mem, &xndx); + GElf_Sym *sym = gelf_getsymshndx (symdata, xndxdata, + inner, &sym_mem, + &xndx); if (sym == NULL) INTERNAL_ERROR (fname); - scnidx = sym->st_shndx; + size_t scnidx = sym->st_shndx; if (scnidx == SHN_UNDEF || scnidx >= shnum || (scnidx >= SHN_LORESERVE && scnidx <= SHN_HIRESERVE @@ -775,24 +782,23 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, scnidx = xndx; if (shdr_info[scnidx].idx == 0) - { - /* Mark this section as used. */ - shdr_info[scnidx].idx = 1; - changes |= scnidx < cnt; - } + /* This symbol table has a real symbol in + a discarded section. So preserve the + original table in the debug file. */ + shdr_info[cnt].debug_data = symdata; } } /* Cross referencing happens: - for the cases the ELF specification says. That are - + SHT_DYNAMIC in sh_link to string table - + SHT_HASH in sh_link to symbol table - + SHT_REL and SHT_RELA in sh_link to symbol table - + SHT_SYMTAB and SHT_DYNSYM in sh_link to string table - + SHT_GROUP in sh_link to symbol table - + SHT_SYMTAB_SHNDX in sh_link to symbol table - Other (OS or architecture-specific) sections might as - well use this field so we process it unconditionally. + + SHT_DYNAMIC in sh_link to string table + + SHT_HASH in sh_link to symbol table + + SHT_REL and SHT_RELA in sh_link to symbol table + + SHT_SYMTAB and SHT_DYNSYM in sh_link to string table + + SHT_GROUP in sh_link to symbol table + + SHT_SYMTAB_SHNDX in sh_link to symbol table + Other (OS or architecture-specific) sections might as + well use this field so we process it unconditionally. - references inside section groups - specially marked references in sh_info if the SHF_INFO_LINK flag is set @@ -815,34 +821,63 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, /* Mark the section as investigated. */ shdr_info[cnt].idx = 2; } + + if (debug_fname != NULL + && (shdr_info[cnt].idx == 0 || shdr_info[cnt].debug_data != NULL)) + { + /* This section is being preserved in the debug file. + Sections it refers to must be preserved there too. + + In this pass we mark sections to be preserved in both + files by setting the .debug_data pointer to the original + file's .data pointer. Below, we'll copy the section + contents. */ + + inline void check_preserved (size_t i) + { + if (i != 0 && shdr_info[i].idx != 0) + { + if (shdr_info[i].data == NULL) + shdr_info[i].data = elf_getdata (shdr_info[i].scn, NULL); + if (shdr_info[i].data == NULL) + INTERNAL_ERROR (fname); + + shdr_info[i].debug_data = shdr_info[i].data; + changes |= i < cnt; + } + } + + check_preserved (shdr_info[cnt].shdr.sh_link); + if (SH_INFO_LINK_P (&shdr_info[cnt].shdr)) + check_preserved (shdr_info[cnt].shdr.sh_info); + } } } while (changes); - /* Write out a copy of all the sections to the debug output file. - The ones that are not removed in the stripped file are SHT_NOBITS */ + /* Copy the removed sections to the debug output file. + The ones that are not removed in the stripped file are SHT_NOBITS. */ if (debug_fname != NULL) { for (cnt = 1; cnt < shnum; ++cnt) { - Elf_Data *debugdata; - GElf_Shdr debugshdr; - int discard_section; - scn = elf_newscn (debugelf); if (scn == NULL) error (EXIT_FAILURE, 0, gettext ("while generating output file: %s"), elf_errmsg (-1)); - discard_section = shdr_info[cnt].idx > 0 && cnt != ehdr->e_shstrndx; + bool discard_section = (shdr_info[cnt].idx > 0 + && shdr_info[cnt].debug_data == NULL + && shdr_info[cnt].shdr.sh_type != SHT_NOTE + && cnt != ehdr->e_shstrndx); /* Set the section header in the new file. */ - debugshdr = shdr_info[cnt].shdr; + GElf_Shdr debugshdr = shdr_info[cnt].shdr; if (discard_section) debugshdr.sh_type = SHT_NOBITS; - if (unlikely (gelf_update_shdr (scn, &debugshdr)) == 0) + if (unlikely (gelf_update_shdr (scn, &debugshdr) == 0)) /* There cannot be any overflows. */ INTERNAL_ERROR (fname); @@ -855,14 +890,22 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, } /* Set the data. This is done by copying from the old file. */ - debugdata = elf_newdata (scn); + Elf_Data *debugdata = elf_newdata (scn); if (debugdata == NULL) INTERNAL_ERROR (fname); - /* Copy the structure. */ + /* Copy the structure. This data may be modified in place + before we write out the file. */ *debugdata = *shdr_info[cnt].data; if (discard_section) debugdata->d_buf = NULL; + else if (shdr_info[cnt].debug_data != NULL) + { + /* Copy the original data before it gets modified. */ + shdr_info[cnt].debug_data = debugdata; + debugdata->d_buf = memcpy (xmalloc (debugdata->d_size), + debugdata->d_buf, debugdata->d_size); + } } /* Finish the ELF header. Fill in the fields not handled by @@ -879,46 +922,13 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, debugehdr->e_flags = ehdr->e_flags; debugehdr->e_shstrndx = ehdr->e_shstrndx; - if (unlikely (gelf_update_ehdr (debugelf, debugehdr)) == 0) + if (unlikely (gelf_update_ehdr (debugelf, debugehdr) == 0)) { error (0, 0, gettext ("%s: error while creating ELF header: %s"), debug_fname, elf_errmsg (-1)); result = 1; goto fail_close; } - - /* Finally write the file. */ - if (unlikely (elf_update (debugelf, ELF_C_WRITE)) == -1) - { - error (0, 0, gettext ("while writing `%s': %s"), - debug_fname, elf_errmsg (-1)); - result = 1; - goto fail_close; - } - - /* Create the real output file. First rename, then change the - mode. */ - if (rename (tmp_debug_fname, debug_fname) != 0 - || fchmod (debug_fd, mode) != 0) - { - error (0, errno, gettext ("while creating '%s'"), debug_fname); - result = 1; - goto fail_close; - } - - /* The temporary file does not exist anymore. */ - tmp_debug_fname = NULL; - - /* Compute the checksum which we will add to the executable. */ - if (crc32_file (debug_fd, &debug_crc) != 0) - { - error (0, errno, - gettext ("while computing checksum for debug information")); - unlink (debug_fname); - result = 1; - goto fail_close; - } - } /* Mark the section header string table as unused, we will create @@ -928,7 +938,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, /* We need a string table for the section headers. */ shst = ebl_strtabinit (true); if (shst == NULL) - error (EXIT_FAILURE, errno, gettext ("while preparing output for `%s'"), + error (EXIT_FAILURE, errno, gettext ("while preparing output for '%s'"), output_fname ?: fname); /* Assign new section numbers. */ @@ -958,9 +968,6 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, /* Create the reference to the file with the debug info. */ if (debug_fname != NULL) { - char *debug_basename; - off_t crc_offset; - /* Add the section header string table section name. */ shdr_info[cnt].se = ebl_strtabadd (shst, ".gnu_debuglink", 15); shdr_info[cnt].idx = idx++; @@ -991,8 +998,8 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, error (EXIT_FAILURE, 0, gettext ("cannot allocate section data: %s"), elf_errmsg (-1)); - debug_basename = basename (debug_fname); - crc_offset = strlen (debug_basename) + 1; + char *debug_basename = basename (debug_fname_embed ?: debug_fname); + off_t crc_offset = strlen (debug_basename) + 1; /* Align to 4 byte boundary */ crc_offset = ((crc_offset - 1) & ~3) + 4; @@ -1002,21 +1009,20 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, shdr_info[cnt].data->d_buf = xcalloc (1, shdr_info[cnt].data->d_size); strcpy (shdr_info[cnt].data->d_buf, debug_basename); - /* Store the crc value in the correct byteorder */ - if ((__BYTE_ORDER == __LITTLE_ENDIAN - && ehdr->e_ident[EI_DATA] == ELFDATA2MSB) - || (__BYTE_ORDER == __BIG_ENDIAN - && ehdr->e_ident[EI_DATA] == ELFDATA2LSB)) - debug_crc = bswap_32 (debug_crc); - memcpy ((char *)shdr_info[cnt].data->d_buf + crc_offset, - (char *) &debug_crc, 4); + + /* Cache this Elf_Data describing the CRC32 word in the section. + We'll fill this in when we have written the debug file. */ + debuglink_crc_data = *shdr_info[cnt].data; + debuglink_crc_data.d_buf = ((char *) debuglink_crc_data.d_buf + + crc_offset); + debuglink_crc_data.d_size = 4; /* One more section done. */ ++cnt; } /* Index of the section header table in the shdr_info array. */ - size_t shdridx = cnt; + shdridx = cnt; /* Add the section header string table section name. */ shdr_info[cnt].se = ebl_strtabadd (shst, ".shstrtab", 10); @@ -1112,7 +1118,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, /* We know the size. */ shdr_info[cnt].shdr.sh_size = shdr_info[cnt].data->d_size; - /* We have to adjust symtol tables. The st_shndx member might + /* We have to adjust symbol tables. The st_shndx member might have to be updated. */ if (shdr_info[cnt].shdr.sh_type == SHT_DYNSYM || shdr_info[cnt].shdr.sh_type == SHT_SYMTAB) @@ -1240,7 +1246,8 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, shdr_info[cnt].shdr.sh_info = destidx - 1; } } - else + else if (debug_fname == NULL + || shdr_info[cnt].debug_data == NULL) /* This is a section symbol for a section which has been removed. */ assert (GELF_ST_TYPE (sym->st_info) == STT_SECTION); @@ -1282,302 +1289,339 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, /* Adjust symbol references if symbol tables changed. */ if (any_symtab_changes) - { - /* Find all relocation sections which use this - symbol table. */ - for (cnt = 1; cnt <= shdridx; ++cnt) + /* Find all relocation sections which use this symbol table. */ + for (cnt = 1; cnt <= shdridx; ++cnt) + { + /* Update section headers when the data size has changed. + We also update the SHT_NOBITS section in the debug + file so that the section headers match in sh_size. */ + inline void update_section_size (const Elf_Data *newdata) { - if (shdr_info[cnt].idx == 0) - /* Ignore sections which are discarded. */ - continue; + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + shdr->sh_size = newdata->d_size; + (void) gelf_update_shdr (scn, shdr); + if (debugelf != NULL) + { + /* libelf will use d_size to set sh_size. */ + Elf_Data *debugdata = elf_getdata (elf_getscn (debugelf, + cnt), NULL); + debugdata->d_size = newdata->d_size; + } + } - if (shdr_info[cnt].shdr.sh_type == SHT_REL - || shdr_info[cnt].shdr.sh_type == SHT_RELA) + if (shdr_info[cnt].idx == 0 && debug_fname == NULL) + /* Ignore sections which are discarded. When we are saving a + relocation section in a separate debug file, we must fix up + the symbol table references. */ + continue; + + const Elf32_Word symtabidx = shdr_info[cnt].old_sh_link; + const Elf32_Word *const newsymidx = shdr_info[symtabidx].newsymidx; + switch (shdr_info[cnt].shdr.sh_type) + { + inline bool no_symtab_updates (void) { /* If the symbol table hasn't changed, do not do anything. */ - if (shdr_info[shdr_info[cnt].old_sh_link].newsymidx == NULL) - continue; + if (shdr_info[symtabidx].newsymidx == NULL) + return true; - Elf32_Word *newsymidx - = shdr_info[shdr_info[cnt].old_sh_link].newsymidx; - Elf_Data *d = elf_getdata (elf_getscn (newelf, + /* If the symbol table is not discarded, but additionally + duplicated in the separate debug file and this section + is discarded, don't adjust anything. */ + return (shdr_info[cnt].idx == 0 + && shdr_info[symtabidx].debug_data != NULL); + } + + case SHT_REL: + case SHT_RELA: + if (no_symtab_updates ()) + break; + + Elf_Data *d = elf_getdata (shdr_info[cnt].idx == 0 + ? elf_getscn (debugelf, cnt) + : elf_getscn (newelf, shdr_info[cnt].idx), - NULL); - assert (d != NULL); - size_t nrels = (shdr_info[cnt].shdr.sh_size - / shdr_info[cnt].shdr.sh_entsize); + NULL); + assert (d != NULL); + size_t nrels = (shdr_info[cnt].shdr.sh_size + / shdr_info[cnt].shdr.sh_entsize); - if (shdr_info[cnt].shdr.sh_type == SHT_REL) - for (size_t relidx = 0; relidx < nrels; ++relidx) - { - GElf_Rel rel_mem; - if (gelf_getrel (d, relidx, &rel_mem) == NULL) - INTERNAL_ERROR (fname); + if (shdr_info[cnt].shdr.sh_type == SHT_REL) + for (size_t relidx = 0; relidx < nrels; ++relidx) + { + GElf_Rel rel_mem; + if (gelf_getrel (d, relidx, &rel_mem) == NULL) + INTERNAL_ERROR (fname); - size_t symidx = GELF_R_SYM (rel_mem.r_info); - if (newsymidx[symidx] != symidx) - { - rel_mem.r_info - = GELF_R_INFO (newsymidx[symidx], - GELF_R_TYPE (rel_mem.r_info)); + size_t symidx = GELF_R_SYM (rel_mem.r_info); + if (newsymidx[symidx] != symidx) + { + rel_mem.r_info + = GELF_R_INFO (newsymidx[symidx], + GELF_R_TYPE (rel_mem.r_info)); - if (gelf_update_rel (d, relidx, &rel_mem) == 0) - INTERNAL_ERROR (fname); - } - } - else - for (size_t relidx = 0; relidx < nrels; ++relidx) - { - GElf_Rela rel_mem; - if (gelf_getrela (d, relidx, &rel_mem) == NULL) - INTERNAL_ERROR (fname); + if (gelf_update_rel (d, relidx, &rel_mem) == 0) + INTERNAL_ERROR (fname); + } + } + else + for (size_t relidx = 0; relidx < nrels; ++relidx) + { + GElf_Rela rel_mem; + if (gelf_getrela (d, relidx, &rel_mem) == NULL) + INTERNAL_ERROR (fname); - size_t symidx = GELF_R_SYM (rel_mem.r_info); - if (newsymidx[symidx] != symidx) - { - rel_mem.r_info - = GELF_R_INFO (newsymidx[symidx], - GELF_R_TYPE (rel_mem.r_info)); + size_t symidx = GELF_R_SYM (rel_mem.r_info); + if (newsymidx[symidx] != symidx) + { + rel_mem.r_info + = GELF_R_INFO (newsymidx[symidx], + GELF_R_TYPE (rel_mem.r_info)); - if (gelf_update_rela (d, relidx, &rel_mem) == 0) - INTERNAL_ERROR (fname); - } - } - } - else if (shdr_info[cnt].shdr.sh_type == SHT_HASH) - { - /* We have to recompute the hash table. */ - Elf32_Word symtabidx = shdr_info[cnt].old_sh_link; + if (gelf_update_rela (d, relidx, &rel_mem) == 0) + INTERNAL_ERROR (fname); + } + } + break; - /* We do not have to do anything if the symbol table was - not changed. */ - if (shdr_info[symtabidx].newsymidx == NULL) - continue; + case SHT_HASH: + if (no_symtab_updates ()) + break; - /* The symbol version section in the new file. */ - scn = elf_getscn (newelf, shdr_info[cnt].idx); + /* We have to recompute the hash table. */ - /* The symbol table data. */ - Elf_Data *symd = elf_getdata (elf_getscn (newelf, - shdr_info[symtabidx].idx), - NULL); - assert (symd != NULL); + assert (shdr_info[cnt].idx > 0); - /* The hash table data. */ - Elf_Data *hashd = elf_getdata (scn, NULL); - assert (hashd != NULL); + /* The hash section in the new file. */ + scn = elf_getscn (newelf, shdr_info[cnt].idx); - if (shdr_info[cnt].shdr.sh_entsize == sizeof (Elf32_Word)) - { - /* Sane arches first. */ - Elf32_Word *bucket = (Elf32_Word *) hashd->d_buf; + /* The symbol table data. */ + Elf_Data *symd = elf_getdata (elf_getscn (newelf, + shdr_info[symtabidx].idx), + NULL); + assert (symd != NULL); - size_t strshndx = shdr_info[symtabidx].old_sh_link; - size_t elsize = gelf_fsize (elf, ELF_T_SYM, 1, - ehdr->e_version); + /* The hash table data. */ + Elf_Data *hashd = elf_getdata (scn, NULL); + assert (hashd != NULL); - /* Convert to the correct byte order. */ - if (gelf_xlatetom (newelf, hashd, hashd, - BYTE_ORDER == LITTLE_ENDIAN - ? ELFDATA2LSB : ELFDATA2MSB) == NULL) - INTERNAL_ERROR (fname); + if (shdr_info[cnt].shdr.sh_entsize == sizeof (Elf32_Word)) + { + /* Sane arches first. */ + Elf32_Word *bucket = (Elf32_Word *) hashd->d_buf; - /* Adjust the nchain value. The symbol table size - changed. We keep the same size for the bucket array. */ - bucket[1] = symd->d_size / elsize; - Elf32_Word nbucket = bucket[0]; - bucket += 2; - Elf32_Word *chain = bucket + nbucket; - - /* New size of the section. */ - GElf_Shdr shdr_mem; - GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); - shdr->sh_size = hashd->d_size - = (2 + symd->d_size / elsize + nbucket) - * sizeof (Elf32_Word); - (void) gelf_update_shdr (scn, shdr); - - /* Clear the arrays. */ - memset (bucket, '\0', - (symd->d_size / elsize + nbucket) - * sizeof (Elf32_Word)); - - for (size_t inner = shdr_info[symtabidx].shdr.sh_info; - inner < symd->d_size / elsize; ++inner) - { - GElf_Sym sym_mem; - GElf_Sym *sym = gelf_getsym (symd, inner, &sym_mem); - assert (sym != NULL); + size_t strshndx = shdr_info[symtabidx].old_sh_link; + size_t elsize = gelf_fsize (elf, ELF_T_SYM, 1, + ehdr->e_version); - const char *name = elf_strptr (elf, strshndx, - sym->st_name); - assert (name != NULL); - size_t hidx = elf_hash (name) % nbucket; + /* Adjust the nchain value. The symbol table size + changed. We keep the same size for the bucket array. */ + bucket[1] = symd->d_size / elsize; + Elf32_Word nbucket = bucket[0]; + bucket += 2; + Elf32_Word *chain = bucket + nbucket; + + /* New size of the section. */ + hashd->d_size = ((2 + symd->d_size / elsize + nbucket) + * sizeof (Elf32_Word)); + update_section_size (hashd); + + /* Clear the arrays. */ + memset (bucket, '\0', + (symd->d_size / elsize + nbucket) + * sizeof (Elf32_Word)); + + for (size_t inner = shdr_info[symtabidx].shdr.sh_info; + inner < symd->d_size / elsize; ++inner) + { + GElf_Sym sym_mem; + GElf_Sym *sym = gelf_getsym (symd, inner, &sym_mem); + assert (sym != NULL); - if (bucket[hidx] == 0) - bucket[hidx] = inner; - else - { - hidx = bucket[hidx]; + const char *name = elf_strptr (elf, strshndx, + sym->st_name); + assert (name != NULL); + size_t hidx = elf_hash (name) % nbucket; - while (chain[hidx] != 0) - hidx = chain[hidx]; + if (bucket[hidx] == 0) + bucket[hidx] = inner; + else + { + hidx = bucket[hidx]; - chain[hidx] = inner; - } - } - } - else - { - /* Alpha and S390 64-bit use 64-bit SHT_HASH entries. */ - assert (shdr_info[cnt].shdr.sh_entsize - == sizeof (Elf64_Xword)); + while (chain[hidx] != 0) + hidx = chain[hidx]; - Elf64_Xword *bucket = (Elf64_Xword *) hashd->d_buf; + chain[hidx] = inner; + } + } + } + else + { + /* Alpha and S390 64-bit use 64-bit SHT_HASH entries. */ + assert (shdr_info[cnt].shdr.sh_entsize + == sizeof (Elf64_Xword)); - size_t strshndx = shdr_info[symtabidx].old_sh_link; - size_t elsize = gelf_fsize (elf, ELF_T_SYM, 1, - ehdr->e_version); + Elf64_Xword *bucket = (Elf64_Xword *) hashd->d_buf; - /* Convert to the correct byte order. */ - if (gelf_xlatetom (newelf, hashd, hashd, - BYTE_ORDER == LITTLE_ENDIAN - ? ELFDATA2LSB : ELFDATA2MSB) == NULL) - INTERNAL_ERROR (fname); + size_t strshndx = shdr_info[symtabidx].old_sh_link; + size_t elsize = gelf_fsize (elf, ELF_T_SYM, 1, + ehdr->e_version); - /* Adjust the nchain value. The symbol table size - changed. We keep the same size for the bucket array. */ - bucket[1] = symd->d_size / elsize; - Elf64_Xword nbucket = bucket[0]; - bucket += 2; - Elf64_Xword *chain = bucket + nbucket; - - /* New size of the section. */ - GElf_Shdr shdr_mem; - GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); - shdr->sh_size = hashd->d_size - = (2 + symd->d_size / elsize + nbucket) - * sizeof (Elf64_Xword); - (void) gelf_update_shdr (scn, shdr); - - /* Clear the arrays. */ - memset (bucket, '\0', - (symd->d_size / elsize + nbucket) - * sizeof (Elf64_Xword)); - - for (size_t inner = shdr_info[symtabidx].shdr.sh_info; - inner < symd->d_size / elsize; ++inner) - { - GElf_Sym sym_mem; - GElf_Sym *sym = gelf_getsym (symd, inner, &sym_mem); - assert (sym != NULL); + /* Adjust the nchain value. The symbol table size + changed. We keep the same size for the bucket array. */ + bucket[1] = symd->d_size / elsize; + Elf64_Xword nbucket = bucket[0]; + bucket += 2; + Elf64_Xword *chain = bucket + nbucket; + + /* New size of the section. */ + hashd->d_size = ((2 + symd->d_size / elsize + nbucket) + * sizeof (Elf64_Xword)); + update_section_size (hashd); + + /* Clear the arrays. */ + memset (bucket, '\0', + (symd->d_size / elsize + nbucket) + * sizeof (Elf64_Xword)); + + for (size_t inner = shdr_info[symtabidx].shdr.sh_info; + inner < symd->d_size / elsize; ++inner) + { + GElf_Sym sym_mem; + GElf_Sym *sym = gelf_getsym (symd, inner, &sym_mem); + assert (sym != NULL); - const char *name = elf_strptr (elf, strshndx, - sym->st_name); - assert (name != NULL); - size_t hidx = elf_hash (name) % nbucket; + const char *name = elf_strptr (elf, strshndx, + sym->st_name); + assert (name != NULL); + size_t hidx = elf_hash (name) % nbucket; - if (bucket[hidx] == 0) - bucket[hidx] = inner; - else - { - hidx = bucket[hidx]; + if (bucket[hidx] == 0) + bucket[hidx] = inner; + else + { + hidx = bucket[hidx]; - while (chain[hidx] != 0) - hidx = chain[hidx]; + while (chain[hidx] != 0) + hidx = chain[hidx]; - chain[hidx] = inner; - } - } - } + chain[hidx] = inner; + } + } + } + break; - /* Convert back to the file byte order. */ - if (gelf_xlatetof (newelf, hashd, hashd, - BYTE_ORDER == LITTLE_ENDIAN - ? ELFDATA2LSB : ELFDATA2MSB) == NULL) - INTERNAL_ERROR (fname); - } - else if (shdr_info[cnt].shdr.sh_type == SHT_GNU_versym) - { - /* If the symbol table changed we have to adjust the - entries. */ - Elf32_Word symtabidx = shdr_info[cnt].old_sh_link; + case SHT_GNU_versym: + /* If the symbol table changed we have to adjust the entries. */ + if (no_symtab_updates ()) + break; - /* We do not have to do anything if the symbol table was - not changed. */ - if (shdr_info[symtabidx].newsymidx == NULL) - continue; + assert (shdr_info[cnt].idx > 0); + + /* The symbol version section in the new file. */ + scn = elf_getscn (newelf, shdr_info[cnt].idx); + + /* The symbol table data. */ + symd = elf_getdata (elf_getscn (newelf, shdr_info[symtabidx].idx), + NULL); + assert (symd != NULL); + + /* The version symbol data. */ + Elf_Data *verd = elf_getdata (scn, NULL); + assert (verd != NULL); + + /* The symbol version array. */ + GElf_Half *verstab = (GElf_Half *) verd->d_buf; + + /* Walk through the list and */ + size_t elsize = gelf_fsize (elf, verd->d_type, 1, + ehdr->e_version); + for (size_t inner = 1; inner < verd->d_size / elsize; ++inner) + if (newsymidx[inner] != 0) + /* Overwriting the same array works since the + reordering can only move entries to lower indices + in the array. */ + verstab[newsymidx[inner]] = verstab[inner]; + + /* New size of the section. */ + verd->d_size = gelf_fsize (newelf, verd->d_type, + symd->d_size + / gelf_fsize (elf, symd->d_type, 1, + ehdr->e_version), + ehdr->e_version); + update_section_size (verd); + break; + + case SHT_GROUP: + if (no_symtab_updates ()) + break; - /* The symbol version section in the new file. */ - scn = elf_getscn (newelf, shdr_info[cnt].idx); + /* Yes, the symbol table changed. + Update the section header of the section group. */ + scn = elf_getscn (newelf, shdr_info[cnt].idx); + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + assert (shdr != NULL); - /* The symbol table data. */ - Elf_Data *symd = elf_getdata (elf_getscn (newelf, - shdr_info[symtabidx].idx), - NULL); - assert (symd != NULL); + shdr->sh_info = newsymidx[shdr->sh_info]; - /* The version symbol data. */ - Elf_Data *verd = elf_getdata (scn, NULL); - assert (verd != NULL); + (void) gelf_update_shdr (scn, shdr); + break; + } + } - /* Convert to the correct byte order. */ - if (gelf_xlatetom (newelf, verd, verd, - BYTE_ORDER == LITTLE_ENDIAN - ? ELFDATA2LSB : ELFDATA2MSB) == NULL) - INTERNAL_ERROR (fname); + /* Now that we have done all adjustments to the data, + we can actually write out the debug file. */ + if (debug_fname != NULL) + { + uint32_t debug_crc; + Elf_Data debug_crc_data = + { + .d_type = ELF_T_WORD, + .d_buf = &debug_crc, + .d_size = sizeof (debug_crc), + .d_version = EV_CURRENT + }; - /* The symbol version array. */ - GElf_Half *verstab = (GElf_Half *) verd->d_buf; - - /* New indices of the symbols. */ - Elf32_Word *newsymidx = shdr_info[symtabidx].newsymidx; - - /* Walk through the list and */ - size_t elsize = gelf_fsize (elf, verd->d_type, 1, - ehdr->e_version); - for (size_t inner = 1; inner < verd->d_size / elsize; ++inner) - if (newsymidx[inner] != 0) - /* Overwriting the same array works since the - reordering can only move entries to lower indices - in the array. */ - verstab[newsymidx[inner]] = verstab[inner]; - - /* New size of the section. */ - GElf_Shdr shdr_mem; - GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); - shdr->sh_size = verd->d_size - = gelf_fsize (newelf, verd->d_type, - symd->d_size / gelf_fsize (elf, symd->d_type, 1, - ehdr->e_version), - ehdr->e_version); - (void) gelf_update_shdr (scn, shdr); - - /* Convert back to the file byte order. */ - if (gelf_xlatetof (newelf, verd, verd, - BYTE_ORDER == LITTLE_ENDIAN - ? ELFDATA2LSB : ELFDATA2MSB) == NULL) - INTERNAL_ERROR (fname); - } - else if (shdr_info[cnt].shdr.sh_type == SHT_GROUP) - { - /* Check whether the associated symbol table changed. */ - if (shdr_info[shdr_info[cnt].old_sh_link].newsymidx != NULL) - { - /* Yes the symbol table changed. Update the section - header of the section group. */ - scn = elf_getscn (newelf, shdr_info[cnt].idx); - GElf_Shdr shdr_mem; - GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); - assert (shdr != NULL); + /* Finally write the file. */ + if (unlikely (elf_update (debugelf, ELF_C_WRITE) == -1)) + { + error (0, 0, gettext ("while writing '%s': %s"), + debug_fname, elf_errmsg (-1)); + result = 1; + goto fail_close; + } - size_t stabidx = shdr_info[cnt].old_sh_link; - shdr->sh_info = shdr_info[stabidx].newsymidx[shdr->sh_info]; + /* Create the real output file. First rename, then change the + mode. */ + if (rename (tmp_debug_fname, debug_fname) != 0 + || fchmod (debug_fd, mode) != 0) + { + error (0, errno, gettext ("while creating '%s'"), debug_fname); + result = 1; + goto fail_close; + } - (void) gelf_update_shdr (scn, shdr); - } - } + /* The temporary file does not exist anymore. */ + tmp_debug_fname = NULL; + + /* Compute the checksum which we will add to the executable. */ + if (crc32_file (debug_fd, &debug_crc) != 0) + { + error (0, errno, + gettext ("while computing checksum for debug information")); + unlink (debug_fname); + result = 1; + goto fail_close; } + + /* Store it in the debuglink section data. */ + if (unlikely (gelf_xlatetof (newelf, &debuglink_crc_data, + &debug_crc_data, ehdr->e_ident[EI_DATA]) + != &debuglink_crc_data)) + INTERNAL_ERROR (fname); } /* Finally finish the ELF header. Fill in the fields not handled by @@ -1641,7 +1685,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, /* Finally write the file. */ if (elf_update (newelf, ELF_C_WRITE) == -1) { - error (0, 0, gettext ("while writing `%s': %s"), + error (0, 0, gettext ("while writing '%s': %s"), fname, elf_errmsg (-1)); result = 1; } @@ -1653,7 +1697,11 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, table indices. */ if (any_symtab_changes) for (cnt = 1; cnt <= shdridx; ++cnt) - free (shdr_info[cnt].newsymidx); + { + free (shdr_info[cnt].newsymidx); + if (shdr_info[cnt].debug_data != NULL) + free (shdr_info[cnt].debug_data->d_buf); + } /* Free the memory. */ if ((shnum + 2) * sizeof (struct shdr_info) > MAX_STACK_ALLOC) @@ -1669,14 +1717,14 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, /* That was it. Close the descriptors. */ if (elf_end (newelf) != 0) { - error (0, 0, gettext ("error while finishing `%s': %s"), fname, + error (0, 0, gettext ("error while finishing '%s': %s"), fname, elf_errmsg (-1)); result = 1; } if (debugelf != NULL && elf_end (debugelf) != 0) { - error (0, 0, gettext ("error while finishing `%s': %s"), debug_fname, + error (0, 0, gettext ("error while finishing '%s': %s"), debug_fname, elf_errmsg (-1)); result = 1; } @@ -1700,7 +1748,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, if (futimes (fd, tvp) != 0) { error (0, errno, gettext ("\ -cannot set access and modification date of \"%s\""), +cannot set access and modification date of '%s'"), output_fname ?: fname); result = 1; } @@ -1757,13 +1805,16 @@ handle_ar (int fd, Elf *elf, const char *prefix, const char *fname, if (unlikely (futimes (fd, tvp) != 0)) { error (0, errno, gettext ("\ -cannot set access and modification date of \"%s\""), fname); +cannot set access and modification date of '%s'"), fname); result = 1; } } if (unlikely (close (fd) != 0)) - error (EXIT_FAILURE, errno, gettext ("while closing `%s'"), fname); + error (EXIT_FAILURE, errno, gettext ("while closing '%s'"), fname); return result; } + + +#include "debugpred.h" diff --git a/src/symbolhash.c b/src/symbolhash.c index da2ae6f6..670cf056 100644 --- a/src/symbolhash.c +++ b/src/symbolhash.c @@ -1,16 +1,28 @@ /* Symbol hash table implementation. Copyright (C) 2001, 2002 Red Hat, Inc. + This file is part of Red Hat elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 2001. - This program is Open Source software; you can redistribute it and/or - modify it under the terms of the Open Software License version 1.0 as - published by the Open Source Initiative. + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. - You should have received a copy of the Open Software License along - with this program; if not, you may obtain a copy of the Open Software - License version 1.0 from http://www.opensource.org/licenses/osl.php or - by writing the Open Source Initiative c/o Lawrence Rosen, Esq., - 3001 King Ranch Road, Ukiah, CA 95482. */ + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ #ifdef HAVE_CONFIG_H # include <config.h> diff --git a/src/symbolhash.h b/src/symbolhash.h index a8798c2a..54b95397 100644 --- a/src/symbolhash.h +++ b/src/symbolhash.h @@ -1,15 +1,27 @@ /* Copyright (C) 2001, 2002 Red Hat, Inc. + This file is part of Red Hat elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 2001. - This program is Open Source software; you can redistribute it and/or - modify it under the terms of the Open Software License version 1.0 as - published by the Open Source Initiative. + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. - You should have received a copy of the Open Software License along - with this program; if not, you may obtain a copy of the Open Software - License version 1.0 from http://www.opensource.org/licenses/osl.php or - by writing the Open Source Initiative c/o Lawrence Rosen, Esq., - 3001 King Ranch Road, Ukiah, CA 95482. */ + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ #ifndef SYMBOLHASH_H #define SYMBOLHASH_H 1 diff --git a/src/unaligned.h b/src/unaligned.h index 524b35c8..ad7c55a5 100644 --- a/src/unaligned.h +++ b/src/unaligned.h @@ -1,16 +1,28 @@ /* Unaligned memory access functionality. - Copyright (C) 2000, 2001, 2002, 2003 Red Hat, Inc. + Copyright (C) 2000, 2001, 2002, 2003, 2008 Red Hat, Inc. + This file is part of Red Hat elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 2001. - This program is Open Source software; you can redistribute it and/or - modify it under the terms of the Open Software License version 1.0 as - published by the Open Source Initiative. + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. - You should have received a copy of the Open Software License along - with this program; if not, you may obtain a copy of the Open Software - License version 1.0 from http://www.opensource.org/licenses/osl.php or - by writing the Open Source Initiative c/o Lawrence Rosen, Esq., - 3001 King Ranch Road, Ukiah, CA 95482. */ + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ #ifndef _UNALIGNED_H #define _UNALIGNED_H 1 @@ -77,19 +89,19 @@ union u_8ubyte_unaligned #else # define add_2ubyte_unaligned(ptr, value) \ do { \ - union u_2ubyte_unaligned *_ptr = (ptr); \ + union u_2ubyte_unaligned *_ptr = (void *) (ptr); \ uint16_t _val = bswap_16 (_ptr->u) + (value); \ _ptr->u = bswap_16 (_val); \ } while (0) # define add_4ubyte_unaligned(ptr, value) \ do { \ - union u_4ubyte_unaligned *_ptr = (ptr); \ + union u_4ubyte_unaligned *_ptr = (void *) (ptr); \ uint32_t _val = bswap_32 (_ptr->u) + (value); \ _ptr->u = bswap_32 (_val); \ } while (0) # define add_8ubyte_unaligned(ptr, value) \ do { \ - union u_8ubyte_unaligned *_ptr = (ptr); \ + union u_8ubyte_unaligned *_ptr = (void *) (ptr); \ uint64_t _val = bswap_64 (_ptr->u) + (value); \ _ptr->u = bswap_64 (_val); \ } while (0) diff --git a/src/unstrip.c b/src/unstrip.c new file mode 100644 index 00000000..676a0c7f --- /dev/null +++ b/src/unstrip.c @@ -0,0 +1,2317 @@ +/* Combine stripped files with separate symbols and debug information. + Copyright (C) 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + Written by Roland McGrath <roland@redhat.com>, 2007. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +/* TODO: + + * SHX_XINDEX + + * prelink vs .debug_* linked addresses + + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <argp.h> +#include <assert.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <fnmatch.h> +#include <libintl.h> +#include <locale.h> +#include <mcheck.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdio_ext.h> +#include <inttypes.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <gelf.h> +#include <libebl.h> +#include <libdwfl.h> +#include "system.h" + +#ifndef _ +# define _(str) gettext (str) +#endif + +/* Name and version of program. */ +static void print_version (FILE *stream, struct argp_state *state); +void (*argp_program_version_hook) (FILE *, struct argp_state *) + = print_version; + +/* Bug report address. */ +const char *argp_program_bug_address = PACKAGE_BUGREPORT; + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + /* Group 2 will follow group 1 from dwfl_standard_argp. */ + { "match-file-names", 'f', NULL, 0, + N_("Match MODULE against file names, not module names"), 2 }, + { "ignore-missing", 'i', NULL, 0, N_("Silently skip unfindable files"), 0 }, + + { NULL, 0, NULL, 0, N_("Output options:"), 0 }, + { "output", 'o', "FILE", 0, N_("Place output into FILE"), 0 }, + { "output-directory", 'd', "DIRECTORY", + 0, N_("Create multiple output files under DIRECTORY"), 0 }, + { "module-names", 'm', NULL, 0, N_("Use module rather than file names"), 0 }, + { "all", 'a', NULL, 0, + N_("Create output for modules that have no separate debug information"), + 0 }, + { "relocate", 'R', NULL, 0, + N_("Apply relocations to section contents in ET_REL files"), 0 }, + { "list-only", 'n', NULL, 0, + N_("Only list module and file names, build IDs"), 0 }, + { NULL, 0, NULL, 0, NULL, 0 } +}; + +struct arg_info +{ + const char *output_file; + const char *output_dir; + Dwfl *dwfl; + char **args; + bool list; + bool all; + bool ignore; + bool modnames; + bool match_files; + bool relocate; +}; + +/* Handle program arguments. */ +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + struct arg_info *info = state->input; + + switch (key) + { + case ARGP_KEY_INIT: + state->child_inputs[0] = &info->dwfl; + break; + + case 'o': + if (info->output_file != NULL) + { + argp_error (state, _("-o option specified twice")); + return EINVAL; + } + info->output_file = arg; + break; + + case 'd': + if (info->output_dir != NULL) + { + argp_error (state, _("-d option specified twice")); + return EINVAL; + } + info->output_dir = arg; + break; + + case 'm': + info->modnames = true; + break; + case 'f': + info->match_files = true; + break; + case 'a': + info->all = true; + break; + case 'i': + info->ignore = true; + break; + case 'n': + info->list = true; + break; + case 'R': + info->relocate = true; + break; + + case ARGP_KEY_ARGS: + case ARGP_KEY_NO_ARGS: + /* We "consume" all the arguments here. */ + info->args = &state->argv[state->next]; + + if (info->output_file != NULL && info->output_dir != NULL) + { + argp_error (state, _("only one of -o or -d allowed")); + return EINVAL; + } + + if (info->list && (info->dwfl == NULL + || info->output_dir != NULL + || info->output_file != NULL)) + { + argp_error (state, + _("-n cannot be used with explicit files or -o or -d")); + return EINVAL; + } + + if (info->output_dir != NULL) + { + struct stat64 st; + error_t fail = 0; + if (stat64 (info->output_dir, &st) < 0) + fail = errno; + else if (!S_ISDIR (st.st_mode)) + fail = ENOTDIR; + if (fail) + { + argp_failure (state, EXIT_FAILURE, fail, + _("output directory '%s'"), info->output_dir); + return fail; + } + } + + if (info->dwfl == NULL) + { + if (state->next + 2 != state->argc) + { + argp_error (state, _("exactly two file arguments are required")); + return EINVAL; + } + + if (info->ignore || info->all || info->modnames || info->relocate) + { + argp_error (state, _("\ +-m, -a, -R, and -i options not allowed with explicit files")); + return EINVAL; + } + + /* Bail out immediately to prevent dwfl_standard_argp's parser + from defaulting to "-e a.out". */ + return ENOSYS; + } + else if (info->output_file == NULL && info->output_dir == NULL + && !info->list) + { + argp_error (state, + _("-o or -d is required when using implicit files")); + return EINVAL; + } + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +/* Print the version information. */ +static void +print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) +{ + fprintf (stream, "unstrip (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION); + fprintf (stream, _("\ +Copyright (C) %s Red Hat, Inc.\n\ +This is free software; see the source for copying conditions. There is NO\n\ +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ +"), "2008"); + fprintf (stream, gettext ("Written by %s.\n"), "Roland McGrath"); +} + +#define ELF_CHECK(call, msg) \ + do \ + { \ + if (!(call)) \ + error (EXIT_FAILURE, 0, msg, elf_errmsg (-1)); \ + } while (0) + +/* Copy INELF to newly-created OUTELF, exit via error for any problems. */ +static void +copy_elf (Elf *outelf, Elf *inelf) +{ + ELF_CHECK (gelf_newehdr (outelf, gelf_getclass (inelf)), + _("cannot create ELF header: %s")); + + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr = gelf_getehdr (inelf, &ehdr_mem); + ELF_CHECK (gelf_update_ehdr (outelf, ehdr), + _("cannot copy ELF header: %s")); + + if (ehdr->e_phnum > 0) + { + ELF_CHECK (gelf_newphdr (outelf, ehdr->e_phnum), + _("cannot create program headers: %s")); + + GElf_Phdr phdr_mem; + for (uint_fast16_t i = 0; i < ehdr->e_phnum; ++i) + ELF_CHECK (gelf_update_phdr (outelf, i, + gelf_getphdr (inelf, i, &phdr_mem)), + _("cannot copy program header: %s")); + } + + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (inelf, scn)) != NULL) + { + Elf_Scn *newscn = elf_newscn (outelf); + + GElf_Shdr shdr_mem; + ELF_CHECK (gelf_update_shdr (newscn, gelf_getshdr (scn, &shdr_mem)), + _("cannot copy section header: %s")); + + Elf_Data *data = elf_getdata (scn, NULL); + ELF_CHECK (data != NULL, _("cannot get section data: %s")); + Elf_Data *newdata = elf_newdata (newscn); + ELF_CHECK (newdata != NULL, _("cannot copy section data: %s")); + *newdata = *data; + elf_flagdata (newdata, ELF_C_SET, ELF_F_DIRTY); + } +} + +/* Create directories containing PATH. */ +static void +make_directories (const char *path) +{ + const char *lastslash = strrchr (path, '/'); + if (lastslash == NULL) + return; + + while (lastslash > path && lastslash[-1] == '/') + --lastslash; + if (lastslash == path) + return; + + char *dir = strndupa (path, lastslash - path); + while (mkdir (dir, 0777) < 0 && errno != EEXIST) + if (errno == ENOENT) + make_directories (dir); + else + error (EXIT_FAILURE, errno, _("cannot create directory '%s'"), dir); +} + + +/* The binutils linker leaves gratuitous section symbols in .symtab + that strip has to remove. Older linkers likewise include a + symbol for every section, even unallocated ones, in .dynsym. + Because of this, the related sections can shrink in the stripped + file from their original size. Older versions of strip do not + adjust the sh_size field in the debuginfo file's SHT_NOBITS + version of the section header, so it can appear larger. */ +static bool +section_can_shrink (const GElf_Shdr *shdr) +{ + switch (shdr->sh_type) + { + case SHT_SYMTAB: + case SHT_DYNSYM: + case SHT_HASH: + case SHT_GNU_versym: + return true; + } + return false; +} + +/* See if this symbol table has a leading section symbol for every single + section, in order. The binutils linker produces this. While we're here, + update each section symbol's st_value. */ +static size_t +symtab_count_leading_section_symbols (Elf *elf, Elf_Scn *scn, size_t shnum, + Elf_Data *newsymdata) +{ + Elf_Data *data = elf_getdata (scn, NULL); + Elf_Data *shndxdata = NULL; /* XXX */ + + for (size_t i = 1; i < shnum; ++i) + { + GElf_Sym sym_mem; + GElf_Word shndx = SHN_UNDEF; + GElf_Sym *sym = gelf_getsymshndx (data, shndxdata, i, &sym_mem, &shndx); + ELF_CHECK (sym != NULL, _("cannot get symbol table entry: %s")); + + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (elf_getscn (elf, i), &shdr_mem); + ELF_CHECK (shdr != NULL, _("cannot get section header: %s")); + + if (sym->st_shndx != SHN_XINDEX) + shndx = sym->st_shndx; + + if (shndx != i || GELF_ST_TYPE (sym->st_info) != STT_SECTION) + return i; + + sym->st_value = shdr->sh_addr; + if (sym->st_shndx != SHN_XINDEX) + shndx = SHN_UNDEF; + ELF_CHECK (gelf_update_symshndx (newsymdata, shndxdata, i, sym, shndx), + _("cannot update symbol table: %s")); + } + + return shnum; +} + +/* We expanded the output section, so update its header. */ +static void +update_sh_size (Elf_Scn *outscn, const Elf_Data *data) +{ + GElf_Shdr shdr_mem; + GElf_Shdr *newshdr = gelf_getshdr (outscn, &shdr_mem); + ELF_CHECK (newshdr != NULL, _("cannot get section header: %s")); + + newshdr->sh_size = data->d_size; + + ELF_CHECK (gelf_update_shdr (outscn, newshdr), + _("cannot update section header: %s")); +} + +/* Update relocation sections using the symbol table. */ +static void +adjust_relocs (Elf_Scn *outscn, Elf_Scn *inscn, const GElf_Shdr *shdr, + size_t map[], const GElf_Shdr *symshdr) +{ + Elf_Data *data = elf_getdata (outscn, NULL); + + inline void adjust_reloc (GElf_Xword *info) + { + size_t ndx = GELF_R_SYM (*info); + if (ndx != STN_UNDEF) + *info = GELF_R_INFO (map[ndx - 1], GELF_R_TYPE (*info)); + } + + switch (shdr->sh_type) + { + case SHT_REL: + for (size_t i = 0; i < shdr->sh_size / shdr->sh_entsize; ++i) + { + GElf_Rel rel_mem; + GElf_Rel *rel = gelf_getrel (data, i, &rel_mem); + adjust_reloc (&rel->r_info); + ELF_CHECK (gelf_update_rel (data, i, rel), + _("cannot update relocation: %s")); + } + break; + + case SHT_RELA: + for (size_t i = 0; i < shdr->sh_size / shdr->sh_entsize; ++i) + { + GElf_Rela rela_mem; + GElf_Rela *rela = gelf_getrela (data, i, &rela_mem); + adjust_reloc (&rela->r_info); + ELF_CHECK (gelf_update_rela (data, i, rela), + _("cannot update relocation: %s")); + } + break; + + case SHT_GROUP: + { + GElf_Shdr shdr_mem; + GElf_Shdr *newshdr = gelf_getshdr (outscn, &shdr_mem); + ELF_CHECK (newshdr != NULL, _("cannot get section header: %s")); + if (newshdr->sh_info != STN_UNDEF) + { + newshdr->sh_info = map[newshdr->sh_info - 1]; + ELF_CHECK (gelf_update_shdr (outscn, newshdr), + _("cannot update section header: %s")); + } + break; + } + + case SHT_HASH: + /* We must expand the table and rejigger its contents. */ + { + const size_t nsym = symshdr->sh_size / symshdr->sh_entsize; + const size_t onent = shdr->sh_size / shdr->sh_entsize; + assert (data->d_size == shdr->sh_size); + +#define CONVERT_HASH(Hash_Word) \ + { \ + const Hash_Word *const old_hash = data->d_buf; \ + const size_t nbucket = old_hash[0]; \ + const size_t nchain = old_hash[1]; \ + const Hash_Word *const old_bucket = &old_hash[2]; \ + const Hash_Word *const old_chain = &old_bucket[nbucket]; \ + assert (onent == 2 + nbucket + nchain); \ + \ + const size_t nent = 2 + nbucket + nsym; \ + Hash_Word *const new_hash = xcalloc (nent, sizeof new_hash[0]); \ + Hash_Word *const new_bucket = &new_hash[2]; \ + Hash_Word *const new_chain = &new_bucket[nbucket]; \ + \ + new_hash[0] = nbucket; \ + new_hash[1] = nsym; \ + for (size_t i = 0; i < nbucket; ++i) \ + if (old_bucket[i] != STN_UNDEF) \ + new_bucket[i] = map[old_bucket[i] - 1]; \ + \ + for (size_t i = 1; i < nchain; ++i) \ + if (old_chain[i] != STN_UNDEF) \ + new_chain[map[i - 1]] = map[old_chain[i] - 1]; \ + \ + data->d_buf = new_hash; \ + data->d_size = nent * sizeof new_hash[0]; \ + } + + switch (shdr->sh_entsize) + { + case 4: + CONVERT_HASH (Elf32_Word); + break; + case 8: + CONVERT_HASH (Elf64_Xword); + break; + default: + abort (); + } + + elf_flagdata (data, ELF_C_SET, ELF_F_DIRTY); + update_sh_size (outscn, data); + +#undef CONVERT_HASH + } + break; + + case SHT_GNU_versym: + /* We must expand the table and move its elements around. */ + { + const size_t nent = symshdr->sh_size / symshdr->sh_entsize; + const size_t onent = shdr->sh_size / shdr->sh_entsize; + assert (nent >= onent); + + /* We don't bother using gelf_update_versym because there is + really no conversion to be done. */ + assert (sizeof (Elf32_Versym) == sizeof (GElf_Versym)); + assert (sizeof (Elf64_Versym) == sizeof (GElf_Versym)); + GElf_Versym *versym = xcalloc (nent, sizeof versym[0]); + + for (size_t i = 1; i < onent; ++i) + { + GElf_Versym *v = gelf_getversym (data, i, &versym[map[i - 1]]); + ELF_CHECK (v != NULL, _("cannot get symbol version: %s")); + } + + data->d_buf = versym; + data->d_size = nent * shdr->sh_entsize; + elf_flagdata (data, ELF_C_SET, ELF_F_DIRTY); + update_sh_size (outscn, data); + } + break; + + default: + error (EXIT_FAILURE, 0, + _("unexpected section type in [%Zu] with sh_link to symtab"), + elf_ndxscn (inscn)); + } +} + +/* Adjust all the relocation sections in the file. */ +static void +adjust_all_relocs (Elf *elf, Elf_Scn *symtab, const GElf_Shdr *symshdr, + size_t map[]) +{ + size_t new_sh_link = elf_ndxscn (symtab); + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (elf, scn)) != NULL) + if (scn != symtab) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + ELF_CHECK (shdr != NULL, _("cannot get section header: %s")); + if (shdr->sh_type != SHT_NOBITS && shdr->sh_link == new_sh_link) + adjust_relocs (scn, scn, shdr, map, symshdr); + } +} + +/* The original file probably had section symbols for all of its + sections, even the unallocated ones. To match it as closely as + possible, add in section symbols for the added sections. */ +static Elf_Data * +add_new_section_symbols (Elf_Scn *old_symscn, size_t old_shnum, + Elf *elf, bool rel, Elf_Scn *symscn, size_t shnum) +{ + const size_t added = shnum - old_shnum; + + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (symscn, &shdr_mem); + ELF_CHECK (shdr != NULL, _("cannot get section header: %s")); + + const size_t nsym = shdr->sh_size / shdr->sh_entsize; + size_t symndx_map[nsym - 1]; + + shdr->sh_info += added; + shdr->sh_size += added * shdr->sh_entsize; + + ELF_CHECK (gelf_update_shdr (symscn, shdr), + _("cannot update section header: %s")); + + Elf_Data *symdata = elf_getdata (symscn, NULL); + Elf_Data *shndxdata = NULL; /* XXX */ + + symdata->d_size = shdr->sh_size; + symdata->d_buf = xmalloc (symdata->d_size); + + /* Copy the existing section symbols. */ + Elf_Data *old_symdata = elf_getdata (old_symscn, NULL); + for (size_t i = 0; i < old_shnum; ++i) + { + GElf_Sym sym_mem; + GElf_Word shndx = SHN_UNDEF; + GElf_Sym *sym = gelf_getsymshndx (old_symdata, shndxdata, + i, &sym_mem, &shndx); + ELF_CHECK (gelf_update_symshndx (symdata, shndxdata, i, + sym, shndx), + _("cannot update symbol table: %s")); + + if (i > 0) + symndx_map[i - 1] = i; + } + + /* Add in the new section symbols. */ + for (size_t i = old_shnum; i < shnum; ++i) + { + GElf_Shdr i_shdr_mem; + GElf_Shdr *i_shdr = gelf_getshdr (elf_getscn (elf, i), &i_shdr_mem); + ELF_CHECK (i_shdr != NULL, _("cannot get section header: %s")); + GElf_Sym sym = + { + .st_value = rel ? 0 : i_shdr->sh_addr, + .st_info = GELF_ST_INFO (STB_LOCAL, STT_SECTION), + .st_shndx = i < SHN_LORESERVE ? i : SHN_XINDEX + }; + GElf_Word shndx = i < SHN_LORESERVE ? SHN_UNDEF : i; + ELF_CHECK (gelf_update_symshndx (symdata, shndxdata, i, + &sym, shndx), + _("cannot update symbol table: %s")); + } + + /* Now copy the rest of the existing symbols. */ + for (size_t i = old_shnum; i < nsym; ++i) + { + GElf_Sym sym_mem; + GElf_Word shndx = SHN_UNDEF; + GElf_Sym *sym = gelf_getsymshndx (old_symdata, shndxdata, + i, &sym_mem, &shndx); + ELF_CHECK (gelf_update_symshndx (symdata, shndxdata, + i + added, sym, shndx), + _("cannot update symbol table: %s")); + + symndx_map[i - 1] = i + added; + } + + /* Adjust any relocations referring to the old symbol table. */ + adjust_all_relocs (elf, symscn, shdr, symndx_map); + + return symdata; +} + +/* This has the side effect of updating STT_SECTION symbols' values, + in case of prelink adjustments. */ +static Elf_Data * +check_symtab_section_symbols (Elf *elf, bool rel, Elf_Scn *scn, + size_t shnum, size_t shstrndx, + Elf_Scn *oscn, size_t oshnum, size_t oshstrndx, + size_t debuglink) +{ + size_t n = symtab_count_leading_section_symbols (elf, oscn, oshnum, + elf_getdata (scn, NULL)); + + if (n == oshnum) + return add_new_section_symbols (oscn, n, elf, rel, scn, shnum); + + if (n == oshstrndx || (n == debuglink && n == oshstrndx - 1)) + return add_new_section_symbols (oscn, n, elf, rel, scn, shstrndx); + + return NULL; +} + +struct section +{ + Elf_Scn *scn; + const char *name; + Elf_Scn *outscn; + struct Ebl_Strent *strent; + GElf_Shdr shdr; +}; + +static int +compare_alloc_sections (const struct section *s1, const struct section *s2, + bool rel) +{ + if (!rel) + { + /* Sort by address. */ + if (s1->shdr.sh_addr < s2->shdr.sh_addr) + return -1; + if (s1->shdr.sh_addr > s2->shdr.sh_addr) + return 1; + } + + /* At the same address, preserve original section order. */ + return (ssize_t) elf_ndxscn (s1->scn) - (ssize_t) elf_ndxscn (s2->scn); +} + +static int +compare_unalloc_sections (const GElf_Shdr *shdr1, const GElf_Shdr *shdr2, + const char *name1, const char *name2) +{ + /* Sort by sh_flags as an arbitrary ordering. */ + if (shdr1->sh_flags < shdr2->sh_flags) + return -1; + if (shdr1->sh_flags > shdr2->sh_flags) + return 1; + + /* Sort by name as last resort. */ + return strcmp (name1, name2); +} + +static int +compare_sections (const void *a, const void *b, bool rel) +{ + const struct section *s1 = a; + const struct section *s2 = b; + + /* Sort all non-allocated sections last. */ + if ((s1->shdr.sh_flags ^ s2->shdr.sh_flags) & SHF_ALLOC) + return (s1->shdr.sh_flags & SHF_ALLOC) ? -1 : 1; + + return ((s1->shdr.sh_flags & SHF_ALLOC) + ? compare_alloc_sections (s1, s2, rel) + : compare_unalloc_sections (&s1->shdr, &s2->shdr, + s1->name, s2->name)); +} + +static int +compare_sections_rel (const void *a, const void *b) +{ + return compare_sections (a, b, true); +} + +int +compare_sections_nonrel (const void *a, const void *b) +{ + return compare_sections (a, b, false); +} + + +struct symbol +{ + size_t *map; + + union + { + const char *name; + struct Ebl_Strent *strent; + }; + union + { + struct + { + GElf_Addr value; + GElf_Xword size; + GElf_Word shndx; + union + { + struct + { + uint8_t info; + uint8_t other; + } info; + int16_t compare; + }; + }; + + /* For a symbol discarded after first sort, this matches its better's + map pointer. */ + size_t *duplicate; + }; +}; + +/* Collect input symbols into our internal form. */ +static void +collect_symbols (Elf *outelf, bool rel, Elf_Scn *symscn, Elf_Scn *strscn, + const size_t nent, const GElf_Addr bias, + const size_t scnmap[], struct symbol *table, size_t *map, + struct section *split_bss) +{ + Elf_Data *symdata = elf_getdata (symscn, NULL); + Elf_Data *strdata = elf_getdata (strscn, NULL); + Elf_Data *shndxdata = NULL; /* XXX */ + + for (size_t i = 1; i < nent; ++i) + { + GElf_Sym sym_mem; + GElf_Word shndx = SHN_UNDEF; + GElf_Sym *sym = gelf_getsymshndx (symdata, shndxdata, i, + &sym_mem, &shndx); + ELF_CHECK (sym != NULL, _("cannot get symbol table entry: %s")); + if (sym->st_shndx != SHN_XINDEX) + shndx = sym->st_shndx; + + if (sym->st_name >= strdata->d_size) + error (EXIT_FAILURE, 0, + _("invalid string offset in symbol [%Zu]"), i); + + struct symbol *s = &table[i - 1]; + s->map = &map[i - 1]; + s->name = strdata->d_buf + sym->st_name; + s->value = sym->st_value + bias; + s->size = sym->st_size; + s->shndx = shndx; + s->info.info = sym->st_info; + s->info.other = sym->st_other; + + if (scnmap != NULL && shndx != SHN_UNDEF && shndx < SHN_LORESERVE) + s->shndx = scnmap[shndx - 1]; + + if (GELF_ST_TYPE (s->info.info) == STT_SECTION && !rel) + { + /* Update the value to match the output section. */ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (elf_getscn (outelf, s->shndx), + &shdr_mem); + ELF_CHECK (shdr != NULL, _("cannot get section header: %s")); + s->value = shdr->sh_addr; + } + else if (split_bss != NULL + && s->value < split_bss->shdr.sh_addr + && s->value >= split_bss[-1].shdr.sh_addr + && shndx == elf_ndxscn (split_bss->outscn)) + /* This symbol was in .bss and was split into .dynbss. */ + s->shndx = elf_ndxscn (split_bss[-1].outscn); + } +} + + +#define CMP(value) \ + if (s1->value < s2->value) \ + return -1; \ + if (s1->value > s2->value) \ + return 1 + +/* Compare symbols with a consistent ordering, + but one only meaningful for equality. */ +static int +compare_symbols (const void *a, const void *b) +{ + const struct symbol *s1 = a; + const struct symbol *s2 = b; + + CMP (value); + CMP (size); + CMP (shndx); + + return (s1->compare - s2->compare) ?: strcmp (s1->name, s2->name); +} + +/* Compare symbols for output order after slots have been assigned. */ +static int +compare_symbols_output (const void *a, const void *b) +{ + const struct symbol *s1 = a; + const struct symbol *s2 = b; + int cmp; + + /* Sort discarded symbols last. */ + cmp = (s1->name == NULL) - (s2->name == NULL); + + if (cmp == 0) + /* Local symbols must come first. */ + cmp = ((GELF_ST_BIND (s2->info.info) == STB_LOCAL) + - (GELF_ST_BIND (s1->info.info) == STB_LOCAL)); + + if (cmp == 0) + /* binutils always puts section symbols first. */ + cmp = ((GELF_ST_TYPE (s2->info.info) == STT_SECTION) + - (GELF_ST_TYPE (s1->info.info) == STT_SECTION)); + + if (cmp == 0) + { + if (GELF_ST_TYPE (s1->info.info) == STT_SECTION) + { + /* binutils always puts section symbols in section index order. */ + CMP (shndx); + else + assert (s1 == s2); + } + + /* Nothing really matters, so preserve the original order. */ + CMP (map); + else + assert (s1 == s2); + } + + return cmp; +} + +#undef CMP + +/* Return true iff the flags, size, and name match. */ +static bool +sections_match (const struct section *sections, size_t i, + const GElf_Shdr *shdr, const char *name) +{ + return (sections[i].shdr.sh_flags == shdr->sh_flags + && (sections[i].shdr.sh_size == shdr->sh_size + || (sections[i].shdr.sh_size < shdr->sh_size + && section_can_shrink (§ions[i].shdr))) + && !strcmp (sections[i].name, name)); +} + +/* Locate a matching allocated section in SECTIONS. */ +static struct section * +find_alloc_section (const GElf_Shdr *shdr, GElf_Addr bias, const char *name, + struct section sections[], size_t nalloc) +{ + const GElf_Addr addr = shdr->sh_addr + bias; + size_t l = 0, u = nalloc; + while (l < u) + { + size_t i = (l + u) / 2; + if (addr < sections[i].shdr.sh_addr) + u = i; + else if (addr > sections[i].shdr.sh_addr) + l = i + 1; + else + { + /* We've found allocated sections with this address. + Find one with matching size, flags, and name. */ + while (i > 0 && sections[i - 1].shdr.sh_addr == addr) + --i; + for (; i < nalloc && sections[i].shdr.sh_addr == addr; + ++i) + if (sections_match (sections, i, shdr, name)) + return §ions[i]; + break; + } + } + return NULL; +} + +static inline const char * +get_section_name (size_t ndx, const GElf_Shdr *shdr, const Elf_Data *shstrtab) +{ + if (shdr->sh_name >= shstrtab->d_size) + error (EXIT_FAILURE, 0, _("cannot read section [%Zu] name: %s"), + ndx, elf_errmsg (-1)); + return shstrtab->d_buf + shdr->sh_name; +} + +/* Fix things up when prelink has moved some allocated sections around + and the debuginfo file's section headers no longer match up. + This fills in SECTIONS[0..NALLOC-1].outscn or exits. + If there was a .bss section that was split into two sections + with the new one preceding it in sh_addr, we return that pointer. */ +static struct section * +find_alloc_sections_prelink (Elf *debug, Elf_Data *debug_shstrtab, + Elf *main, const GElf_Ehdr *main_ehdr, + Elf_Data *main_shstrtab, GElf_Addr bias, + struct section *sections, + size_t nalloc, size_t nsections) +{ + /* Clear assignments that might have been bogus. */ + for (size_t i = 0; i < nalloc; ++i) + sections[i].outscn = NULL; + + Elf_Scn *undo = NULL; + for (size_t i = nalloc; i < nsections; ++i) + { + const struct section *sec = §ions[i]; + if (sec->shdr.sh_type == SHT_PROGBITS + && !(sec->shdr.sh_flags & SHF_ALLOC) + && !strcmp (sec->name, ".gnu.prelink_undo")) + { + undo = sec->scn; + break; + } + } + + /* Find the original allocated sections before prelinking. */ + struct section *undo_sections = NULL; + size_t undo_nalloc = 0; + if (undo != NULL) + { + Elf_Data *undodata = elf_rawdata (undo, NULL); + ELF_CHECK (undodata != NULL, + _("cannot read '.gnu.prelink_undo' section: %s")); + + union + { + Elf32_Ehdr e32; + Elf64_Ehdr e64; + } ehdr; + Elf_Data dst = + { + .d_buf = &ehdr, + .d_size = sizeof ehdr, + .d_type = ELF_T_EHDR, + .d_version = EV_CURRENT + }; + Elf_Data src = *undodata; + src.d_size = gelf_fsize (main, ELF_T_EHDR, 1, EV_CURRENT); + src.d_type = ELF_T_EHDR; + ELF_CHECK (gelf_xlatetom (main, &dst, &src, + main_ehdr->e_ident[EI_DATA]) != NULL, + _("cannot read '.gnu.prelink_undo' section: %s")); + + uint_fast16_t phnum; + uint_fast16_t shnum; + if (ehdr.e32.e_ident[EI_CLASS] == ELFCLASS32) + { + phnum = ehdr.e32.e_phnum; + shnum = ehdr.e32.e_shnum; + } + else + { + phnum = ehdr.e64.e_phnum; + shnum = ehdr.e64.e_shnum; + } + + size_t phsize = gelf_fsize (main, ELF_T_PHDR, phnum, EV_CURRENT); + src.d_buf += src.d_size + phsize; + src.d_size = gelf_fsize (main, ELF_T_SHDR, shnum - 1, EV_CURRENT); + src.d_type = ELF_T_SHDR; + if ((size_t) (src.d_buf - undodata->d_buf) > undodata->d_size + || undodata->d_size - (src.d_buf - undodata->d_buf) != src.d_size) + error (EXIT_FAILURE, 0, _("invalid contents in '%s' section"), + ".gnu.prelink_undo"); + + union + { + Elf32_Shdr s32[shnum - 1]; + Elf64_Shdr s64[shnum - 1]; + } shdr; + dst.d_buf = &shdr; + dst.d_size = sizeof shdr; + ELF_CHECK (gelf_xlatetom (main, &dst, &src, + main_ehdr->e_ident[EI_DATA]) != NULL, + _("cannot read '.gnu.prelink_undo' section: %s")); + + undo_sections = xmalloc ((shnum - 1) * sizeof undo_sections[0]); + for (size_t i = 0; i < shnum - 1; ++i) + { + struct section *sec = &undo_sections[undo_nalloc]; + if (ehdr.e32.e_ident[EI_CLASS] == ELFCLASS32) + { +#define COPY(field) sec->shdr.field = shdr.s32[i].field + COPY (sh_name); + COPY (sh_type); + COPY (sh_flags); + COPY (sh_addr); + COPY (sh_offset); + COPY (sh_size); + COPY (sh_link); + COPY (sh_info); + COPY (sh_addralign); + COPY (sh_entsize); +#undef COPY + } + else + sec->shdr = shdr.s64[i]; + if (sec->shdr.sh_flags & SHF_ALLOC) + { + sec->shdr.sh_addr += bias; + sec->name = get_section_name (i + 1, &sec->shdr, main_shstrtab); + sec->scn = elf_getscn (main, i + 1); /* Really just for ndx. */ + sec->outscn = NULL; + sec->strent = NULL; + ++undo_nalloc; + } + } + qsort (undo_sections, undo_nalloc, + sizeof undo_sections[0], compare_sections_nonrel); + } + + bool fail = false; + inline void check_match (bool match, Elf_Scn *scn, const char *name) + { + if (!match) + { + fail = true; + error (0, 0, _("cannot find matching section for [%Zu] '%s'"), + elf_ndxscn (scn), name); + } + } + + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (debug, scn)) != NULL) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + ELF_CHECK (shdr != NULL, _("cannot get section header: %s")); + + if (!(shdr->sh_flags & SHF_ALLOC)) + continue; + + const char *name = get_section_name (elf_ndxscn (scn), shdr, + debug_shstrtab); + + if (undo_sections != NULL) + { + struct section *sec = find_alloc_section (shdr, 0, name, + undo_sections, + undo_nalloc); + if (sec != NULL) + { + sec->outscn = scn; + continue; + } + } + + /* If there is no prelink info, we are just here to find + the sections to give error messages about. */ + for (size_t i = 0; shdr != NULL && i < nalloc; ++i) + if (sections[i].outscn == scn) + shdr = NULL; + check_match (shdr == NULL, scn, name); + } + + if (fail) + exit (EXIT_FAILURE); + + /* Now we have lined up output sections for each of the original sections + before prelinking. Translate those to the prelinked sections. + This matches what prelink's undo_sections does. */ + struct section *split_bss = NULL; + for (size_t i = 0; i < undo_nalloc; ++i) + { + const struct section *undo_sec = &undo_sections[i]; + + const char *name = undo_sec->name; + scn = undo_sec->scn; /* This is just for elf_ndxscn. */ + + for (size_t j = 0; j < nalloc; ++j) + { + struct section *sec = §ions[j]; +#define RELA_SCALED(field) \ + (2 * sec->shdr.field == 3 * undo_sec->shdr.field) + if (sec->outscn == NULL + && sec->shdr.sh_name == undo_sec->shdr.sh_name + && sec->shdr.sh_flags == undo_sec->shdr.sh_flags + && sec->shdr.sh_addralign == undo_sec->shdr.sh_addralign + && (((sec->shdr.sh_type == undo_sec->shdr.sh_type + && sec->shdr.sh_entsize == undo_sec->shdr.sh_entsize + && (sec->shdr.sh_size == undo_sec->shdr.sh_size + || (sec->shdr.sh_size > undo_sec->shdr.sh_size + && main_ehdr->e_type == ET_EXEC + && !strcmp (sec->name, ".dynstr")))) + || (sec->shdr.sh_size == undo_sec->shdr.sh_size + && ((sec->shdr.sh_entsize == undo_sec->shdr.sh_entsize + && undo_sec->shdr.sh_type == SHT_NOBITS) + || undo_sec->shdr.sh_type == SHT_PROGBITS) + && !strcmp (sec->name, ".plt"))) + || (sec->shdr.sh_type == SHT_RELA + && undo_sec->shdr.sh_type == SHT_REL + && RELA_SCALED (sh_entsize) && RELA_SCALED (sh_size)) + || (sec->shdr.sh_entsize == undo_sec->shdr.sh_entsize + && (sec->shdr.sh_type == undo_sec->shdr.sh_type + || (sec->shdr.sh_type == SHT_PROGBITS + && undo_sec->shdr.sh_type == SHT_NOBITS)) + && sec->shdr.sh_size < undo_sec->shdr.sh_size + && (!strcmp (sec->name, ".bss") + || !strcmp (sec->name, ".sbss")) + && (split_bss = sec) > sections))) + { + sec->outscn = undo_sec->outscn; + undo_sec = NULL; + break; + } + } + + check_match (undo_sec == NULL, scn, name); + } + + free (undo_sections); + + if (fail) + exit (EXIT_FAILURE); + + return split_bss; +} + +/* Create new .shstrtab contents, subroutine of copy_elided_sections. + This can't be open coded there and still use variable-length auto arrays, + since the end of our block would free other VLAs too. */ +static Elf_Data * +new_shstrtab (Elf *unstripped, size_t unstripped_shnum, + Elf_Data *shstrtab, size_t unstripped_shstrndx, + struct section *sections, size_t stripped_shnum, + struct Ebl_Strtab *strtab) +{ + if (strtab == NULL) + return NULL; + + struct Ebl_Strent *unstripped_strent[unstripped_shnum - 1]; + memset (unstripped_strent, 0, sizeof unstripped_strent); + for (struct section *sec = sections; + sec < §ions[stripped_shnum - 1]; + ++sec) + if (sec->outscn != NULL) + { + if (sec->strent == NULL) + { + sec->strent = ebl_strtabadd (strtab, sec->name, 0); + ELF_CHECK (sec->strent != NULL, + _("cannot add section name to string table: %s")); + } + unstripped_strent[elf_ndxscn (sec->outscn) - 1] = sec->strent; + } + + /* Add names of sections we aren't touching. */ + for (size_t i = 0; i < unstripped_shnum - 1; ++i) + if (unstripped_strent[i] == NULL) + { + Elf_Scn *scn = elf_getscn (unstripped, i + 1); + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + const char *name = get_section_name (i + 1, shdr, shstrtab); + unstripped_strent[i] = ebl_strtabadd (strtab, name, 0); + ELF_CHECK (unstripped_strent[i] != NULL, + _("cannot add section name to string table: %s")); + } + else + unstripped_strent[i] = NULL; + + /* Now finalize the string table so we can get offsets. */ + Elf_Data *strtab_data = elf_getdata (elf_getscn (unstripped, + unstripped_shstrndx), NULL); + ELF_CHECK (elf_flagdata (strtab_data, ELF_C_SET, ELF_F_DIRTY), + _("cannot update section header string table data: %s")); + ebl_strtabfinalize (strtab, strtab_data); + + /* Update the sh_name fields of sections we aren't modifying later. */ + for (size_t i = 0; i < unstripped_shnum - 1; ++i) + if (unstripped_strent[i] != NULL) + { + Elf_Scn *scn = elf_getscn (unstripped, i + 1); + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + shdr->sh_name = ebl_strtaboffset (unstripped_strent[i]); + if (i + 1 == unstripped_shstrndx) + shdr->sh_size = strtab_data->d_size; + ELF_CHECK (gelf_update_shdr (scn, shdr), + _("cannot update section header: %s")); + } + + return strtab_data; +} + +/* Fill in any SHT_NOBITS sections in UNSTRIPPED by + copying their contents and sh_type from STRIPPED. */ +static void +copy_elided_sections (Elf *unstripped, Elf *stripped, + const GElf_Ehdr *stripped_ehdr, GElf_Addr bias) +{ + size_t unstripped_shstrndx; + ELF_CHECK (elf_getshstrndx (unstripped, &unstripped_shstrndx) == 0, + _("cannot get section header string table section index: %s")); + + size_t stripped_shstrndx; + ELF_CHECK (elf_getshstrndx (stripped, &stripped_shstrndx) == 0, + _("cannot get section header string table section index: %s")); + + size_t unstripped_shnum; + ELF_CHECK (elf_getshnum (unstripped, &unstripped_shnum) == 0, + _("cannot get section count: %s")); + + size_t stripped_shnum; + ELF_CHECK (elf_getshnum (stripped, &stripped_shnum) == 0, + _("cannot get section count: %s")); + + /* Cache the stripped file's section details. */ + struct section sections[stripped_shnum - 1]; + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (stripped, scn)) != NULL) + { + size_t i = elf_ndxscn (scn) - 1; + GElf_Shdr *shdr = gelf_getshdr (scn, §ions[i].shdr); + ELF_CHECK (shdr != NULL, _("cannot get section header: %s")); + sections[i].name = elf_strptr (stripped, stripped_shstrndx, + shdr->sh_name); + if (sections[i].name == NULL) + error (EXIT_FAILURE, 0, _("cannot read section [%Zu] name: %s"), + elf_ndxscn (scn), elf_errmsg (-1)); + sections[i].scn = scn; + sections[i].outscn = NULL; + sections[i].strent = NULL; + } + + const struct section *stripped_symtab = NULL; + + /* Sort the sections, allocated by address and others after. */ + qsort (sections, stripped_shnum - 1, sizeof sections[0], + stripped_ehdr->e_type == ET_REL + ? compare_sections_rel : compare_sections_nonrel); + size_t nalloc = stripped_shnum - 1; + while (nalloc > 0 && !(sections[nalloc - 1].shdr.sh_flags & SHF_ALLOC)) + { + --nalloc; + if (sections[nalloc].shdr.sh_type == SHT_SYMTAB) + stripped_symtab = §ions[nalloc]; + } + + /* Locate a matching unallocated section in SECTIONS. */ + inline struct section *find_unalloc_section (const GElf_Shdr *shdr, + const char *name) + { + size_t l = nalloc, u = stripped_shnum - 1; + while (l < u) + { + size_t i = (l + u) / 2; + struct section *sec = §ions[i]; + int cmp = compare_unalloc_sections (shdr, &sec->shdr, + name, sec->name); + if (cmp < 0) + u = i; + else if (cmp > 0) + l = i + 1; + else + return sec; + } + return NULL; + } + + Elf_Data *shstrtab = elf_getdata (elf_getscn (unstripped, + unstripped_shstrndx), NULL); + ELF_CHECK (shstrtab != NULL, + _("cannot read section header string table: %s")); + + /* Match each debuginfo section with its corresponding stripped section. */ + bool check_prelink = false; + Elf_Scn *unstripped_symtab = NULL; + size_t unstripped_strtab_ndx = SHN_UNDEF; + size_t alloc_avail = 0; + scn = NULL; + while ((scn = elf_nextscn (unstripped, scn)) != NULL) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + ELF_CHECK (shdr != NULL, _("cannot get section header: %s")); + + if (shdr->sh_type == SHT_SYMTAB) + { + unstripped_symtab = scn; + unstripped_strtab_ndx = shdr->sh_link; + continue; + } + + const size_t ndx = elf_ndxscn (scn); + if (ndx == unstripped_shstrndx) + continue; + + const char *name = get_section_name (ndx, shdr, shstrtab); + + struct section *sec = NULL; + if (shdr->sh_flags & SHF_ALLOC) + { + if (stripped_ehdr->e_type != ET_REL) + { + /* Look for the section that matches. */ + sec = find_alloc_section (shdr, bias, name, sections, nalloc); + if (sec == NULL) + { + /* We couldn't figure it out. It may be a prelink issue. */ + check_prelink = true; + continue; + } + } + else + { + /* The sh_addr of allocated sections does not help us, + but the order usually matches. */ + if (likely (sections_match (sections, alloc_avail, shdr, name))) + sec = §ions[alloc_avail++]; + else + for (size_t i = alloc_avail + 1; i < nalloc; ++i) + if (sections_match (sections, i, shdr, name)) + { + sec = §ions[i]; + break; + } + } + } + else + { + /* Look for the section that matches. */ + sec = find_unalloc_section (shdr, name); + if (sec == NULL) + { + /* An additional unallocated section is fine if not SHT_NOBITS. + We looked it up anyway in case it's an unallocated section + copied in both files (e.g. SHT_NOTE), and don't keep both. */ + if (shdr->sh_type != SHT_NOBITS) + continue; + + /* Somehow some old .debug files wound up with SHT_NOBITS + .comment sections, so let those pass. */ + if (!strcmp (name, ".comment")) + continue; + } + } + + if (sec == NULL) + error (EXIT_FAILURE, 0, + _("cannot find matching section for [%Zu] '%s'"), + elf_ndxscn (scn), name); + + sec->outscn = scn; + } + + /* If that failed due to changes made by prelink, we take another tack. + We keep track of a .bss section that was partly split into .dynbss + so that collect_symbols can update symbols' st_shndx fields. */ + struct section *split_bss = NULL; + if (check_prelink) + { + Elf_Data *data = elf_getdata (elf_getscn (stripped, stripped_shstrndx), + NULL); + ELF_CHECK (data != NULL, + _("cannot read section header string table: %s")); + split_bss = find_alloc_sections_prelink (unstripped, shstrtab, + stripped, stripped_ehdr, + data, bias, sections, + nalloc, stripped_shnum - 1); + } + + /* Make sure each main file section has a place to go. */ + const struct section *stripped_dynsym = NULL; + size_t debuglink = SHN_UNDEF; + size_t ndx_section[stripped_shnum - 1]; + struct Ebl_Strtab *strtab = NULL; + for (struct section *sec = sections; + sec < §ions[stripped_shnum - 1]; + ++sec) + { + size_t secndx = elf_ndxscn (sec->scn); + + if (sec->outscn == NULL) + { + /* We didn't find any corresponding section for this. */ + + if (secndx == stripped_shstrndx) + { + /* We only need one .shstrtab. */ + ndx_section[secndx - 1] = unstripped_shstrndx; + continue; + } + + if (unstripped_symtab != NULL && sec == stripped_symtab) + { + /* We don't need a second symbol table. */ + ndx_section[secndx - 1] = elf_ndxscn (unstripped_symtab); + continue; + } + + if (unstripped_symtab != NULL && stripped_symtab != NULL + && secndx == stripped_symtab->shdr.sh_link) + { + /* ... nor its string table. */ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (unstripped_symtab, &shdr_mem); + ELF_CHECK (shdr != NULL, _("cannot get section header: %s")); + ndx_section[secndx - 1] = shdr->sh_link; + continue; + } + + if (!(sec->shdr.sh_flags & SHF_ALLOC) + && !strcmp (sec->name, ".gnu_debuglink")) + { + /* This was created by stripping. We don't want it. */ + debuglink = secndx; + ndx_section[secndx - 1] = SHN_UNDEF; + continue; + } + + sec->outscn = elf_newscn (unstripped); + Elf_Data *newdata = elf_newdata (sec->outscn); + ELF_CHECK (newdata != NULL && gelf_update_shdr (sec->outscn, + &sec->shdr), + _("cannot add new section: %s")); + + if (strtab == NULL) + strtab = ebl_strtabinit (true); + sec->strent = ebl_strtabadd (strtab, sec->name, 0); + ELF_CHECK (sec->strent != NULL, + _("cannot add section name to string table: %s")); + } + + /* Cache the mapping of original section indices to output sections. */ + ndx_section[secndx - 1] = elf_ndxscn (sec->outscn); + } + + /* We added some sections, so we need a new shstrtab. */ + Elf_Data *strtab_data = new_shstrtab (unstripped, unstripped_shnum, + shstrtab, unstripped_shstrndx, + sections, stripped_shnum, + strtab); + + /* Get the updated section count. */ + ELF_CHECK (elf_getshnum (unstripped, &unstripped_shnum) == 0, + _("cannot get section count: %s")); + + bool placed[unstripped_shnum - 1]; + memset (placed, 0, sizeof placed); + + /* Now update the output sections and copy in their data. */ + GElf_Off offset = 0; + for (const struct section *sec = sections; + sec < §ions[stripped_shnum - 1]; + ++sec) + if (sec->outscn != NULL) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (sec->outscn, &shdr_mem); + ELF_CHECK (shdr != NULL, _("cannot get section header: %s")); + + /* In an ET_REL file under --relocate, the sh_addr of SHF_ALLOC + sections will have been set nonzero by relocation. This + touched the shdrs of whichever file had the symtab. sh_addr + is still zero in the corresponding shdr. The relocated + address is what we want to use. */ + if (stripped_ehdr->e_type != ET_REL + || !(shdr_mem.sh_flags & SHF_ALLOC) + || shdr_mem.sh_addr == 0) + shdr_mem.sh_addr = sec->shdr.sh_addr; + + shdr_mem.sh_type = sec->shdr.sh_type; + shdr_mem.sh_size = sec->shdr.sh_size; + shdr_mem.sh_info = sec->shdr.sh_info; + shdr_mem.sh_link = sec->shdr.sh_link; + if (sec->shdr.sh_link != SHN_UNDEF) + shdr_mem.sh_link = ndx_section[sec->shdr.sh_link - 1]; + if (shdr_mem.sh_flags & SHF_INFO_LINK) + shdr_mem.sh_info = ndx_section[sec->shdr.sh_info - 1]; + + if (strtab != NULL) + shdr_mem.sh_name = ebl_strtaboffset (sec->strent); + + Elf_Data *indata = elf_getdata (sec->scn, NULL); + ELF_CHECK (indata != NULL, _("cannot get section data: %s")); + Elf_Data *outdata = elf_getdata (sec->outscn, NULL); + ELF_CHECK (outdata != NULL, _("cannot copy section data: %s")); + *outdata = *indata; + elf_flagdata (outdata, ELF_C_SET, ELF_F_DIRTY); + + /* Preserve the file layout of the allocated sections. */ + if (stripped_ehdr->e_type != ET_REL && (shdr_mem.sh_flags & SHF_ALLOC)) + { + shdr_mem.sh_offset = sec->shdr.sh_offset; + placed[elf_ndxscn (sec->outscn) - 1] = true; + + const GElf_Off end_offset = (shdr_mem.sh_offset + + (shdr_mem.sh_type == SHT_NOBITS + ? 0 : shdr_mem.sh_size)); + if (end_offset > offset) + offset = end_offset; + } + + ELF_CHECK (gelf_update_shdr (sec->outscn, &shdr_mem), + _("cannot update section header: %s")); + + if (shdr_mem.sh_type == SHT_SYMTAB || shdr_mem.sh_type == SHT_DYNSYM) + { + /* We must adjust all the section indices in the symbol table. */ + + Elf_Data *shndxdata = NULL; /* XXX */ + + for (size_t i = 1; i < shdr_mem.sh_size / shdr_mem.sh_entsize; ++i) + { + GElf_Sym sym_mem; + GElf_Word shndx = SHN_UNDEF; + GElf_Sym *sym = gelf_getsymshndx (outdata, shndxdata, + i, &sym_mem, &shndx); + ELF_CHECK (sym != NULL, + _("cannot get symbol table entry: %s")); + if (sym->st_shndx != SHN_XINDEX) + shndx = sym->st_shndx; + + if (shndx != SHN_UNDEF && shndx < SHN_LORESERVE) + { + if (shndx >= stripped_shnum) + error (EXIT_FAILURE, 0, + _("symbol [%Zu] has invalid section index"), i); + + shndx = ndx_section[shndx - 1]; + if (shndx < SHN_LORESERVE) + { + sym->st_shndx = shndx; + shndx = SHN_UNDEF; + } + else + sym->st_shndx = SHN_XINDEX; + + ELF_CHECK (gelf_update_symshndx (outdata, shndxdata, + i, sym, shndx), + _("cannot update symbol table: %s")); + } + } + + if (shdr_mem.sh_type == SHT_SYMTAB) + stripped_symtab = sec; + if (shdr_mem.sh_type == SHT_DYNSYM) + stripped_dynsym = sec; + } + } + + /* We may need to update the symbol table. */ + Elf_Data *symdata = NULL; + struct Ebl_Strtab *symstrtab = NULL; + Elf_Data *symstrdata = NULL; + if (unstripped_symtab != NULL && (stripped_symtab != NULL + || check_prelink /* Section adjustments. */ + || (stripped_ehdr->e_type != ET_REL + && bias != 0))) + { + /* Merge the stripped file's symbol table into the unstripped one. */ + const size_t stripped_nsym = (stripped_symtab == NULL ? 1 + : (stripped_symtab->shdr.sh_size + / stripped_symtab->shdr.sh_entsize)); + + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (unstripped_symtab, &shdr_mem); + ELF_CHECK (shdr != NULL, _("cannot get section header: %s")); + const size_t unstripped_nsym = shdr->sh_size / shdr->sh_entsize; + + /* First collect all the symbols from both tables. */ + + const size_t total_syms = stripped_nsym - 1 + unstripped_nsym - 1; + struct symbol symbols[total_syms]; + size_t symndx_map[total_syms]; + + if (stripped_symtab != NULL) + collect_symbols (unstripped, stripped_ehdr->e_type == ET_REL, + stripped_symtab->scn, + elf_getscn (stripped, stripped_symtab->shdr.sh_link), + stripped_nsym, 0, ndx_section, + symbols, symndx_map, NULL); + + Elf_Scn *unstripped_strtab = elf_getscn (unstripped, shdr->sh_link); + collect_symbols (unstripped, stripped_ehdr->e_type == ET_REL, + unstripped_symtab, unstripped_strtab, unstripped_nsym, + stripped_ehdr->e_type == ET_REL ? 0 : bias, NULL, + &symbols[stripped_nsym - 1], + &symndx_map[stripped_nsym - 1], split_bss); + + /* Next, sort our array of all symbols. */ + qsort (symbols, total_syms, sizeof symbols[0], compare_symbols); + + /* Now we can weed out the duplicates. Assign remaining symbols + new slots, collecting a map from old indices to new. */ + size_t nsym = 0; + for (struct symbol *s = symbols; s < &symbols[total_syms]; ++s) + { + /* Skip a section symbol for a removed section. */ + if (s->shndx == SHN_UNDEF + && GELF_ST_TYPE (s->info.info) == STT_SECTION) + { + s->name = NULL; /* Mark as discarded. */ + *s->map = STN_UNDEF; + s->duplicate = NULL; + continue; + } + + struct symbol *n = s; + while (n + 1 < &symbols[total_syms] && !compare_symbols (s, n + 1)) + ++n; + + while (s < n) + { + /* This is a duplicate. Its twin will get the next slot. */ + s->name = NULL; /* Mark as discarded. */ + s->duplicate = n->map; + ++s; + } + + /* Allocate the next slot. */ + *s->map = ++nsym; + } + + /* Now we sort again, to determine the order in the output. */ + qsort (symbols, total_syms, sizeof symbols[0], compare_symbols_output); + + if (nsym < total_syms) + /* The discarded symbols are now at the end of the table. */ + assert (symbols[nsym].name == NULL); + + /* Now a final pass updates the map with the final order, + and builds up the new string table. */ + symstrtab = ebl_strtabinit (true); + for (size_t i = 0; i < nsym; ++i) + { + assert (symbols[i].name != NULL); + assert (*symbols[i].map != 0); + *symbols[i].map = 1 + i; + symbols[i].strent = ebl_strtabadd (symstrtab, symbols[i].name, 0); + } + + /* Scan the discarded symbols too, just to update their slots + in SYMNDX_MAP to refer to their live duplicates. */ + for (size_t i = nsym; i < total_syms; ++i) + { + assert (symbols[i].name == NULL); + if (symbols[i].duplicate == NULL) + assert (*symbols[i].map == STN_UNDEF); + else + { + assert (*symbols[i].duplicate != STN_UNDEF); + *symbols[i].map = *symbols[i].duplicate; + } + } + + /* Now we are ready to write the new symbol table. */ + symdata = elf_getdata (unstripped_symtab, NULL); + symstrdata = elf_getdata (unstripped_strtab, NULL); + Elf_Data *shndxdata = NULL; /* XXX */ + + ebl_strtabfinalize (symstrtab, symstrdata); + elf_flagdata (symstrdata, ELF_C_SET, ELF_F_DIRTY); + + shdr->sh_size = symdata->d_size = (1 + nsym) * shdr->sh_entsize; + symdata->d_buf = xmalloc (symdata->d_size); + + GElf_Sym sym; + memset (&sym, 0, sizeof sym); + ELF_CHECK (gelf_update_symshndx (symdata, shndxdata, 0, &sym, SHN_UNDEF), + _("cannot update symbol table: %s")); + + shdr->sh_info = 1; + for (size_t i = 0; i < nsym; ++i) + { + struct symbol *s = &symbols[i]; + + /* Fill in the symbol details. */ + sym.st_name = ebl_strtaboffset (s->strent); + sym.st_value = s->value; /* Already biased to output address. */ + sym.st_size = s->size; + sym.st_shndx = s->shndx; /* Already mapped to output index. */ + sym.st_info = s->info.info; + sym.st_other = s->info.other; + + /* Keep track of the number of leading local symbols. */ + if (GELF_ST_BIND (sym.st_info) == STB_LOCAL) + { + assert (shdr->sh_info == 1 + i); + shdr->sh_info = 1 + i + 1; + } + + ELF_CHECK (gelf_update_symshndx (symdata, shndxdata, 1 + i, + &sym, SHN_UNDEF), + _("cannot update symbol table: %s")); + + } + elf_flagdata (symdata, ELF_C_SET, ELF_F_DIRTY); + ELF_CHECK (gelf_update_shdr (unstripped_symtab, shdr), + _("cannot update section header: %s")); + + if (stripped_symtab != NULL) + { + /* Adjust any relocations referring to the old symbol table. */ + const size_t old_sh_link = elf_ndxscn (stripped_symtab->scn); + for (const struct section *sec = sections; + sec < §ions[stripped_shnum - 1]; + ++sec) + if (sec->outscn != NULL && sec->shdr.sh_link == old_sh_link) + adjust_relocs (sec->outscn, sec->scn, &sec->shdr, + symndx_map, shdr); + } + + /* Also adjust references to the other old symbol table. */ + adjust_all_relocs (unstripped, unstripped_symtab, shdr, + &symndx_map[stripped_nsym - 1]); + } + else if (stripped_symtab != NULL && stripped_shnum != unstripped_shnum) + check_symtab_section_symbols (unstripped, + stripped_ehdr->e_type == ET_REL, + stripped_symtab->scn, + unstripped_shnum, unstripped_shstrndx, + stripped_symtab->outscn, + stripped_shnum, stripped_shstrndx, + debuglink); + + if (stripped_dynsym != NULL) + (void) check_symtab_section_symbols (unstripped, + stripped_ehdr->e_type == ET_REL, + stripped_dynsym->outscn, + unstripped_shnum, + unstripped_shstrndx, + stripped_dynsym->scn, stripped_shnum, + stripped_shstrndx, debuglink); + + /* We need to preserve the layout of the stripped file so the + phdrs will match up. This requires us to do our own layout of + the added sections. We do manual layout even for ET_REL just + so we can try to match what the original probably had. */ + + elf_flagelf (unstripped, ELF_C_SET, ELF_F_LAYOUT); + + if (offset == 0) + /* For ET_REL we are starting the layout from scratch. */ + offset = gelf_fsize (unstripped, ELF_T_EHDR, 1, EV_CURRENT); + + bool skip_reloc = false; + do + { + skip_reloc = !skip_reloc; + for (size_t i = 0; i < unstripped_shnum - 1; ++i) + if (!placed[i]) + { + scn = elf_getscn (unstripped, 1 + i); + + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + ELF_CHECK (shdr != NULL, _("cannot get section header: %s")); + + if (skip_reloc + && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)) + continue; + + GElf_Off align = shdr->sh_addralign ?: 1; + offset = (offset + align - 1) & -align; + shdr->sh_offset = offset; + if (shdr->sh_type != SHT_NOBITS) + offset += shdr->sh_size; + + ELF_CHECK (gelf_update_shdr (scn, shdr), + _("cannot update section header: %s")); + + if (unstripped_shstrndx == 1 + i) + { + /* Place the section headers immediately after + .shstrtab, and update the ELF header. */ + + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr = gelf_getehdr (unstripped, &ehdr_mem); + ELF_CHECK (ehdr != NULL, _("cannot get ELF header: %s")); + + GElf_Off sh_align = gelf_getclass (unstripped) * 4; + offset = (offset + sh_align - 1) & -sh_align; + ehdr->e_shnum = unstripped_shnum; + ehdr->e_shoff = offset; + offset += unstripped_shnum * ehdr->e_shentsize; + ELF_CHECK (gelf_update_ehdr (unstripped, ehdr), + _("cannot update ELF header: %s")); + } + + placed[i] = true; + } + } while (skip_reloc); + + if (stripped_ehdr->e_phnum > 0) + ELF_CHECK (gelf_newphdr (unstripped, stripped_ehdr->e_phnum), + _("cannot create program headers: %s")); + + /* Copy each program header from the stripped file. */ + for (uint_fast16_t i = 0; i < stripped_ehdr->e_phnum; ++i) + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = gelf_getphdr (stripped, i, &phdr_mem); + ELF_CHECK (phdr != NULL, _("cannot get program header: %s")); + + ELF_CHECK (gelf_update_phdr (unstripped, i, phdr), + _("cannot update program header: %s")); + } + + /* Finally, write out the file. */ + ELF_CHECK (elf_update (unstripped, ELF_C_WRITE) > 0, + _("cannot write output file: %s")); + + if (strtab != NULL) + { + ebl_strtabfree (strtab); + free (strtab_data->d_buf); + } + + if (symdata != NULL) + free (symdata->d_buf); + if (symstrtab != NULL) + { + ebl_strtabfree (symstrtab); + free (symstrdata->d_buf); + } +} + +/* Process one pair of files, already opened. */ +static void +handle_file (const char *output_file, bool create_dirs, + Elf *stripped, const GElf_Ehdr *stripped_ehdr, + Elf *unstripped) +{ + /* Determine the address bias between the debuginfo file and the main + file, which may have been modified by prelinking. */ + GElf_Addr bias = 0; + if (unstripped != NULL) + for (uint_fast16_t i = 0; i < stripped_ehdr->e_phnum; ++i) + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = gelf_getphdr (stripped, i, &phdr_mem); + ELF_CHECK (phdr != NULL, _("cannot get program header: %s")); + if (phdr->p_type == PT_LOAD) + { + GElf_Phdr unstripped_phdr_mem; + GElf_Phdr *unstripped_phdr = gelf_getphdr (unstripped, i, + &unstripped_phdr_mem); + ELF_CHECK (unstripped_phdr != NULL, + _("cannot get program header: %s")); + bias = phdr->p_vaddr - unstripped_phdr->p_vaddr; + break; + } + } + + /* One day we could adjust all the DWARF data (like prelink itself does). */ + if (bias != 0) + { + if (output_file == NULL) + error (0, 0, _("\ +DWARF data not adjusted for prelinking bias; consider prelink -u")); + else + error (0, 0, _("\ +DWARF data in '%s' not adjusted for prelinking bias; consider prelink -u"), + output_file); + } + + if (output_file == NULL) + /* Modify the unstripped file in place. */ + copy_elided_sections (unstripped, stripped, stripped_ehdr, bias); + else + { + if (create_dirs) + make_directories (output_file); + + /* Copy the unstripped file and then modify it. */ + int outfd = open64 (output_file, O_RDWR | O_CREAT, + stripped_ehdr->e_type == ET_REL ? 0666 : 0777); + if (outfd < 0) + error (EXIT_FAILURE, errno, _("cannot open '%s'"), output_file); + Elf *outelf = elf_begin (outfd, ELF_C_WRITE, NULL); + ELF_CHECK (outelf != NULL, _("cannot create ELF descriptor: %s")); + + if (unstripped == NULL) + { + /* Actually, we are just copying out the main file as it is. */ + copy_elf (outelf, stripped); + if (stripped_ehdr->e_type != ET_REL) + elf_flagelf (outelf, ELF_C_SET, ELF_F_LAYOUT); + ELF_CHECK (elf_update (outelf, ELF_C_WRITE) > 0, + _("cannot write output file: %s")); + } + else + { + copy_elf (outelf, unstripped); + copy_elided_sections (outelf, stripped, stripped_ehdr, bias); + } + + elf_end (outelf); + close (outfd); + } +} + +static int +open_file (const char *file, bool writable) +{ + int fd = open64 (file, writable ? O_RDWR : O_RDONLY); + if (fd < 0) + error (EXIT_FAILURE, errno, _("cannot open '%s'"), file); + return fd; +} + +/* Handle a pair of files we need to open by name. */ +static void +handle_explicit_files (const char *output_file, bool create_dirs, + const char *stripped_file, const char *unstripped_file) +{ + int stripped_fd = open_file (stripped_file, false); + Elf *stripped = elf_begin (stripped_fd, ELF_C_READ, NULL); + GElf_Ehdr stripped_ehdr; + ELF_CHECK (gelf_getehdr (stripped, &stripped_ehdr), + _("cannot create ELF descriptor: %s")); + + int unstripped_fd = -1; + Elf *unstripped = NULL; + if (unstripped_file != NULL) + { + unstripped_fd = open_file (unstripped_file, output_file == NULL); + unstripped = elf_begin (unstripped_fd, + (output_file == NULL ? ELF_C_RDWR : ELF_C_READ), + NULL); + GElf_Ehdr unstripped_ehdr; + ELF_CHECK (gelf_getehdr (unstripped, &unstripped_ehdr), + _("cannot create ELF descriptor: %s")); + + if (memcmp (stripped_ehdr.e_ident, unstripped_ehdr.e_ident, EI_NIDENT) + || stripped_ehdr.e_type != unstripped_ehdr.e_type + || stripped_ehdr.e_machine != unstripped_ehdr.e_machine + || stripped_ehdr.e_phnum != unstripped_ehdr.e_phnum) + error (EXIT_FAILURE, 0, _("'%s' and '%s' do not seem to match"), + stripped_file, unstripped_file); + } + + handle_file (output_file, create_dirs, stripped, &stripped_ehdr, unstripped); + + elf_end (stripped); + close (stripped_fd); + + elf_end (unstripped); + close (unstripped_fd); +} + + +/* Handle a pair of files opened implicitly by libdwfl for one module. */ +static void +handle_dwfl_module (const char *output_file, bool create_dirs, + Dwfl_Module *mod, bool all, bool ignore, bool relocate) +{ + GElf_Addr bias; + Elf *stripped = dwfl_module_getelf (mod, &bias); + if (stripped == NULL) + { + if (ignore) + return; + + const char *file; + const char *modname = dwfl_module_info (mod, NULL, NULL, NULL, + NULL, NULL, &file, NULL); + if (file == NULL) + error (EXIT_FAILURE, 0, + _("cannot find stripped file for module '%s': %s"), + modname, dwfl_errmsg (-1)); + else + error (EXIT_FAILURE, 0, + _("cannot open stripped file '%s' for module '%s': %s"), + modname, file, dwfl_errmsg (-1)); + } + + Elf *debug = dwarf_getelf (dwfl_module_getdwarf (mod, &bias)); + if (debug == NULL && !all) + { + if (ignore) + return; + + const char *file; + const char *modname = dwfl_module_info (mod, NULL, NULL, NULL, + NULL, NULL, NULL, &file); + if (file == NULL) + error (EXIT_FAILURE, 0, + _("cannot find debug file for module '%s': %s"), + modname, dwfl_errmsg (-1)); + else + error (EXIT_FAILURE, 0, + _("cannot open debug file '%s' for module '%s': %s"), + modname, file, dwfl_errmsg (-1)); + } + + if (debug == stripped) + { + if (all) + debug = NULL; + else + { + const char *file; + const char *modname = dwfl_module_info (mod, NULL, NULL, NULL, + NULL, NULL, &file, NULL); + error (EXIT_FAILURE, 0, _("module '%s' file '%s' is not stripped"), + modname, file); + } + } + + GElf_Ehdr stripped_ehdr; + ELF_CHECK (gelf_getehdr (stripped, &stripped_ehdr), + _("cannot create ELF descriptor: %s")); + + if (stripped_ehdr.e_type == ET_REL) + { + if (!relocate) + { + /* We can't use the Elf handles already open, + because the DWARF sections have been relocated. */ + + const char *stripped_file = NULL; + const char *unstripped_file = NULL; + (void) dwfl_module_info (mod, NULL, NULL, NULL, NULL, NULL, + &stripped_file, &unstripped_file); + + handle_explicit_files (output_file, create_dirs, + stripped_file, unstripped_file); + return; + } + + /* Relocation is what we want! This ensures that all sections that can + get sh_addr values assigned have them, even ones not used in DWARF. + They might still be used in the symbol table. */ + if (dwfl_module_relocations (mod) < 0) + error (EXIT_FAILURE, 0, + _("cannot cache section addresses for module '%s': %s"), + dwfl_module_info (mod, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + dwfl_errmsg (-1)); + } + + handle_file (output_file, create_dirs, stripped, &stripped_ehdr, debug); +} + +/* Handle one module being written to the output directory. */ +static void +handle_output_dir_module (const char *output_dir, Dwfl_Module *mod, + bool all, bool ignore, bool modnames, bool relocate) +{ + if (! modnames) + { + /* Make sure we've searched for the ELF file. */ + GElf_Addr bias; + (void) dwfl_module_getelf (mod, &bias); + } + + const char *file; + const char *name = dwfl_module_info (mod, NULL, NULL, NULL, + NULL, NULL, &file, NULL); + + if (file == NULL && ignore) + return; + + char *output_file; + if (asprintf (&output_file, "%s/%s", output_dir, modnames ? name : file) < 0) + error (EXIT_FAILURE, 0, _("memory exhausted")); + + handle_dwfl_module (output_file, true, mod, all, ignore, relocate); +} + + +static void +list_module (Dwfl_Module *mod) +{ + /* Make sure we have searched for the files. */ + GElf_Addr bias; + bool have_elf = dwfl_module_getelf (mod, &bias) != NULL; + bool have_dwarf = dwfl_module_getdwarf (mod, &bias) != NULL; + + const char *file; + const char *debug; + Dwarf_Addr start; + Dwarf_Addr end; + const char *name = dwfl_module_info (mod, NULL, &start, &end, + NULL, NULL, &file, &debug); + if (file != NULL && debug != NULL && (debug == file || !strcmp (debug, file))) + debug = "."; + + const unsigned char *id; + GElf_Addr id_vaddr; + int id_len = dwfl_module_build_id (mod, &id, &id_vaddr); + + printf ("%#" PRIx64 "+%#" PRIx64 " ", start, end - start); + + if (id_len > 0) + { + do + printf ("%02" PRIx8, *id++); + while (--id_len > 0); + if (id_vaddr != 0) + printf ("@%#" PRIx64, id_vaddr); + } + else + putchar ('-'); + + printf (" %s %s %s\n", + file ?: have_elf ? "." : "-", + debug ?: have_dwarf ? "." : "-", + name); +} + + +struct match_module_info +{ + char **patterns; + Dwfl_Module *found; + bool match_files; +}; + +static int +match_module (Dwfl_Module *mod, + void **userdata __attribute__ ((unused)), + const char *name, + Dwarf_Addr start __attribute__ ((unused)), + void *arg) +{ + struct match_module_info *info = arg; + + if (info->patterns[0] == NULL) /* Match all. */ + { + match: + info->found = mod; + return DWARF_CB_ABORT; + } + + if (info->match_files) + { + /* Make sure we've searched for the ELF file. */ + GElf_Addr bias; + (void) dwfl_module_getelf (mod, &bias); + + const char *file; + const char *check = dwfl_module_info (mod, NULL, NULL, NULL, + NULL, NULL, &file, NULL); + assert (check == name); + if (file == NULL) + return DWARF_CB_OK; + + name = file; + } + + for (char **p = info->patterns; *p != NULL; ++p) + if (fnmatch (*p, name, 0) == 0) + goto match; + + return DWARF_CB_OK; +} + +/* Handle files opened implicitly via libdwfl. */ +static void +handle_implicit_modules (const struct arg_info *info) +{ + struct match_module_info mmi = { info->args, NULL, info->match_files }; + inline ptrdiff_t next (ptrdiff_t offset) + { + return dwfl_getmodules (info->dwfl, &match_module, &mmi, offset); + } + ptrdiff_t offset = next (0); + if (offset == 0) + error (EXIT_FAILURE, 0, _("no matching modules found")); + + if (info->list) + do + list_module (mmi.found); + while ((offset = next (offset)) > 0); + else if (info->output_dir == NULL) + { + if (next (offset) != 0) + error (EXIT_FAILURE, 0, _("matched more than one module")); + handle_dwfl_module (info->output_file, false, mmi.found, + info->all, info->ignore, info->relocate); + } + else + do + handle_output_dir_module (info->output_dir, mmi.found, + info->all, info->ignore, + info->modnames, info->relocate); + while ((offset = next (offset)) > 0); +} + +int +main (int argc, char **argv) +{ + /* Make memory leak detection possible. */ + mtrace (); + + /* We use no threads here which can interfere with handling a stream. */ + __fsetlocking (stdin, FSETLOCKING_BYCALLER); + __fsetlocking (stdout, FSETLOCKING_BYCALLER); + __fsetlocking (stderr, FSETLOCKING_BYCALLER); + + /* Set locale. */ + setlocale (LC_ALL, ""); + + /* Make sure the message catalog can be found. */ + bindtextdomain (PACKAGE_TARNAME, LOCALEDIR); + + /* Initialize the message catalog. */ + textdomain (PACKAGE_TARNAME); + + /* Parse and process arguments. */ + const struct argp_child argp_children[] = + { + { + .argp = dwfl_standard_argp (), + .header = N_("Input selection options:"), + .group = 1, + }, + { .argp = NULL }, + }; + const struct argp argp = + { + .options = options, + .parser = parse_opt, + .children = argp_children, + .args_doc = N_("STRIPPED-FILE DEBUG-FILE\n[MODULE...]"), + .doc = N_("\ +Combine stripped files with separate symbols and debug information.\v\ +The first form puts the result in DEBUG-FILE if -o was not given.\n\ +\n\ +MODULE arguments give file name patterns matching modules to process.\n\ +With -f these match the file name of the main (stripped) file \ +(slashes are never special), otherwise they match the simple module names. \ +With no arguments, process all modules found.\n\ +\n\ +Multiple modules are written to files under OUTPUT-DIRECTORY, \ +creating subdirectories as needed. \ +With -m these files have simple module names, otherwise they have the \ +name of the main file complete with directory underneath OUTPUT-DIRECTORY.\n\ +\n\ +With -n no files are written, but one line to standard output for each module:\ +\n\tSTART+SIZE BUILDID FILE DEBUGFILE MODULENAME\n\ +START and SIZE are hexadecimal giving the address bounds of the module. \ +BUILDID is hexadecimal for the build ID bits, or - if no ID is known; \ +the hexadecimal may be followed by @0xADDR giving the address where the \ +ID resides if that is known. \ +FILE is the file name found for the module, or - if none was found, \ +or . if an ELF image is available but not from any named file. \ +DEBUGFILE is the separate debuginfo file name, \ +or - if no debuginfo was found, or . if FILE contains the debug information.\ +") + }; + + int remaining; + struct arg_info info = { .args = NULL }; + error_t result = argp_parse (&argp, argc, argv, 0, &remaining, &info); + if (result == ENOSYS) + assert (info.dwfl == NULL); + else if (result) + return EXIT_FAILURE; + assert (info.args != NULL); + + /* Tell the library which version we are expecting. */ + elf_version (EV_CURRENT); + + if (info.dwfl == NULL) + { + assert (result == ENOSYS); + + if (info.output_dir != NULL) + { + char *file; + if (asprintf (&file, "%s/%s", info.output_dir, info.args[0]) < 0) + error (EXIT_FAILURE, 0, _("memory exhausted")); + handle_explicit_files (file, true, info.args[0], info.args[1]); + free (file); + } + else + handle_explicit_files (info.output_file, false, + info.args[0], info.args[1]); + } + else + { + /* parse_opt checked this. */ + assert (info.output_file != NULL || info.output_dir != NULL || info.list); + + handle_implicit_modules (&info); + + dwfl_end (info.dwfl); + } + + return 0; +} + + +#include "debugpred.h" diff --git a/src/versionhash.c b/src/versionhash.c index 79b2e105..6126eb95 100644 --- a/src/versionhash.c +++ b/src/versionhash.c @@ -1,16 +1,28 @@ /* Version symbol hash table implementation. Copyright (C) 2001, 2002 Red Hat, Inc. + This file is part of Red Hat elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 2001. - This program is Open Source software; you can redistribute it and/or - modify it under the terms of the Open Software License version 1.0 as - published by the Open Source Initiative. + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. - You should have received a copy of the Open Software License along - with this program; if not, you may obtain a copy of the Open Software - License version 1.0 from http://www.opensource.org/licenses/osl.php or - by writing the Open Source Initiative c/o Lawrence Rosen, Esq., - 3001 King Ranch Road, Ukiah, CA 95482. */ + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ #ifdef HAVE_CONFIG_H # include <config.h> diff --git a/src/versionhash.h b/src/versionhash.h index 243aeeb5..63ca1145 100644 --- a/src/versionhash.h +++ b/src/versionhash.h @@ -1,15 +1,27 @@ /* Copyright (C) 2001, 2002 Red Hat, Inc. + This file is part of Red Hat elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 2001. - This program is Open Source software; you can redistribute it and/or - modify it under the terms of the Open Software License version 1.0 as - published by the Open Source Initiative. + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. - You should have received a copy of the Open Software License along - with this program; if not, you may obtain a copy of the Open Software - License version 1.0 from http://www.opensource.org/licenses/osl.php or - by writing the Open Source Initiative c/o Lawrence Rosen, Esq., - 3001 King Ranch Road, Ukiah, CA 95482. */ + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ #ifndef VERSIONHASH_H #define VERSIONHASH_H 1 @@ -1,16 +1,28 @@ /* Macros to enable writing native and generic ELF access code. Copyright (C) 2003 Red Hat, Inc. + This file is part of Red Hat elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 2003. - This program is Open Source software; you can redistribute it and/or - modify it under the terms of the Open Software License version 1.0 as - published by the Open Source Initiative. - - You should have received a copy of the Open Software License along - with this program; if not, you may obtain a copy of the Open Software - License version 1.0 from http://www.opensource.org/licenses/osl.php or - by writing the Open Source Initiative c/o Lawrence Rosen, Esq., - 3001 King Ranch Road, Ukiah, CA 95482. */ + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ #ifdef HAVE_CONFIG_H # include <config.h> @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a |