diff options
author | Ben Cheng <bccheng@google.com> | 2013-11-20 14:45:36 -0800 |
---|---|---|
committer | Ben Cheng <bccheng@google.com> | 2013-11-20 14:45:36 -0800 |
commit | 25b3c049e70834cf33790a28643ab058b507b35c (patch) | |
tree | 39d448fecbc10f8da80f59ab09530b487be66c89 /0.153/src | |
parent | e77913632d2f2824def79f8e747e0be2a4adc584 (diff) | |
download | android_external_elfutils-25b3c049e70834cf33790a28643ab058b507b35c.tar.gz android_external_elfutils-25b3c049e70834cf33790a28643ab058b507b35c.tar.bz2 android_external_elfutils-25b3c049e70834cf33790a28643ab058b507b35c.zip |
Initial code drop of version 0.153
Change-Id: I97d4765497db839471de6b10aa612bfaca110297
Diffstat (limited to '0.153/src')
43 files changed, 48402 insertions, 0 deletions
diff --git a/0.153/src/ChangeLog b/0.153/src/ChangeLog new file mode 100644 index 00000000..ff19b484 --- /dev/null +++ b/0.153/src/ChangeLog @@ -0,0 +1,2321 @@ +2012-01-31 Mark Wielaard <mjw@redhat.com> + + * readelf.c (attr_callback): Don't special case DW_FORM_sec_offset. + +2012-01-21 Ulrich Drepper <drepper@gmail.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. + + * nm.c (argp_children): Define. + (argp): Hook up argp_children. + (handle_ar): Optimize puts call. + (show_symbols_bsd): Use positional parameters to also print color + codes. Don't print STT_FILE symbols. + * objdump.c (options): Improve help text. + (argp_children): Define. + (argp): Hook up argp_children. + (disasm_info): Add elements for color codes. + (disasm_output): Print color codes as well. + (show_disasm): Set up disasm_info data for callback. + +2012-01-20 Roland McGrath <roland@hack.frob.com> + + * arlib-argp.c (arlib_deterministic_output): Initialize from + configured value. + (help_filter): New function. + (argp): Use it. + + * ar.c (main): Handle oper_none as usage error. + + * arlib-argp.c (options, parse_opt): Grok -U as inverse of -D. + + * ranlib.c (argp): Use arlib_argp_children. + + * arlib.c (arlib_init): Obey arlib_deterministic_output. + + * arlib-argp.c: New file. + * Makefile.am (libar_a_SOURCES): Add it. + * arlib.h (arlib_deterministic_output, arlib_argp_children): + Declare new variables. + * ar.c (deterministic_output): Variable removed. + (do_oper_insert): Use arlib_deterministic_output instead. + (options, parse_opt): Don't handle -D here. Add group numbers. + (argp): Use arlib_argp_children. + +2011-12-20 Roland McGrath <roland@hack.frob.com> + + * readelf.c (print_debug): Initialize DUMMY_DBG.elf. + Reported by Karel Klic <kklic@redhat.com>. + +2011-11-05 Roland McGrath <roland@hack.frob.com> + + * ar.c (deterministic_output): New flag variable. + (options, parse_opt): Grok -D to set it. + (do_oper_insert): When set, use zero from mtime, uid, and gid. + + * ar.c (do_oper_insert): Fix check on elf_rawfile return value. + +2011-10-04 Marek Polacek <mpolacek@redhat.com> + + * readelf.c (register_info): Assume the right size of an array. + +2011-10-03 Ulrich Drepper <drepper@gmail.com> + + * nm.c: Recognize option --mark-special. Still recognize --mark-weak + but don't show it in help anymore. + (mark_special): Renamed from mark_weak. + (parse_opt): Adjust. + (class_type_char): Take additional parameters for ELF file and ELF + header. Treat TLS symbols like objects. + In case of D symbols, show u for unique symbols, R for symbols in + read-only sections, B for symbols in BSS sections. + (show_symbols_bsd): Take additional parameters for ELF file and ELF + header. Adjust for class_type_char change. Show TLS symbols with + @ after them in case --mark-special is selected. + (show_symbols_posix): Likewise. + (show_symbols): Adjust calls to show_symbols_bsd and + show_symbols_posix. + (show_symbols_sysv): Avoid printing adress and size for undefined + symbols. Don't print initial special entry and section entries. + +2011-10-02 Ulrich Drepper <drepper@gmail.com> + + * Makefile.am (demanglelib): Define. + (nm_LDADD): Add demanglelib. + * nm.c (options): Add -C option. + (demangle): Define as global variable. + (parse_opt): Recognize -C. + (show_symbols_sysv): Handle demangling. + (show_symbols_bad): Likewise. + (show_symbols_posix): Likewise. + (show_symbols): Likewise. + +2011-07-09 Roland McGrath <roland@hack.frob.com> + + * readelf.c (options, parse_opt): Grok -W/--wide and ignore it. + + * ar.c (parse_opt): Grok -u. + +2011-05-30 Mark Wielaard <mjw@redhat.com> + + * strip.c (relocate): Make offset check overflow-proof. + +2011-05-23 Mark Wielaard <mjw@redhat.com> + + * strip.c (relocate): Take new arguments is_rela to indicate + whether the relocation is from a SHT_REL or SHT_RELA section. + Relocate against any debug section symbol, not just STT_SECTION + symbols. For SHT_REL relocations, fetch addend from offset and + add it to symbol value if not zero. + +2011-05-23 Mark Wielaard <mjw@redhat.com> + + * strip.c (OPT_RELOC_DEBUG): New option. + (argp_option): Add new --reloc-debug-sections option. + (main): Check new option. + (parse_opt): Likewise. + (handle_elf): Remove any relocations between debug sections + in ET_REL for the debug file when requested. + +2011-05-18 Mark Wielaard <mjw@redhat.com> + + * strip.c (handle_elf): Make sure all sections of a removed group + section are removed too. Don't discard SHT_GROUP sections, copy + section table before it gets modified. Section group signature + symbols don't have to be retained. + +2011-05-16 Jakub Jelinek <jakub@redhat.com> + + * readelf.c (print_ops): Handle DW_OP_GNU_const_type, + DW_OP_GNU_regval_type, DW_OP_GNU_deref_type, DW_OP_GNU_convert + and DW_OP_GNU_reinterpret. + +2011-05-17 Mark Wielaard <mjw@redhat.com> + + * readelf.c (dwarf_tag_string): Fixup DW_TAG_GNU_call_site and + DW_TAG_GNU_call_site_parameter return strings. + +2011-05-11 Marek Polacek <mpolacek@redhat.com> + + * nm.c (show_symbols_sysv): Remove unused if/else, remove + unused `prefix' and `fname' parameters. + +2011-05-07 Marek Polacek <mpolacek@redhat.com> + + * unstrip.c (compare_sections_nonrel): Mark this function as static. + +2011-04-26 Mark Wielaard <mjw@redhat.com> + + * readelf.c (handle_notes_data): Call ebl_object_note_type_name + with note name. + +2011-04-14 Mark Wielaard <mjw@redhat.com> + + * readelf.c (options): Add gdb_index. + (section_e): Define section_gdb_index. + (parse_opt): Recognize gdb_index debug-dump argument. + (print_gdb_index_section): New function. + (print_debug): Add gdb_index to debug_sections. + +2011-03-24 Petr Machata <pmachata@redhat.com> + + * readelf.c (print_debug_line_section): Emit initial space for all + opcode lines. Print offset in front of each opcode. + +2011-03-22 Marek Polacek <mpolacek@redhat.com> + + * readelf.c (handle_dynamic): Don't segfault at DT_PLTREL case. + +2011-03-22 Mark Wielaard <mjw@redhat.com> + + * readelf.c (dwarf_tag_string): Support DW_TAG_GNU_call_site + and DW_TAG_GNU_call_site_parameter. + (dwarf_attr_string): Support DW_AT_GNU_call_site_value, + DW_AT_GNU_call_site_data_value, + DW_AT_GNU_call_site_target, + DW_AT_GNU_call_site_target_clobbered, + DW_AT_GNU_tail_call, + DW_AT_GNU_all_tail_call_sites, + DW_AT_GNU_all_call_sites, + and DW_AT_GNU_all_source_call_sites. + (print_ops): Handle DW_OP_GNU_entry_value. + (attr_callback): Handle DW_AT_GNU_call_site_value, + DW_AT_GNU_call_site_data_value, + DW_AT_GNU_call_site_target, + and DW_AT_GNU_call_site_target_clobbered. + +2011-03-10 Mark Wielaard <mjw@redhat.com> + + * elflint.c (check_symtab): Use ebl_check_st_other_bits. + +2011-02-27 Jan Kratochvil <jan.kratochvil@redhat.com> + + * readelf.c (reset_listptr): Clear TABLE->TABLE. + +2011-02-25 Mark Wielaard <mjw@redhat.com> + + * readelf.c (dwarf_attr_string): Add DW_AT_GNU_* handling. + (dwarf_form_string): Properly format and return unknown form. + +2011-02-23 Roland McGrath <roland@redhat.com> + + * readelf.c (section_name): New function. + (print_debug_abbrev_section): Use it instead of constant. + (print_debug_aranges_section): Likewise. + (print_debug_ranges_section): Likewise. + (print_debug_units): Likewise. + (print_debug_line_section): Likewise. + (print_debug_loc_section): Likewise. + (print_debug_macinfo_section): Likewise. + (print_debug_pubnames_section): Likewise. + (print_debug_str_section): Likewise. + (print_debug) [USE_ZLIB]: Match .zdebug_* sections too. + (print_debug_abbrev_section): Use decoded d_size, not sh_size. + (print_debug_str_section): Likewise. + + * readelf.c (dwarf_attr_string): Grok DW_AT_GNU_odr_signature. + +2011-02-11 Roland McGrath <roland@redhat.com> + + * elfcmp.c (verbose): New variable. + (options, parse_opt): Grok -l/--verbose to set it. + (main): Under -l, keep going after first difference. + + * elfcmp.c (ignore_build_id): New variable. + (options, parse_opt): Grok --ignore-build-id to set it. + (main): For SHT_NOTE sections, compare note details rather than raw + bytes. Under --ignore-build-id, don't complain about differing build + ID contents if lengths match. + +2011-02-08 Roland McGrath <roland@redhat.com> + + * ldscript.y (filename_id_star): Remove unused variable. + + * unstrip.c (copy_elided_sections): Remove unused variable. + + * elflint.c (check_dynamic): Remove unused variables. + + * elflint.c (check_symtab): Warn about missing xndx section only once. + + * ldgeneric.c (check_for_duplicate2): Remove unused variable. + +2011-01-06 Roland McGrath <roland@redhat.com> + + * strip.c (handle_elf): Under --strip-sections, remove all + non-allocated sections and never generate .gnu_debuglink. + +2011-01-04 Roland McGrath <roland@redhat.com> + + * strip.c (remove_shdrs): New variable. + (options, parse_opt): Grok --strip-sections to set it. + (handle_elf): When that's set, truncate off .shstrtab and shdrs. + +2010-11-10 Roland McGrath <roland@redhat.com> + + * findtextrel.c (process_file): Don't assume order of sections. + Reported by Mike Hommey <mh@glandium.org>. + +2010-07-26 Roland McGrath <roland@redhat.com> + + * readelf.c (print_ops): Handle DW_OP_GNU_implicit_pointer. + +2010-08-30 Roland McGrath <roland@redhat.com> + + Print .debug_loc/.debug_ranges with cognizance of actual DIE uses. + * readelf.c (parse_opt): Add section_info to implicit_debug_sections + for ranges, loc. + (struct listptr, struct listptr_table): New types. + (compare_listptr, reset_listptr, sort_listptr): New functions. + (notice_listptr, skip_listptr_hole): New functions. + (struct attrcb_args): Add silent member. + (attr_callback): Call notice_listptr for loclistptr and rangelistptr. + Suppress output if silent, but still call notice_listptr. + (print_debug_units): Suppress output if section_info not requested. + (print_debug_loc_section): Call sort_listptr, skip_listptr_hole. + (print_debug_ranges_section): Likewise. + (print_debug): Call reset_listptr on both tables. + + * readelf.c (print_debug_ranges_section): Print empty list. + (print_debug_loc_section): Likewise. + + * readelf.c (print_debug_loc_section): Check for bogus length + before calling print_ops. + (print_ops): Check harder for bogus data that would read off end. + +2010-08-11 Roland McGrath <roland@redhat.com> + + * readelf.c (for_each_section_argument): Process all sections with + matching name, not just the first. + +2010-07-26 Roland McGrath <roland@redhat.com> + + * readelf.c (print_ops): Take new argument for CU version. + Fix DW_OP_call_ref decoding to depend on it. + (print_debug_loc_section): Update caller. + (print_cfa_program): Take new argument, pass it down. + (print_debug_frame_section): Update caller. + (struct attrcb_args): New member version. + (print_debug_units): Initialize it. + +2010-07-02 Roland McGrath <roland@redhat.com> + + * readelf.c (print_debug_frame_section): Use format_dwarf_addr for + initial_location. + +2010-06-30 Roland McGrath <roland@redhat.com> + + * strings.c (main): Use STDIN_FILENO, not STDOUT_FILENO. + Ignore st_size for a non-S_ISREG file descriptor. + (read_block): Move assert after no-mmap bail-out. + (read_block_no_mmap): Fix size calculations for moving buffer remnant. + +2010-06-22 Roland McGrath <roland@redhat.com> + + * readelf.c (print_debug_line_section): Fix braino in DW_LNS_set_isa. + +2010-06-21 Roland McGrath <roland@redhat.com> + + * readelf.c (dwarf_tag_string): Handle new v4 tags. + (dwarf_attr_string): Add new attributes. + (dwarf_tag_string): Handle DW_TAG_GNU_*. + + * readelf.c (print_ops): Use 64-bit types for LEB128 operands. + (print_cfa_program): Likewise. + +2010-06-20 Roland McGrath <roland@redhat.com> + + * readelf.c (print_debug_units): New function, broken out of ... + (print_debug_info_section): ... here. Call it. + (print_debug_types_section): New function. + (enum section_e): Add section_types alias for section_info. + (print_debug): Add types to the sections table. + + * readelf.c (print_debug_frame_section): Handle version 4 format. + + * readelf.c (print_debug_line_section): Handle version 4 format. + +2010-06-14 Roland McGrath <roland@redhat.com> + + * unstrip.c (copy_elided_sections): Make sure all sections' data have + been read in before we write anything out. + +2010-06-04 Roland McGrath <roland@redhat.com> + + * unstrip.c (update_shdr): New function. + (update_sh_size): Call it instead of gelf_update_shdr. + (adjust_relocs, add_new_section_symbols): Likewise. + (new_shstrtab, copy_elided_sections): Likewise. + + * unstrip.c (copy_elided_sections): Bail if stripped file has more + sections than unstripped file, rather than getting confused later. + +2010-06-01 Roland McGrath <roland@redhat.com> + + * readelf.c (dwarf_form_string): Handle DWARF 4 forms. + (attr_callback): Handle DW_FORM_flag_present, DW_FORM_exprloc, + DW_FORM_sec_offset, DW_FORM_ref_sig8. + + * readelf.c (print_debug): Don't bail if libdw setup fails. + Suppress complaint if we only want .eh_frame anyway. + +2010-05-28 Ulrich Drepper <drepper@redhat.com> + + * readelf.c (attr_callback): Also print form information. + +2010-05-19 Roland McGrath <roland@redhat.com> + + * addr2line.c (find_symbol): Short-circuit on empty name. + (handle_address): Handle SYMBOL with no +OFFSET. + +2010-05-08 Roland McGrath <roland@redhat.com> + + * readelf.c (print_ops): Take new arg OFFSET_SIZE. + Use that for DW_OP_call_ref, not ADDRSIZE. + (print_cfa_program): Update caller. + (struct attrcb_args): Add offset_size field. + (attr_callback): Use it for print_ops call. + (print_debug_info_section): Initialize it. + (print_ops): Likewise. + +2010-04-14 Roland McGrath <roland@redhat.com> + + * readelf.c (handle_core_item): Fix bitmask printing. + +2010-04-06 Roland McGrath <roland@redhat.com> + + * ld.c (options): Fix some typos in messages. + * elflint.c (check_scn_group, check_group): Likewise. + * ldscript.y (add_id_list): Likewise. + * readelf.c (print_hash_info): Add xgettext:no-c-format magic comment + before translated string containing a literal %. + +2010-02-26 Roland McGrath <roland@redhat.com> + + * readelf.c (process_file): Don't leak an fd in failure case. + +2010-02-15 Roland McGrath <roland@redhat.com> + + * Makefile.am: Use config/eu.am for common stuff. + + * readelf.c (print_debug_frame_section): Add a cast to avoid sign + mismatch in comparison. + +2010-02-02 Roland McGrath <roland@redhat.com> + + * readelf.c (print_encoding_base): Handle DW_EH_PE_absptr (zero). + (read_encoded): Likewise. + (print_debug_frame_section): Check for bogus augmentation length. + For P augmentation, use read_encoded, print the encoding description, + and use hex for unsigned values. + +2010-01-15 Roland McGrath <roland@redhat.com> + + * ar.c: Include <sys/stat.h>. + * elflint.c: Likewise. + * readelf.c: Likewise. + * strip.c: Likewise. + * unstrip.c: Likewise + +2010-01-07 Roland McGrath <roland@redhat.com> + + * readelf.c (print_ehdr): Handle PN_XNUM. + (phnum): New static variable. + (process_elf_file): Set it with elf_getphdrnum. + (print_phdr): Use phnum instead of EHDR->e_phnum. + (print_dynamic, handle_notes): Likewise. + (handle_relocs_rel, handle_relocs_rela): Likewise. + + * elfcmp.c (main): Use elf_getshdrnum and elf_getphdrnum. + + * elflint.c (phnum): New static variable. + (check_elf_header): Set it, handling PN_XNUM. + Use that in place of EHDR->e_phnum throughout. + (check_symtab, check_reloc_shdr, check_dynamic): Likewise. + (unknown_dependency_p, check_sections, check_program_header): Likewise. + +2010-01-05 Roland McGrath <roland@redhat.com> + + * readelf.c (dwarf_attr_string): Match DW_AT_GNU_vector and + DW_AT_GNU_template_name. + +2010-01-04 Roland McGrath <roland@redhat.com> + + * readelf.c (handle_notes_data): Grab NT_AUXV only for name "CORE". + (handle_core_note): Pass NHDR and NAME to ebl_core_note. + (handle_core_item): Handle .format of '\n' as \n-separated strings. + + * readelf.c (implicit_debug_sections): New variable. + (parse_opt): Set it instead of print_debug_sections for -a. + OR them together for print_debug check. + (print_debug): OR them together for section check. + + * readelf.c (options): Repartition into set implied by -a and others. + Correct -a text to match reality. + + * readelf.c (struct section_argument): Add bool member 'implicit'. + (parse_opt): Set it for -a cases, clear it for -x args. + (for_each_section_argument): Don't complain about a missing section by + name if it's implicit. + +2009-11-16 Roland McGrath <roland@redhat.com> + + * readelf.c (print_string_section): Punt SHT_NOBITS like empty + sections, just as dump_data_section already does. + +2009-09-21 Ulrich Drepper <drepper@redhat.com> + + * elflint.c (special_sections): Allow MERGE and STRINGS flags to be + set for .comment section. + Patch by Mark Wielaard <mjw@redhat.com>. + +2009-09-08 Roland McGrath <roland@redhat.com> + + * ar.c (main): Fix typo in message format. + +2009-08-21 Roland McGrath <roland@redhat.com> + + * readelf.c (attr_callback): Use print_block only when we don't use + print_ops. + +2009-08-14 Roland McGrath <roland@redhat.com> + + * ar.c (do_oper_extract): Use pathconf instead of statfs. + +2009-08-01 Ulrich Drepper <drepper@redhat.com> + + * debugpred.h: Add two most const. + +2009-07-26 Mark Wielaard <mjw@redhat.com> + + * elflint.c (check_note_data): Recognize NT_GNU_GOLD_VERSION. + +2009-07-25 Mark Wielaard <mjw@redhat.com> + + * Makefile.am (addr2line_LDADD): Add $(libelf). + +2009-07-24 Roland McGrath <roland@redhat.com> + + * readelf.c (print_block): New function. + (print_ops): Use it. + (attr_callback): Use it for DW_FORM_block* forms. + +2009-07-20 Mark Wielaard <mjw@redhat.com> + + * readelf.c (print_ops): Add handling of DW_OP_implicit_value + and DW_OP_stack_value. + +2009-07-14 Ulrich Drepper <drepper@redhat.com> + + * elflint.c (check_elf_header): Allow Linux ABI. + (check_symtab): Handle STB_GNU_UNIQUE. + +2009-07-08 Mark Wielaard <mjw@redhat.com> + + * readelf.c (attr_callback): Handle DW_Form constants for + DW_AT_data_member_location. + +2009-07-06 Roland McGrath <roland@redhat.com> + + * readelf.c (register_info): New function. Handle unknown register #s. + (print_cfa_program): Use it. + (handle_core_register, handle_core_registers): Likewise. + +2009-06-28 Roland McGrath <roland@redhat.com> + + * readelf.c (print_address_names): New static variable. + (options, parse_opt): Grok -N/--numeric-addresses to clear it. + (format_dwarf_addr): Don't look up name if !print_address_names. + +2009-06-13 Ulrich Drepper <drepper@redhat.com> + + * ldgeneric.c: Don't use deprecated libelf functions. + * nm.c: Likewise. + * objdump.c: Likewise. + * readelf.c: Likewise. + * size.c: Likewise. + * strip.c: Likewise. + * unstrip.c: Likewise. + * ld.h: Fix up comment. + +2009-06-01 Ulrich Drepper <drepper@redhat.com> + + * readelf.c (print_relocs): Expect ELF header argument and pass on + to handle_relocs_rel* functions. Adjust caller. + (handle_relocs_rel): Add ELF header argument. Add special case for + the IRELATIVE relocations in statically linked executables. + (handle_relocs_rela): Likewise. + +2009-04-29 Ulrich Drepper <drepper@redhat.com> + + * elflint.c (check_symtab): Add tests of st_other field. + +2009-04-23 Ulrich Drepper <drepper@redhat.com> + + * Makefile [BUILD_STATIC] (libdw): Add $(zip_LIBS). + +2009-04-20 Roland McGrath <roland@redhat.com> + + * addr2line.c (print_dwarf_function): Honor -s and -A for file names + of inline call sites. + + * addr2line.c (just_section): New variable. + (adjust_to_section): New function, broken out of ... + (handle_address): ... here. + (options, parse_opt): Add -j/--section=NAME to set it. + +2009-04-15 Roland McGrath <roland@redhat.com> + + * readelf.c (print_debug_frame_section): Check for DW_CIE_ID_64 in + 64-bit format header, DW_CIE_ID_32 in 32-bit format header. + +2009-04-14 Roland McGrath <roland@redhat.com> + + * readelf.c (print_attributes): Treat SHT_ARM_ATTRIBUTES on EM_ARM + like SHT_GNU_ATTRIBUTES. + + * readelf.c (handle_core_registers): Fix error message. + + * strip.c (handle_elf: check_preserved): Don't note any change when + .debug_data is already filled from a previous pass. + +2009-02-05 Ulrich Drepper <drepper@redhat.com> + + * objdump.c (show_relocs_x): Minor cleanups. + + * readelf.c (print_cfa_program): Correct a few labels. + Print first DW_CFA_expression and DW_CFA_val_expression parameter + as register. + +2009-02-01 Ulrich Drepper <drepper@redhat.com> + + * objdump.c (show_relocs_rel, show_relocs_rela): Split common parts + into ... + (show_relocs_x): ...here. New function. + (show_relocs): Better spacing in output. + + * objdump.c (show_relocs_rela): Show offsets as signed values. + + * ar.c (main): Fix recognition of invalid modes for a, b, i modifiers. + Improve some error messages. + Use program_invocation_short_name instead of AR macro. + * Makefile.am (CFLAGS_ar): Remove. + * elflint.c (parse_opt): ARGP_HELP_EXIT_ERR does nothing for argp_help. + * objdump.c (parse_opt): Likewise. + * readelf.c (parse_opt): Likewise. + +2009-01-27 Roland McGrath <roland@redhat.com> + + * readelf.c (print_ops): Notice short length, don't overrun buffer + (still need to fix LEB128). + + * readelf.c (print_ops): Fix DW_OP_call[24] decoding. + + * readelf.c (print_ops): Print (empty)\n when LEN == 0. + +2009-01-24 Ulrich Drepper <drepper@redhat.com> + + * readelf.c (print_debug_frame_section): Fix computation of vma_base + for PC-relative mode. + +2009-01-23 Ulrich Drepper <drepper@redhat.com> + + * size.c (process_file): When handling archive, close file descriptor + here. For unknown file format also close file descriptor. + (handle_ar): Don't close file descriptor here. + + * readelf.c (parse_opt): Move code to add to dump_data_sections and + string_sections list in local function add_dump_section. Adjust 'x' + key handling. For 'a' key add .strtab, .dynstr, and .comment section + to string_sections list. + +2009-01-22 Roland McGrath <roland@redhat.com> + + * readelf.c (print_phdr): Don't print section mapping when no sections. + + * Makefile.am (AM_CFLAGS): Pass -Wno-format for *_no_Wformat. + + * readelf.c (print_debug_frame_section): Initialize IS_SIGNED to false + and reset it only for the 'true' cases. + + * Makefile.am (addr2line_no_Wformat): New variable. + + * readelf.c (print_debug_frame_section): Use t instead of j formats + for ptrdiff_t OFFSET. + +2009-01-21 Ulrich Drepper <drepper@redhat.com> + + * elflint.c (check_program_header): Fix typo in .eh_frame_hdr section + test. Handle debuginfo files. + (check_exception_data): First sanity test. + +2009-01-17 Ulrich Drepper <drepper@redhat.com> + + * readelf.c (print_debug_exception_table): Show target of ar_disp + field. + + * elflint.c (check_program_header): Add most consistency checks for + PT_GNU_EH_FRAME entry. + + * addr2line.c: Use ARGP_PROGRAM_VERSION_HOOK_DEF and + ARGP_PROGRAM_BUG_ADDRESS_DEF. + * 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. + +2009-01-16 Ulrich Drepper <drepper@redhat.com> + + * elflint.c (check_program_header): Check that PT_GNU_EH_FRAME entry + matches .eh_frame_hdr section, if it is available. Also check that + the segment is allocated, not writable, not executable. + + * readelf.c: Add -e option. Dump exception and unwind related + sections. Add -e to -a. + (print_encoding_base): Handle DW_EH_PE_omit. + (print_debug_exception_table): Beginning of support. + (print_debug): Hook up print_debug_exception_table for + .gcc_except_table sections. + + * readelf.c (print_debug_frame_section): Some fixes for last change. + +2009-01-15 Ulrich Drepper <drepper@redhat.com> + + * readelf.c (print_encoding): Now a toplevel function. + (print_relinfo): Likewise. + (print_encoding_base): Broken out of print_debug_frame_section. + (print_debug_frame_section): Print different header for .eh_frame + sections. Fix recognition of matching CIEs in .debug_frame sections. + Print absolute offset for PC-relative FDE locations. Don't print + table header for FDEs if the table is empty. + (read_encoded): New function. + (print_debug_frame_hdr_section): New function. + (print_debug): Hook up print_debug_frame_hdr_section for .eh_frame_hdr + sections. + + * readelf.c (handle_relocs_rel): Print section number. + (print_debug_abbrev_section): Likewise. + (print_debug_aranges_section): Likewise. + (print_debug_ranges_section): Likewise. + (print_debug_info_section): Likewise. + (print_debug_line_section): Likewise. + (print_debug_loc_section): Likewise. + (print_debug_macinfo_section): Likewise. + (print_debug_pubnames_section): Likewise. + (print_debug_str_section): Likewise. + +2009-01-10 Ulrich Drepper <drepper@redhat.com> + + * strings.c (read_block): Fix typo in error message string. + +2009-01-07 Ulrich Drepper <drepper@redhat.com> + + * ld.c (ld_new_searchdir): Fix adding to search path list. + +2009-01-06 Ulrich Drepper <drepper@redhat.com> + + * readelf.c: Implement call frame debug section dumping. + +2009-01-05 Roland McGrath <roland@redhat.com> + + * elfcmp.c: Exit with status 2 for errors (like cmp, diff, grep). + Status 1 (aka EXIT_FAILURE) is only for completed OK but not equal. + +2009-01-01 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. + +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. + * strip.c: Likewise. + +2004-08-16 Ulrich Drepper <drepper@redhat.com> + + * strip.c (handle_elf): Rewrite dynamic memory handling to use of + allocate to work around gcc 3.4 bug. + +2004-01-25 Ulrich Drepper <drepper@redhat.com> + + * ldlex.l (invalid_char): Better error message. + +2004-01-23 Ulrich Drepper <drepper@redhat.com> + + * readelf.c: Print SHT_GNU_LIBLIST sections. + + * none_ld.c: New file. + +2004-01-21 Ulrich Drepper <drepper@redhat.com> + + * Makefile.am: Enable building of machine specific linker. + +2004-01-20 Ulrich Drepper <drepper@redhat.com> + + * Makefile.am: Support building with mudflap. + + * i386_ld.c: Fix warnings gcc 3.4 spits out. + * ldgeneric.c: Likewise. + * ldscript.y: Likewise. + * readelf.c: Likewise. + * strip.c: Likewise. + + * readelf.c (print_debug_line_section): Determine address size + correctly. + +2004-01-19 Ulrich Drepper <drepper@redhat.com> + + * readelf.c (print_phdr): Show which sections are covered by the + PT_GNU_RELRO entry. + + * elflint.c (check_program_header): Check PT_GNU_RELRO entry. + + * readelf.c (print_debug_macinfo_section): Implement. + +2004-01-18 Ulrich Drepper <drepper@redhat.com> + + * readelf.c (print_debug_line_section): Implement. + +2004-01-17 Ulrich Drepper <drepper@redhat.com> + + * src/elflint.c: Use PACKAGE_NAME instead of PACKAGE. + * src/ld.c: Likewise. + * src/nm.c: Likewise. + * src/readelf.c: Likewise. + * src/size.c: Likewise. + * src/strip.c: Likewise. + + * strip.c: Add a few more unlikely. Reduce scope of some variables. + + * Makefile.am: Support building with mudflap. + +2004-01-16 Ulrich Drepper <drepper@redhat.com> + + * readelf.c (print_debug_info_section): Free dies memory. + + * readelf.c: Print .debug_info section content. + +2004-01-13 Ulrich Drepper <drepper@redhat.com> + + * readelf.c (print_shdr): Add support for SHF_ORDERED and SHF_EXCLUDE. + +2004-01-12 Ulrich Drepper <drepper@redhat.com> + + * readelf.c (print_debug_aranges): Implement using libdw. + +2004-01-11 Ulrich Drepper <drepper@redhat.com> + + * nm.c: Adjust for Dwarf_Files type and dwarf_lineno interface change. + + * readelf.c: Use libdw instead of libdwarf. Not all of the old + behavior is available yet. + * Makefile.am: Link readelf with libdw. Remove libdwarf include path. + +2004-01-09 Ulrich Drepper <drepper@redhat.com> + + * nm.c (get_local_names): Adjust call to dwarf_nextcu. + + * nm.c: Implement getting information about local variables. + +2004-01-07 Ulrich Drepper <drepper@redhat.com> + + * nm.c: Read also debug information for local symbols. + +2004-01-05 Ulrich Drepper <drepper@redhat.com> + + * nm.c: Shuffle dwarf handling code around so the maximum column + width can be computed ahead of printing. Avoid collection symbols + which are not printed anyway. + + * nm.c: Rewrite dwarf handling to use libdw. + * Makefile.am (AM_CFLAGS): Add -std parameter. + (INCLUDES): Find header in libdw subdir. + (nm_LDADD): Replace libdwarf with libdw. + + * elflint.c: Update copyright year. + * readelf.c: Likewise. + * size.c: Likewise. + * strip.c: Likewise. + * nm.c: Likewise. + +2003-12-31 Ulrich Drepper <drepper@redhat.com> + + * strip.c (process_file): Close file before returning. + +2003-11-19 Ulrich Drepper <drepper@redhat.com> + + * readelf.c (handle_dynamic): Make column for tag name wider. + +2003-09-29 Ulrich Drepper <drepper@redhat.com> + + * readelf.c (handle_dynamic): Always terminate tag name with a space. + +2003-09-25 Ulrich Drepper <drepper@redhat.com> + + * strip.c (process_file): Don't mmap the input file, we modify the + data structures and don't want the change end up on disk. + +2003-09-23 Jakub Jelinek <jakub@redhat.com> + + * unaligned.h (union u_2ubyte_unaligned, + union u_4ubyte_unaligned, union u_8ubyte_unaligned): Add + packed attribute. + (add_2ubyte_unaligned, add_4ubyte_unaligned, + add_8ubyte_unaligned): Avoid nesting bswap_NN macros. + Read/store value through _ptr->u instead of *_ptr. + +2003-09-22 Ulrich Drepper <drepper@redhat.com> + + * size.c (show_sysv): Change type of maxlen to int. + + * strip.c (handle_elf): Handle the 64-bit archs which is 64-bit + buckets. + + * i386_ld.c: Many many fixes and extensions. + * ld.c: Likewise. + * ldgeneric.c: Likewise. + +2003-08-16 Ulrich Drepper <drepper@redhat.com> + + * ldgeneric.c (check_definition): Don't add symbol on dso_list if + the reference is from another DSO. + +2003-08-15 Ulrich Drepper <drepper@redhat.com> + + * ldgeneric.c (find_entry_point): It is no fatal error if no entry + point is found when creating a DSO. + +2003-08-14 Ulrich Drepper <drepper@redhat.com> + + * ld.c (main): Always call FLAG_UNRESOLVED. + * ldgeneric.c (ld_generic_flag_unresolved): Only complain about + undefined symbols if not creating DSO or ld_state.nodefs is not set. + +2003-08-13 Ulrich Drepper <drepper@redhat.com> + + * Makefile.in: Depend on libebl.a, not libebl.so. + + * ld.c (main): Mark stream for linker script as locked by caller. + (read_version_script): Likewise. + * ldlex.c: Define fread and fwrite to _unlocked variant. + + * i386_ld.c (elf_i386_finalize_plt): Replace #ifdefs with uses of + target_bswap_32. + * unaligned.h: Define target_bswap_16, target_bswap_32, and + target_bswap_64. + (store_2ubyte_unaligned, store_4ubyte_unaligned, + store_8ubyte_unaligned): Define using new macros. + +2003-08-12 Ulrich Drepper <drepper@redhat.com> + + * i386_ld.c (elf_i386_finalize_plt): Use packed structs to access + possibly unaligned memory. Support use of big endian machines. + +2003-08-11 Ulrich Drepper <drepper@redhat.com> + + * Moved to CVS archive. diff --git a/0.153/src/Makefile.am b/0.153/src/Makefile.am new file mode 100644 index 00000000..dc835cbd --- /dev/null +++ b/0.153/src/Makefile.am @@ -0,0 +1,181 @@ +## Process this file with automake to create Makefile.in +## +## Copyright (C) 1996-2012 Red Hat, Inc. +## This file is part of Red Hat elfutils. +## +## 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 $(top_srcdir)/config/eu.am +DEFS += $(YYDEBUG) -DDEBUGPRED=@DEBUGPRED@ \ + -DSRCDIR=\"$(shell cd $(srcdir);pwd)\" -DOBJDIR=\"$(shell pwd)\" +INCLUDES += -I$(srcdir)/../libelf -I$(srcdir)/../libebl \ + -I$(srcdir)/../libdw -I$(srcdir)/../libdwfl \ + -I$(srcdir)/../libasm + +AM_LDFLAGS = -Wl,-rpath-link,../libelf:../libdw + +no_mudflap.os = -fmudflap + +YACC = @YACC@ -d +AM_YFLAGS = -pld +AM_LFLAGS = -Pld -olex.yy.c +## Uncomment to enable debugging of linker script parser +##YYDEBUG = -DYYDEBUG=1 + +native_ld = @native_ld@ +base_cpu = @base_cpu@ + +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 libar.a +native_ld_cflags = -DBASE_ELF_NAME=elf_$(base_cpu) +else +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 + + +ld_SOURCES = ld.c ldgeneric.c ldlex.l ldscript.y symbolhash.c sectionhash.c \ + versionhash.c + +libar_a_SOURCES = arlib.c arlib2.c arlib-argp.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) 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 BUILD_STATIC +libasm = ../libasm/libasm.a +libdw = ../libdw/libdw.a $(zip_LIBS) $(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 + +if DEMANGLE +demanglelib = -lstdc++ +endif + +nm_no_Wformat = yes +size_no_Wformat = yes +strings_no_Wformat = yes +addr2line_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 \ + $(demanglelib) +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) $(libmudflap) -ldl +findtextrel_LDADD = $(libdw) $(libelf) $(libmudflap) +addr2line_LDADD = $(libdw) $(libelf) $(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) +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 +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 + $(LINK) -shared -o $@ -Wl,--whole-archive,$<,--no-whole-archive \ + $(libelf) $(libeu) \ + -Wl,--version-script,$(srcdir)/libld_elf_i386.map + $(textrel_check) +endif + +# 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 + +CLEANFILES += none_ld.os $(ld_modules:.c=.os) *.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/0.153/src/Makefile.in b/0.153/src/Makefile.in new file mode 100644 index 00000000..bccaaf95 --- /dev/null +++ b/0.153/src/Makefile.in @@ -0,0 +1,895 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 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. + +@SET_MAKE@ + + + + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +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 = @build@ +host_triplet = @host@ +DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in $(top_srcdir)/config/eu.am ChangeLog \ + ldlex.c ldscript.c ylwrap +@MUDFLAP_TRUE@am__append_1 = -fmudflap +bin_PROGRAMS = readelf$(EXEEXT) nm$(EXEEXT) size$(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) +# 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_2 = libdummy.a +# -ldl is always needed for libebl. +@NATIVE_LD_TRUE@am__append_3 = libld_elf.a +@NATIVE_LD_TRUE@am_libld_elf_i386_pic_a_OBJECTS = +subdir = src +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \ + $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/m4/zip.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LIBRARIES = $(noinst_LIBRARIES) +AR = ar +ARFLAGS = cru +libar_a_AR = $(AR) $(ARFLAGS) +libar_a_LIBADD = +am_libar_a_OBJECTS = arlib.$(OBJEXT) arlib2.$(OBJEXT) \ + arlib-argp.$(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_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)" "$(DESTDIR)$(bindir)" +am__EXEEXT_1 = libld_elf_i386.so$(EXEEXT) +PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS) +addr2line_SOURCES = addr2line.c +addr2line_OBJECTS = addr2line.$(OBJEXT) +am__DEPENDENCIES_1 = +@BUILD_STATIC_FALSE@am__DEPENDENCIES_2 = ../libdw/libdw.so +@BUILD_STATIC_TRUE@am__DEPENDENCIES_2 = ../libdw/libdw.a \ +@BUILD_STATIC_TRUE@ $(am__DEPENDENCIES_1) $(libelf) $(libebl) +addr2line_DEPENDENCIES = $(am__DEPENDENCIES_2) $(libelf) \ + $(am__DEPENDENCIES_1) +ar_SOURCES = ar.c +ar_OBJECTS = ar.$(OBJEXT) +ar_DEPENDENCIES = libar.a $(libelf) $(libeu) $(am__DEPENDENCIES_1) +elfcmp_SOURCES = elfcmp.c +elfcmp_OBJECTS = elfcmp.$(OBJEXT) +elfcmp_DEPENDENCIES = $(libebl) $(libelf) $(am__DEPENDENCIES_1) +elflint_SOURCES = elflint.c +elflint_OBJECTS = elflint.$(OBJEXT) +elflint_DEPENDENCIES = $(libebl) $(libelf) $(libeu) \ + $(am__DEPENDENCIES_1) +findtextrel_SOURCES = findtextrel.c +findtextrel_OBJECTS = findtextrel.$(OBJEXT) +findtextrel_DEPENDENCIES = $(am__DEPENDENCIES_2) $(libelf) \ + $(am__DEPENDENCIES_1) +am_ld_OBJECTS = ld.$(OBJEXT) ldgeneric.$(OBJEXT) ldlex.$(OBJEXT) \ + ldscript.$(OBJEXT) symbolhash.$(OBJEXT) sectionhash.$(OBJEXT) \ + versionhash.$(OBJEXT) +ld_OBJECTS = $(am_ld_OBJECTS) +ld_DEPENDENCIES = $(libebl) $(libelf) $(libeu) $(am__DEPENDENCIES_1) \ + $(am__append_3) +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) +nm_DEPENDENCIES = $(am__DEPENDENCIES_2) $(libebl) $(libelf) $(libeu) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +objdump_SOURCES = objdump.c +objdump_OBJECTS = objdump.$(OBJEXT) +objdump_DEPENDENCIES = $(libasm) $(libebl) $(libelf) $(libeu) \ + $(am__DEPENDENCIES_1) +ranlib_SOURCES = ranlib.c +ranlib_OBJECTS = ranlib.$(OBJEXT) +ranlib_DEPENDENCIES = libar.a $(libelf) $(libeu) $(am__DEPENDENCIES_1) +readelf_SOURCES = readelf.c +readelf_OBJECTS = readelf.$(OBJEXT) +readelf_DEPENDENCIES = $(am__DEPENDENCIES_2) $(libebl) $(libelf) \ + $(libeu) $(am__DEPENDENCIES_1) +size_SOURCES = size.c +size_OBJECTS = size.$(OBJEXT) +size_DEPENDENCIES = $(libelf) $(libeu) $(am__DEPENDENCIES_1) +strings_SOURCES = strings.c +strings_OBJECTS = strings.$(OBJEXT) +strings_DEPENDENCIES = $(libelf) $(libeu) $(am__DEPENDENCIES_1) +strip_SOURCES = strip.c +strip_OBJECTS = strip.$(OBJEXT) +strip_DEPENDENCIES = $(libebl) $(libelf) $(libeu) \ + $(am__DEPENDENCIES_1) +unstrip_SOURCES = unstrip.c +unstrip_OBJECTS = unstrip.$(OBJEXT) +unstrip_DEPENDENCIES = $(libebl) $(libelf) $(am__DEPENDENCIES_2) \ + $(libeu) $(am__DEPENDENCIES_1) +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +SCRIPTS = $(bin_SCRIPTS) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +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 = $(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@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEBUGPRED = @DEBUGPRED@ +DEFS = -D_GNU_SOURCE -DHAVE_CONFIG_H -DLOCALEDIR='"${localedir}"' \ + $(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@ +EXEEXT = @EXEEXT@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +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@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBEBL_SUBDIR = @LIBEBL_SUBDIR@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MODVERSION = @MODVERSION@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +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@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +base_cpu = @base_cpu@ +bindir = @bindir@ +build = @build@ +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@ +zip_LIBS = @zip_LIBS@ +INCLUDES = -I. -I$(srcdir) -I$(top_srcdir)/lib -I.. \ + -I$(srcdir)/../libelf -I$(srcdir)/../libebl \ + -I$(srcdir)/../libdw -I$(srcdir)/../libdwfl \ + -I$(srcdir)/../libasm +AM_CFLAGS = -std=gnu99 -Wall -Wshadow $(if \ + $($(*F)_no_Werror),,-Werror) $(if \ + $($(*F)_no_Wunused),,-Wunused -Wextra) $(if \ + $($(*F)_no_Wformat),-Wno-format,-Wformat=2) $($(*F)_CFLAGS) \ + $(am__append_1) +@MUDFLAP_FALSE@libmudflap = +@MUDFLAP_TRUE@libmudflap = -lmudflap +COMPILE.os = $(filter-out -fprofile-arcs -ftest-coverage $(no_mudflap.os),\ + $(COMPILE)) + +CLEANFILES = *.gcno *.gcda make-debug-archive none_ld.os \ + $(ld_modules:.c=.os) *.gconv +textrel_check = if readelf -d $@ | fgrep -q TEXTREL; then exit 1; fi +AM_LDFLAGS = -Wl,-rpath-link,../libelf:../libdw +no_mudflap.os = -fmudflap +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 libar.a $(ld_dsos) \ +@NATIVE_LD_FALSE@ $(am__append_2) +@NATIVE_LD_TRUE@noinst_LIBRARIES = libld_elf.a libar.a $(am__append_2) +@NATIVE_LD_TRUE@native_ld_cflags = -DBASE_ELF_NAME=elf_$(base_cpu) +@NEVER_TRUE@libdummy_a_SOURCES = i386_ld.c +ld_SOURCES = ld.c ldgeneric.c ldlex.l ldscript.y symbolhash.c sectionhash.c \ + versionhash.c + +libar_a_SOURCES = arlib.c arlib2.c arlib-argp.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) \ + arlib.h debugpred.h make-debug-archive.in +ld_modules = i386_ld.c +bin_SCRIPTS = make-debug-archive +@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 $(zip_LIBS) $(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 +@DEMANGLE_TRUE@demanglelib = -lstdc++ +nm_no_Wformat = yes +size_no_Wformat = yes +strings_no_Wformat = yes +addr2line_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 \ + $(demanglelib) + +size_LDADD = $(libelf) $(libeu) $(libmudflap) +strip_LDADD = $(libebl) $(libelf) $(libeu) $(libmudflap) -ldl +ld_LDADD = $(libebl) $(libelf) $(libeu) $(libmudflap) -ldl \ + $(am__append_3) +ld_LDFLAGS = -rdynamic +elflint_LDADD = $(libebl) $(libelf) $(libeu) $(libmudflap) -ldl +findtextrel_LDADD = $(libdw) $(libelf) $(libmudflap) +addr2line_LDADD = $(libdw) $(libelf) $(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) +unstrip_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(libmudflap) -ldl +ldlex_no_Werror = yes + +# Machine-specific linker code. +@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: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/config/eu.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnits src/Makefile'; \ + $(am__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: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +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) + $(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)'; test -n "$(bindir)" || list=; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p; \ + then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) + +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_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) + $(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_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_OBJECTS) $(readelf_LDADD) $(LIBS) +size$(EXEEXT): $(size_OBJECTS) $(size_DEPENDENCIES) + @rm -f size$(EXEEXT) + $(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_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)'; test -n "$(bindir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n' \ + -e 'h;s|.*|.|' \ + -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) { files[d] = files[d] " " $$1; \ + if (++n[d] == $(am__install_max)) { \ + print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ + else { print "f", d "/" $$4, $$1 } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(bin_SCRIPTS)'; test -n "$(bindir)" || exit 0; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 's,.*/,,;$(transform)'`; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +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) + +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-argp.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@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(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@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(am__mv) $(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: + $(am__skiplex) $(SHELL) $(YLWRAP) $< $(LEX_OUTPUT_ROOT).c $@ -- $(LEXCOMPILE) + +.y.c: + $(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; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + 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; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + 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; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__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)'; \ + 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; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$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) $(SCRIPTS) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(bindir)" "$(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) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_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 + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +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 + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS install-binSCRIPTS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: installcheck-binPROGRAMS installcheck-binSCRIPTS + +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-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-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-binSCRIPTS + + +%.os: %.c %.o +@AMDEP_TRUE@ if $(COMPILE.os) -c -o $@ -fpic -DPIC -DSHARED -MT $@ -MD -MP \ +@AMDEP_TRUE@ -MF "$(DEPDIR)/$*.Tpo" `test -f '$<' || echo '$(srcdir)/'`$<; \ +@AMDEP_TRUE@ then cat "$(DEPDIR)/$*.Tpo" >> "$(DEPDIR)/$*.Po"; \ +@AMDEP_TRUE@ rm -f "$(DEPDIR)/$*.Tpo"; \ +@AMDEP_TRUE@ else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; \ +@AMDEP_TRUE@ fi +@AMDEP_FALSE@ $(COMPILE.os) -c -o $@ -fpic -DPIC -DSHARED $< + +ldlex.o: ldscript.c +ldscript.h: ldscript.c +@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) + +# 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 + +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/0.153/src/addr2line.c b/0.153/src/addr2line.c new file mode 100644 index 00000000..2fcc1b10 --- /dev/null +++ b/0.153/src/addr2line.c @@ -0,0 +1,575 @@ +/* Locate source files and line information for given addresses + Copyright (C) 2005-2010, 2012 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> + +#include <system.h> + + +/* Name and version of program. */ +static void print_version (FILE *stream, struct argp_state *state); +ARGP_PROGRAM_VERSION_HOOK_DEF = print_version; + +/* Bug report address. */ +ARGP_PROGRAM_BUG_ADDRESS_DEF = 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 }, + { "flags", 'F', NULL, 0, N_("Also show line table flags"), 0 }, + { "section", 'j', "NAME", 0, + N_("Treat addresses as offsets relative to NAME section."), 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 line flags should be shown. */ +static bool show_flags; + +/* 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; + +/* If non-null, take address parameters as relative to named section. */ +static const char *just_section; + + +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\ +"), "2012"); + 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) +{ + 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 'F': + show_flags = true; + break; + + case 'S': + show_symbols = true; + break; + + case 'j': + just_section = arg; + 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; + + const char *comp_dir = ""; + const char *comp_dir_sep = ""; + + if (file == NULL) + file = "???"; + else if (only_basenames) + file = basename (file); + else if (use_comp_dir && file[0] != '/') + { + const char *const *dirs; + size_t ndirs; + if (dwarf_getsrcdirs (files, &dirs, &ndirs) == 0 + && dirs[0] != NULL) + { + comp_dir = dirs[0]; + comp_dir_sep = "/"; + } + } + + if (lineno == 0) + printf (" from %s%s%s", + comp_dir, comp_dir_sep, file); + else if (colno == 0) + printf (" at %s%s%s:%u", + comp_dir, comp_dir_sep, file, lineno); + else + printf (" at %s%s%s:%u:%u", + comp_dir, comp_dir_sep, 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 || symbol_name[0] == '\0') + 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 bool +adjust_to_section (const char *name, uintmax_t *addr, Dwfl *dwfl) +{ + /* 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; + return true; + } + } + + return false; +} + +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 i, j; + char *name = NULL; + if (sscanf (string, "(%m[^)])%" PRIiMAX "%n", &name, &addr, &i) == 2 + && string[i] == '\0') + parsed = adjust_to_section (name, &addr, dwfl); + switch (sscanf (string, "%m[^-+]%n%" PRIiMAX "%n", &name, &i, &addr, &j)) + { + default: + break; + case 1: + addr = 0; + j = i; + case 2: + if (string[j] != '\0') + break; + + /* 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; + } + break; + } + + free (name); + if (!parsed) + return 1; + } + else if (just_section != NULL + && !adjust_to_section (just_section, &addr, dwfl)) + 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", + comp_dir, comp_dir_sep, src, lineno, linecol); + else + printf ("%s%s%s:%d", + comp_dir, comp_dir_sep, src, lineno); + + if (show_flags) + { + Dwarf_Addr bias; + Dwarf_Line *info = dwfl_dwarf_line (line, &bias); + assert (info != NULL); + + inline void show (int (*get) (Dwarf_Line *, bool *), + const char *note) + { + bool flag; + if ((*get) (info, &flag) == 0 && flag) + fputs (note, stdout); + } + inline void show_int (int (*get) (Dwarf_Line *, unsigned int *), + const char *name) + { + unsigned int val; + if ((*get) (info, &val) == 0 && val != 0) + printf (" (%s %u)", name, val); + } + + show (&dwarf_linebeginstatement, " (is_stmt)"); + show (&dwarf_lineblock, " (basic_block)"); + show (&dwarf_lineprologueend, " (prologue_end)"); + show (&dwarf_lineepiloguebegin, " (epilogue_begin)"); + show_int (&dwarf_lineisa, "isa"); + show_int (&dwarf_linediscriminator, "discriminator"); + } + putchar ('\n'); + } + else + puts ("??:0"); + + return 0; +} + + +#include "debugpred.h" diff --git a/0.153/src/ar.c b/0.153/src/ar.c new file mode 100644 index 00000000..721f4c34 --- /dev/null +++ b/0.153/src/ar.c @@ -0,0 +1,1547 @@ +/* Create, modify, and extract from archives. + Copyright (C) 2005-2012 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/stat.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); +ARGP_PROGRAM_VERSION_HOOK_DEF = 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. */ +ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT; + + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + { NULL, 0, NULL, 0, N_("Commands:"), 1 }, + { 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:"), 2 }, + { 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, arlib_argp_children, 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_move && operation != oper_replace) + 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 for 'a', 'b', and 'i' modifiers")); + argp_help (&argp, stderr, ARGP_HELP_USAGE | ARGP_HELP_SEE, + program_invocation_short_name); + 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, + program_invocation_short_name); + 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 ("'%c' 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, program_invocation_short_name); + exit (EXIT_FAILURE); + } + + const char *arfname = argv[remaining++]; + argv += remaining; + argc -= remaining; + + int status; + switch (operation) + { + case oper_none: + error (0, 0, gettext ("command option required")); + argp_help (&argp, stderr, ARGP_HELP_STD_ERR, + program_invocation_short_name); + status = 1; + break; + + 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\ +"), "2012"); + 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, + program_invocation_short_name); + 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 'u': + update_newer = 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)); + + size_t name_max = 0; + inline bool should_truncate_fname (void) + { + if (errno == ENAMETOOLONG && allow_truncate_fname) + { + if (name_max == 0) + { + long int len = pathconf (".", _PC_NAME_MAX); + if (len > 0) + name_max = len; + } + return name_max != 0; + } + return false; + } + + 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 (should_truncate_fname ()) + { + /* Try to truncate the name. First find out by how + much. */ + printlen = name_max; + char truncfname[name_max + 1]; + *((char *) mempcpy (truncfname, arhdr->ar_name, + name_max)) = '\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 (should_truncate_fname ()) + { + /* Try to truncate the name. First find out by how + much. */ + printlen = name_max; + char truncfname[name_max + 1]; + *((char *) mempcpy (truncfname, arhdr->ar_name, + name_max)) = '\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", + program_invocation_short_name, 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"), + program_invocation_short_name, 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 = arlib_deterministic_output ? 0 : newst.st_mtime; + found[cnt]->uid = arlib_deterministic_output ? 0 : newst.st_uid; + found[cnt]->gid = arlib_deterministic_output ? 0 : 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]->mem == 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/0.153/src/arlib-argp.c b/0.153/src/arlib-argp.c new file mode 100644 index 00000000..a0e669cf --- /dev/null +++ b/0.153/src/arlib-argp.c @@ -0,0 +1,101 @@ +/* Options common to ar and ranlib. + Copyright (C) 2012 Red Hat, Inc. + + 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 <libintl.h> + +#include "arlib.h" + +bool arlib_deterministic_output = DEFAULT_AR_DETERMINISTIC; + +static const struct argp_option options[] = + { + { NULL, 'D', NULL, 0, + N_("Use zero for uid, gid, and date in archive members."), 0 }, + { NULL, 'U', NULL, 0, + N_("Use actual uid, gid, and date in archive members."), 0 }, + + { NULL, 0, NULL, 0, NULL, 0 } + }; + +static error_t +parse_opt (int key, char *arg __attribute__ ((unused)), + struct argp_state *state __attribute__ ((unused))) +{ + switch (key) + { + case 'D': + arlib_deterministic_output = true; + break; + + case 'U': + arlib_deterministic_output = false; + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static char * +help_filter (int key, const char *text, void *input __attribute__ ((unused))) +{ + inline char *text_for_default (void) + { + char *new_text; + if (unlikely (asprintf (&new_text, gettext ("%s (default)"), text) < 0)) + return (char *) text; + return new_text; + } + + switch (key) + { + case 'D': + if (DEFAULT_AR_DETERMINISTIC) + return text_for_default (); + break; + case 'U': + if (! DEFAULT_AR_DETERMINISTIC) + return text_for_default (); + break; + } + + return (char *) text; +} + +static const struct argp argp = + { + options, parse_opt, NULL, NULL, NULL, help_filter, NULL + }; + +const struct argp_child arlib_argp_children[] = + { + { &argp, 0, "", 2 }, + { NULL, 0, NULL, 0 } + }; diff --git a/0.153/src/arlib.c b/0.153/src/arlib.c new file mode 100644 index 00000000..bcf9344b --- /dev/null +++ b/0.153/src/arlib.c @@ -0,0 +1,280 @@ +/* Functions to handle creation of Linux archives. + Copyright (C) 2007-2012 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 + 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), + (arlib_deterministic_output ? 0 + : (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/0.153/src/arlib.h b/0.153/src/arlib.h new file mode 100644 index 00000000..ea77b23e --- /dev/null +++ b/0.153/src/arlib.h @@ -0,0 +1,105 @@ +/* Copyright (C) 2007-2012 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 <argp.h> +#include <byteswap.h> +#include <endian.h> +#include <libelf.h> +#include <obstack.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <sys/types.h> + + +/* State of -D/-U flags. */ +extern bool arlib_deterministic_output; + +/* For options common to ar and ranlib. */ +extern const struct argp_child arlib_argp_children[]; + + +/* 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/0.153/src/arlib2.c b/0.153/src/arlib2.c new file mode 100644 index 00000000..7098fec1 --- /dev/null +++ b/0.153/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/0.153/src/debugpred.h b/0.153/src/debugpred.h new file mode 100644 index 00000000..41d46796 --- /dev/null +++ b/0.153/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 *const __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 *const *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/0.153/src/elf32-i386.script b/0.153/src/elf32-i386.script new file mode 100644 index 00000000..2083278f --- /dev/null +++ b/0.153/src/elf32-i386.script @@ -0,0 +1,229 @@ +ENTRY(_start); + +SEARCH_DIR(/lib); +SEARCH_DIR(/usr/lib); +SEARCH_DIR(/usr/local/lib); +SEARCH_DIR(/usr/i686-pc-linux-gnu/lib); + +INTERP(/lib/ld-linux.so.2); + +PAGESIZE(4k); + +SEGMENT [RX] +{ +#ifdef SHARED + . = SIZEOF_HEADERS; +#else + . = 0x08048000 + SIZEOF_HEADERS; +#endif + + .interp; + .note.ABI-tag; + .note.gnu.build-id; + .hash; + .gnu.hash; + .dynsym; + .dynstr; + .gnu.version; + .gnu.version_d; + .gnu.version_r; + .rel.dyn; + .rel.plt; + .init { KEEP (*(.init)) } + .plt; + .text + { + *(.text) + *(.text.*) + *(.stub) + *(.gnu.warning) + *(.gnu.linkonce.t.*) + } + .fini { KEEP (*(.fini)) } + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + .rodata + { + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + } + .rodata1; + .eh_frame_hdr; + . = ALIGN(32 / 8); + PROVIDE (__preinit_array_start = .); + .preinit_array + { + *(.preinit_array) + } + PROVIDE (__preinit_array_end = .); + PROVIDE (__init_array_start = .); + .init_array + { + *(.init_array) + } + PROVIDE (__init_array_end = .); + PROVIDE (__fini_array_start = .); + .fini_array + { + *(.fini_array) + } + PROVIDE (__fini_array_end = .); +} + +SEGMENT [RW] +{ + .sdata2 + { + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + } + .sbss2 + { + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + } + /* 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)); + .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 + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + .dtors + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + .jcr; + .dynamic; + .got; + .got.plt; + .data + { + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + } + /* 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) + *(.sdata.*) + *(.gnu.linkonce.s.*) + } + _edata = .; + PROVIDE (edata = .); + __bss_start = .; + .sbss + { + PROVIDE (__sbss_start = .); + PROVIDE (___sbss_start = .); + *(.dynsbss) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + *(.scommon) + PROVIDE (__sbss_end = .); + PROVIDE (___sbss_end = .); + } + .bss + { + *(.dynbss) + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + /* Align here to ensure that the .bss section occupies space up to + _end. Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. */ + . = ALIGN(32 / 8); + } + . = ALIGN(32 / 8); + _end = .; + PROVIDE (end = .); +} + +SEGMENT [] +{ + /* Stabs debugging sections. */ + .stab; + .stabstr; + .stab.excl; + .stab.exclstr; + .stab.index; + .stab.indexstr; + .comment; + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug; + .line; + /* GNU DWARF 1 extensions */ + .debug_srcinfo; + .debug_sfnames; + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges; + .debug_pubnames; + /* DWARF 2 */ + .debug_info + { + *(.debug_info) + *(.gnu.linkonce.wi.*) + } + .debug_abbrev; + .debug_line; + .debug_frame; + .debug_str; + .debug_loc; + .debug_macinfo; + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames; + .debug_funcnames; + .debug_typenames; + .debug_varnames; + /* These must appear regardless of . */ +} diff --git a/0.153/src/elfcmp.c b/0.153/src/elfcmp.c new file mode 100644 index 00000000..99296d19 --- /dev/null +++ b/0.153/src/elfcmp.c @@ -0,0 +1,908 @@ +/* Compare relevant content of two ELF files. + Copyright (C) 2005-2012 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 <system.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); +ARGP_PROGRAM_VERSION_HOOK_DEF = print_version; + +/* Bug report address. */ +ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT; + +/* Values for the parameters which have no short form. */ +#define OPT_GAPS 0x100 +#define OPT_HASH_INEXACT 0x101 +#define OPT_IGNORE_BUILD_ID 0x102 + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + { NULL, 0, NULL, 0, N_("Control options:"), 0 }, + { "verbose", 'l', NULL, 0, + N_("Output all differences, not just the first"), 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 }, + { "ignore-build-id", OPT_IGNORE_BUILD_ID, NULL, 0, + N_("Ignore differences in build ID"), 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 multiple differences should be output. */ +static bool verbose; + +/* True iff SHT_HASH treatment should be generous. */ +static bool hash_inexact; + +/* True iff build ID notes should be ignored. */ +static bool ignore_build_id; + +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); + } + + if (quiet) + verbose = false; + + /* 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 (2, 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 (2, 0, gettext ("cannot get ELF header of '%s': %s"), + fname2, elf_errmsg (-1)); + +#define DIFFERENCE \ + do \ + { \ + result = 1; \ + if (! verbose) \ + goto out; \ + } \ + while (0) + + /* 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); + DIFFERENCE; + } + + size_t shnum1; + size_t shnum2; + if (unlikely (elf_getshdrnum (elf1, &shnum1) != 0)) + error (2, 0, gettext ("cannot get section count of '%s': %s"), + fname1, elf_errmsg (-1)); + if (unlikely (elf_getshdrnum (elf2, &shnum2) != 0)) + error (2, 0, gettext ("cannot get section count of '%s': %s"), + fname2, elf_errmsg (-1)); + if (unlikely (shnum1 != shnum2)) + { + if (! quiet) + error (0, 0, gettext ("%s %s diff: section count"), fname1, fname2); + DIFFERENCE; + } + + size_t phnum1; + size_t phnum2; + if (unlikely (elf_getphdrnum (elf1, &phnum1) != 0)) + error (2, 0, gettext ("cannot get program header count of '%s': %s"), + fname1, elf_errmsg (-1)); + if (unlikely (elf_getphdrnum (elf2, &phnum2) != 0)) + error (2, 0, gettext ("cannot get program header count of '%s': %s"), + fname2, elf_errmsg (-1)); + if (unlikely (phnum1 != phnum2)) + { + if (! quiet) + error (0, 0, gettext ("%s %s diff: program header count"), + fname1, fname2); + DIFFERENCE; + } + + /* 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)) + { + error (0, 0, gettext ("%s %s differ: section [%zu], [%zu] name"), + fname1, fname2, elf_ndxscn (scn1), elf_ndxscn (scn2)); + DIFFERENCE; + } + + /* 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) + { + error (0, 0, gettext ("%s %s differ: section [%zu] '%s' header"), + fname1, fname2, elf_ndxscn (scn1), sname1); + DIFFERENCE; + } + + Elf_Data *data1 = elf_getdata (scn1, NULL); + if (data1 == NULL) + error (2, 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 (2, 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 (2, 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 (2, 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)); + } + DIFFERENCE; + break; + } + + 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; + + case SHT_NOTE: + /* Parse the note format and compare the notes themselves. */ + { + GElf_Nhdr note1; + GElf_Nhdr note2; + + size_t off1 = 0; + size_t off2 = 0; + size_t name_offset; + size_t desc_offset; + while (off1 < data1->d_size + && (off1 = gelf_getnote (data1, off1, ¬e1, + &name_offset, &desc_offset)) > 0) + { + const char *name1 = data1->d_buf + name_offset; + const void *desc1 = data1->d_buf + desc_offset; + if (off2 >= data2->d_size) + { + if (! quiet) + error (0, 0, gettext ("\ +%s %s differ: section [%zu] '%s' number of notes"), + fname1, fname2, elf_ndxscn (scn1), sname1); + DIFFERENCE; + } + off2 = gelf_getnote (data2, off2, ¬e2, + &name_offset, &desc_offset); + if (off2 == 0) + error (2, 0, gettext ("\ +cannot read note section [%zu] '%s' in '%s': %s"), + elf_ndxscn (scn2), sname2, fname2, elf_errmsg (-1)); + const char *name2 = data2->d_buf + name_offset; + const void *desc2 = data2->d_buf + desc_offset; + + if (note1.n_namesz != note2.n_namesz + || memcmp (name1, name2, note1.n_namesz)) + { + if (! quiet) + error (0, 0, gettext ("\ +%s %s differ: section [%zu] '%s' note name"), + fname1, fname2, elf_ndxscn (scn1), sname1); + DIFFERENCE; + } + if (note1.n_type != note2.n_type) + { + if (! quiet) + error (0, 0, gettext ("\ +%s %s differ: section [%zu] '%s' note '%s' type"), + fname1, fname2, elf_ndxscn (scn1), sname1, name1); + DIFFERENCE; + } + if (note1.n_descsz != note2.n_descsz + || memcmp (desc1, desc2, note1.n_descsz)) + { + if (note1.n_type == NT_GNU_BUILD_ID + && note1.n_namesz == sizeof "GNU" + && !memcmp (name1, "GNU", sizeof "GNU")) + { + if (note1.n_descsz != note2.n_descsz) + { + if (! quiet) + error (0, 0, gettext ("\ +%s %s differ: build ID length"), + fname1, fname2); + DIFFERENCE; + } + else if (! ignore_build_id) + { + if (! quiet) + error (0, 0, gettext ("\ +%s %s differ: build ID content"), + fname1, fname2); + DIFFERENCE; + } + } + else + { + if (! quiet) + error (0, 0, gettext ("\ +%s %s differ: section [%zu] '%s' note '%s' content"), + fname1, fname2, elf_ndxscn (scn1), sname1, + name1); + DIFFERENCE; + } + } + } + if (off2 < data2->d_size) + { + if (! quiet) + error (0, 0, gettext ("\ +%s %s differ: section [%zu] '%s' number of notes"), + fname1, fname2, elf_ndxscn (scn1), sname1); + DIFFERENCE; + } + } + 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); + } + DIFFERENCE; + } + break; + } + } + + if (unlikely (scn1 != scn2)) + { + if (! quiet) + error (0, 0, + gettext ("%s %s differ: unequal amount of important sections"), + fname1, fname2); + DIFFERENCE; + } + + /* 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 + phnum1 * 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 (2, 0, gettext ("cannot load data of '%s': %s"), + fname1, elf_errmsg (-1)); + + raw2 = elf_rawfile (elf2, &size2); + if (raw2 == NULL ) + error (2, 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 (unsigned int ndx = 0; ndx < phnum1; ++ndx) + { + GElf_Phdr phdr1_mem; + GElf_Phdr *phdr1 = gelf_getphdr (elf1, ndx, &phdr1_mem); + if (ehdr1 == NULL) + error (2, 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 (2, 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); + DIFFERENCE; + } + + 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); + DIFFERENCE; + break; + } + + } + 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\ +"), "2012"); + 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 'l': + verbose = 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; + + case OPT_IGNORE_BUILD_ID: + ignore_build_id = 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 (2, errno, gettext ("cannot open '%s'"), fname); + Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL); + if (elf == NULL) + error (2, 0, + gettext ("cannot create ELF descriptor for '%s': %s"), + fname, elf_errmsg (-1)); + Ebl *ebl = ebl_openbackend (elf); + if (ebl == NULL) + error (2, 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 (2, 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 (2, 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 (2, 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 (2, 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/0.153/src/elflint.c b/0.153/src/elflint.c new file mode 100644 index 00000000..abca8b75 --- /dev/null +++ b/0.153/src/elflint.c @@ -0,0 +1,4390 @@ +/* Pedantic checking of ELF files compliance with gABI/psABI spec. + Copyright (C) 2001-2012 Red Hat, Inc. + This file is part of Red Hat elfutils. + Written by Ulrich Drepper <drepper@redhat.com>, 2001. + + 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 <byteswap.h> +#include <endian.h> +#include <error.h> +#include <fcntl.h> +#include <gelf.h> +#include <inttypes.h> +#include <libintl.h> +#include <locale.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/param.h> + +#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); +ARGP_PROGRAM_VERSION_HOOK_DEF = print_version; + +/* Bug report address. */ +ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT; + +#define ARGP_strict 300 +#define ARGP_gnuld 301 + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + { "strict", ARGP_strict, NULL, 0, + 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"), 0 }, + { NULL, 0, NULL, 0, NULL, 0 } +}; + +/* Short description of program. */ +static const char doc[] = N_("\ +Pedantic checking of ELF files compliance with gABI/psABI spec."); + +/* 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 +}; + + +/* Declarations of local functions. */ +static void process_file (int fd, Elf *elf, const char *prefix, + const char *suffix, const char *fname, size_t size, + 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...) \ + do { \ + printf (str, ##args); \ + ++error_count; \ + } while (0) +static unsigned int error_count; + +/* True if we should perform very strict testing. */ +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; + +/* Index of section header string table. */ +static uint32_t shstrndx; + +/* Array to count references in section groups. */ +static int *scnref; + + +int +main (int argc, char *argv[]) +{ + /* Set locale. */ + setlocale (LC_ALL, ""); + + /* Initialize the message catalog. */ + textdomain (PACKAGE_TARNAME); + + /* Parse and process arguments. */ + int remaining; + argp_parse (&argp, argc, argv, 0, &remaining, NULL); + + /* 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. */ + bool only_one = remaining + 1 == argc; + do + { + /* Open the file. */ + 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 = elf_begin (fd, ELF_C_READ_MMAP, NULL); + if (elf == NULL) + ERROR (gettext ("cannot generate Elf descriptor: %s\n"), + elf_errmsg (-1)); + else + { + unsigned int prev_error_count = error_count; + struct stat64 st; + + if (fstat64 (fd, &st) != 0) + { + printf ("cannot stat '%s': %m\n", argv[remaining]); + close (fd); + continue; + } + + process_file (fd, elf, NULL, NULL, argv[remaining], st.st_size, + only_one); + + /* Now we can close the descriptor. */ + if (elf_end (elf) != 0) + ERROR (gettext ("error while closing Elf descriptor: %s\n"), + elf_errmsg (-1)); + + if (prev_error_count == error_count && !be_quiet) + puts (gettext ("No errors")); + } + + close (fd); + } + while (++remaining < argc); + + return error_count != 0; +} + + +/* Handle program arguments. */ +static error_t +parse_opt (int key, char *arg __attribute__ ((unused)), + struct argp_state *state __attribute__ ((unused))) +{ + switch (key) + { + case ARGP_strict: + be_strict = true; + break; + + case 'q': + 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, program_invocation_short_name); + exit (EXIT_FAILURE); + + 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, "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\ +"), "2012"); + fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); +} + + +/* Process one file. */ +static void +process_file (int fd, Elf *elf, const char *prefix, const char *suffix, + const char *fname, size_t size, bool only_one) +{ + /* We can handle two types of files: ELF files and archives. */ + Elf_Kind kind = elf_kind (elf); + + switch (kind) + { + case ELF_K_ELF: + /* Yes! It's an ELF file. */ + process_elf_file (elf, prefix, suffix, fname, size, only_one); + break; + + 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 new_suffix[(suffix == NULL ? 0 : strlen (suffix)) + 2]; + char *cp = new_prefix; + + /* Create the full name of the file. */ + if (prefix != NULL) + { + cp = mempcpy (cp, prefix, prefix_len); + *cp++ = '('; + strcpy (stpcpy (new_suffix, suffix), ")"); + } + else + new_suffix[0] = '\0'; + memcpy (cp, fname, fname_len); + + /* It's an archive. We process each file in it. */ + while ((subelf = elf_begin (fd, cmd, elf)) != NULL) + { + kind = elf_kind (subelf); + + /* Call this function recursively. */ + if (kind == ELF_K_ELF || kind == ELF_K_AR) + { + Elf_Arhdr *arhdr = elf_getarhdr (subelf); + assert (arhdr != NULL); + + process_file (fd, subelf, new_prefix, new_suffix, + arhdr->ar_name, arhdr->ar_size, false); + } + + /* Get next archive element. */ + cmd = elf_next (subelf); + if (elf_end (subelf) != 0) + ERROR (gettext (" error while freeing sub-ELF descriptor: %s\n"), + elf_errmsg (-1)); + } + } + break; + + default: + /* We cannot do anything. */ + ERROR (gettext ("\ +Not an ELF file - it has the wrong magic bytes at the start\n")); + break; + } +} + + +static const char * +section_name (Ebl *ebl, int idx) +{ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr; + + shdr = gelf_getshdr (elf_getscn (ebl->elf, idx), &shdr_mem); + + return elf_strptr (ebl->elf, shstrndx, shdr->sh_name); +} + + +static const int valid_e_machine[] = + { + EM_M32, EM_SPARC, EM_386, EM_68K, EM_88K, EM_860, EM_MIPS, EM_S370, + EM_MIPS_RS3_LE, EM_PARISC, EM_VPP500, EM_SPARC32PLUS, EM_960, EM_PPC, + EM_PPC64, EM_S390, EM_V800, EM_FR20, EM_RH32, EM_RCE, EM_ARM, + EM_FAKE_ALPHA, EM_SH, EM_SPARCV9, EM_TRICORE, EM_ARC, EM_H8_300, + EM_H8_300H, EM_H8S, EM_H8_500, EM_IA_64, EM_MIPS_X, EM_COLDFIRE, + EM_68HC12, EM_MMA, EM_PCP, EM_NCPU, EM_NDR1, EM_STARCORE, EM_ME16, + EM_ST100, EM_TINYJ, EM_X86_64, EM_PDSP, EM_FX66, EM_ST9PLUS, EM_ST7, + 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_ALPHA + }; +#define nvalid_e_machine \ + (sizeof (valid_e_machine) / sizeof (valid_e_machine[0])) + + +/* Numbers of sections and program headers. */ +static unsigned int shnum; +static unsigned int phnum; + + +static void +check_elf_header (Ebl *ebl, GElf_Ehdr *ehdr, size_t size) +{ + char buf[512]; + size_t cnt; + + /* Check e_ident field. */ + if (ehdr->e_ident[EI_MAG0] != ELFMAG0) + ERROR ("e_ident[%d] != '%c'\n", EI_MAG0, ELFMAG0); + if (ehdr->e_ident[EI_MAG1] != ELFMAG1) + ERROR ("e_ident[%d] != '%c'\n", EI_MAG1, ELFMAG1); + if (ehdr->e_ident[EI_MAG2] != ELFMAG2) + ERROR ("e_ident[%d] != '%c'\n", EI_MAG2, ELFMAG2); + if (ehdr->e_ident[EI_MAG3] != ELFMAG3) + ERROR ("e_ident[%d] != '%c'\n", EI_MAG3, ELFMAG3); + + if (ehdr->e_ident[EI_CLASS] != ELFCLASS32 + && ehdr->e_ident[EI_CLASS] != ELFCLASS64) + ERROR (gettext ("e_ident[%d] == %d is no known class\n"), + EI_CLASS, ehdr->e_ident[EI_CLASS]); + + if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB + && ehdr->e_ident[EI_DATA] != ELFDATA2MSB) + ERROR (gettext ("e_ident[%d] == %d is no known data encoding\n"), + EI_DATA, ehdr->e_ident[EI_DATA]); + + if (ehdr->e_ident[EI_VERSION] != EV_CURRENT) + ERROR (gettext ("unknown ELF header version number e_ident[%d] == %d\n"), + EI_VERSION, ehdr->e_ident[EI_VERSION]); + + /* We currently don't handle any OS ABIs other than Linux. */ + if (ehdr->e_ident[EI_OSABI] != ELFOSABI_NONE + && ehdr->e_ident[EI_OSABI] != ELFOSABI_LINUX) + ERROR (gettext ("unsupported OS ABI e_ident[%d] == '%s'\n"), + EI_OSABI, + ebl_osabi_name (ebl, ehdr->e_ident[EI_OSABI], buf, sizeof (buf))); + + /* No ABI versions other than zero supported either. */ + if (ehdr->e_ident[EI_ABIVERSION] != 0) + ERROR (gettext ("unsupport ABI version e_ident[%d] == %d\n"), + EI_ABIVERSION, ehdr->e_ident[EI_ABIVERSION]); + + for (cnt = EI_PAD; cnt < EI_NIDENT; ++cnt) + if (ehdr->e_ident[cnt] != 0) + ERROR (gettext ("e_ident[%zu] is not zero\n"), cnt); + + /* Check the e_type field. */ + if (ehdr->e_type != ET_REL && ehdr->e_type != ET_EXEC + && ehdr->e_type != ET_DYN && ehdr->e_type != ET_CORE) + ERROR (gettext ("unknown object file type %d\n"), ehdr->e_type); + + /* Check the e_machine field. */ + for (cnt = 0; cnt < nvalid_e_machine; ++cnt) + if (valid_e_machine[cnt] == ehdr->e_machine) + break; + if (cnt == nvalid_e_machine) + ERROR (gettext ("unknown machine type %d\n"), ehdr->e_machine); + + /* Check the e_version field. */ + if (ehdr->e_version != EV_CURRENT) + ERROR (gettext ("unknown object file version\n")); + + /* Check the e_phoff and e_phnum fields. */ + if (ehdr->e_phoff == 0) + { + if (ehdr->e_phnum != 0) + ERROR (gettext ("invalid program header offset\n")); + else if (ehdr->e_type == ET_EXEC || ehdr->e_type == ET_DYN) + ERROR (gettext ("\ +executables and DSOs cannot have zero program header offset\n")); + } + else if (ehdr->e_phnum == 0) + ERROR (gettext ("invalid number of program header entries\n")); + + /* Check the e_shoff field. */ + shnum = ehdr->e_shnum; + shstrndx = ehdr->e_shstrndx; + if (ehdr->e_shoff == 0) + { + if (ehdr->e_shnum != 0) + ERROR (gettext ("invalid section header table offset\n")); + else if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN + && ehdr->e_type != ET_CORE) + ERROR (gettext ("section header table must be present\n")); + } + else + { + if (ehdr->e_shnum == 0) + { + /* Get the header of the zeroth section. The sh_size field + might contain the section number. */ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem); + if (shdr != NULL) + { + /* The error will be reported later. */ + if (shdr->sh_size == 0) + ERROR (gettext ("\ +invalid number of section header table entries\n")); + else + shnum = shdr->sh_size; + } + } + + if (ehdr->e_shstrndx == SHN_XINDEX) + { + /* Get the header of the zeroth section. The sh_size field + might contain the section number. */ + GElf_Shdr shdr_mem; + 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")); + } + + phnum = ehdr->e_phnum; + if (ehdr->e_phnum == PN_XNUM) + { + /* Get the header of the zeroth section. The sh_info field + might contain the phnum count. */ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem); + if (shdr != NULL) + { + /* The error will be reported later. */ + if (shdr->sh_info < PN_XNUM) + ERROR (gettext ("\ +invalid number of program header table entries\n")); + else + phnum = shdr->sh_info; + } + } + + /* Check the e_flags field. */ + if (!ebl_machine_flag_check (ebl, ehdr->e_flags)) + ERROR (gettext ("invalid machine flags: %s\n"), + ebl_machine_flag_name (ebl, ehdr->e_flags, buf, sizeof (buf))); + + /* Check e_ehsize, e_phentsize, and e_shentsize fields. */ + if (gelf_getclass (ebl->elf) == ELFCLASS32) + { + if (ehdr->e_ehsize != 0 && ehdr->e_ehsize != sizeof (Elf32_Ehdr)) + ERROR (gettext ("invalid ELF header size: %hd\n"), ehdr->e_ehsize); + + if (ehdr->e_phentsize != 0 && ehdr->e_phentsize != sizeof (Elf32_Phdr)) + ERROR (gettext ("invalid program header size: %hd\n"), + ehdr->e_phentsize); + else if (ehdr->e_phoff + phnum * ehdr->e_phentsize > size) + ERROR (gettext ("invalid program header position or size\n")); + + if (ehdr->e_shentsize != 0 && ehdr->e_shentsize != sizeof (Elf32_Shdr)) + ERROR (gettext ("invalid section header size: %hd\n"), + ehdr->e_shentsize); + else if (ehdr->e_shoff + shnum * ehdr->e_shentsize > size) + ERROR (gettext ("invalid section header position or size\n")); + } + else if (gelf_getclass (ebl->elf) == ELFCLASS64) + { + if (ehdr->e_ehsize != 0 && ehdr->e_ehsize != sizeof (Elf64_Ehdr)) + ERROR (gettext ("invalid ELF header size: %hd\n"), ehdr->e_ehsize); + + if (ehdr->e_phentsize != 0 && ehdr->e_phentsize != sizeof (Elf64_Phdr)) + ERROR (gettext ("invalid program header size: %hd\n"), + ehdr->e_phentsize); + else if (ehdr->e_phoff + phnum * ehdr->e_phentsize > size) + ERROR (gettext ("invalid program header position or size\n")); + + if (ehdr->e_shentsize != 0 && ehdr->e_shentsize != sizeof (Elf64_Shdr)) + ERROR (gettext ("invalid section header size: %hd\n"), + ehdr->e_shentsize); + else if (ehdr->e_shoff + ehdr->e_shnum * ehdr->e_shentsize > size) + ERROR (gettext ("invalid section header position or size\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, int idx) +{ + if (scnref[idx] == 0) + { + /* No reference so far. Search following sections, maybe the + order is wrong. */ + size_t cnt; + + for (cnt = idx + 1; cnt < shnum; ++cnt) + { + Elf_Scn *scn = elf_getscn (ebl->elf, cnt); + GElf_Shdr 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 + somewhere else. */ + continue; + + if (shdr->sh_type != SHT_GROUP) + continue; + + Elf_Data *data = elf_getdata (scn, NULL); + if (data == NULL || data->d_size < sizeof (Elf32_Word)) + /* Cannot check the section. */ + continue; + + 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; + } + + out: + 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, idx)); + else + ERROR (gettext ("\ +section [%2d] '%s': section group [%2zu] '%s' does not precede group member\n"), + idx, section_name (ebl, idx), + cnt, section_name (ebl, cnt)); + } +} + + +static void +check_symtab (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) +{ + bool no_xndx_warned = false; + int no_pt_tls = 0; + 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; + } + + 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, shdr->sh_link), + idx, section_name (ebl, idx)); + strshdr = NULL; + } + + /* Search for an extended section index table section. */ + Elf_Data *xndxdata = NULL; + Elf32_Word xndxscnidx = 0; + 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); + 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) + { + 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 (shdr->sh_entsize != gelf_fsize (ebl->elf, ELF_T_SYM, 1, EV_CURRENT)) + ERROR (gettext ("\ +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; + Elf32_Word xndx; + 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, 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, idx), "st_name"); + if (sym->st_value != 0) + ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"), + 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, idx), "st_size"); + if (sym->st_info != 0) + ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"), + 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, idx), "st_other"); + if (sym->st_shndx != 0) + ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"), + 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, xndxscnidx)); + } + + 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, idx), cnt, elf_errmsg (-1)); + continue; + } + + const char *name = NULL; + 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, idx), cnt); + else + { + name = elf_strptr (ebl->elf, shdr->sh_link, sym->st_name); + assert (name != NULL); + } + + if (sym->st_shndx == SHN_XINDEX) + { + if (xndxdata == NULL) + { + if (!no_xndx_warned) + ERROR (gettext ("\ +section [%2d] '%s': symbol %zu: too large section index but no extended section index section\n"), + 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, xndxscnidx), cnt, + xndx); + } + else if ((sym->st_shndx >= SHN_LORESERVE + // && sym->st_shndx <= SHN_HIRESERVE always true + && sym->st_shndx != SHN_ABS + && sym->st_shndx != SHN_COMMON) + || (sym->st_shndx >= shnum + && (sym->st_shndx < SHN_LORESERVE + /* || sym->st_shndx > SHN_HIRESERVE always false */))) + ERROR (gettext ("\ +section [%2d] '%s': symbol %zu: invalid section index\n"), + idx, section_name (ebl, idx), cnt); + else + xndx = sym->st_shndx; + + 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, idx), cnt); + + if (GELF_ST_BIND (sym->st_info) >= STB_NUM + && !ebl_symbol_binding_name (ebl, GELF_ST_BIND (sym->st_info), NULL, + 0)) + ERROR (gettext ("\ +section [%2d] '%s': symbol %zu: unknown symbol binding\n"), + idx, section_name (ebl, idx), cnt); + if (GELF_ST_BIND (sym->st_info) == STB_GNU_UNIQUE + && GELF_ST_TYPE (sym->st_info) != STT_OBJECT) + ERROR (gettext ("\ +section [%2d] '%s': symbol %zu: unique symbol not of object type\n"), + idx, section_name (ebl, idx), cnt); + + if (xndx == SHN_COMMON) + { + /* Common symbols can only appear in relocatable files. */ + if (ehdr->e_type != ET_REL) + ERROR (gettext ("\ +section [%2d] '%s': symbol %zu: COMMON only allowed in relocatable files\n"), + 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, 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, idx), cnt); + } + else if (xndx > 0 && xndx < shnum) + { + GElf_Shdr destshdr_mem; + GElf_Shdr *destshdr; + + 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 (! 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, 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, 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, idx), cnt, + (int) xndx, section_name (ebl, xndx)); + + if (ehdr->e_type == ET_REL) + { + /* For object files the symbol value must fall + into the section. */ + 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, 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, idx), cnt, + (int) xndx, section_name (ebl, xndx)); + } + else + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = NULL; + unsigned int pcnt; + + for (pcnt = 0; pcnt < phnum; ++pcnt) + { + phdr = gelf_getphdr (ebl->elf, pcnt, &phdr_mem); + if (phdr != NULL && phdr->p_type == PT_TLS) + break; + } + + if (pcnt == phnum) + { + 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, idx), cnt); + } + else + { + if (sym->st_value + < 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, 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, 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, idx), cnt, + (int) xndx, section_name (ebl, xndx)); + } + } + } + } + } + + if (GELF_ST_BIND (sym->st_info) == STB_LOCAL) + { + 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, 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, 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, idx), cnt); + + if (name != NULL) + { + if (strcmp (name, "_GLOBAL_OFFSET_TABLE_") == 0) + { + /* Check that address and size match the global offset table. */ + + 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) + { + 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); + + if (destshdr != NULL) + { + /* Found it. */ + if (!ebl_check_special_symbol (ebl, ehdr, sym, name, + destshdr)) + { + 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 %s section address %#" PRIx64 "\n"), + idx, section_name (ebl, idx), + (uint64_t) sym->st_value, + sname, (uint64_t) destshdr->sh_addr); + + if (!gnuld && sym->st_size != destshdr->sh_size) + ERROR (gettext ("\ +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, + sname, (uint64_t) destshdr->sh_size); + } + } + else + ERROR (gettext ("\ +section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol present, but no .got section\n"), + 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. */ + for (unsigned int pcnt = 0; pcnt < 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 ("\ +section [%2d] '%s': _DYNAMIC_ symbol value %#" PRIx64 " does not match dynamic segment address %#" PRIx64 "\n"), + idx, section_name (ebl, idx), + (uint64_t) sym->st_value, + (uint64_t) phdr->p_vaddr); + + 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, idx), + (uint64_t) sym->st_size, + (uint64_t) phdr->p_memsz); + + break; + } + } + } + + if (GELF_ST_VISIBILITY (sym->st_other) != STV_DEFAULT + && shdr->sh_type == SHT_DYNSYM) + ERROR (gettext ("\ +section [%2d] '%s': symbol %zu: symbol in dynamic symbol table with non-default visibility\n"), + idx, section_name (ebl, idx), cnt); + if (! ebl_check_st_other_bits (ebl, sym->st_other)) + ERROR (gettext ("\ +section [%2d] '%s': symbol %zu: unknown bit set in st_other\n"), + idx, section_name (ebl, idx), cnt); + + } +} + + +static bool +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, idx), is_rela ? ".rela.dyn" : ".rel.dyn")) + return false; + + /* When a .rel.dyn section is used a DT_RELCOUNT dynamic section + entry can be present as well. */ + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) + { + GElf_Shdr rcshdr_mem; + const GElf_Shdr *rcshdr = gelf_getshdr (scn, &rcshdr_mem); + assert (rcshdr != NULL); + + if (rcshdr->sh_type == SHT_DYNAMIC) + { + /* Found the dynamic section. Look through it. */ + Elf_Data *d = elf_getdata (scn, NULL); + size_t cnt; + + for (cnt = 1; cnt < rcshdr->sh_size / rcshdr->sh_entsize; ++cnt) + { + GElf_Dyn dyn_mem; + GElf_Dyn *dyn = gelf_getdyn (d, cnt, &dyn_mem); + assert (dyn != NULL); + + if (dyn->d_tag == DT_RELCOUNT) + { + /* 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, 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); + } + } + } + } + + break; + } + } + + return true; +} + + +struct loaded_segment +{ + GElf_Addr from; + GElf_Addr to; + bool read_only; + struct loaded_segment *next; +}; + + +/* 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, idx)); + else if (shdr->sh_info != 0) + { + *destshdrp = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_info), + destshdr_memp); + if (*destshdrp != NULL) + { + 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, idx)); + else + { + /* 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, idx)); + } + } + + 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, idx)); + } + } + + 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 (unsigned int i = 0; i < phnum; ++i) + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = gelf_getphdr (ebl->elf, i, &phdr_mem); + if (phdr == NULL) + continue; + + if (phdr->p_type == PT_LOAD) + { + 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; + } + } + } + } + + /* 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, idx), cnt); + + 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, idx), cnt); + + /* 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, idx), cnt, + ebl_reloc_type_name (ebl, GELF_R_SYM (r_info), + buf, sizeof (buf))); + } + + 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) + { + 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; + } + + 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)) + { + ERROR (gettext ("\ +section [%2d] '%s': relocations are against loaded and unloaded data\n"), + idx, section_name (ebl, idx)); + *statep = state_error; + } + } +} + + +static void +check_rela (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_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) + { + GElf_Rela rela_mem; + GElf_Rela *rela = gelf_getrela (data, cnt, &rela_mem); + if (rela == NULL) + { + 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); + } + + 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 (size_t cnt = 0; 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 (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, + rel->r_offset, rel->r_info, destshdr, reldyn, loaded, + &state); + } + + while (loaded != NULL) + { + struct loaded_segment *old = loaded; + loaded = loaded->next; + free (old); + } +} + + +/* Number of dynamic sections. */ +static int ndynamic; + + +static void +check_dynamic (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) +{ + Elf_Data *data; + GElf_Shdr strshdr_mem; + GElf_Shdr *strshdr; + size_t cnt; + static const bool dependencies[DT_NUM][DT_NUM] = + { + [DT_NEEDED] = { [DT_STRTAB] = true }, + [DT_PLTRELSZ] = { [DT_JMPREL] = true }, + [DT_HASH] = { [DT_SYMTAB] = true }, + [DT_STRTAB] = { [DT_STRSZ] = 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 }, + [DT_STRSZ] = { [DT_STRTAB] = true }, + [DT_SYMENT] = { [DT_SYMTAB] = true }, + [DT_SONAME] = { [DT_STRTAB] = true }, + [DT_RPATH] = { [DT_STRTAB] = true }, + [DT_REL] = { [DT_RELSZ] = true, [DT_RELENT] = true }, + [DT_RELSZ] = { [DT_REL] = true }, + [DT_RELENT] = { [DT_REL] = true }, + [DT_JMPREL] = { [DT_PLTRELSZ] = true, [DT_PLTREL] = true }, + [DT_RUNPATH] = { [DT_STRTAB] = true }, + [DT_PLTREL] = { [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, + [DT_SYMBOLIC] = true, + [DT_TEXTREL] = true, + [DT_BIND_NOW] = true + }; + static const bool mandatory[DT_NUM] = + { + [DT_NULL] = true, + [DT_STRTAB] = true, + [DT_SYMTAB] = true, + [DT_STRSZ] = true, + [DT_SYMENT] = true + }; + + 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")); + + 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; + } + + strshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), &strshdr_mem); + 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, 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, idx)); + + if (shdr->sh_info != 0) + ERROR (gettext ("section [%2d] '%s': sh_info not zero\n"), + 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 = 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, idx), cnt, elf_errmsg (-1)); + continue; + } + + if (has_dt[DT_NULL] && dyn->d_tag != DT_NULL && ! non_null_warned) + { + ERROR (gettext ("\ +section [%2d] '%s': non-DT_NULL entries follow DT_NULL entry\n"), + 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, idx), cnt); + + if (dyn->d_tag >= 0 && dyn->d_tag < DT_NUM) + { + if (has_dt[dyn->d_tag] + && dyn->d_tag != DT_NEEDED + && dyn->d_tag != DT_NULL + && dyn->d_tag != DT_POSFLAG_1) + { + char buf[50]; + ERROR (gettext ("\ +section [%2d] '%s': entry %zu: more than one entry with tag %s\n"), + idx, section_name (ebl, idx), cnt, + ebl_dynamic_tag_name (ebl, dyn->d_tag, + buf, sizeof (buf))); + } + + if (be_strict && level2[dyn->d_tag]) + { + char buf[50]; + ERROR (gettext ("\ +section [%2d] '%s': entry %zu: level 2 tag %s used\n"), + 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, idx), cnt); + + /* 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 < 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 >= 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]) + { + for (int inner = 0; inner < DT_NUM; ++inner) + if (dependencies[cnt][inner] && ! has_dt[inner]) + { + char buf1[50]; + char buf2[50]; + + ERROR (gettext ("\ +section [%2d] '%s': contains %s entry but not %s\n"), + idx, section_name (ebl, idx), + ebl_dynamic_tag_name (ebl, cnt, buf1, sizeof (buf1)), + ebl_dynamic_tag_name (ebl, inner, buf2, sizeof (buf2))); + } + } + else + { + if (mandatory[cnt]) + { + char buf[50]; + ERROR (gettext ("\ +section [%2d] '%s': mandatory tag %s not present\n"), + 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, 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, 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, GElf_Shdr *shdr, int idx) +{ + 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; + } + + 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, 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, 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, idx)); + + if (shdr->sh_info != 0) + ERROR (gettext ("section [%2d] '%s': sh_info not zero\n"), + idx, section_name (ebl, idx)); + + for (size_t cnt = idx + 1; cnt < shnum; ++cnt) + { + GElf_Shdr 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, idx), + cnt, section_name (ebl, cnt)); + break; + } + } + + 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 (size_t cnt = 1; cnt < data->d_size / sizeof (Elf32_Word); ++cnt) + { + Elf32_Word xndx = ((Elf32_Word *) data->d_buf)[cnt]; + + if (xndx != 0) + { + GElf_Sym sym_data; + GElf_Sym *sym = gelf_getsym (symdata, cnt, &sym_data); + if (sym == NULL) + { + ERROR (gettext ("cannot get data for symbol %zu\n"), cnt); + continue; + } + + if (sym->st_shndx != SHN_XINDEX) + ERROR (gettext ("\ +extended section index is %" PRIu32 " but symbol index is not XINDEX\n"), + (uint32_t) xndx); + } + } +} + + +static void +check_sysv_hash (Ebl *ebl, GElf_Shdr *shdr, Elf_Data *data, int idx, + GElf_Shdr *symshdr) +{ + Elf32_Word nbucket = ((Elf32_Word *) data->d_buf)[0]; + Elf32_Word nchain = ((Elf32_Word *) 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 (((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, idx)); + return; + } + + 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, idx)); + + 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': 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, idx)); + + 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 initial administrative entries\n"), + idx, section_name (ebl, idx)); + return; + } + + 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; + + case SHT_GNU_HASH: + check_gnu_hash (ebl, shdr, data, idx, symshdr); + break; + + 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) + { + 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; + } + + 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); + + if (sym_data == NULL || sym_shdr == NULL) + return; + + 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_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, idx), #name) + + TEST (name, 1); + TEST (flags, 1); + TEST (addr, 1); + TEST (offset, 1); + TEST (size, idx != 0); + TEST (link, idx != 0); + TEST (info, 1); + TEST (addralign, 1); + TEST (entsize, 1); +} + + +static void +check_group (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) +{ + if (ehdr->e_type != ET_REL) + { + ERROR (gettext ("\ +section [%2d] '%s': section groups only allowed in relocatable object files\n"), + 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 (symscn, &symshdr_mem); + if (symshdr == NULL) + ERROR (gettext ("section [%2d] '%s': cannot get symbol table: %s\n"), + 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, 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, idx)); + + if (shdr->sh_flags != 0) + ERROR (gettext ("section [%2d] '%s': sh_flags not zero\n"), + 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 cannot 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, 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, idx), elf_errmsg (-1)); + else + { + size_t elsize = elf32_fsize (ELF_T_WORD, 1, EV_CURRENT); + size_t cnt; + Elf32_Word val; + + if (data->d_size % elsize != 0) + ERROR (gettext ("\ +section [%2d] '%s': section size not multiple of sizeof(Elf32_Word)\n"), + 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, 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, idx)); + else if (data->d_size < 3 * elsize) + ERROR (gettext ("\ +section [%2d] '%s': section group with only one member\n"), + idx, section_name (ebl, idx)); + } + +#if ALLOW_UNALIGNED + val = *((Elf32_Word *) data->d_buf); +#else + memcpy (&val, data->d_buf, elsize); +#endif + if ((val & ~GRP_COMDAT) != 0) + ERROR (gettext ("section [%2d] '%s': unknown section group flags\n"), + idx, section_name (ebl, idx)); + + for (cnt = elsize; cnt < data->d_size; cnt += elsize) + { +#if ALLOW_UNALIGNED + val = *((Elf32_Word *) ((char *) data->d_buf + cnt)); +#else + memcpy (&val, (char *) data->d_buf + cnt, elsize); +#endif + + if (val > shnum) + ERROR (gettext ("\ +section [%2d] '%s': section index %Zu out of range\n"), + idx, section_name (ebl, idx), cnt / elsize); + else + { + GElf_Shdr 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, 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, 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, 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, val)); + } + } + } +} + + +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; + const char *name; + } known_flags[] = + { +#define NEWFLAG(name) { SHF_##name, #name } + NEWFLAG (WRITE), + NEWFLAG (ALLOC), + NEWFLAG (EXECINSTR), + NEWFLAG (MERGE), + NEWFLAG (STRINGS), + NEWFLAG (INFO_LINK), + NEWFLAG (LINK_ORDER), + NEWFLAG (OS_NONCONFORMING), + NEWFLAG (GROUP), + NEWFLAG (TLS) + }; +#undef NEWFLAG + const size_t nknown_flags = sizeof (known_flags) / sizeof (known_flags[0]); + + char *cp = buf; + + for (size_t cnt = 0; cnt < nknown_flags; ++cnt) + if (flags & known_flags[cnt].flag) + { + if (cp != buf && len > 1) + { + *cp++ = '|'; + --len; + } + + size_t ncopy = MIN (len - 1, strlen (known_flags[cnt].name)); + cp = mempcpy (cp, known_flags[cnt].name, ncopy); + len -= ncopy; + + flags ^= known_flags[cnt].flag; + } + + if (flags != 0 || cp == buf) + snprintf (cp, len - 1, "%" PRIx64, (uint64_t) flags); + + *cp = '\0'; + + return buf; +} + + +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, int idx) +{ + 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 (symscn, &symshdr_mem); + if (symshdr == NULL) + /* The error has already been reported. */ + return; + + if (symshdr->sh_type != SHT_DYNSYM) + { + ERROR (gettext ("\ +section [%2d] '%s' refers in sh_link to section [%2d] '%s' which is no dynamic symbol table\n"), + 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, 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; + + 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 int +unknown_dependency_p (Elf *elf, const char *fname) +{ + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = NULL; + + unsigned int i; + for (i = 0; i < phnum; ++i) + if ((phdr = gelf_getphdr (elf, i, &phdr_mem)) != NULL + && phdr->p_type == PT_DYNAMIC) + break; + + if (i == phnum) + return 1; + assert (phdr != NULL); + Elf_Scn *scn = gelf_offscn (elf, phdr->p_offset); + GElf_Shdr shdr_mem; + 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_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, 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, atleast, 0, SHF_MERGE | SHF_STRINGS }, + { ".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))) + + +/* Indeces of some sections we need later. */ +static size_t eh_frame_hdr_scnndx; +static size_t eh_frame_scnndx; +static size_t gcc_except_table_scnndx; + + +static void +check_sections (Ebl *ebl, GElf_Ehdr *ehdr) +{ + if (ehdr->e_shoff == 0) + /* No section header. */ + return; + + /* Allocate array to count references in section groups. */ + scnref = (int *) xcalloc (shnum, sizeof (int)); + + /* 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. */ + 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 + { + if (shdr->sh_name != 0) + ERROR (gettext ("zeroth section has nonzero name\n")); + if (shdr->sh_type != 0) + ERROR (gettext ("zeroth section has nonzero type\n")); + if (shdr->sh_flags != 0) + ERROR (gettext ("zeroth section has nonzero flags\n")); + if (shdr->sh_addr != 0) + ERROR (gettext ("zeroth section has nonzero address\n")); + if (shdr->sh_offset != 0) + ERROR (gettext ("zeroth section has nonzero offset\n")); + if (shdr->sh_addralign != 0) + ERROR (gettext ("zeroth section has nonzero align value\n")); + if (shdr->sh_entsize != 0) + ERROR (gettext ("zeroth section has nonzero entry size value\n")); + + if (shdr->sh_size != 0 && ehdr->e_shnum != 0) + ERROR (gettext ("\ +zeroth section has nonzero size value while ELF header has nonzero shnum value\n")); + + if (shdr->sh_link != 0 && ehdr->e_shstrndx != SHN_XINDEX) + ERROR (gettext ("\ +zeroth section has nonzero link value while ELF header does not signal overflow in shstrndx\n")); + + if (shdr->sh_info != 0 && ehdr->e_phnum != PN_XNUM) + ERROR (gettext ("\ +zeroth section has nonzero link value while ELF header does not signal overflow in phnum\n")); + } + + int *segment_flags = xcalloc (phnum, sizeof segment_flags[0]); + + 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, cnt), elf_errmsg (-1)); + continue; + } + + const char *scnname = elf_strptr (ebl->elf, shstrndx, shdr->sh_name); + + if (scnname == NULL) + ERROR (gettext ("section [%2zu]: invalid name\n"), cnt); + else + { + /* Check whether it is one of the special sections defined in + the gABI. */ + size_t s; + for (s = 0; s < nspecial_sections; ++s) + if (strncmp (scnname, special_sections[s].name, + special_sections[s].namelen) == 0) + { + char stbuf1[100]; + char stbuf2[100]; + char stbuf3[100]; + + 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, + ebl_section_type_name (ebl, special_sections[s].type, + stbuf1, sizeof (stbuf1)), + ebl_section_type_name (ebl, shdr->sh_type, + stbuf2, sizeof (stbuf2))); + + 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].attrflag == exact || !gnuld)) + ERROR (gettext ("\ +section [%2zu] '%s' has wrong flags: expected %s, is %s\n"), + cnt, scnname, + section_flags_string (special_sections[s].attr, + stbuf1, sizeof (stbuf1)), + section_flags_string (shdr->sh_flags + & ~SHF_LINK_ORDER, + stbuf2, sizeof (stbuf2))); + } + else if (special_sections[s].attrflag == atleast) + { + if ((shdr->sh_flags & special_sections[s].attr) + != special_sections[s].attr + || ((shdr->sh_flags & ~(SHF_LINK_ORDER | SHF_GROUP + | special_sections[s].attr + | special_sections[s].attr2)) + != 0)) + ERROR (gettext ("\ +section [%2zu] '%s' has wrong flags: expected %s and possibly %s, is %s\n"), + cnt, scnname, + section_flags_string (special_sections[s].attr, + stbuf1, sizeof (stbuf1)), + section_flags_string (special_sections[s].attr2, + stbuf2, sizeof (stbuf2)), + section_flags_string (shdr->sh_flags + & ~(SHF_LINK_ORDER + | SHF_GROUP), + stbuf3, sizeof (stbuf3))); + } + + if (strcmp (scnname, ".interp") == 0) + { + dot_interp_section = true; + + if (ehdr->e_type == ET_REL) + ERROR (gettext ("\ +section [%2zu] '%s' present in object file\n"), + cnt, scnname); + + if ((shdr->sh_flags & SHF_ALLOC) != 0 + && !has_loadable_segment) + ERROR (gettext ("\ +section [%2zu] '%s' has SHF_ALLOC flag set but there is no loadable segment\n"), + cnt, scnname); + else if ((shdr->sh_flags & SHF_ALLOC) == 0 + && has_loadable_segment) + ERROR (gettext ("\ +section [%2zu] '%s' has SHF_ALLOC flag not set but there are loadable segments\n"), + cnt, scnname); + } + else + { + if (strcmp (scnname, ".symtab_shndx") == 0 + && ehdr->e_type != ET_REL) + ERROR (gettext ("\ +section [%2zu] '%s' is extension section index table in non-object file\n"), + cnt, scnname); + + /* These sections must have the SHF_ALLOC flag set iff + a loadable segment is available. + + .relxxx + .strtab + .symtab + .symtab_shndx + + Check that if there is a reference from the + loaded section these sections also have the + ALLOC flag set. */ +#if 0 + // XXX TODO + if ((shdr->sh_flags & SHF_ALLOC) != 0 + && !has_loadable_segment) + ERROR (gettext ("\ +section [%2zu] '%s' has SHF_ALLOC flag set but there is no loadable segment\n"), + cnt, scnname); + else if ((shdr->sh_flags & SHF_ALLOC) == 0 + && has_loadable_segment) + ERROR (gettext ("\ +section [%2zu] '%s' has SHF_ALLOC flag not set but there are loadable segments\n"), + cnt, scnname); +#endif + } + + break; + } + + /* Remember a few special sections for later. */ + if (strcmp (scnname, ".eh_frame_hdr") == 0) + eh_frame_hdr_scnndx = cnt; + else if (strcmp (scnname, ".eh_frame") == 0) + eh_frame_scnndx = cnt; + else if (strcmp (scnname, ".gcc_except_table") == 0) + gcc_except_table_scnndx = cnt; + } + + 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, 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 + && 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 & ~(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, cnt)); + + // XXX TODO more tests!? + } + + if (shdr->sh_link >= shnum) + ERROR (gettext ("\ +section [%2zu] '%s': invalid section reference in link value\n"), + 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, 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, 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, cnt)); + + if (shdr->sh_flags & SHF_GROUP) + 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) + { + /* Make sure the section is contained in a loaded segment + and that the initialization part matches NOBITS sections. */ + unsigned int pcnt; + GElf_Phdr phdr_mem; + GElf_Phdr *phdr; + + for (pcnt = 0; pcnt < phnum; ++pcnt) + if ((phdr = gelf_getphdr (ebl->elf, pcnt, &phdr_mem)) != NULL + && ((phdr->p_type == PT_LOAD + && (shdr->sh_flags & SHF_TLS) == 0) + || (phdr->p_type == PT_TLS + && (shdr->sh_flags & SHF_TLS) != 0)) + && phdr->p_offset <= 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, cnt), pcnt); + + if (shdr->sh_type == SHT_NOBITS) + { + 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, cnt), pcnt); + } + else + { + 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, 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; + } + + if (pcnt == phnum) + ERROR (gettext ("\ +section [%2zu] '%s': alloc flag set but section not in any loaded segment\n"), + 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, cnt)); + + switch (shdr->sh_type) + { + case SHT_DYNSYM: + 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, shdr, cnt); + break; + + case SHT_REL: + check_rel (ebl, ehdr, shdr, cnt); + break; + + case SHT_DYNAMIC: + check_dynamic (ebl, ehdr, shdr, cnt); + break; + + case SHT_SYMTAB_SHNDX: + check_symtab_shndx (ebl, ehdr, shdr, cnt); + break; + + case SHT_HASH: + 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, 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: + /* 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, 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: + /* Nothing. */ + break; + } + } + + if (has_interp_segment && !dot_interp_section) + ERROR (gettext ("INTERP program header entry but no .interp section\n")); + + if (!is_debuginfo) + for (unsigned int pcnt = 0; pcnt < 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); + + 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); + } + } + + free (segment_flags); + + if (version_namelist != NULL) + { + 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); + + /* Check for duplicate index numbers. */ + do + { + struct version_namelist *runp = version_namelist->next; + while (runp != NULL) + { + 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); + } + 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 (hash_idx != 0 && gnu_hash_idx != 0) + compare_hash_gnu_hash (ebl, ehdr, hash_idx, gnu_hash_idx); + + free (scnref); +} + + +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 (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: + case NT_GNU_GOLD_VERSION: + 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: + if (shndx == 0) + ERROR (gettext ("\ +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); + } + } + + 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; + + 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); +} + + +/* Index of the PT_GNU_EH_FRAME program eader entry. */ +static int pt_gnu_eh_frame_pndx; + + +static void +check_program_header (Ebl *ebl, GElf_Ehdr *ehdr) +{ + if (ehdr->e_phoff == 0) + return; + + if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN + && ehdr->e_type != ET_CORE) + ERROR (gettext ("\ +only executables, shared objects, and core files can have program headers\n")); + + int num_pt_interp = 0; + int num_pt_tls = 0; + int num_pt_relro = 0; + + for (unsigned int cnt = 0; cnt < phnum; ++cnt) + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr; + + phdr = gelf_getphdr (ebl->elf, cnt, &phdr_mem); + if (phdr == NULL) + { + ERROR (gettext ("cannot get program header entry %d: %s\n"), + cnt, elf_errmsg (-1)); + continue; + } + + if (phdr->p_type >= PT_NUM && phdr->p_type != PT_GNU_EH_FRAME + && 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 %#" PRIx64 "\n"), + cnt, (uint64_t) phdr->p_type); + + if (phdr->p_type == PT_LOAD) + has_loadable_segment = true; + else if (phdr->p_type == PT_INTERP) + { + if (++num_pt_interp != 1) + { + if (num_pt_interp == 2) + ERROR (gettext ("\ +more than one INTERP entry in program header\n")); + } + has_interp_segment = true; + } + else if (phdr->p_type == PT_TLS) + { + if (++num_pt_tls == 2) + ERROR (gettext ("more than one TLS 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) + { + 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) + ERROR (gettext ("\ +more than one GNU_RELRO entry in program header\n")); + else + { + /* Check that the region is in a writable segment. */ + unsigned int inner; + for (inner = 0; inner < phnum; ++inner) + { + GElf_Phdr phdr2_mem; + GElf_Phdr *phdr2; + + phdr2 = gelf_getphdr (ebl->elf, inner, &phdr2_mem); + if (phdr2 == NULL) + continue; + + if (phdr2->p_type == PT_LOAD + && phdr->p_vaddr >= phdr2->p_vaddr + && (phdr->p_vaddr + phdr->p_memsz + <= phdr2->p_vaddr + phdr2->p_memsz)) + { + if ((phdr2->p_flags & PF_W) == 0) + ERROR (gettext ("\ +loadable segment GNU_RELRO applies to is not writable\n")); + if ((phdr2->p_flags & ~PF_W) != (phdr->p_flags & ~PF_W)) + ERROR (gettext ("\ +loadable segment [%u] flags do not match GNU_RELRO [%u] flags\n"), + cnt, inner); + break; + } + } + + if (inner >= phnum) + ERROR (gettext ("\ +%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. */ + unsigned int inner; + for (inner = 0; inner < 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 (inner >= 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")); + } + else if (phdr->p_type == PT_GNU_EH_FRAME) + { + /* If there is an .eh_frame_hdr section it must be + referenced by this program header entry. */ + Elf_Scn *scn = NULL; + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = NULL; + bool any = false; + while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) + { + any = true; + shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr != NULL + && shdr->sh_type == (is_debuginfo + ? SHT_NOBITS : SHT_PROGBITS) + && ! strcmp (".eh_frame_hdr", + elf_strptr (ebl->elf, shstrndx, shdr->sh_name))) + { + if (! is_debuginfo) + { + if (phdr->p_offset != shdr->sh_offset) + ERROR (gettext ("\ +call frame search table reference in program header has wrong offset\n")); + if (phdr->p_memsz != shdr->sh_size) + ERROR (gettext ("\ +call frame search table size mismatch in program and section header\n")); + } + break; + } + } + + if (scn == NULL) + { + /* If there is no section header table we don't + complain. But if there is one there should be an + entry for .eh_frame_hdr. */ + if (any) + ERROR (gettext ("\ +PT_GNU_EH_FRAME present but no .eh_frame_hdr section\n")); + } + else + { + /* The section must be allocated and not be writable and + executable. */ + if ((phdr->p_flags & PF_R) == 0) + ERROR (gettext ("\ +call frame search table must be allocated\n")); + else if (shdr != NULL && (shdr->sh_flags & SHF_ALLOC) == 0) + ERROR (gettext ("\ +section [%2zu] '%s' must be allocated\n"), elf_ndxscn (scn), ".eh_frame_hdr"); + + if ((phdr->p_flags & PF_W) != 0) + ERROR (gettext ("\ +call frame search table must not be writable\n")); + else if (shdr != NULL && (shdr->sh_flags & SHF_WRITE) != 0) + ERROR (gettext ("\ +section [%2zu] '%s' must not be writable\n"), + elf_ndxscn (scn), ".eh_frame_hdr"); + + if ((phdr->p_flags & PF_X) != 0) + ERROR (gettext ("\ +call frame search table must not be executable\n")); + else if (shdr != NULL && (shdr->sh_flags & SHF_EXECINSTR) != 0) + ERROR (gettext ("\ +section [%2zu] '%s' must not be executable\n"), + elf_ndxscn (scn), ".eh_frame_hdr"); + } + + /* Remember which entry this is. */ + pt_gnu_eh_frame_pndx = cnt; + } + + 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); + + if (phdr->p_align > 1) + { + if (!powerof2 (phdr->p_align)) + ERROR (gettext ("\ +program header entry %d: alignment not a power of 2\n"), cnt); + else if ((phdr->p_vaddr - phdr->p_offset) % phdr->p_align != 0) + ERROR (gettext ("\ +program header entry %d: file offset and virtual address not module of alignment\n"), cnt); + } + } +} + + +static void +check_exception_data (Ebl *ebl __attribute__ ((unused)), + GElf_Ehdr *ehdr __attribute__ ((unused))) +{ + if ((ehdr->e_type == ET_EXEC || ehdr->e_type == ET_DYN) + && pt_gnu_eh_frame_pndx == 0 && eh_frame_hdr_scnndx != 0) + ERROR (gettext ("executable/DSO with .eh_frame_hdr section does not have " + "a PT_GNU_EH_FRAME program header entry")); +} + + +/* Process one file. */ +static void +process_elf_file (Elf *elf, const char *prefix, const char *suffix, + const char *fname, size_t size, bool only_one) +{ + /* 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); + Ebl *ebl; + + /* Print the file name. */ + if (!only_one) + { + if (prefix != NULL) + printf ("\n%s(%s)%s:\n", prefix, fname, suffix); + else + printf ("\n%s:\n", fname); + } + + if (ehdr == NULL) + { + ERROR (gettext ("cannot read ELF header: %s\n"), elf_errmsg (-1)); + return; + } + + ebl = ebl_openbackend (elf); + /* If there is no appropriate backend library we cannot test + architecture and OS specific features. Any encountered extension + is an error. */ + + /* Go straight by the gABI, check all the parts in turn. */ + check_elf_header (ebl, ehdr, size); + + /* Check the program header. */ + check_program_header (ebl, ehdr); + + /* Next the section headers. It is OK if there are no section + headers at all. */ + check_sections (ebl, ehdr); + + /* Check the exception handling data, if it exists. */ + if (pt_gnu_eh_frame_pndx != 0 || eh_frame_hdr_scnndx != 0 + || eh_frame_scnndx != 0 || gcc_except_table_scnndx != 0) + check_exception_data (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/0.153/src/findtextrel.c b/0.153/src/findtextrel.c new file mode 100644 index 00000000..444f3a2b --- /dev/null +++ b/0.153/src/findtextrel.c @@ -0,0 +1,627 @@ +/* Locate source files or functions which caused text relocations. + Copyright (C) 2005-2010, 2012 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> + +#include <system.h> + + +struct segments +{ + GElf_Addr from; + GElf_Addr to; +}; + + +/* Name and version of program. */ +static void print_version (FILE *stream, struct argp_state *state); +ARGP_PROGRAM_VERSION_HOOK_DEF = print_version; + +/* Bug report address. */ +ARGP_PROGRAM_BUG_ADDRESS_DEF = 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\ +"), "2012"); + 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; + bool seen_dynamic = false; + bool have_textrel = false; + while ((scn = elf_nextscn (elf, scn)) != NULL + && (!seen_dynamic || symscn == 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; + } + + switch (shdr->sh_type) + { + case SHT_DYNAMIC: + if (!seen_dynamic) + { + seen_dynamic = true; + + 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)) + have_textrel = true; + } + } + break; + + case SHT_SYMTAB: + symscn = scn; + break; + } + } + + if (!have_textrel) + { + error (0, 0, gettext ("no text relocations reported in '%s'"), fname); + return 1; + } + + 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/0.153/src/i386_ld.c b/0.153/src/i386_ld.c new file mode 100644 index 00000000..2702ef85 --- /dev/null +++ b/0.153/src/i386_ld.c @@ -0,0 +1,1110 @@ +/* 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. + + 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 <libintl.h> +#include <stdlib.h> +#include <string.h> + +// XXX For debugging +#include <stdio.h> + +#include <system.h> +#include "ld.h" +#include "list.h" +/* x86 is little endian. */ +#define UNALIGNED_ACCESS_CLASS LITTLE_ENDIAN +#include "unaligned.h" +#include "xelf.h" + + +/* The old callbacks. */ +static int (*old_open_outfile) (struct ld_state *, int, int, int); + + +static int +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. */ + return old_open_outfile (statep, EM_386, ELFCLASS32, ELFDATA2LSB); +} + + +/* 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 __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. */ + runp = firstp; + data = NULL; + do + { + Elf_Data *reltgtdata; + Elf_Data *insymdata; + Elf_Data *inxndxdata = NULL; + size_t maxcnt; + size_t cnt; + const Elf32_Word *symindirect; + struct symbol **symref; + struct usedfiles *file = runp->fileinfo; + XElf_Shdr *shdr = &SCNINFO_SHDR (runp->shdr); + + /* Get the output section data buffer for this input section. */ + data = elf_getdata (outscn, data); + assert (data != NULL); + + /* Get the data for section in the input file this relocation + section is relocating. Since these buffers are reused in the + output modifying these buffers has the correct result. */ + reltgtdata = elf_getdata (file->scninfo[shdr->sh_info].scn, NULL); + + /* Get the data for the input section symbol table for this + relocation section. */ + insymdata = elf_getdata (file->scninfo[shdr->sh_link].scn, NULL); + assert (insymdata != NULL); + + /* And the extended section index table. */ + inxndxdata = runp->fileinfo->xndxdata; + + /* Number of relocations. */ + maxcnt = shdr->sh_size / shdr->sh_entsize; + + /* Array directing local symbol table offsets to output symbol + table offsets. */ + symindirect = file->symindirect; + + /* References to the symbol records. */ + symref = file->symref; + + /* Iterate over all the relocations in the section. */ + for (cnt = 0; cnt < maxcnt; ++cnt) + { + XElf_Rel_vardef (rel); + Elf32_Word si; + XElf_Sym_vardef (sym); + Elf32_Word xndx; + + /* Get the relocation data itself. x86 uses Rel + relocations. In case we have to handle Rela as well the + whole loop probably should be duplicated. */ + xelf_getrel (data, cnt, rel); + assert (rel != NULL); + + /* Compute the symbol index in the output file. */ + si = symindirect[XELF_R_SYM (rel->r_info)]; + if (si == 0) + { + /* This happens if the symbol is locally undefined or + superceded by some other definition. */ + assert (symref[XELF_R_SYM (rel->r_info)] != NULL); + si = symref[XELF_R_SYM (rel->r_info)]->outsymidx; + } + /* Take reordering performed to sort the symbol table into + account. */ + si = dblindirect[si]; + + /* Get the symbol table entry. */ + xelf_getsymshndx (insymdata, inxndxdata, XELF_R_SYM (rel->r_info), + sym, xndx); + if (sym->st_shndx != SHN_XINDEX) + xndx = sym->st_shndx; + 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 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 + input file. Therefore we have to adjust the addend which + in the case of Rel relocations is in the target section + itself. */ + if (XELF_ST_TYPE (sym->st_info) == STT_SECTION) + { + /* 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. */ + Elf32_Word toadd = file->scninfo[xndx].offset; + if (toadd != 0) + add_4ubyte_unaligned (reltgtdata->d_buf + rel->r_offset, + toadd); + } + + /* Adjust the offset for the position of the input section + content in the output section. */ + rel->r_offset += file->scninfo[shdr->sh_info].offset; + + /* And finally adjust the index of the symbol in the output + symbol table. */ + rel->r_info = XELF_R_INFO (si, XELF_R_TYPE (rel->r_info)); + + /* Store the result. */ + (void) xelf_update_rel (data, cnt, rel); + } + + runp = runp->next; + } + while (runp != firstp); +} + + +/* Each PLT entry has 16 bytes. We need one entry as overhead for + the code to set up the call into the runtime relocation. */ +#define PLT_ENTRY_SIZE 16 + +static void +elf_i386_initialize_plt (struct ld_state *statep, Elf_Scn *scn) +{ + Elf_Data *data; + XElf_Shdr_vardef (shdr); + + /* Change the entry size in the section header. */ + xelf_getshdr (scn, shdr); + assert (shdr != NULL); + shdr->sh_entsize = PLT_ENTRY_SIZE; + (void) xelf_update_shdr (scn, shdr); + + data = elf_newdata (scn); + if (data == NULL) + error (EXIT_FAILURE, 0, gettext ("cannot allocate PLT section: %s"), + elf_errmsg (-1)); + + /* We need one special PLT entry (performing the jump to the runtime + 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); + assert (data->d_type == ELF_T_BYTE); + data->d_off = 0; + data->d_align = 8; + + statep->nplt_used = 1; +} + + +static void +elf_i386_initialize_pltrel (struct ld_state *statep, Elf_Scn *scn) +{ + Elf_Data *data; + + data = elf_newdata (scn); + if (data == NULL) + error (EXIT_FAILURE, 0, gettext ("cannot allocate PLTREL section: %s"), + elf_errmsg (-1)); + + /* One relocation per PLT entry. */ + 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; +} + + +static void +elf_i386_initialize_got (struct ld_state *statep, Elf_Scn *scn) +{ + /* If we come here we better need a GOT. */ + assert (statep->ngot != 0); + + Elf_Data *data = elf_newdata (scn); + if (data == NULL) + error (EXIT_FAILURE, 0, gettext ("cannot allocate GOT section: %s"), + elf_errmsg (-1)); + + /* 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. */ + 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); +} + + +/* The first entry in an absolute procedure linkage table looks like + this. See the SVR4 ABI i386 supplement to see how this works. */ +static const unsigned char elf_i386_plt0_entry[PLT_ENTRY_SIZE] = +{ + 0xff, 0x35, /* pushl contents of address */ + 0, 0, 0, 0, /* replaced with address of .got + 4. */ + 0xff, 0x25, /* jmp indirect */ + 0, 0, 0, 0, /* replaced with address of .got + 8. */ + 0x0f, 0x0b, /* ud2a, to prevent further decoding. */ + 0, 0 /* pad out to 16 bytes. */ +}; + +/* Type describing the first PLT entry in non-PIC. */ +struct plt0_entry +{ + /* First a 'push' of the second GOT entry. */ + unsigned char push_instr[2]; + uint32_t gotp4_addr; + /* Second, a 'jmp indirect' to the third GOT entry. */ + unsigned char jmp_instr[2]; + uint32_t gotp8_addr; + /* Padding. */ + unsigned char padding[4]; +} __attribute__ ((packed)); + +/* The first entry in a PIC procedure linkage table look like this. */ +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) */ + 0x0f, 0x0b, /* ud2a, to prevent further decoding. */ + 0, 0 /* pad out to 16 bytes. */ +}; + +/* Contents of all but the first PLT entry in executable. */ +static const unsigned char elf_i386_plt_entry[PLT_ENTRY_SIZE] = +{ + 0xff, 0x25, /* jmp indirect */ + 0, 0, 0, 0, /* replaced with address of this symbol in .got. */ + 0x68, /* pushl immediate */ + 0, 0, 0, 0, /* replaced with offset into relocation table. */ + 0xe9, /* jmp relative */ + 0, 0, 0, 0 /* replaced with offset to start of .plt. */ +}; + +/* Contents of all but the first PLT entry in DSOs. */ +static const unsigned char elf_i386_pic_plt_entry[PLT_ENTRY_SIZE] = +{ + 0xff, 0xa3, /* jmp *offset(%ebx) */ + 0, 0, 0, 0, /* replaced with offset of this symbol in .got. */ + 0x68, /* pushl immediate */ + 0, 0, 0, 0, /* replaced with offset into relocation table. */ + 0xe9, /* jmp relative */ + 0, 0, 0, 0 /* replaced with offset to start of .plt. */ +}; + +/* Type describing a PLT entry. */ +struct plt_entry +{ + /* The first instruction is 'jmp indirect' or 'jmp *offset(%ebs)'. */ + unsigned char jmp_instr[2]; + uint32_t offset_got; + /* The second instruction is 'push immediate'. */ + unsigned char push_instr; + uint32_t push_imm; + /* Finally a 'jmp relative'. */ + unsigned char jmp_instr2; + uint32_t plt0_offset; +} __attribute__ ((packed)); + + +static void +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; + const bool build_dso = statep->file_type == dso_file_type; + + /* 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.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 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_Shdr_vardef (pltshdr); + xelf_getshdr (scn, pltshdr); + assert (pltshdr != 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), + NULL); + assert (symdata != NULL); + } + + /* Create the .plt section. */ + scn = elf_getscn (statep->outelf, statep->pltscnidx); + Elf_Data *pltdata = elf_getdata (scn, NULL); + assert (pltdata != NULL); + + /* 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 (pltdata->d_buf, elf_i386_pic_plt0_entry, PLT_ENTRY_SIZE); + else + { + /* Copy the skeleton. */ + memcpy (pltdata->d_buf, elf_i386_plt0_entry, PLT_ENTRY_SIZE); + + /* And fill in the addresses. */ + 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); + } + + /* For DSOs we need GOT offsets, otherwise the GOT address. */ + Elf32_Addr gotaddr_off = build_dso ? 0 : gotaddr; + + /* Create the remaining entries. */ + const unsigned char *plt_template + = build_dso ? elf_i386_pic_plt_entry : elf_i386_plt_entry; + + for (size_t idx = nsym_local; idx < nsym; ++idx) + { + 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; + + 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 + + (2 + pltidx) * sizeof (Elf32_Addr)); + /* Offset into relocation table. */ + addr->push_imm = target_bswap_32 ((pltidx - 1) * sizeof (Elf32_Rel)); + /* Offset to start of .plt. */ + addr->plt0_offset = target_bswap_32 (-(1 + pltidx) * PLT_ENTRY_SIZE); + + + XElf_Rel_vardef (rel); + 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 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); + } +} + + +static int +elf_i386_rel_type (struct ld_state *statep __attribute__ ((__unused__))) +{ + /* ELF/i386 uses REL. */ + return DT_REL; +} + + +static void +elf_i386_count_relocations (struct ld_state *statep, struct scninfo *scninfo) +{ + /* We go through the list of input sections and count those relocations + which are not handled by the linker. At the same time we have to + see how many GOT entries we need and how much .bss space is needed + for copy relocations. */ + Elf_Data *data = elf_getdata (scninfo->scn, NULL); + XElf_Shdr *shdr = &SCNINFO_SHDR (scninfo->shdr); + size_t maxcnt = shdr->sh_size / shdr->sh_entsize; + size_t relsize = 0; + size_t cnt; + struct symbol *sym; + + assert (shdr->sh_type == SHT_REL); + + for (cnt = 0; cnt < maxcnt; ++cnt) + { + XElf_Rel_vardef (rel); + + xelf_getrel (data, cnt, rel); + /* XXX Should we complain about failing accesses? */ + if (rel != NULL) + { + 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 + || scninfo->fileinfo->symref[r_sym]->in_dso + || statep->file_type == dso_file_type) + { + relsize += sizeof (Elf32_Rel); + ++statep->nrel_got; + } + + /* Even if this relocation is not emitted in the output + file it requires a GOT entry. */ + ++statep->ngot; + + /* FALLTHROUGH */ + + case R_386_GOTOFF: + case R_386_GOTPC: + statep->need_got = true; + break; + + case R_386_32: + case R_386_PC32: + /* These relocations cause text relocations in DSOs. */ + if (linked_from_dso_p (scninfo, r_sym)) + { + 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 + { + /* Non-function objects from a DSO need to get a + copy relocation. */ + sym = scninfo->fileinfo->symref[r_sym]; + + /* Only do this if we have not requested a copy + relocation already. */ + if (unlikely (sym->type != STT_FUNC) && ! sym->need_copy) + { + sym->need_copy = 1; + ++statep->ncopy; + relsize += sizeof (Elf32_Rel); + } + } + } + else if (statep->file_type == dso_file_type + && XELF_R_TYPE (rel->r_info) == R_386_32) + relsize += sizeof (Elf32_Rel); + + break; + + case R_386_PLT32: + /* We might need a PLT entry. But we cannot say for sure + here since one of the symbols might turn up being + defined in the executable (if we create such a thing). + If a DSO is created we still might use a local + definition. + + 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 + && !statep->statically) + { + sym = scninfo->fileinfo->symref[r_sym]; + sym->type = STT_FUNC; + sym->in_dso = 1; + sym->defined = 1; + + /* Remove from the list of unresolved symbols. */ + --statep->nunresolved; + if (! sym->weak) + --statep->nunresolved_nonweak; + CDBL_LIST_DEL (statep->unresolved, sym); + + /* Add to the list of symbols we expect from a DSO. */ + ++statep->nplt; + ++statep->nfrom_dso; + CDBL_LIST_ADD_REAR (statep->from_dso, sym); + } + 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: + case R_386_TLS_GD_CALL: + case R_386_TLS_GD_POP: + case R_386_TLS_LDM_32: + case R_386_TLS_LDM_PUSH: + case R_386_TLS_LDM_CALL: + case R_386_TLS_LDM_POP: + case R_386_TLS_IE_32: + case R_386_TLS_LE_32: + /* XXX */ + abort (); + break; + + case R_386_NONE: + /* Nothing to be done. */ + break; + + /* These relocation should never be generated by an + assembler. */ + case R_386_COPY: + case R_386_GLOB_DAT: + case R_386_JMP_SLOT: + case R_386_RELATIVE: + case R_386_TLS_DTPMOD32: + case R_386_TLS_DTPOFF32: + case R_386_TLS_TPOFF32: + /* Unknown relocation. */ + default: + abort (); + } + } + } + + scninfo->relsize = relsize; +} + + +static void +elf_i386_create_relocations (struct ld_state *statep, + const Elf32_Word *dblindirect __attribute__ ((unused))) +{ + /* Get the address of the got section. */ + Elf_Scn *pltscn = elf_getscn (statep->outelf, statep->pltscnidx); + Elf32_Shdr *shdr = elf32_getshdr (pltscn); + assert (shdr != NULL); + Elf32_Addr pltaddr = shdr->sh_addr; + + Elf_Scn *gotscn = elf_getscn (statep->outelf, statep->gotscnidx); + // 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; + size_t ngotconst = statep->nrel_got; + + struct scninfo *first = statep->rellist->next; + struct scninfo *runp = first; + do + { + XElf_Shdr *rshdr = &SCNINFO_SHDR (runp->shdr); + Elf_Data *reldata = elf_getdata (runp->scn, NULL); + int nrels = rshdr->sh_size / rshdr->sh_entsize; + + /* 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; + + /* This is the offset of the input section we are looking at in + the output file. */ + XElf_Addr inscnoffset = scninfo[rshdr->sh_info].offset; + + /* The target section. We use the data from the input file. */ + Elf_Data *data = elf_getdata (scninfo[rshdr->sh_info].scn, NULL); + + /* We cannot handle relocations against merge-able sections. */ + assert ((SCNINFO_SHDR (scninfo[rshdr->sh_link].shdr).sh_flags + & SHF_MERGE) == 0); + + /* Cache the access to the symbol table data. */ + Elf_Data *symdata = elf_getdata (scninfo[rshdr->sh_link].scn, NULL); + + for (int cnt = 0; cnt < nrels; ++cnt) + { + XElf_Rel_vardef (rel); + XElf_Rel *rel2; + xelf_getrel (reldata, cnt, rel); + assert (rel != NULL); + XElf_Addr reladdr = inscnoffset + rel->r_offset; + XElf_Addr value; + + size_t idx = XELF_R_SYM (rel->r_info); + if (idx < runp->fileinfo->nlocalsymbols) + { + XElf_Sym_vardef (sym); + xelf_getsym (symdata, idx, sym); + + /* 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] == 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; + } + } + + /* Address of the relocated memory in the data buffer. */ + 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 + symbol associated with the R_386_GOTPC relocation is + _GLOBAL_OFFSET_TABLE_ which has a value corresponding + to the address of the GOT and the address of the PLT + entry required for R_386_PLT32 is computed above. */ + case R_386_PC32: + case R_386_GOTPC: + case R_386_PLT32: + value -= reladdr; + /* FALLTHROUGH */ + + case R_386_32: + if (linked_from_dso_p (scninfo, idx) + && statep->file_type != dso_file_type + && symref[idx]->type != STT_FUNC) + { + value = (ld_state.copy_section->offset + + symref[idx]->merge.value); + + if (unlikely (symref[idx]->need_copy)) + { + /* Add a relocation to initialize the GOT entry. */ + assert (symref[idx]->outdynsymidx != 0); +#if NATIVE_ELF != 0 + xelf_getrel_ptr (reldyndata, nreldyn, rel2); +#else + rel2 = &rel_mem; +#endif + rel2->r_offset = value; + rel2->r_info + = 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. */ + Elf32_Word symidx = symref[idx]->outdynsymidx; + Elf_Scn *symscn = elf_getscn (statep->outelf, + statep->dynsymscnidx); + Elf_Data *outsymdata = elf_getdata (symscn, NULL); + assert (outsymdata != NULL); + XElf_Sym_vardef (sym); + xelf_getsym (outsymdata, symidx, sym); + sym->st_value = value; + sym->st_shndx = statep->copy_section->outscnndx; + (void) xelf_update_sym (outsymdata, symidx, sym); + + symidx = symref[idx]->outsymidx; + if (symidx != 0) + { + symidx = statep->dblindirect[symidx]; + symscn = elf_getscn (statep->outelf, + statep->symscnidx); + outsymdata = elf_getdata (symscn, NULL); + assert (outsymdata != NULL); + xelf_getsym (outsymdata, symidx, sym); + sym->st_value = value; + sym->st_shndx = statep->copy_section->outscnndx; + (void) xelf_update_sym (outsymdata, symidx, sym); + } + + /* Remember that we set up the copy relocation. */ + symref[idx]->need_copy = 0; + } + } + else if (statep->file_type == dso_file_type + && XELF_R_TYPE (rel->r_info) == R_386_32) + { +#if NATIVE_ELF != 0 + xelf_getrel_ptr (reldyndata, nreldyn, rel2); +#else + rel2 = &rel_mem; +#endif + rel2->r_offset = value; + + /* 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; + } + add_4ubyte_unaligned (relloc, value); + break; + + case R_386_GOT32: + if (! symref[idx]->defined || symref[idx]->in_dso) + { + thisgotidx = nreldyn++; + assert (thisgotidx < statep->nrel_got); + + /* Add a relocation to initialize the GOT entry. */ +#if NATIVE_ELF != 0 + xelf_getrel_ptr (reldyndata, thisgotidx, rel2); +#else + rel2 = &rel_mem; +#endif + 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_GOTIE: + case R_386_TLS_LDM: + case R_386_16: + case R_386_PC16: + case R_386_8: + case R_386_PC8: + case R_386_TLS_GD_32: + case R_386_TLS_GD_PUSH: + case R_386_TLS_GD_CALL: + case R_386_TLS_GD_POP: + case R_386_TLS_LDM_32: + case R_386_TLS_LDM_PUSH: + case R_386_TLS_LDM_CALL: + case R_386_TLS_LDM_POP: + case R_386_TLS_IE_32: + case R_386_TLS_LE_32: + // XXX For now fall through + break; + + case R_386_NONE: + /* Nothing to do. */ + break; + + case R_386_COPY: + case R_386_JMP_SLOT: + case R_386_RELATIVE: + case R_386_GLOB_DAT: + case R_386_TLS_DTPMOD32: + case R_386_TLS_DTPOFF32: + case R_386_TLS_TPOFF32: + default: + /* Should not happen. */ + abort (); + } + } + } + while ((runp = runp->next) != first); +} + + +int +elf_i386_ld_init (struct ld_state *statep) +{ + /* We have a few callbacks available. */ + old_open_outfile = statep->callbacks.open_outfile; + statep->callbacks.open_outfile = elf_i386_open_outfile; + + statep->callbacks.relocate_section = elf_i386_relocate_section; + + statep->callbacks.initialize_plt = elf_i386_initialize_plt; + 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; + + statep->callbacks.rel_type = elf_i386_rel_type; + + statep->callbacks.count_relocations = elf_i386_count_relocations; + + statep->callbacks.create_relocations = elf_i386_create_relocations; + + return 0; +} diff --git a/0.153/src/ld.c b/0.153/src/ld.c new file mode 100644 index 00000000..6384e86a --- /dev/null +++ b/0.153/src/ld.c @@ -0,0 +1,1619 @@ +/* Copyright (C) 2001-2010, 2012 Red Hat, Inc. + This file is part of Red Hat elfutils. + Written by Ulrich Drepper <drepper@redhat.com>, 2001. + + 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 <libelf.h> +#include <libintl.h> +#include <locale.h> +#include <mcheck.h> +#include <stdio.h> +#include <stdio_ext.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <system.h> +#include "ld.h" +#include "list.h" + + +/* Name and version of program. */ +static void print_version (FILE *stream, struct argp_state *state); +ARGP_PROGRAM_VERSION_HOOK_DEF = print_version; + +/* Bug report address. */ +ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT; + + +/* Values for the various options. */ +enum + { + ARGP_whole_archive = 300, + ARGP_no_whole_archive, + ARGP_static, + ARGP_dynamic, + ARGP_pagesize, + ARGP_rpath_link, + ARGP_runpath, + ARGP_runpath_link, + ARGP_version_script, + ARGP_gc_sections, + 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 + }; + + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + { 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."), 0 }, + { "no-whole-archive", ARGP_no_whole_archive, NULL, 0, + N_("Stop including the whole archives 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."), 0 }, + { "-z allextract", '\0', NULL, OPTION_DOC, + N_("Same as --whole-archive."), 0 }, + { "-z defaultextract", '\0', NULL, OPTION_DOC, N_("\ +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."), 0 }, + { "-z muldefs", '\0', NULL, OPTION_DOC, + N_("Allow multiple definitions; first is used."), 0 }, + { "-z defs | nodefs", '\0', NULL, OPTION_DOC, + 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."), 0 }, + { "-z now", '\0', NULL, OPTION_DOC, + N_("Relocation will not be processed lazily."), 0 }, + { "-z nodelete", '\0', NULL, OPTION_DOC, + N_("Object cannot be unloaded at runtime."), 0 }, + { "-z initfirst", '\0', NULL, OPTION_DOC, + N_("Mark object to be initialized first."), 0 }, + { "-z lazyload | nolazyload", '\0', NULL, OPTION_DOC, + N_("Enable/disable lazy-loading flag for following dependencies."), 0 }, + { "-z nodlopen", '\0', NULL, OPTION_DOC, + N_("Mark object as not loadable with 'dlopen'."), 0 }, + { "-z ignore | record", '\0', NULL, OPTION_DOC, + N_("Ignore/record dependencies on unused DSOs."), 0 }, + { "-z systemlibrary", '\0', NULL, OPTION_DOC, + 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."), 0 }, + { "dynamic", ARGP_dynamic, NULL, OPTION_HIDDEN, NULL, 0 }, + { "-B dynamic", ARGP_dynamic, NULL, OPTION_DOC, + 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."), 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."), 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."), 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."), 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."), + 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"), 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, 0 } +}; + +/* Short description of program. */ +static const char doc[] = N_("Combine object and archive files."); + +/* Strings for arguments in help texts. */ +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); + +/* Data structure to communicate with argp functions. */ +static struct argp argp_1st = +{ + options, parse_opt_1st, args_doc, doc, NULL, NULL, NULL +}; +static struct argp argp_2nd = +{ + options, parse_opt_2nd, args_doc, doc, NULL, NULL, NULL +}; + + +/* Linker state. This contains all global information. */ +struct ld_state ld_state; + +/* List of the input files. */ +static struct file_list +{ + const char *name; + struct file_list *next; +} *input_file_list; + +/* If nonzero be verbose. */ +int verbose; + +/* If nonzero, trade speed for less memory/address space usage. */ +int conserve_memory; + +/* The emulation name to use. */ +static const char *emulation; + +/* Keep track of the nesting level. Even though we don't handle nested + groups we still keep track to improve the error messages. */ +static int group_level; + +/* The last file we processed. */ +static struct usedfiles *last_file; + +/* The default linker script. */ +/* XXX We'll do this a bit different in the real solution. */ +static const char *linker_script = SRCDIR "/elf32-i386.script"; + +/* Nonzero if an error occurred while loading the input files. */ +static int error_loading; + + +/* Intermediate storage for the LD_LIBRARY_PATH information from the + environment. */ +static char *ld_library_path1; + +/* Flag used to communicate with the scanner. */ +int ld_scan_version_script; + +/* Name of the input file. */ +const char *ldin_fname; + +/* Define by parser if required. */ +extern int lddebug; + + +/* Prototypes for local functions. */ +static void parse_z_option (const char *arg); +static void parse_z_option_2 (const char *arg); +static void parse_B_option (const char *arg); +static void parse_B_option_2 (const char *arg); +static void determine_output_format (void); +static void load_needed (void); +static void collect_sections (void); +static void add_rxxpath (struct pathelement **pathp, const char *str); +static void gen_rxxpath_data (void); +static void read_version_script (const char *fname); +static void create_lscript_symbols (void); +static void create_special_section_symbol (struct symbol **symp, + const char *name); + + +int +main (int argc, char *argv[]) +{ + int remaining; + int err; + +#ifndef NDEBUG + /* Enable memory debugging. */ + mtrace (); +#endif + + /* Sanity check. We always want to use the LFS functionality. */ + if (sizeof (off_t) != sizeof (off64_t)) + abort (); + + /* 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); + + /* Before we start tell the ELF library which version we are using. */ + elf_version (EV_CURRENT); + + /* The user can use the LD_LIBRARY_PATH environment variable to add + additional lookup directories. */ + ld_library_path1 = getenv ("LD_LIBRARY_PATH"); + + /* Initialize the memory handling. */ +#define obstack_chunk_alloc xmalloc +#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); + + /* We need at least one input file. */ + if (input_file_list == NULL) + { + error (0, 0, gettext ("At least one input file needed")); + argp_help (&argp_1st, stderr, ARGP_HELP_SEE, "ld"); + exit (EXIT_FAILURE); + } + + /* 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) + error (EXIT_FAILURE, 0, gettext ("error while preparing linking")); + + /* XXX Read the linker script now. Since we later will have the linker + script built in we don't go into trouble to make sure we handle GROUP + 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'"), + linker_script); + /* No need for locking. */ + __fsetlocking (ldin, FSETLOCKING_BYCALLER); + + ld_state.srcfiles = NULL; + ldlineno = 1; + ld_scan_version_script = 0; + ldin_fname = linker_script; + if (ldparse () != 0) + /* Something went wrong during parsing. */ + exit (EXIT_FAILURE); + fclose (ldin); + + /* We now might have a list of directories to look for libraries in + named by the linker script. Put them in a different list so that + they are searched after all paths given by the user on the + command line. */ + ld_state.default_paths = ld_state.paths; + ld_state.paths = ld_state.tailpaths = NULL; + + /* Get runpath/rpath information in usable form. */ + gen_rxxpath_data (); + + /* Parse and process arguments for real. */ + argp_parse (&argp_2nd, argc, argv, ARGP_IN_ORDER, &remaining, NULL); + /* All options should have been processed by the argp parser. */ + assert (remaining == argc); + + /* Process the last file. */ + while (last_file != NULL) + /* Try to open the file. */ + error_loading |= FILE_PROCESS (-1, last_file, &ld_state, &last_file); + + /* Stop if there has been a problem while reading the input files. */ + if (error_loading) + exit (error_loading); + + /* See whether all opened -( were closed. */ + if (group_level > 0) + { + error (0, 0, gettext ("-( without matching -)")); + argp_help (&argp_1st, stderr, ARGP_HELP_SEE, "ld"); + exit (EXIT_FAILURE); + } + + /* When we create a relocatable file we don't have to look for the + DT_NEEDED DSOs and we also don't test for undefined symbols. */ + if (ld_state.file_type != relocatable_file_type) + { + /* At this point we have loaded all the direct dependencies. What + remains to be done is find the indirect dependencies. These are + DSOs which are referenced by the DT_NEEDED entries in the DSOs + which are direct dependencies. We have to transitively find and + load all these dependencies. */ + load_needed (); + + /* At this point all object files and DSOs are read. If there + are still undefined symbols left they might have to be + synthesized from the linker script. */ + create_lscript_symbols (); + + /* Now that we have loaded all the object files we can determine + whether we have any non-weak unresolved references left. If + there are any we stop. If the user used the '-z nodefs' option + and we are creating a DSO don't perform the tests. */ + if (FLAG_UNRESOLVED (&ld_state) != 0) + exit (1); + } + + /* Collect information about the relocations which will be carried + forward into the output. We have to do this here and now since + we need to know which sections have to be created. */ + if (ld_state.file_type != relocatable_file_type) + { + void *p ; + struct scnhead *h; + + p = NULL; + while ((h = ld_section_tab_iterate (&ld_state.section_tab, &p)) != NULL) + if (h->type == SHT_REL || h->type == SHT_RELA) + { + struct scninfo *runp = h->last; + do + { + /* If we are processing the relocations determine how + many will be in the output file. Also determine + how many GOT entries are needed. */ + COUNT_RELOCATIONS (&ld_state, runp); + + ld_state.relsize_total += runp->relsize; + } + while ((runp = runp->next) != h->last); + } + } + + /* Not part of the gABI, but part of every psABI: the symbols for the + GOT section. Add the symbol if necessary. */ + if (ld_state.need_got) + create_special_section_symbol (&ld_state.got_symbol, + "_GLOBAL_OFFSET_TABLE_"); + /* Similarly for the _DYNAMIC symbol which points to the dynamic + section. */ + if (dynamically_linked_p ()) + create_special_section_symbol (&ld_state.dyn_symbol, "_DYNAMIC"); + + /* We are ready to start working on the output file. Not all + information has been gather or created yet. This will be done as + we go. Open the file now. */ + if (OPEN_OUTFILE (&ld_state, EM_NONE, ELFCLASSNONE, ELFDATANONE) != 0) + exit (1); + + /* Create the sections which are generated by the linker and are not + present in the input file. The output file must already have + been opened since we need the ELF descriptor to deduce type + sizes. */ + GENERATE_SECTIONS (&ld_state); + + /* At this point we have read all the files and know all the + sections which have to be linked into the application. We do now + create an array listing all the sections. We will than pass this + array to a system specific function which can reorder it at will. + The functions can also merge sections if this is what is + wanted. */ + collect_sections (); + + /* Create the output sections now. This may requires sorting them + first. */ + CREATE_SECTIONS (&ld_state); + + /* Create the output file data. Appropriate code for the selected + output file type is called. */ + if (CREATE_OUTFILE (&ld_state) != 0) + exit (1); + + /* Finalize the output file, write the data out. */ + err |= FINALIZE (&ld_state); + + /* Return with an non-zero exit status also if any error message has + been printed. */ + return err | (error_message_count != 0); +} + + +static void +replace_args (int argc, char *argv[]) +{ + 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; + } +} + + +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; + } + 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 __attribute__ ((unused))) +{ + switch (key) + { + case 'B': + parse_B_option (arg); + break; + + case 'c': + linker_script = arg; + break; + + case 'E': + ld_state.export_all_dynamic = true; + break; + + case 'G': + if (ld_state.file_type != no_file_type) + error (EXIT_FAILURE, 0, + gettext ("only one option of -G and -r is allowed")); + ld_state.file_type = dso_file_type; + + /* If we generate a DSO we have to export all symbols. */ + ld_state.export_all_dynamic = true; + break; + + case 'h': + ld_state.soname = arg; + break; + + case 'i': + /* Discard the LD_LIBRARY_PATH value we found. */ + ld_library_path1 = NULL; + break; + + case 'I': + ld_state.interp = arg; + break; + + case 'm': + if (emulation != NULL) + error (EXIT_FAILURE, 0, gettext ("more than one '-m' parameter")); + emulation = arg; + break; + + case 'Q': + if (arg[1] == '\0' && (arg[0] == 'y' || arg[0] == 'Y')) + ld_state.add_ld_comment = true; + else if (arg[1] == '\0' && (arg[0] == 'n' || arg[0] == 'N')) + ld_state.add_ld_comment = true; + else + error (EXIT_FAILURE, 0, gettext ("unknown option `-%c %s'"), 'Q', arg); + break; + + case 'r': + if (ld_state.file_type != no_file_type) + error (EXIT_FAILURE, 0, + gettext ("only one option of -G and -r is allowed")); + ld_state.file_type = relocatable_file_type; + break; + + case 'S': + ld_state.strip = strip_debug; + break; + + case 't': + ld_state.trace_files = true; + break; + + case 'v': + verbose = 1; + break; + + case 'z': + /* The SysV linker used 'z' to pass various flags to the linker. + We follow this. See 'parse_z_option' for the options we + recognize. */ + parse_z_option (arg); + break; + + case ARGP_pagesize: + { + char *endp; + ld_state.pagesize = strtoul (arg, &endp, 0); + if (*endp != '\0') + { + if (endp[1] == '\0' && tolower (*endp) == 'k') + ld_state.pagesize *= 1024; + else if (endp[1] == '\0' && tolower (*endp) == 'm') + ld_state.pagesize *= 1024 * 1024; + else + { + error (0, 0, + gettext ("invalid page size value '%s': ignored"), + arg); + ld_state.pagesize = 0; + } + } + } + break; + + case 'R': + add_rxxpath (&ld_state.rpath, arg); + break; + + case ARGP_rpath_link: + add_rxxpath (&ld_state.rpath_link, arg); + break; + + case ARGP_runpath: + add_rxxpath (&ld_state.runpath, arg); + break; + + case ARGP_runpath_link: + add_rxxpath (&ld_state.runpath_link, arg); + break; + + case ARGP_gc_sections: + case ARGP_no_gc_sections: + 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) + { + if (ld_state.strip == strip_all) + ld_state.strip = strip_everything; + else + ld_state.strip = strip_all; + break; + } + /* FALLTHROUGH */ + + case 'e': + case 'o': + case 'O': + case ARGP_whole_archive: + case ARGP_no_whole_archive: + case ARGP_as_needed: + case ARGP_no_as_needed: + case 'L': + case '(': + case ')': + case 'l': + case ARGP_static: + case ARGP_dynamic: + case ARGP_version_script: + /* We'll handle these in the second pass. */ + break; + + case ARGP_KEY_ARG: + { + struct file_list *newp; + + newp = (struct file_list *) xmalloc (sizeof (struct file_list)); + newp->name = arg; +#ifndef NDEBUG + newp->next = NULL; +#endif + CSNGL_LIST_ADD_REAR (input_file_list, newp); + } + break; + +#if YYDEBUG + case ARGP_yydebug: + lddebug = 1; + break; +#endif + + case ARGP_no_undefined: + ld_state.nodefs = false; + break; + + case ARGP_conserve: + conserve_memory = 1; + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + + +/* Handle program arguments for real. */ +static error_t +parse_opt_2nd (int key, char *arg, + struct argp_state *state __attribute__ ((unused))) +{ + static bool group_start_requested; + static bool group_end_requested; + + switch (key) + { + case 'B': + parse_B_option_2 (arg); + break; + + case 'e': + ld_state.entry = arg; + break; + + case 'o': + if (ld_state.outfname != NULL) + { + error (0, 0, gettext ("More than one output file name given.")); + see_help: + argp_help (&argp_2nd, stderr, ARGP_HELP_SEE, "ld"); + exit (EXIT_FAILURE); + } + ld_state.outfname = arg; + break; + + case 'O': + if (arg == NULL) + ld_state.optlevel = 1; + else + { + char *endp; + unsigned long int level = strtoul (arg, &endp, 10); + if (*endp != '\0') + { + error (0, 0, gettext ("Invalid optimization level `%s'"), arg); + goto see_help; + } + ld_state.optlevel = level; + } + break; + + case ARGP_whole_archive: + ld_state.extract_rule = allextract; + break; + case ARGP_no_whole_archive: + 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. */ + ld_state.statically = key == ARGP_static; + break; + + case 'z': + /* The SysV linker used 'z' to pass various flags to the linker. + We follow this. See 'parse_z_option' for the options we + recognize. */ + parse_z_option_2 (arg); + break; + + case ARGP_version_script: + read_version_script (arg); + break; + + case 'L': + /* Add a new search directory. */ + ld_new_searchdir (arg); + break; + + case '(': + /* Start a link group. We have to be able to determine the object + file which is named next. Do this by remembering a pointer to + the pointer which will point to the next object. */ + if (verbose && (group_start_requested || !group_end_requested)) + error (0, 0, gettext ("nested -( -) groups are not allowed")); + + /* Increment the nesting level. */ + ++group_level; + + /* Record group start. */ + group_start_requested = true; + group_end_requested = false; + break; + + case ')': + /* End a link group. If there is no group open this is clearly + a bug. If there is a group open insert a back reference + pointer in the record for the last object of the group. If + there is no new object or just one don't do anything. */ + if (!group_end_requested) + { + if (group_level == 0) + { + error (0, 0, gettext ("-) without matching -(")); + goto see_help; + } + } + else + last_file->group_end = true; + + if (group_level > 0) + --group_level; + break; + + case 'l': + case ARGP_KEY_ARG: + { + while (last_file != NULL) + /* Try to open the file. */ + error_loading |= FILE_PROCESS (-1, last_file, &ld_state, &last_file); + + last_file = ld_new_inputfile (arg, + key == 'l' + ? archive_file_type + : relocatable_file_type); + if (group_start_requested) + { + last_file->group_start = true; + + group_start_requested = false; + group_end_requested = true; + } + } + break; + + default: + /* We can catch all other options here. They either have + already been handled or, if the parameter was not correct, + the error has been reported. */ + break; + } + return 0; +} + + +/* Load all the DSOs named as dependencies in other DSOs we already + loaded. */ +static void +load_needed (void) +{ + struct usedfiles *first; + struct usedfiles *runp; + + /* XXX There is one problem here: do we allow references from + regular object files to be satisfied by these implicit + dependencies? The old linker allows this and several libraries + depend on this. Solaris' linker does not allow this; it provides + the user with a comprehensive error message explaining the + situation. + + XXX IMO the old ld behavior is correct since this is also how the + dynamic linker will work. It will look for unresolved references + in all loaded DSOs. + + XXX Should we add an option to get Solaris compatibility? */ + if (ld_state.needed == NULL) + return; + + runp = first = ld_state.needed->next; + do + { + struct usedfiles *ignore; + struct usedfiles *next = runp->next; + int err; + + err = FILE_PROCESS (-1, runp, &ld_state, &ignore); + if (err != 0) + /* Something went wrong. */ + exit (err); + + runp = next; + } + while (runp != first); +} + + +/* Print the version information. */ +static void +print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) +{ + 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\ +"), "2012"); + fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); +} + + +/* There are a lot of -z options, parse them here. Some of them have + to be parsed in the first pass, others must be handled in the + second pass. */ +static void +parse_z_option (const char *arg) +{ + if (strcmp (arg, "nodefaultlib") == 0 + /* This is only meaningful if we create a DSO. */ + && ld_state.file_type == dso_file_type) + ld_state.dt_flags_1 |= DF_1_NODEFLIB; + else if (strcmp (arg, "muldefs") == 0) + ld_state.muldefs = true; + else if (strcmp (arg, "nodefs") == 0) + ld_state.nodefs = true; + else if (strcmp (arg, "defs") == 0) + ld_state.nodefs = false; + else if (strcmp (arg, "now") == 0) + /* We could also set the DF_1_NOW flag in DT_FLAGS_1 but this isn't + necessary. */ + ld_state.dt_flags |= DF_BIND_NOW; + else if (strcmp (arg, "origin") == 0) + /* We could also set the DF_1_ORIGIN flag in DT_FLAGS_1 but this isn't + necessary. */ + ld_state.dt_flags |= DF_ORIGIN; + else if (strcmp (arg, "nodelete") == 0 + /* This is only meaningful if we create a DSO. */ + && ld_state.file_type == dso_file_type) + ld_state.dt_flags_1 |= DF_1_NODELETE; + else if (strcmp (arg, "initfirst") == 0) + ld_state.dt_flags_1 |= DF_1_INITFIRST; + else if (strcmp (arg, "nodlopen") == 0 + /* 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, "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, "ignore") != 0 + && strcmp (arg, "record") != 0) + error (0, 0, gettext ("unknown option `-%c %s'"), 'z', arg); +} + + +static void +parse_z_option_2 (const char *arg) +{ + if (strcmp (arg, "allextract") == 0) + ld_state.extract_rule = allextract; + else if (strcmp (arg, "defaultextract") == 0) + ld_state.extract_rule = defaultextract; + else if (strcmp (arg, "weakextract") == 0) + ld_state.extract_rule = weakextract; + else if (strcmp (arg, "lazyload") == 0) + 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; +} + + +/* There are a lot of -B options, parse them here. */ +static void +parse_B_option (const char *arg) +{ + if (strcmp (arg, "local") == 0) + ld_state.default_bind_local = true; + else if (strcmp (arg, "symbolic") != 0 + && strcmp (arg, "static") != 0 + && strcmp (arg, "dynamic") != 0) + error (0, 0, gettext ("unknown option '-%c %s'"), 'B', arg); +} + + +/* The same functionality, but called in the second pass over the + parameters. */ +static void +parse_B_option_2 (const char *arg) +{ + if (strcmp (arg, "static") == 0) + ld_state.statically = true; + else if (strcmp (arg, "dynamic") == 0) + ld_state.statically = false; + else if (strcmp (arg, "symbolic") == 0 + /* This is only meaningful if we create a DSO. */ + && ld_state.file_type == dso_file_type) + ld_state.dt_flags |= DF_SYMBOLIC; +} + + +static void +determine_output_format (void) +{ + /* First change the 'input_file_list' variable in a simple + single-linked list. */ + struct file_list *last = input_file_list; + input_file_list = input_file_list->next; + last->next = NULL; + + /* Determine the target configuration which we are supposed to use. + The user can use the '-m' option to select one. If this is + missing we are trying to load one file and determine the + architecture from that. */ + if (emulation != NULL) + { + ld_state.ebl = ebl_openbackend_emulation (emulation); + + assert (ld_state.ebl != NULL); + } + else + { + /* Find an ELF input file and let it determine the ELf backend. */ + struct file_list *runp = input_file_list; + + while (runp != NULL) + { + int fd = open (runp->name, O_RDONLY); + if (fd != -1) + { + int try (Elf *elf) + { + int result = 0; + + if (elf == NULL) + return 0; + + if (elf_kind (elf) == ELF_K_ELF) + { + /* We have an ELF file. We now can find out + what the output format should be. */ + XElf_Ehdr_vardef(ehdr); + + /* Get the ELF header of the object. */ + xelf_getehdr (elf, ehdr); + if (ehdr != NULL) + ld_state.ebl = + ebl_openbackend_machine (ehdr->e_machine); + + result = 1; + } + else if (elf_kind (elf) == ELF_K_AR) + { + /* Try the archive members. This could + potentially lead to wrong results if the + archive contains files for more than one + architecture. But this is the user's + problem. */ + Elf *subelf; + Elf_Cmd cmd = ELF_C_READ_MMAP; + + while ((subelf = elf_begin (fd, cmd, elf)) != NULL) + { + cmd = elf_next (subelf); + + if (try (subelf) != 0) + break; + } + } + + elf_end (elf); + + return result; + } + + if (try (elf_begin (fd, ELF_C_READ_MMAP, NULL)) != 0) + /* Found a file. */ + break; + } + + runp = runp->next; + } + + if (ld_state.ebl == NULL) + { + error (0, 0, gettext ("\ +could not find input file to determine output file format")); + error (EXIT_FAILURE, 0, gettext ("\ +try again with an appropriate '-m' parameter")); + } + } + + /* We don't need the list of input files anymore. The second run over + the parameters will handle them. */ + while (input_file_list != NULL) + { + struct file_list *oldp = input_file_list; + input_file_list = input_file_list->next; + free (oldp); + } + + /* We also know now what kind of file we are supposed to create. If + the user hasn't selected anythign we create and executable. */ + if (ld_state.file_type == no_file_type) + ld_state.file_type = executable_file_type; +} + +/* Add DIR to the list of directories searched for object files and + libraries. */ +void +ld_new_searchdir (const char *dir) +{ + struct pathelement *newpath; + + newpath = (struct pathelement *) + obstack_calloc (&ld_state.smem, sizeof (struct pathelement)); + + newpath->pname = dir; + + /* Enqueue the file. */ + if (ld_state.tailpaths == NULL) + ld_state.paths = ld_state.tailpaths = newpath->next = newpath; + else + { + ld_state.tailpaths->next = newpath; + ld_state.tailpaths = newpath; + newpath->next = ld_state.paths; + } +} + + +struct usedfiles * +ld_new_inputfile (const char *fname, enum file_type type) +{ + struct usedfiles *newfile = (struct usedfiles *) + obstack_calloc (&ld_state.smem, sizeof (struct usedfiles)); + + 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; + + return newfile; +} + + +/* Create an array listing all the sections. We will than pass this + array to a system specific function which can reorder it at will. + The functions can also merge sections if this is what is + wanted. */ +static void +collect_sections (void) +{ + void *p ; + struct scnhead *h; + size_t cnt; + + /* We have that many sections. At least for now. */ + ld_state.nallsections = ld_state.section_tab.filled; + + /* Allocate the array. We allocate one more entry than computed so + far since we might need a new section for the copy relocations. */ + ld_state.allsections = + (struct scnhead **) obstack_alloc (&ld_state.smem, + (ld_state.nallsections + 1) + * sizeof (struct scnhead *)); + + /* Fill the array. We rely here on the hash table iterator to + return the entries in the order they were added. */ + cnt = 0; + p = NULL; + while ((h = ld_section_tab_iterate (&ld_state.section_tab, &p)) != NULL) + { + struct scninfo *runp; + bool used = false; + + if (h->kind == scn_normal) + { + runp = h->last; + do + { + if (h->type == SHT_REL || h->type == SHT_RELA) + { + if (runp->used) + /* This is a relocation section. If the section + it is relocating is used in the result so must + the relocation section. */ + runp->used + = runp->fileinfo->scninfo[SCNINFO_SHDR (runp->shdr).sh_info].used; + } + + /* Accumulate the result. */ + used |= runp->used; + + /* Next input section. */ + runp = runp->next; + } + while (runp != h->last); + + h->used = used; + } + + ld_state.allsections[cnt++] = h; + } + ld_state.nusedsections = cnt; + + assert (cnt == ld_state.nallsections); +} + + +/* Add given path to the end of list. */ +static void +add_rxxpath (struct pathelement **pathp, const char *str) +{ + struct pathelement *newp; + + /* The path elements can in theory be freed after we read all the + files. But the amount of memory we are talking about is small + and the cost of free() calls is not neglectable. */ + newp = (struct pathelement *) obstack_alloc (&ld_state.smem, sizeof (*newp)); + newp->pname = str; + newp->exist = 0; +#ifndef NDEBUG + newp->next = NULL; +#endif + + CSNGL_LIST_ADD_REAR (*pathp, newp); +} + + +/* Convert lists of possibly colon-separated directory lists into lists + where each entry is for a single directory. */ +static void +normalize_dirlist (struct pathelement **pathp) +{ + struct pathelement *firstp = *pathp; + + do + { + const char *pname = (*pathp)->pname; + const char *colonp = strchrnul (pname, ':'); + + if (colonp != NULL) + { + struct pathelement *lastp = *pathp; + struct pathelement *newp; + + while (1) + { + if (colonp == pname) + lastp->pname = "."; + else + lastp->pname = obstack_strndup (&ld_state.smem, pname, + colonp - pname); + + if (*colonp == '\0') + break; + pname = colonp + 1; + + newp = (struct pathelement *) obstack_alloc (&ld_state.smem, + sizeof (*newp)); + newp->next = lastp->next; + newp->exist = 0; + lastp = lastp->next = newp; + + colonp = strchrnul (pname, ':'); + } + + pathp = &lastp->next; + } + else + pathp = &(*pathp)->next; + } + while (*pathp != firstp); +} + + +/* Called after all parameters are parsed to bring the runpath/rpath + information into a usable form. */ +static void +gen_rxxpath_data (void) +{ + char *ld_library_path2; + + /* Convert the information in true single-linked lists for easy use. + At this point we also discard the rpath information if runpath + information is provided. rpath is deprecated and should not be + used (or ever be invented for that matter). */ + if (ld_state.rpath != NULL) + { + struct pathelement *endp = ld_state.rpath; + ld_state.rpath = ld_state.rpath->next; + endp->next = NULL; + } + if (ld_state.rpath_link != NULL) + { + struct pathelement *endp = ld_state.rpath_link; + ld_state.rpath_link = ld_state.rpath_link->next; + endp->next = NULL; + } + + if (ld_state.runpath != NULL) + { + struct pathelement *endp = ld_state.runpath; + ld_state.runpath = ld_state.runpath->next; + endp->next = NULL; + + /* If rpath information is also available discard it. + XXX Should there be a possibility to avoid this? */ + while (ld_state.rpath != NULL) + { + struct pathelement *old = ld_state.rpath; + ld_state.rpath = ld_state.rpath->next; + free (old); + } + } + if (ld_state.runpath_link != NULL) + { + struct pathelement *endp = ld_state.runpath_link; + ld_state.runpath_link = ld_state.runpath_link->next; + endp->next = NULL; + + /* If rpath information is also available discard it. + XXX Should there be a possibility to avoid this? */ + while (ld_state.rpath_link != NULL) + { + struct pathelement *old = ld_state.rpath_link; + ld_state.rpath_link = ld_state.rpath_link->next; + free (old); + } + + /* The information in the strings in the list can actually be + directory lists themselves, with entries separated by colons. + Convert the list now to a list with one list entry for each + directory. */ + normalize_dirlist (&ld_state.runpath_link); + } + else if (ld_state.rpath_link != NULL) + /* Same as for the runpath_link above. */ + normalize_dirlist (&ld_state.rpath_link); + + + /* As a related task, handle the LD_LIBRARY_PATH value here. First + we have to possibly split the value found (if it contains a + semicolon). Then we have to split the value in list of + directories, i.e., split at the colons. */ + if (ld_library_path1 != NULL) + { + ld_library_path2 = strchr (ld_library_path1, ';'); + if (ld_library_path2 == NULL) + { + /* If no semicolon is present the directories are looked at + after the -L parameters (-> ld_library_path2). */ + ld_library_path2 = ld_library_path1; + ld_library_path1 = NULL; + } + else + { + /* NUL terminate the first part. */ + *ld_library_path2++ = '\0'; + + /* Convert the string value in a list. */ + add_rxxpath (&ld_state.ld_library_path1, ld_library_path1); + normalize_dirlist (&ld_state.ld_library_path1); + } + + add_rxxpath (&ld_state.ld_library_path2, ld_library_path2); + normalize_dirlist (&ld_state.ld_library_path2); + } +} + + +static void +read_version_script (const char *fname) +{ + /* Open the file. The name is supposed to be the complete (relative + 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'"), + fname); + /* No need for locking. */ + __fsetlocking (ldin, FSETLOCKING_BYCALLER); + + /* Tell the parser that this is a version script. */ + ld_scan_version_script = 1; + + ldlineno = 1; + ldin_fname = fname; + if (ldparse () != 0) + /* Something went wrong during parsing. */ + exit (EXIT_FAILURE); + + fclose (ldin); +} + + +static void +create_lscript_symbols (void) +{ + /* Walk through the data from the linker script and generate all the + symbols which are required to be present and those marked + with PROVIDE if there is a undefined reference. */ + if (ld_state.output_segments == NULL) + return; + + struct output_segment *segment = ld_state.output_segments->next; + do + { + struct output_rule *orule; + + for (orule = segment->output_rules; orule != NULL; orule = orule->next) + if (orule->tag == output_assignment + /* The assignments to "." (i.e., the PC) have to be + ignored here. */ + && strcmp (orule->val.assignment->variable, ".") != 0) + { + struct symbol *s = ld_state.unresolved; + + /* Check whether the symbol is needed. */ + if (likely (s != NULL)) + { + struct symbol *first = s; + const char *providename = orule->val.assignment->variable; + + /* Determine whether the provided symbol is still + undefined. */ + // XXX TODO Loop inside a loop. Gag! Must rewrite. */ + do + if (strcmp (s->name, providename) == 0) + { + /* Not defined but referenced. */ + if (unlikely (!s->defined)) + { + /* Put on the list of symbols. First remove it from + whatever list it currently is on. */ + CDBL_LIST_DEL (ld_state.unresolved, s); + --ld_state.nunresolved; + goto use_it; + } + + if (unlikely (!orule->val.assignment->provide_flag)) + { + /* The symbol is already defined and now again + in the linker script. This is an error. */ + error (0, 0, gettext ("\ +duplicate definition of '%s' in linker script"), + providename); + goto next_rule; + } + } + while ((s = s->next) != first); + } + + /* If the symbol only has to be provided if it is needed, + ignore it here since it is not undefined. */ + if (orule->val.assignment->provide_flag) + continue; + + /* Allocate memory for this new symbol. */ + s = (struct symbol *) + obstack_calloc (&ld_state.smem, sizeof (struct symbol)); + + /* Initialize it. */ + s->name = orule->val.assignment->variable; + + /* Insert it into the symbol hash table. */ + unsigned long int hval = elf_hash (s->name); + if (unlikely (ld_symbol_tab_insert (&ld_state.symbol_tab, + hval, s) != 0)) + { + /* This means the symbol is defined somewhere else. + Maybe it comes from a DSO or so. Get the + definition. */ + free (s); + struct symbol *old = ld_symbol_tab_find (&ld_state.symbol_tab, + hval, s); + assert (old != NULL); + free (s); + + /* If this is a definition from the application itself + this means a duplicate definition. */ + if (! old->in_dso) + { + error (0, 0, gettext ("\ +duplicate definition of '%s' in linker script"), + s->name); + goto next_rule; + } + + /* We use the definition from the linker script. */ + s = old; + } + + use_it: + /* The symbol is (now) defined. */ + s->defined = 1; + s->type = STT_NOTYPE; + + /* Add a reference to the symbol record. We will come + across it when creating the output file. */ + orule->val.assignment->sym = s; + + SNGL_LIST_PUSH (ld_state.lscript_syms, s); + ++ld_state.nlscript_syms; + + next_rule: + ; + } + + segment = segment->next; + } + while (segment != ld_state.output_segments->next); +} + + +/* Create creation of spection section symbols representing sections in the + output file. This is done for symbols like _GLOBAL_OFFSET_TABLE_ and + _DYNAMIC. */ +static void +create_special_section_symbol (struct symbol **symp, const char *name) +{ + if (*symp == NULL) + { + /* No symbol defined found yet. Create one. */ + struct symbol *newsym = (struct symbol *) + obstack_calloc (&ld_state.smem, sizeof (*newsym)); + + newsym->name = name; + // XXX Should we mark the symbol hidden? They are hardly useful + // used outside the current object. + + /* Add to the symbol table. */ + if (unlikely (ld_symbol_tab_insert (&ld_state.symbol_tab, + elf_hash (name), newsym) != 0)) + abort (); + + *symp = newsym; + } + else if ((*symp)->defined) + /* Cannot happen. We do use this symbol from any input file. */ + abort (); + + (*symp)->defined = 1; + (*symp)->local = 1; + (*symp)->hidden = 1; + (*symp)->type = STT_OBJECT; + + ++ld_state.nsymtab; +} + + +#include "debugpred.h" diff --git a/0.153/src/ld.h b/0.153/src/ld.h new file mode 100644 index 00000000..72d5b27b --- /dev/null +++ b/0.153/src/ld.h @@ -0,0 +1,1143 @@ +/* Copyright (C) 2001, 2002, 2003, 2005, 2006, 2008, 2009 Red Hat, Inc. + This file is part of Red Hat elfutils. + Written by Ulrich Drepper <drepper@redhat.com>, 2001. + + 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 LD_H +#define LD_H 1 + +#include <dlfcn.h> +#include <obstack.h> +#include <stdbool.h> +#include <stdio.h> +#include "xelf.h" + + +/* Recommended size of the buffer passed to ld_strerror. */ +#define ERRBUFSIZE (512) + +/* Character used to introduce version name after symbol. */ +#define VER_CHR '@' + + +/* Methods for handling archives. */ +enum extract_rule + { + defaultextract, /* Weak references don't cause archive member to + be used. */ + weakextract, /* Weak references cause archive member to be + extracted. */ + allextract /* Extract all archive members regardless of + references (aka whole-archive). */ + }; + + +/* Type of output file. */ +enum file_type + { + no_file_type = 0, /* None selected so far. */ + executable_file_type, /* Executable. */ + dso_file_type, /* DSO. */ + dso_needed_file_type, /* DSO introduced by DT_NEEDED. */ + relocatable_file_type, /* Relocatable object file. */ + archive_file_type /* Archive (input only). */ + }; + + +struct usedfiles +{ + /* The next file given at the command line. */ + struct usedfiles *next; + /* Nonzero if this file is the beginning of a group. */ + bool group_start; + /* Nonzero if this file is the end of a group. */ + bool group_end; + /* Pointer to the beginning of the group. It is necessary to + explain why we cannot simply use the 'next' pointer and have a + circular single-linked list like in many cases. The problem is + that the last archive of the group, if it is the last file of the + group, contains the only existing pointer to the next file we + have to look at. All files are initially connected via the + 'next' pointer in a single-linked list. Therefore we cannot + overwrite this value. It instead will be used once the group is + handled and we go on processing the rest of the files. */ + struct usedfiles *group_backref; + + /* Name/path of the file. */ + const char *fname; + /* Resolved file name. */ + const char *rfname; + /* Name used as reference in DT_NEEDED entries. This is normally + the SONAME. If it is missing it's normally the fname above. */ + const char *soname; + /* Handle for the SONAME in the string table. */ + struct Ebl_Strent *sonameent; + + /* Help to identify duplicates. */ + dev_t dev; + ino_t ino; + + enum + { + not_opened, + opened, + in_archive, + closed + } status; + + /* How to extract elements from archives. */ + enum extract_rule extract_rule; + + /* Lazy-loading rule. */ + bool lazyload; + + /* If this is a DSO the flag indicates whether the file is directly + 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. */ + int archive_seq; + + /* Pointer to the record for the archive containing this file. */ + struct usedfiles *archive_file; + + /* Type of file. We have to distinguish these types since they + are searched for differently. */ + enum file_type file_type; + /* This is the ELF library handle for this file. */ + Elf *elf; + + /* The ELF header. */ +#if NATIVE_ELF != 0 + XElf_Ehdr *ehdr; +# define FILEINFO_EHDR(fi) (*(fi)) +#else + XElf_Ehdr ehdr; +# define FILEINFO_EHDR(fi) (fi) +#endif + + /* Index of the section header string table section. We use a + separate field and not the e_shstrndx field in the ELF header + since in case of a file with more than 64000 sections the index + might be stored in the section header of section zero. The + elf_getshdrstrndx() function can find the value but it is too + costly to repeat this call over and over. */ + size_t shstrndx; + + /* Info about the sections of the file. */ + struct scninfo + { + /* Handle for the section. Note that we can store a section + handle here because the file is not changing. This together + with the knowledge about the libelf library is enough for us to + assume the section reference remains valid at all times. */ + Elf_Scn *scn; + /* Section header. */ +#if NATIVE_ELF != 0 + XElf_Shdr *shdr; +# define SCNINFO_SHDR(si) (*(si)) +#else + XElf_Shdr shdr; +# define SCNINFO_SHDR(si) (si) +#endif + /* Offset of this files section in the combined section. */ + XElf_Off offset; + /* Index of the section in the output file. */ + Elf32_Word outscnndx; + /* Index of the output section in the 'allsection' array. */ + 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 + and group sections). */ + struct symbol *symbols; + /* Size of relocations in this section. Only used for relocation + sections. */ + size_t relsize; + /* Pointer to next section which is put in the given output + section. */ + struct scninfo *next; + } *scninfo; + + /* List of section group sections. */ + struct scninfo *groups; + + /* The symbol table section. + + XXX Maybe support for more than one symbol table is needed. */ + Elf_Data *symtabdata; + /* Extra section index table section. */ + Elf_Data *xndxdata; + /* Dynamic symbol table section. */ + Elf_Data *dynsymtabdata; + /* The version number section. */ + Elf_Data *versymdata; + /* The defined versions. */ + Elf_Data *verdefdata; + /* Number of versions defined. */ + size_t nverdef; + /* True if the version with the given index number is used in the + output. */ + XElf_Versym *verdefused; + /* How many versions are used. */ + size_t nverdefused; + /* Handle for name of the version. */ + struct Ebl_Strent **verdefent; + /* The needed versions. */ + Elf_Data *verneeddata; + /* String table section associated with the symbol table. */ + Elf32_Word symstridx; + /* String table section associated with the dynamic symbol table. */ + Elf32_Word dynsymstridx; + /* Number of entries in the symbol table. */ + size_t nsymtab; + size_t nlocalsymbols; + size_t ndynsymtab; + /* Dynamic section. */ + Elf_Scn *dynscn; + + /* Indirection table for the symbols defined here. */ + Elf32_Word *symindirect; + Elf32_Word *dynsymindirect; + /* For undefined or common symbols we need a reference to the symbol + record. */ + struct symbol **symref; + struct symbol **dynsymref; + + /* This is the file descriptor. The value is -1 if the descriptor + was already closed. This can happen if we needed file descriptors + to open new files. */ + int fd; + /* This flag is true if the descriptor was passed to the generic + functions from somewhere else. This is an implementation detail; + no machine-specific code must use this flag. */ + bool fd_passed; + + /* True if any of the sections is merge-able. */ + bool has_merge_sections; +}; + + +/* Functions to test for the various types of files we handle. */ +static inline int +ld_file_rel_p (struct usedfiles *file) +{ + return (elf_kind (file->elf) == ELF_K_ELF + && FILEINFO_EHDR (file->ehdr).e_type == ET_REL); +} + +static inline int +ld_file_dso_p (struct usedfiles *file) +{ + return (elf_kind (file->elf) == ELF_K_ELF + && FILEINFO_EHDR (file->ehdr).e_type == ET_DYN); +} + +static inline int +ld_file_ar_p (struct usedfiles *file) +{ + return elf_kind (file->elf) == ELF_K_AR; +} + + +struct pathelement +{ + /* The next path to search. */ + struct pathelement *next; + /* The path name. */ + const char *pname; + /* Larger than zero if the directory exists, smaller than zero if not, + zero if it is not yet known. */ + int exist; +}; + + +/* Forward declaration. */ +struct ld_state; + + +/* Callback functions. */ +struct callbacks +{ + /* Library names passed to the linker as -lXXX represent files named + libXXX.YY. The YY part can have different forms, depending on the + architecture. The generic set is .so and .a (in this order). */ + const char **(*lib_extensions) (struct ld_state *) + __attribute__ ((__const__)); +#define LIB_EXTENSION(state) \ + DL_CALL_FCT ((state)->callbacks.lib_extensions, (state)) + + /* Process the given file. If the file is not yet open, open it. + The first parameter is a file descriptor for the file which can + be -1 to indicate the file has not yet been found. The second + parameter describes the file to be opened, the last one is the + state of the linker which among other information contain the + paths we look at.*/ + int (*file_process) (int fd, struct usedfiles *, struct ld_state *, + struct usedfiles **); +#define FILE_PROCESS(fd, file, state, nextp) \ + DL_CALL_FCT ((state)->callbacks.file_process, (fd, file, state, nextp)) + + /* Close the given file. */ + int (*file_close) (struct usedfiles *, struct ld_state *); +#define FILE_CLOSE(file, state) \ + DL_CALL_FCT ((state)->callbacks.file_close, (file, state)) + + /* Create the output sections now. This requires knowledge about + all the sections we will need. It may be necessary to sort the + sections in the order they are supposed to appear in the + executable. The sorting use many different kinds of information + to optimize the resulting binary. Important is to respect + segment boundaries and the needed alignment. The mode of the + segments will be determined afterwards automatically by the + output routines. */ + void (*create_sections) (struct ld_state *); +#define CREATE_SECTIONS(state) \ + DL_CALL_FCT ((state)->callbacks.create_sections, (state)) + + /* Determine whether we have any non-weak unresolved references left. */ + int (*flag_unresolved) (struct ld_state *); +#define FLAG_UNRESOLVED(state) \ + DL_CALL_FCT ((state)->callbacks.flag_unresolved, (state)) + + /* Create the sections which are generated by the linker and are not + present in the input file. */ + void (*generate_sections) (struct ld_state *); +#define GENERATE_SECTIONS(state) \ + DL_CALL_FCT ((state)->callbacks.generate_sections, (state)) + + /* Open the output file. The file name is given or "a.out". We + create as much of the ELF structure as possible. */ + int (*open_outfile) (struct ld_state *, int, int, int); +#define OPEN_OUTFILE(state, machine, class, data) \ + DL_CALL_FCT ((state)->callbacks.open_outfile, (state, machine, class, data)) + + /* Create the data for the output file. */ + int (*create_outfile) (struct ld_state *); +#define CREATE_OUTFILE(state) \ + DL_CALL_FCT ((state)->callbacks.create_outfile, (state)) + + /* Process a relocation section. */ + void (*relocate_section) (struct ld_state *, Elf_Scn *, struct scninfo *, + const Elf32_Word *); +#define RELOCATE_SECTION(state, outscn, first, dblindirect) \ + DL_CALL_FCT ((state)->callbacks.relocate_section, (state, outscn, first, \ + dblindirect)) + + /* Allocate a data buffer for the relocations of the given output + section. */ + void (*count_relocations) (struct ld_state *, struct scninfo *); +#define COUNT_RELOCATIONS(state, scninfo) \ + DL_CALL_FCT ((state)->callbacks.count_relocations, (state, scninfo)) + + /* Create relocations for executable or DSO. */ + void (*create_relocations) (struct ld_state *, const Elf32_Word *); +#define CREATE_RELOCATIONS(state, dlbindirect) \ + DL_CALL_FCT ((state)->callbacks.create_relocations, (state, dblindirect)) + + /* Finalize the output file. */ + int (*finalize) (struct ld_state *); +#define FINALIZE(state) \ + DL_CALL_FCT ((state)->callbacks.finalize, (state)) + + /* Check whether special section number is known. */ + bool (*special_section_number_p) (struct ld_state *, size_t); +#define SPECIAL_SECTION_NUMBER_P(state, number) \ + DL_CALL_FCT ((state)->callbacks.special_section_number_p, (state, number)) + + /* Check whether section type is known. */ + bool (*section_type_p) (struct ld_state *, XElf_Word); +#define SECTION_TYPE_P(state, type) \ + DL_CALL_FCT ((state)->callbacks.section_type_p, (state, type)) + + /* Return section flags for .dynamic section. */ + XElf_Xword (*dynamic_section_flags) (struct ld_state *); +#define DYNAMIC_SECTION_FLAGS(state) \ + DL_CALL_FCT ((state)->callbacks.dynamic_section_flags, (state)) + + /* Create the data structures for the .plt section and initialize it. */ + void (*initialize_plt) (struct ld_state *, Elf_Scn *scn); +#define INITIALIZE_PLT(state, scn) \ + DL_CALL_FCT ((state)->callbacks.initialize_plt, (state, scn)) + + /* Create the data structures for the .rel.plt section and initialize it. */ + void (*initialize_pltrel) (struct ld_state *, Elf_Scn *scn); +#define INITIALIZE_PLTREL(state, scn) \ + 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, 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 *); +#define REL_TYPE(state) \ + DL_CALL_FCT ((state)->callbacks.rel_type, (state)) +}; + + +/* Structure for symbol representation. This data structure is used a + lot, so size is important. */ +struct symbol +{ + /* Symbol name. */ + const char *name; + /* Size of the object. */ + XElf_Xword size; + /* Index of the symbol in the symbol table of the object. */ + size_t symidx; + /* Index of the symbol in the symbol table of the output file. */ + size_t outsymidx; + + /* Description where the symbol is found/needed. */ + size_t scndx; + struct usedfiles *file; + /* Index of the symbol table. */ + Elf32_Word symscndx; + + /* Index of the symbol in the dynamic symbol table of the output + file. Note that the value only needs to be 16 bit wide since + there cannot be more sections in an executable or DSO. */ + unsigned int outdynsymidx:16; + + /* Type of the symbol. */ + unsigned int type:4; + /* Various flags. */ + unsigned int defined:1; + unsigned int common:1; + 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 + relocation has been created. */ + unsigned int need_copy:1; + unsigned int in_dso:1; + + union + { + /* Pointer to the handle created by the functions which create + merged section contents. We use 'void *' because there are + different implementations used. */ + void *handle; + XElf_Addr value; + } merge; + + /* Pointer to next/previous symbol on whatever list the symbol is. */ + struct symbol *next; + struct symbol *previous; + /* Pointer to next symbol of the same section (only set for merge-able + sections). */ + struct symbol *next_in_scn; +}; + + +/* Get the definition for the symbol table. */ +#include <symbolhash.h> + +/* Simple single linked list of file names. */ +struct filename_list +{ + const char *name; + struct usedfiles *real; + struct filename_list *next; + bool group_start; + bool group_end; + bool as_needed; +}; + + +/* Data structure to describe expression in linker script. */ +struct expression +{ + enum expression_tag + { + exp_num, + exp_sizeof_headers, + exp_pagesize, + exp_id, + exp_mult, + exp_div, + exp_mod, + exp_plus, + exp_minus, + exp_and, + exp_or, + exp_align + } tag; + + union + { + uintmax_t num; + struct expression *child; + struct + { + struct expression *left; + struct expression *right; + } binary; + const char *str; + } val; +}; + + +/* Data structure for section name with flags. */ +struct input_section_name +{ + const char *name; + bool sort_flag; +}; + +/* File name mask with section name. */ +struct filemask_section_name +{ + const char *filemask; + const char *excludemask; + struct input_section_name *section_name; + bool keep_flag; +}; + +/* Data structure for assignments. */ +struct assignment +{ + const char *variable; + struct expression *expression; + struct symbol *sym; + bool provide_flag; +}; + + +/* Data structure describing input for an output section. */ +struct input_rule +{ + enum + { + input_section, + input_assignment + } tag; + + union + { + struct assignment *assignment; + struct filemask_section_name *section; + } val; + + struct input_rule *next; +}; + + +/* Data structure to describe output section. */ +struct output_section +{ + const char *name; + struct input_rule *input; + XElf_Addr max_alignment; + bool ignored; +}; + + +/* Data structure to describe output file format. */ +struct output_rule +{ + enum + { + output_section, + output_assignment + } tag; + + union + { + struct assignment *assignment; + struct output_section section; + } val; + + struct output_rule *next; +}; + + +/* List of all the segments the linker script describes. */ +struct output_segment +{ + int mode; + struct output_rule *output_rules; + struct output_segment *next; + + XElf_Off offset; + XElf_Addr addr; + XElf_Xword align; +}; + + +/* List of identifiers. */ +struct id_list +{ + union + { + enum id_type + { + id_str, /* Normal string. */ + id_all, /* "*", matches all. */ + id_wild /* Globbing wildcard string. */ + } id_type; + struct + { + bool local; + const char *versionname; + } s; + } u; + const char *id; + struct id_list *next; +}; + + +/* Version information. */ +struct version +{ + struct version *next; + struct id_list *local_names; + struct id_list *global_names; + const char *versionname; + const char *parentname; +}; + + +/* Head for list of sections. */ +struct scnhead +{ + /* Name of the sections. */ + const char *name; + + /* Accumulated flags for the sections. */ + XElf_Xword flags; + + /* Type of the sections. */ + XElf_Word type; + + /* Entry size. If there are differencs between the sections with + the same name this field contains 1. */ + XElf_Word entsize; + + /* If non-NULL pointer to group signature. */ + const char *grp_signature; + + /* Maximum alignment for all sections. */ + XElf_Word align; + + /* Distinguish between normal sections coming from the input file + and sections generated by the linker. */ + enum scn_kind + { + 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_note_gnu_build_id /* Generated .note.gnu.build-id section. */ + } kind; + + /* True is the section is used in the output. */ + bool used; + + /* Total size (only determined this way for relocation sections). */ + size_t relsize; + + /* Filled in by the section sorting to indicate which segment the + section goes in. */ + int segment_nr; + + /* Index of the output section. We cannot store the section handle + directly here since the handle is a pointer in a dynamically + allocated table which might move if it becomes too small for all + the sections. Using the index the correct value can be found at + all times. */ + XElf_Word scnidx; + + /* Index of the STT_SECTION entry for this section in the symbol + table. */ + XElf_Word scnsymidx; + + /* Address of the section in the output file. */ + XElf_Addr addr; + + /* Handle for the section name in the output file's section header + string table. */ + struct Ebl_Strent *nameent; + + /* Tail of list of symbols for this section. Only set if the + section is merge-able. */ + struct symbol *symbols; + + /* Pointer to last section. */ + struct scninfo *last; +}; + + +/* Define hash table for sections. */ +#include <sectionhash.h> + +/* Define hash table for version symbols. */ +#include <versionhash.h> + + +/* State of the linker. */ +struct ld_state +{ + /* ELF backend library handle. */ + Ebl *ebl; + + /* List of all archives participating, in this order. */ + struct usedfiles *archives; + /* End of the list. */ + struct usedfiles *tailarchives; + /* If nonzero we are looking for the beginning of a group. */ + bool group_start_requested; + /* Pointer to the archive starting the group. */ + struct usedfiles *group_start_archive; + + /* List of the DSOs we found. */ + struct usedfiles *dsofiles; + /* Number of DSO files. */ + size_t ndsofiles; + /* Ultimate list of object files which are linked in. */ + struct usedfiles *relfiles; + + /* List the DT_NEEDED DSOs. */ + struct usedfiles *needed; + + /* Temporary storage for the parser. */ + struct filename_list *srcfiles; + + /* List of all the paths to look at. */ + struct pathelement *paths; + /* Tail of the list. */ + struct pathelement *tailpaths; + + /* User provided paths for lookup of DSOs. */ + struct pathelement *rpath; + struct pathelement *rpath_link; + struct pathelement *runpath; + struct pathelement *runpath_link; + struct Ebl_Strent *rxxpath_strent; + int rxxpath_tag; + + /* From the environment variable LD_LIBRARY_PATH. */ + struct pathelement *ld_library_path1; + struct pathelement *ld_library_path2; + + /* Name of the output file. */ + const char *outfname; + /* Name of the temporary file we initially create. */ + const char *tempfname; + /* File descriptor opened for the output file. */ + int outfd; + /* The ELF descriptor for the output file. */ + Elf *outelf; + + /* Type of output file. */ + enum file_type file_type; + + /* Is this a system library or not. */ + bool is_system_library; + + /* Page size to be assumed for the binary. */ + size_t pagesize; + + /* Name of the interpreter for dynamically linked objects. */ + const char *interp; + /* Index of the .interp section. */ + Elf32_Word interpscnidx; + + /* Optimization level. */ + unsigned long int optlevel; + + /* 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; + + /* Sequence number of the last archive we used. */ + int last_archive_used; + + /* If true print to stdout information about the files we are + trying to open. */ + bool trace_files; + + /* If true multiple definitions are not considered an error; the + first is used. */ + bool muldefs; + + /* If true undefined symbols when building DSOs are not fatal. */ + bool nodefs; + + /* If true add line indentifying link-editor to .comment section. */ + bool add_ld_comment; + + /* Stripping while linking. */ + enum + { + strip_none, + strip_debug, + strip_all, + strip_everything + } strip; + + /* The callback function vector. */ + struct callbacks callbacks; + + /* Name of the entry symbol. Can also be a numeric value. */ + const char *entry; + + /* The description of the segments in the output file. */ + struct output_segment *output_segments; + + /* List of the symbols we created from linker script definitions. */ + struct symbol *lscript_syms; + size_t nlscript_syms; + + /* Table with known symbols. */ + ld_symbol_tab symbol_tab; + + /* Table with used sections. */ + ld_section_tab section_tab; + + /* The list of sections once we collected them. */ + struct scnhead **allsections; + size_t nallsections; + size_t nusedsections; + size_t nnotesections; + + /* Beginning of the list of symbols which are still unresolved. */ + struct symbol *unresolved; + /* Number of truely unresolved entries in the list. */ + size_t nunresolved; + /* Number of truely unresolved, non-weak entries in the list. */ + size_t nunresolved_nonweak; + + /* List of common symbols. */ + struct symbol *common_syms; + /* Section for the common symbols. */ + struct scninfo *common_section; + + /* List of symbols defined in DSOs and used in a relocatable file. + DSO symbols not referenced in the relocatable files are not on + the list. If a symbol is on the list the on_dsolist field in the + 'struct symbol' is nonzero. */ + struct symbol *from_dso; + /* Number of entries in from_dso. */ + size_t nfrom_dso; + /* Number of entries in the dynamic symbol table. */ + size_t ndynsym; + /* Number of PLT entries from DSO references. */ + size_t nplt; + /* Number of PLT entries from DSO references. */ + size_t ngot; + /* Number of copy relocations. */ + size_t ncopy; + /* Section for copy relocations. */ + struct scninfo *copy_section; + + /* Keeping track of the number of symbols in the output file. */ + size_t nsymtab; + size_t nlocalsymbols; + + /* Special symbols. */ + struct symbol *init_symbol; + struct symbol *fini_symbol; + + /* The description of the segments in the output file as described + in the default linker script. This information will be used in + addition to the user-provided information. */ + struct output_segment *default_output_segments; + /* Search paths added by the default linker script. */ + struct pathelement *default_paths; + +#ifndef BASE_ELF_NAME + /* The handle of the ld backend library. */ + void *ldlib; +#endif + + /* String table for the section headers. */ + struct Ebl_Strtab *shstrtab; + + /* True if output file should contain symbol table. */ + bool need_symtab; + /* Symbol table section. */ + Elf32_Word symscnidx; + /* Extended section table section. */ + Elf32_Word xndxscnidx; + /* Symbol string table section. */ + Elf32_Word strscnidx; + + /* True if output file should contain dynamic symbol table. */ + bool need_dynsym; + /* Dynamic symbol table section. */ + Elf32_Word dynsymscnidx; + /* Dynamic symbol string table section. */ + Elf32_Word dynstrscnidx; + /* Dynamic symbol hash tables. */ + size_t hashscnidx; + size_t gnuhashscnidx; + + /* Procedure linkage table section. */ + Elf32_Word pltscnidx; + /* Number of entries already in the PLT section. */ + size_t nplt_used; + /* Relocation for procedure linkage table section. */ + Elf32_Word pltrelscnidx; + + /* 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; + + /* Index of the sections to handle versioning. */ + Elf32_Word versymscnidx; + Elf32_Word verneedscnidx; + /* XXX Should the following names be verneed...? */ + /* Number of version definitions in input DSOs used. */ + int nverdefused; + /* Number of input DSOs using versioning. */ + int nverdeffile; + /* 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; + /* At most one of the following two variables is set to true if either + global or local symbol binding is selected as the default. */ + 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; + + /* Array to determine final index of symbol. */ + Elf32_Word *dblindirect; + + /* Section group handling. */ + struct scngroup + { + Elf32_Word outscnidx; + int nscns; + struct member + { + struct scnhead *scn; + struct member *next; + } *member; + struct Ebl_Strent *nameent; + struct symbol *symbol; + struct scngroup *next; + } *groups; + + /* True if the output file needs a .got section. */ + bool need_got; + /* Number of relocations for GOT section caused. */ + size_t nrel_got; + + /* Number of entries needed in the .dynamic section. */ + int ndynamic; + /* To keep track of added entries. */ + int ndynamic_filled; + /* Index for the dynamic section. */ + Elf32_Word dynamicscnidx; + + /* Flags set in the DT_FLAGS word. */ + Elf32_Word dt_flags; + /* Flags set in the DT_FLAGS_1 word. */ + Elf32_Word dt_flags_1; + /* Flags set in the DT_FEATURE_1 word. */ + Elf32_Word dt_feature_1; + + /* Lazy-loading state for dependencies. */ + bool lazyload; + + /* 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; + + /* List of all relocation sections. */ + struct scninfo *rellist; + /* Total size of non-PLT relocations. */ + size_t relsize_total; + + /* Record for the GOT symbol, if known. */ + struct symbol *got_symbol; + /* Record for the dynamic section symbol, if known. */ + struct symbol *dyn_symbol; + + /* Obstack used for small objects which will not be deleted. */ + struct obstack smem; +}; + + +/* The interface to the scanner. */ + +/* Parser entry point. */ +extern int ldparse (void); + +/* The input file. */ +extern FILE *ldin; + +/* Name of the input file. */ +extern const char *ldin_fname; + +/* Current line number. Must be reset for a new file. */ +extern int ldlineno; + +/* If nonzero we are currently parsing a version script. */ +extern int ld_scan_version_script; + +/* Flags defined in ld.c. */ +extern int verbose; +extern int conserve_memory; + + +/* Linker state. This contains all global information. */ +extern struct ld_state ld_state; + + +/* Generic ld helper functions. */ + +/* Append a new directory to search libraries in. */ +extern void ld_new_searchdir (const char *dir); + +/* Append a new file to the list of input files. */ +extern struct usedfiles *ld_new_inputfile (const char *fname, + enum file_type type); + + +/* These are the generic implementations for the callbacks used by ld. */ + +/* Initialize state object. This callback function is called after the + parameters are parsed but before any file is searched for. */ +extern int ld_prepare_state (const char *emulation); + + +/* Function to determine whether an object will be dynamically linked. */ +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, size_t symidx); +#ifdef __GNUC_STDC_INLINE__ +__attribute__ ((__gnu_inline__)) +#endif +extern inline bool +linked_from_dso_p (struct scninfo *scninfo, size_t symidx) +{ + struct usedfiles *file = scninfo->fileinfo; + + /* If this symbol is not undefined in this file it cannot come from + a DSO. */ + if (symidx < file->nlocalsymbols) + return false; + + struct symbol *sym = file->symref[symidx]; + + return sym->defined && sym->in_dso; +} + +#endif /* ld.h */ diff --git a/0.153/src/ldgeneric.c b/0.153/src/ldgeneric.c new file mode 100644 index 00000000..98bdc225 --- /dev/null +++ b/0.153/src/ldgeneric.c @@ -0,0 +1,7140 @@ +/* Copyright (C) 2001-2011 Red Hat, Inc. + This file is part of Red Hat elfutils. + Written by Ulrich Drepper <drepper@redhat.com>, 2001. + + 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 <ctype.h> +#include <dlfcn.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <fnmatch.h> +#include <gelf.h> +#include <inttypes.h> +#include <libintl.h> +#include <stdbool.h> +#include <stdio_ext.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/param.h> +#include <sys/stat.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. */ +static const char **ld_generic_lib_extensions (struct ld_state *) + __attribute__ ((__const__)); +static int ld_generic_file_close (struct usedfiles *fileinfo, + struct ld_state *statep); +static int ld_generic_file_process (int fd, struct usedfiles *fileinfo, + struct ld_state *statep, + struct usedfiles **nextp); +static void ld_generic_generate_sections (struct ld_state *statep); +static void ld_generic_create_sections (struct ld_state *statep); +static int ld_generic_flag_unresolved (struct ld_state *statep); +static int ld_generic_open_outfile (struct ld_state *statep, int machine, + int class, int data); +static int ld_generic_create_outfile (struct ld_state *statep); +static void ld_generic_relocate_section (struct ld_state *statep, + Elf_Scn *outscn, + struct scninfo *firstp, + const Elf32_Word *dblindirect); +static int ld_generic_finalize (struct ld_state *statep); +static bool ld_generic_special_section_number_p (struct ld_state *statep, + size_t number); +static bool ld_generic_section_type_p (struct ld_state *statep, + XElf_Word type); +static XElf_Xword ld_generic_dynamic_section_flags (struct ld_state *statep); +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, + 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); +static void ld_generic_create_relocations (struct ld_state *statep, + const Elf32_Word *dblindirect); + +static int file_process2 (struct usedfiles *fileinfo); +static void mark_section_used (struct scninfo *scninfo, Elf32_Word shndx, + struct scninfo **grpscnp); + + +/* Map symbol index to struct symbol record. */ +static struct symbol **ndxtosym; + +/* String table reference to all symbols in the symbol table. */ +static struct Ebl_Strent **symstrent; + + +/* Check whether file associated with FD is a DSO. */ +static bool +is_dso_p (int fd) +{ + /* We have to read the 'e_type' field. It has the same size (16 + bits) in 32- and 64-bit ELF. */ + XElf_Half e_type; + + return (pread (fd, &e_type, sizeof (e_type), offsetof (XElf_Ehdr, e_type)) + == sizeof (e_type) + && e_type == ET_DYN); +} + + +/* Print the complete name of a file, including the archive it is + contained in. */ +static int +print_file_name (FILE *s, struct usedfiles *fileinfo, int first_level, + int newline) +{ + int npar = 0; + + if (fileinfo->archive_file != NULL) + { + npar = print_file_name (s, fileinfo->archive_file, 0, 0) + 1; + fputc_unlocked ('(', s); + fputs_unlocked (fileinfo->rfname, s); + + if (first_level) + while (npar-- > 0) + fputc_unlocked (')', s); + } + else + fputs_unlocked (fileinfo->rfname, s); + + if (first_level && newline) + fputc_unlocked ('\n', s); + + return npar; +} + + +/* Function to determine whether an object will be dynamically linked. */ +bool +dynamically_linked_p (void) +{ + return (ld_state.file_type == dso_file_type || ld_state.nplt > 0 + || ld_state.ngot > 0); +} + + +bool +linked_from_dso_p (struct scninfo *scninfo, size_t symidx) +{ + struct usedfiles *file = scninfo->fileinfo; + + /* If this symbol is not undefined in this file it cannot come from + a DSO. */ + if (symidx < file->nlocalsymbols) + return false; + + struct symbol *sym = file->symref[symidx]; + + return sym->defined && sym->in_dso; +} + + +/* Initialize state object. This callback function is called after the + parameters are parsed but before any file is searched for. */ +int +ld_prepare_state (const char *emulation) +{ + /* When generating DSO we normally allow undefined symbols. */ + ld_state.nodefs = true; + + /* To be able to detect problems we add a .comment section entry by + default. */ + ld_state.add_ld_comment = true; + + /* XXX We probably should find a better place for this. The index + of the first user-defined version is 2. */ + ld_state.nextveridx = 2; + + /* Pick an not too small number for the initial size of the tables. */ + ld_symbol_tab_init (&ld_state.symbol_tab, 1027); + ld_section_tab_init (&ld_state.section_tab, 67); + ld_version_str_tab_init (&ld_state.version_str_tab, 67); + + /* Initialize the section header string table. */ + ld_state.shstrtab = ebl_strtabinit (true); + if (ld_state.shstrtab == NULL) + error (EXIT_FAILURE, errno, gettext ("cannot create string table")); + + /* Initialize the callbacks. These are the defaults, the appropriate + backend can later install its own callbacks. */ + ld_state.callbacks.lib_extensions = ld_generic_lib_extensions; + ld_state.callbacks.file_process = ld_generic_file_process; + ld_state.callbacks.file_close = ld_generic_file_close; + ld_state.callbacks.generate_sections = ld_generic_generate_sections; + ld_state.callbacks.create_sections = ld_generic_create_sections; + ld_state.callbacks.flag_unresolved = ld_generic_flag_unresolved; + ld_state.callbacks.open_outfile = ld_generic_open_outfile; + ld_state.callbacks.create_outfile = ld_generic_create_outfile; + ld_state.callbacks.relocate_section = ld_generic_relocate_section; + ld_state.callbacks.finalize = ld_generic_finalize; + ld_state.callbacks.special_section_number_p = + ld_generic_special_section_number_p; + ld_state.callbacks.section_type_p = ld_generic_section_type_p; + ld_state.callbacks.dynamic_section_flags = ld_generic_dynamic_section_flags; + 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; + ld_state.callbacks.create_relocations = ld_generic_create_relocations; + +#ifndef BASE_ELF_NAME + /* Find the ld backend library. Use EBL to determine the name if + the user hasn't provided one on the command line. */ + if (emulation == NULL) + { + emulation = ebl_backend_name (ld_state.ebl); + assert (emulation != NULL); + } + size_t emulation_len = strlen (emulation); + + /* Construct the file name. */ + char *fname = (char *) alloca (sizeof "libld_" - 1 + emulation_len + + sizeof ".so"); + strcpy (mempcpy (stpcpy (fname, "libld_"), emulation, emulation_len), ".so"); + + /* Try loading. */ + void *h = dlopen (fname, RTLD_LAZY); + if (h == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot load ld backend library '%s': %s"), + fname, dlerror ()); + + /* Find the initializer. It must be present. */ + char *initname = (char *) alloca (emulation_len + sizeof "_ld_init"); + strcpy (mempcpy (initname, emulation, emulation_len), "_ld_init"); + int (*initfct) (struct ld_state *) + = (int (*) (struct ld_state *)) dlsym (h, initname); + + if (initfct == NULL) + error (EXIT_FAILURE, 0, gettext ("\ +cannot find init function in ld backend library '%s': %s"), + fname, dlerror ()); + + /* Store the handle. */ + ld_state.ldlib = h; + + /* Call the init function. */ + return initfct (&ld_state); +#else +# define INIT_FCT_NAME(base) _INIT_FCT_NAME(base) +# define _INIT_FCT_NAME(base) base##_ld_init + /* Declare and call the initialization function. */ + extern int INIT_FCT_NAME(BASE_ELF_NAME) (struct ld_state *); + return INIT_FCT_NAME(BASE_ELF_NAME) (&ld_state); +#endif +} + + +static int +check_for_duplicate2 (struct usedfiles *newp, struct usedfiles *list) +{ + struct usedfiles *first; + + if (list == NULL) + return 0; + + list = first = list->next; + do + { + /* When searching the needed list we might come across entries + for files which are not yet opened. Stop then, there is + nothing more to test. */ + if (likely (list->status == not_opened)) + break; + + if (unlikely (list->ino == newp->ino) + && unlikely (list->dev == newp->dev)) + { + close (newp->fd); + newp->fd = -1; + newp->status = closed; + if (newp->file_type == relocatable_file_type) + error (0, 0, gettext ("%s listed more than once as input"), + newp->rfname); + + return 1; + } + list = list->next; + } + while (likely (list != first)); + + return 0; +} + + +static int +check_for_duplicate (struct usedfiles *newp) +{ + struct stat st; + + if (unlikely (fstat (newp->fd, &st) < 0)) + { + close (newp->fd); + return errno; + } + + newp->dev = st.st_dev; + newp->ino = st.st_ino; + + return (check_for_duplicate2 (newp, ld_state.relfiles) + || check_for_duplicate2 (newp, ld_state.dsofiles) + || check_for_duplicate2 (newp, ld_state.needed)); +} + + +/* Find a file along the path described in the state. */ +static int +open_along_path2 (struct usedfiles *fileinfo, struct pathelement *path) +{ + const char *fname = fileinfo->fname; + size_t fnamelen = strlen (fname); + int err = ENOENT; + struct pathelement *firstp = path; + + if (path == NULL) + /* Cannot find anything since we have no path. */ + return ENOENT; + + do + { + if (likely (path->exist >= 0)) + { + /* Create the file name. */ + char *rfname = NULL; + size_t dirlen = strlen (path->pname); + int fd = -1; + + if (fileinfo->file_type == archive_file_type) + { + const char **exts = (ld_state.statically + ? (const char *[2]) { ".a", NULL } + : LIB_EXTENSION (&ld_state)); + + /* We have to create the actual file name. We prepend "lib" + and add one of the extensions the platform has. */ + while (*exts != NULL) + { + size_t extlen = strlen (*exts); + rfname = (char *) alloca (dirlen + 5 + fnamelen + extlen); + memcpy (mempcpy (stpcpy (mempcpy (rfname, path->pname, + dirlen), + "/lib"), + fname, fnamelen), + *exts, extlen + 1); + + fd = open (rfname, O_RDONLY); + if (likely (fd != -1) || errno != ENOENT) + { + err = fd == -1 ? errno : 0; + break; + } + + /* Next extension. */ + ++exts; + } + } + else + { + assert (fileinfo->file_type == dso_file_type + || fileinfo->file_type == dso_needed_file_type); + + rfname = (char *) alloca (dirlen + 1 + fnamelen + 1); + memcpy (stpcpy (mempcpy (rfname, path->pname, dirlen), "/"), + fname, fnamelen + 1); + + fd = open (rfname, O_RDONLY); + if (unlikely (fd == -1)) + err = errno; + } + + if (likely (fd != -1)) + { + /* We found the file. This also means the directory + exists. */ + fileinfo->fd = fd; + path->exist = 1; + + /* Check whether we have this file already loaded. */ + if (unlikely (check_for_duplicate (fileinfo) != 0)) + return EAGAIN; + + /* Make a copy of the name. */ + fileinfo->rfname = obstack_strdup (&ld_state.smem, rfname); + + if (unlikely (ld_state.trace_files)) + printf (fileinfo->file_type == archive_file_type + ? gettext ("%s (for -l%s)\n") + : gettext ("%s (for DT_NEEDED %s)\n"), + rfname, fname); + + return 0; + } + + /* The file does not exist. Maybe the whole directory doesn't. + Check it unless we know it exists. */ + if (unlikely (path->exist == 0)) + { + struct stat st; + + /* Keep only the directory name. Note that the path + might be relative. This doesn't matter here. We do + the test in any case even if there is the chance that + somebody wants to change the programs working + directory at some point which would make the result + of this test void. Since changing the working + directory is completely wrong we are not taking this + case into account. */ + rfname[dirlen] = '\0'; + if (unlikely (stat (rfname, &st) < 0) || ! S_ISDIR (st.st_mode)) + /* The directory does not exist or the named file is no + directory. */ + path->exist = -1; + else + path->exist = 1; + } + } + + /* Next path element. */ + path = path->next; + } + while (likely (err == ENOENT && path != firstp)); + + return err; +} + + +static int +open_along_path (struct usedfiles *fileinfo) +{ + const char *fname = fileinfo->fname; + int err = ENOENT; + + if (fileinfo->file_type == relocatable_file_type) + { + /* Only libraries are searched along the path. */ + fileinfo->fd = open (fname, O_RDONLY); + + if (likely (fileinfo->fd != -1)) + { + /* We found the file. */ + if (unlikely (ld_state.trace_files)) + print_file_name (stdout, fileinfo, 1, 1); + + return check_for_duplicate (fileinfo); + } + + /* If the name is an absolute path we are done. */ + err = errno; + } + else + { + /* If the user specified two parts to the LD_LIBRARY_PATH variable + try the first part now. */ + err = open_along_path2 (fileinfo, ld_state.ld_library_path1); + + /* Try the user-specified path next. */ + if (err == ENOENT) + err = open_along_path2 (fileinfo, + fileinfo->file_type == archive_file_type + ? ld_state.paths : ld_state.rpath_link); + + /* Then the second part of the LD_LIBRARY_PATH value. */ + if (unlikely (err == ENOENT)) + { + err = open_along_path2 (fileinfo, ld_state.ld_library_path2); + + /* In case we look for a DSO handle now the RUNPATH. */ + if (err == ENOENT) + { + if (fileinfo->file_type == dso_file_type) + err = open_along_path2 (fileinfo, ld_state.runpath_link); + + /* Finally the path from the default linker script. */ + if (err == ENOENT) + err = open_along_path2 (fileinfo, ld_state.default_paths); + } + } + } + + if (unlikely (err != 0) + && (err != EAGAIN || fileinfo->file_type == relocatable_file_type)) + error (0, err, gettext ("cannot open %s"), fileinfo->fname); + + return err; +} + + +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) +{ + /* We check the type and size of the symbols. In both cases the + information can be missing (size is zero, type is STT_NOTYPE) in + which case we issue no warnings. Otherwise everything must + match. If the type does not match there is no point in checking + the size. */ + + if (XELF_ST_TYPE (sym->st_info) != STT_NOTYPE && oldp->type != STT_NOTYPE + && unlikely (oldp->type != XELF_ST_TYPE (sym->st_info))) + { + char buf1[64]; + char buf2[64]; + + error (0, 0, gettext ("\ +Warning: type of `%s' changed from %s in %s to %s in %s"), + oldp->name, + ebl_symbol_type_name (ld_state.ebl, oldp->type, + buf1, sizeof (buf1)), + oldp->file->rfname, + ebl_symbol_type_name (ld_state.ebl, XELF_ST_TYPE (sym->st_info), + buf2, sizeof (buf2)), + fileinfo->rfname); + } + else if (XELF_ST_TYPE (sym->st_info) == STT_OBJECT + && oldp->size != 0 + && unlikely (oldp->size != sym->st_size)) + error (0, 0, gettext ("\ +Warning: size of `%s' changed from %" PRIu64 " in %s to %" PRIu64 " in %s"), + oldp->name, (uint64_t) oldp->size, oldp->file->rfname, + (uint64_t) sym->st_size, fileinfo->rfname); +} + + +static int +check_definition (const XElf_Sym *sym, size_t shndx, size_t symidx, + struct usedfiles *fileinfo, struct symbol *oldp) +{ + int result = 0; + bool old_in_dso = FILEINFO_EHDR (oldp->file->ehdr).e_type == ET_DYN; + bool new_in_dso = FILEINFO_EHDR (fileinfo->ehdr).e_type == ET_DYN; + bool use_new_def = false; + + if (shndx != SHN_UNDEF + && (! oldp->defined + || (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 + real definition for a previous common-only definition or a + redefinition of a symbol definition in an object file + previously defined in a DSO. First perform some tests which + will show whether the common is really matching the + definition. */ + check_type_and_size (sym, fileinfo, oldp); + + /* We leave the next element intact to not interrupt the list + with the unresolved symbols. Whoever walks the list will + have to check the `defined' flag. But we remember that this + list element is not unresolved anymore. */ + if (! oldp->defined) + { + /* Remove from the list. */ + --ld_state.nunresolved; + if (! oldp->weak) + --ld_state.nunresolved_nonweak; + CDBL_LIST_DEL (ld_state.unresolved, oldp); + } + else if (oldp->common) + /* Remove from the list. */ + CDBL_LIST_DEL (ld_state.common_syms, oldp); + + /* Use the values of the definition from now on. */ + use_new_def = true; + } + 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 + && 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. */ + && (!ld_state.muldefs || verbose) + && ! old_in_dso && fileinfo->file_type == relocatable_file_type) + { + /* We have a double definition. This is a problem. */ + char buf[64]; + XElf_Sym_vardef (oldsym); + struct usedfiles *oldfile; + const char *scnname; + Elf32_Word xndx; + size_t shnum; + + if (elf_getshdrnum (fileinfo->elf, &shnum) < 0) + error (EXIT_FAILURE, 0, + gettext ("cannot determine number of sections: %s"), + elf_errmsg (-1)); + + /* XXX Use only ebl_section_name. */ + if (shndx < SHN_LORESERVE || (shndx > SHN_HIRESERVE && shndx < shnum)) + scnname = elf_strptr (fileinfo->elf, + fileinfo->shstrndx, + SCNINFO_SHDR (fileinfo->scninfo[shndx].shdr).sh_name); + else + // XXX extended section + 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); + fprintf (stderr, + gettext ("(%s+%#" PRIx64 "): multiple definition of %s `%s'\n"), + scnname, + (uint64_t) sym->st_value, + ebl_symbol_type_name (ld_state.ebl, XELF_ST_TYPE (sym->st_info), + buf, sizeof (buf)), + oldp->name); + + oldfile = oldp->file; + xelf_getsymshndx (oldfile->symtabdata, oldfile->xndxdata, oldp->symidx, + oldsym, xndx); + assert (oldsym != NULL); + + /* XXX Use only ebl_section_name. */ + 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, oldp->scndx, oldp->scndx, + buf, sizeof (buf), NULL, shnum); + + /* XXX Print source file and line number. */ + print_file_name (stderr, oldfile, 1, 0); + fprintf (stderr, gettext ("(%s+%#" PRIx64 "): first defined here\n"), + scnname, (uint64_t) oldsym->st_value); + + if (likely (!ld_state.muldefs)) + result = 1; + } + else if (old_in_dso && fileinfo->file_type == relocatable_file_type + && 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. */ + use_new_def = true; + else if (old_in_dso && !new_in_dso && oldp->defined && !oldp->on_dsolist) + { + CDBL_LIST_ADD_REAR (ld_state.from_dso, oldp); + ++ld_state.nfrom_dso; + + /* If the object is a function we allocate a PLT entry, + otherwise only a GOT entry. */ + if (oldp->type == STT_FUNC) + ++ld_state.nplt; + else + ++ld_state.ngot; + + oldp->on_dsolist = 1; + } + 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); + /* Similarly for the alignment. */ + oldp->merge.value = MAX (oldp->merge.value, sym->st_value); + } + + if (unlikely (use_new_def)) + { + /* Adjust the symbol record appropriately and remove + the symbol from the list of symbols which are taken from DSOs. */ + if (old_in_dso && fileinfo->file_type == relocatable_file_type) + { + CDBL_LIST_DEL (ld_state.from_dso, oldp); + --ld_state.nfrom_dso; + + if (likely (oldp->type == STT_FUNC)) + --ld_state.nplt; + else + --ld_state.ngot; + + oldp->on_dsolist = 0; + } + + /* Use the values of the definition from now on. */ + oldp->size = sym->st_size; + oldp->type = XELF_ST_TYPE (sym->st_info); + oldp->symidx = symidx; + oldp->scndx = shndx; + //oldp->symscndx = THESYMSCNDX must be passed; + oldp->file = fileinfo; + oldp->defined = 1; + oldp->in_dso = new_in_dso; + 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; + + // XXX Really exclude SHN_ABS? + if (shndx != SHN_COMMON && shndx != SHN_ABS) + { + struct scninfo *ignore; + mark_section_used (&fileinfo->scninfo[shndx], shndx, &ignore); + } + } + + /* Add to the list of symbols used from DSOs if necessary. */ + if (new_in_dso && !old_in_dso) + { + CDBL_LIST_ADD_REAR (ld_state.from_dso, oldp); + ++ld_state.nfrom_dso; + + /* If the object is a function we allocate a PLT entry, + otherwise only a GOT entry. */ + if (oldp->type == STT_FUNC) + ++ld_state.nplt; + else + ++ld_state.ngot; + + oldp->on_dsolist = 1; + } + else if (shndx == SHN_COMMON) + { + /* Store the alignment. */ + oldp->merge.value = sym->st_value; + + CDBL_LIST_ADD_REAR (ld_state.common_syms, oldp); + } + } + + return result; +} + + +static struct scninfo * +find_section_group (struct usedfiles *fileinfo, Elf32_Word shndx, + Elf_Data **datap) +{ + struct scninfo *runp; + + for (runp = fileinfo->groups; runp != NULL; runp = runp->next) + if (!runp->used) + { + Elf32_Word *grpref; + size_t cnt; + Elf_Data *data; + + data = elf_getdata (runp->scn, NULL); + if (data == NULL) + error (EXIT_FAILURE, 0, + gettext ("%s: cannot get section group data: %s"), + fileinfo->fname, elf_errmsg (-1)); + + /* There cannot be another data block. */ + assert (elf_getdata (runp->scn, data) == NULL); + + grpref = (Elf32_Word *) data->d_buf; + cnt = data->d_size / sizeof (Elf32_Word); + /* Note that we stop after looking at index 1 since index 0 + contains the flags for the section group. */ + while (cnt > 1) + if (grpref[--cnt] == shndx) + { + *datap = data; + return runp; + } + } + + /* If we come here no section group contained the given section + despite the SHF_GROUP flag. This is an error in the input + file. */ + error (EXIT_FAILURE, 0, gettext ("\ +%s: section '%s' with group flag set does not belong to any group"), + fileinfo->fname, + elf_strptr (fileinfo->elf, fileinfo->shstrndx, + SCNINFO_SHDR (fileinfo->scninfo[shndx].shdr).sh_name)); + return NULL; +} + + +/* Mark all sections which belong to the same group as section SHNDX + as used. */ +static void +mark_section_group (struct usedfiles *fileinfo, Elf32_Word shndx, + struct scninfo **grpscnp) +{ + /* First locate the section group. There can be several (many) of + them. */ + size_t cnt; + Elf32_Word *grpref; + Elf_Data *data; + struct scninfo *grpscn = find_section_group (fileinfo, shndx, &data); + *grpscnp = grpscn; + + /* Mark all the sections as used. + + XXX Two possible problems here: + + - the gABI says "The section must be referenced by a section of type + SHT_GROUP". I hope everybody reads this as "exactly one section". + + - section groups are also useful to mark the debugging section which + belongs to a text section. Unconditionally adding debugging sections + is therefore probably not what is wanted if stripping is required. */ + + /* Mark the section group as handled. */ + grpscn->used = true; + + grpref = (Elf32_Word *) data->d_buf; + cnt = data->d_size / sizeof (Elf32_Word); + while (cnt > 1) + { + Elf32_Word idx = grpref[--cnt]; + XElf_Shdr *shdr = &SCNINFO_SHDR (fileinfo->scninfo[idx].shdr); + + if (fileinfo->scninfo[idx].grpid != grpscn->grpid) + error (EXIT_FAILURE, 0, gettext ("\ +%s: section [%2d] '%s' is not in the correct section group"), + fileinfo->fname, (int) idx, + elf_strptr (fileinfo->elf, fileinfo->shstrndx, shdr->sh_name)); + + if (ld_state.strip == strip_none + /* If we are stripping, remove debug sections. */ + || (!ebl_debugscn_p (ld_state.ebl, + elf_strptr (fileinfo->elf, fileinfo->shstrndx, + shdr->sh_name)) + /* And the relocation sections for the debug sections. */ + && ((shdr->sh_type != SHT_RELA && shdr->sh_type != SHT_REL) + || !ebl_debugscn_p (ld_state.ebl, + elf_strptr (fileinfo->elf, + fileinfo->shstrndx, + SCNINFO_SHDR (fileinfo->scninfo[shdr->sh_info].shdr).sh_name))))) + { + struct scninfo *ignore; + + mark_section_used (&fileinfo->scninfo[idx], idx, &ignore); + } + } +} + + +static void +mark_section_used (struct scninfo *scninfo, Elf32_Word shndx, + struct scninfo **grpscnp) +{ + if (likely (scninfo->used)) + /* Nothing to be done. */ + return; + + /* We need this section. */ + scninfo->used = true; + + /* Make sure the section header has been read from the file. */ + XElf_Shdr *shdr = &SCNINFO_SHDR (scninfo->shdr); +#if NATIVE_ELF + if (unlikely (scninfo->shdr == NULL)) +#else + if (unlikely (scninfo->shdr.sh_type == SHT_NULL)) +#endif + { +#if NATIVE_ELF != 0 + shdr = xelf_getshdr (scninfo->scn, scninfo->shdr); +#else + xelf_getshdr_copy (scninfo->scn, shdr, scninfo->shdr); +#endif + if (unlikely (shdr == NULL)) + /* Something is very wrong. The calling code will notice it + soon and print a message. */ + return; + } + + /* Handle section linked by 'sh_link'. */ + if (unlikely (shdr->sh_link != 0)) + { + struct scninfo *ignore; + mark_section_used (&scninfo->fileinfo->scninfo[shdr->sh_link], + shdr->sh_link, &ignore); + } + + /* Handle section linked by 'sh_info'. */ + if (unlikely (shdr->sh_info != 0) && (shdr->sh_flags & SHF_INFO_LINK)) + { + struct scninfo *ignore; + mark_section_used (&scninfo->fileinfo->scninfo[shdr->sh_info], + shdr->sh_info, &ignore); + } + + if (unlikely (shdr->sh_flags & SHF_GROUP) && ld_state.gc_sections) + /* Find the section group which contains this section. */ + mark_section_group (scninfo->fileinfo, shndx, grpscnp); +} + + +/* We collect all sections in a hashing table. All sections with the + same name are collected in a list. Note that we do not determine + which sections are finally collected in the same output section + here. This would be terribly inefficient. It will be done later. */ +static void +add_section (struct usedfiles *fileinfo, struct scninfo *scninfo) +{ + struct scnhead *queued; + struct scnhead search; + unsigned long int hval; + XElf_Shdr *shdr = &SCNINFO_SHDR (scninfo->shdr); + struct scninfo *grpscn = NULL; + Elf_Data *grpscndata = NULL; + + /* See whether we can determine right away whether we need this + section in the output. + + XXX I assume here that --gc-sections only affects extraction + from an archive. If it also affects objects files given on + the command line then somebody must explain to me how the + dependency analysis should work. Should the entry point be + the root? What if it is a numeric value? */ + if (!scninfo->used + && (ld_state.strip == strip_none + || (shdr->sh_flags & SHF_ALLOC) != 0 + || shdr->sh_type == SHT_NOTE + || (shdr->sh_type == SHT_PROGBITS + && strcmp (elf_strptr (fileinfo->elf, + fileinfo->shstrndx, + shdr->sh_name), ".comment") == 0)) + && (fileinfo->status != in_archive || !ld_state.gc_sections)) + /* Mark as used and handle reference recursively if necessary. */ + mark_section_used (scninfo, elf_ndxscn (scninfo->scn), &grpscn); + + if ((shdr->sh_flags & SHF_GROUP) && grpscn == NULL) + /* Determine the symbol which name constitutes the signature + for the section group. */ + grpscn = find_section_group (fileinfo, elf_ndxscn (scninfo->scn), + &grpscndata); + assert (grpscn == NULL || grpscn->symbols->name != NULL); + + /* Determine the section name. */ + search.name = elf_strptr (fileinfo->elf, fileinfo->shstrndx, shdr->sh_name); + search.type = shdr->sh_type; + search.flags = shdr->sh_flags; + search.entsize = shdr->sh_entsize; + search.grp_signature = grpscn != NULL ? grpscn->symbols->name : NULL; + search.kind = scn_normal; + hval = elf_hash (search.name); + + /* Find already queued sections. */ + queued = ld_section_tab_find (&ld_state.section_tab, hval, &search); + if (queued != NULL) + { + bool is_comdat = false; + + /* If this section is part of a COMDAT section group we simply + ignore it since we already have a copy. */ + if (unlikely (shdr->sh_flags & SHF_GROUP)) + { + /* Get the data of the section group section. */ + if (grpscndata == NULL) + { + grpscndata = elf_getdata (grpscn->scn, NULL); + assert (grpscndata != NULL); + } + + /* XXX Possibly unaligned memory access. */ + 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) + { + /* No COMDAT section, we use the data. */ + scninfo->next = queued->last->next; + queued->last = queued->last->next = scninfo; + + queued->flags = ebl_sh_flags_combine (ld_state.ebl, queued->flags, + shdr->sh_flags); + queued->align = MAX (queued->align, shdr->sh_addralign); + } + } + else + { + /* We do not use obstacks here since the memory might be + deallocated. */ + queued = (struct scnhead *) xcalloc (sizeof (struct scnhead), 1); + queued->kind = scn_normal; + queued->name = search.name; + queued->type = shdr->sh_type; + queued->flags = shdr->sh_flags; + queued->align = shdr->sh_addralign; + queued->entsize = shdr->sh_entsize; + queued->grp_signature = grpscn != NULL ? grpscn->symbols->name : NULL; + 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); + } +} + + +static int +add_relocatable_file (struct usedfiles *fileinfo, GElf_Word secttype) +{ + size_t scncnt; + size_t cnt; + Elf_Data *symtabdata = NULL; + Elf_Data *xndxdata = NULL; + Elf_Data *versymdata = NULL; + Elf_Data *verdefdata = NULL; + Elf_Data *verneeddata = NULL; + size_t symstridx = 0; + 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); + + /* Allocate memory for the sections. */ + if (unlikely (elf_getshdrnum (fileinfo->elf, &scncnt) < 0)) + error (EXIT_FAILURE, 0, + gettext ("cannot determine number of sections: %s"), + elf_errmsg (-1)); + + fileinfo->scninfo = (struct scninfo *) + obstack_calloc (&ld_state.smem, scncnt * sizeof (struct scninfo)); + + /* Read all the section headers and find the symbol table. Note + that we don't skip the section with index zero. Even though the + section itself is always empty the section header contains + informaton for the case when the section index for the section + header string table is too large to fit in the ELF header. */ + for (cnt = 0; cnt < scncnt; ++cnt) + { + /* Store the handle for the section. */ + fileinfo->scninfo[cnt].scn = elf_getscn (fileinfo->elf, cnt); + + /* Get the ELF section header and data. */ + XElf_Shdr *shdr; +#if NATIVE_ELF != 0 + if (fileinfo->scninfo[cnt].shdr == NULL) +#else + if (fileinfo->scninfo[cnt].shdr.sh_type == SHT_NULL) +#endif + { +#if NATIVE_ELF != 0 + shdr = xelf_getshdr (fileinfo->scninfo[cnt].scn, + fileinfo->scninfo[cnt].shdr); +#else + xelf_getshdr_copy (fileinfo->scninfo[cnt].scn, shdr, + fileinfo->scninfo[cnt].shdr); +#endif + if (shdr == NULL) + { + /* This should never happen. */ + fprintf (stderr, gettext ("%s: invalid ELF file (%s:%d)\n"), + fileinfo->rfname, __FILE__, __LINE__); + return 1; + } + } + else + shdr = &SCNINFO_SHDR (fileinfo->scninfo[cnt].shdr); + + Elf_Data *data = elf_getdata (fileinfo->scninfo[cnt].scn, NULL); + + /* 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. */ + fileinfo->scninfo[cnt].fileinfo = fileinfo; + + if (unlikely (shdr->sh_type == SHT_SYMTAB) + || unlikely (shdr->sh_type == SHT_DYNSYM)) + { + if (shdr->sh_type == SHT_SYMTAB) + { + assert (fileinfo->symtabdata == NULL); + fileinfo->symtabdata = data; + fileinfo->nsymtab = shdr->sh_size / shdr->sh_entsize; + fileinfo->nlocalsymbols = shdr->sh_info; + fileinfo->symstridx = shdr->sh_link; + } + else + { + assert (fileinfo->dynsymtabdata == NULL); + fileinfo->dynsymtabdata = data; + fileinfo->ndynsymtab = shdr->sh_size / shdr->sh_entsize; + fileinfo->dynsymstridx = shdr->sh_link; + } + + /* If we are looking for the normal symbol table we just + found it. */ + if (secttype == shdr->sh_type) + { + assert (symtabdata == NULL); + symtabdata = data; + symstridx = shdr->sh_link; + nsymbols = shdr->sh_size / shdr->sh_entsize; + nlocalsymbols = shdr->sh_info; + } + } + else if (unlikely (shdr->sh_type == SHT_SYMTAB_SHNDX)) + { + assert (xndxdata == NULL); + fileinfo->xndxdata = xndxdata = data; + } + else if (unlikely (shdr->sh_type == SHT_GNU_versym)) + { + assert (versymdata == 0); + fileinfo->versymdata = versymdata = data; + } + else if (unlikely (shdr->sh_type == SHT_GNU_verdef)) + { + size_t nversions; + + assert (verdefdata == 0); + fileinfo->verdefdata = verdefdata = data; + + /* Allocate the arrays flagging the use of the version and + to track of allocated names. */ + fileinfo->nverdef = nversions = shdr->sh_info; + /* We have NVERSIONS + 1 because the indeces used to access the + sectino start with one; zero represents local binding. */ + fileinfo->verdefused = (XElf_Versym *) + obstack_calloc (&ld_state.smem, + sizeof (XElf_Versym) * (nversions + 1)); + fileinfo->verdefent = (struct Ebl_Strent **) + obstack_alloc (&ld_state.smem, + sizeof (struct Ebl_Strent *) * (nversions + 1)); + } + else if (unlikely (shdr->sh_type == SHT_GNU_verneed)) + { + assert (verneeddata == 0); + fileinfo->verneeddata = verneeddata = data; + } + else if (unlikely (shdr->sh_type == SHT_DYNAMIC)) + { + assert (fileinfo->dynscn == NULL); + fileinfo->dynscn = fileinfo->scninfo[cnt].scn; + } + else if (unlikely (shdr->sh_type == SHT_GROUP)) + { + Elf_Scn *symscn; + XElf_Shdr_vardef (symshdr); + Elf_Data *symdata; + + if (FILEINFO_EHDR (fileinfo->ehdr).e_type != ET_REL) + error (EXIT_FAILURE, 0, gettext ("\ +%s: only files of type ET_REL might contain section groups"), + fileinfo->fname); + + fileinfo->scninfo[cnt].next = fileinfo->groups; + fileinfo->scninfo[cnt].grpid = cnt; + fileinfo->groups = &fileinfo->scninfo[cnt]; + + /* Determine the signature. We create a symbol record for + it. Only the name element is important. */ + fileinfo->scninfo[cnt].symbols = (struct symbol *) + obstack_calloc (&ld_state.smem, sizeof (struct symbol)); + + symscn = elf_getscn (fileinfo->elf, shdr->sh_link); + xelf_getshdr (symscn, symshdr); + symdata = elf_getdata (symscn, NULL); + + if (symshdr != NULL) + { + XElf_Sym_vardef (sym); + + /* We don't need the section index and therefore we don't + have to use 'xelf_getsymshndx'. */ + xelf_getsym (symdata, shdr->sh_info, sym); + if (sym != NULL) + { + struct symbol *symbol = fileinfo->scninfo[cnt].symbols; + +#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; + } + } + if (fileinfo->scninfo[cnt].symbols->name == NULL) + error (EXIT_FAILURE, 0, gettext ("\ +%s: cannot determine signature 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)); + + + /* 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. */ + assert (fileinfo->scninfo[cnt].used == false); + } + else if (! SECTION_TYPE_P (&ld_state, shdr->sh_type) + && unlikely ((shdr->sh_flags & SHF_OS_NONCONFORMING) != 0)) + /* According to the gABI it is a fatal error if the file contains + a section with unknown type and the SHF_OS_NONCONFORMING flag + set. */ + error (EXIT_FAILURE, 0, + gettext ("%s: section '%s' has unknown type: %d"), + fileinfo->fname, + elf_strptr (fileinfo->elf, fileinfo->shstrndx, + shdr->sh_name), + (int) shdr->sh_type); + /* We don't have to add a few section types here. These will be + generated from scratch for the new output file. We also + don't add the sections of DSOs here since these sections are + not used in the resulting object file. */ + else if (likely (fileinfo->file_type == relocatable_file_type) + && likely (cnt > 0) + && likely (shdr->sh_type == SHT_PROGBITS + || shdr->sh_type == SHT_RELA + || shdr->sh_type == SHT_REL + || shdr->sh_type == SHT_NOTE + || shdr->sh_type == SHT_NOBITS + || shdr->sh_type == SHT_INIT_ARRAY + || shdr->sh_type == SHT_FINI_ARRAY + || shdr->sh_type == SHT_PREINIT_ARRAY)) + { + /* 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. */ + if (likely (symtabdata != NULL)) + { + /* 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 || has_tls_symbols)) + { + fileinfo->symref = (struct symbol **) + obstack_calloc (&ld_state.smem, + nsymbols * sizeof (struct symbol *)); + + /* Only handle the local symbols here. */ + for (cnt = 0; cnt < nlocalsymbols; ++cnt) + { + Elf32_Word shndx; + XElf_Sym_vardef (sym); + + xelf_getsymshndx (symtabdata, xndxdata, cnt, sym, shndx); + if (sym == NULL) + { + /* This should never happen. */ + fprintf (stderr, gettext ("%s: invalid ELF file (%s:%d)\n"), + fileinfo->rfname, __FILE__, __LINE__); + return 1; + } + + if (likely (shndx != SHN_XINDEX)) + shndx = sym->st_shndx; + else if (unlikely (shndx == 0)) + { + fprintf (stderr, gettext ("%s: invalid ELF file (%s:%d)\n"), + fileinfo->rfname, __FILE__, __LINE__); + return 1; + } + + 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) + || XELF_ST_TYPE (sym->st_info) == STT_TLS)) + { + /* Create a symbol record for this symbol and add it + to the list for this section. */ + struct symbol *newp; + + newp = (struct symbol *) + obstack_calloc (&ld_state.smem, sizeof (struct symbol)); + + newp->symidx = cnt; + newp->scndx = shndx; + newp->file = fileinfo; + newp->defined = 1; + fileinfo->symref[cnt] = newp; + + if (fileinfo->scninfo[shndx].symbols == NULL) + fileinfo->scninfo[shndx].symbols = newp->next_in_scn + = newp; + else + { + newp->next_in_scn + = fileinfo->scninfo[shndx].symbols->next_in_scn; + fileinfo->scninfo[shndx].symbols + = fileinfo->scninfo[shndx].symbols->next_in_scn = newp; + } + } + } + } + else + /* Create array with pointers to the symbol definitions. Note + that we only allocate memory for the non-local symbols + since we have no merge-able sections. But we store the + pointer as if it was for the whole symbol table. This + saves some memory. */ + fileinfo->symref = (struct symbol **) + obstack_calloc (&ld_state.smem, ((nsymbols - nlocalsymbols) + * sizeof (struct symbol *))) + - nlocalsymbols; + + /* Don't handle local symbols here. It's either not necessary + at all or has already happened. */ + for (cnt = nlocalsymbols; cnt < nsymbols; ++cnt) + { + XElf_Sym_vardef (sym); + Elf32_Word shndx; + xelf_getsymshndx (symtabdata, xndxdata, cnt, sym, shndx); + + if (sym == NULL) + { + /* This should never happen. */ + fprintf (stderr, gettext ("%s: invalid ELF file (%s:%d)\n"), + fileinfo->rfname, __FILE__, __LINE__); + return 1; + } + + if (likely (shndx != SHN_XINDEX)) + shndx = sym->st_shndx; + else if (unlikely (shndx == 0)) + { + fprintf (stderr, gettext ("%s: invalid ELF file (%s:%d)\n"), + fileinfo->rfname, __FILE__, __LINE__); + return 1; + } + + /* We ignore ABS symbols from DSOs. */ + // XXX Is this correct? + if (unlikely (shndx == SHN_ABS) && secttype == SHT_DYNSYM) + continue; + + 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; + + if (xelf_getversym_copy (versymdata, cnt, versym) == NULL) + /* XXX Should we handle faulty input files more graceful? */ + assert (! "xelf_getversym failed"); + + if ((versym & 0x8000) != 0) + /* Ignore the symbol, it's not the default version. */ + continue; + } + + /* See whether we know anything about this symbol. */ + struct symbol search; + search.name = elf_strptr (fileinfo->elf, symstridx, sym->st_name); + unsigned long int hval = elf_hash (search.name); + + /* We ignore the symbols the linker generates. This are + _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 == 165832675ul) + && strcmp (search.name, "_DYNAMIC") == 0) + || (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 + don't ignore it. Let the user get what s/he deserves. */ + && fileinfo->file_type != relocatable_file_type) + continue; + + struct symbol *oldp = ld_symbol_tab_find (&ld_state.symbol_tab, + hval, &search); + struct symbol *newp; + if (likely (oldp == NULL)) + { + /* No symbol of this name known. Add it. */ + newp = (struct symbol *) obstack_alloc (&ld_state.smem, + sizeof (*newp)); + newp->name = search.name; + newp->size = sym->st_size; + newp->type = XELF_ST_TYPE (sym->st_info); + newp->symidx = cnt; + newp->outsymidx = 0; + newp->outdynsymidx = 0; + newp->scndx = shndx; + newp->file = fileinfo; + newp->defined = newp->scndx != SHN_UNDEF; + newp->common = newp->scndx == SHN_COMMON; + 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; + newp->next_in_scn = NULL; +#ifndef NDEBUG + newp->next = NULL; + newp->previous = NULL; +#endif + + if (newp->scndx == SHN_UNDEF) + { + CDBL_LIST_ADD_REAR (ld_state.unresolved, newp); + ++ld_state.nunresolved; + if (! newp->weak) + ++ld_state.nunresolved_nonweak; + } + else if (newp->scndx == SHN_COMMON) + { + /* Store the alignment requirement. */ + newp->merge.value = sym->st_value; + + CDBL_LIST_ADD_REAR (ld_state.common_syms, newp); + } + + /* Insert the new symbol. */ + if (unlikely (ld_symbol_tab_insert (&ld_state.symbol_tab, + hval, newp) != 0)) + /* This cannot happen. */ + abort (); + + fileinfo->symref[cnt] = newp; + + /* We have a few special symbols to recognize. The symbols + _init and _fini are the initialization and finalization + functions respectively. They have to be made known in + the dynamic section and therefore we have to find out + now whether these functions exist or not. */ + if (hval == 6685956 && strcmp (newp->name, "_init") == 0) + ld_state.init_symbol = newp; + else if (hval == 6672457 && strcmp (newp->name, "_fini") == 0) + ld_state.fini_symbol = newp; + } + 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; + else + /* Use the previously allocated symbol record. It has + been updated in check_definition(), if necessary. */ + newp = fileinfo->symref[cnt] = oldp; + + /* Mark the section the symbol we need comes from as used. */ + if (shndx != SHN_UNDEF + && (shndx < SHN_LORESERVE || shndx > SHN_HIRESERVE)) + { + struct scninfo *ignore; + +#ifndef NDEBUG + size_t shnum; + assert (elf_getshdrnum (fileinfo->elf, &shnum) == 0); + assert (shndx < shnum); +#endif + + /* Mark section (and all dependencies) as used. */ + mark_section_used (&fileinfo->scninfo[shndx], shndx, &ignore); + + /* Check whether the section is merge-able. In this case we + have to record the symbol. */ + if (SCNINFO_SHDR (fileinfo->scninfo[shndx].shdr).sh_flags + & SHF_MERGE) + { + if (fileinfo->scninfo[shndx].symbols == NULL) + fileinfo->scninfo[shndx].symbols = newp->next_in_scn + = newp; + else + { + newp->next_in_scn + = fileinfo->scninfo[shndx].symbols->next_in_scn; + fileinfo->scninfo[shndx].symbols + = fileinfo->scninfo[shndx].symbols->next_in_scn = newp; + } + } + } + } + + /* This file is used. */ + if (likely (fileinfo->file_type == relocatable_file_type)) + { + if (unlikely (ld_state.relfiles == NULL)) + ld_state.relfiles = fileinfo->next = fileinfo; + else + { + fileinfo->next = ld_state.relfiles->next; + ld_state.relfiles = ld_state.relfiles->next = fileinfo; + } + + /* Update some summary information in the state structure. */ + ld_state.nsymtab += fileinfo->nsymtab; + ld_state.nlocalsymbols += fileinfo->nlocalsymbols; + } + else if (likely (fileinfo->file_type == dso_file_type)) + { + CSNGL_LIST_ADD_REAR (ld_state.dsofiles, fileinfo); + ++ld_state.ndsofiles; + + if (fileinfo->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. */ + ++ld_state.ndsofiles; + } + } + + return 0; +} + + +int +ld_handle_filename_list (struct filename_list *fnames) +{ + struct filename_list *runp; + int res = 0; + + for (runp = fnames; runp != NULL; runp = runp->next) + { + struct usedfiles *curp; + + /* Create a record for the new file. */ + curp = runp->real = ld_new_inputfile (runp->name, relocatable_file_type); + + /* Set flags for group handling. */ + 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. */ + do + res |= FILE_PROCESS (-1, curp, &ld_state, &curp); + while (curp != NULL); + } + + /* Free the list. */ + while (fnames != NULL) + { + runp = fnames; + fnames = fnames->next; + free (runp); + } + + return res; +} + + +/* Handle opening of the given file with ELF descriptor. */ +static int +open_elf (struct usedfiles *fileinfo, Elf *elf) +{ + int res = 0; + + if (elf == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot get descriptor for ELF file (%s:%d): %s\n"), + __FILE__, __LINE__, elf_errmsg (-1)); + + if (unlikely (elf_kind (elf) == ELF_K_NONE)) + { + struct filename_list *fnames; + + /* We don't have to look at this file again. */ + fileinfo->status = closed; + + /* Let's see whether this is a linker script. */ + if (fileinfo->fd != -1) + /* Create a stream from the file handle we know. */ + ldin = fdopen (fileinfo->fd, "r"); + else + { + /* Get the memory for the archive member. */ + char *content; + size_t contentsize; + + /* Get the content of the file. */ + content = elf_rawfile (elf, &contentsize); + if (content == NULL) + { + fprintf (stderr, gettext ("%s: invalid ELF file (%s:%d)\n"), + fileinfo->rfname, __FILE__, __LINE__); + return 1; + } + + /* The content of the file is available in memory. Read the + memory region as a stream. */ + ldin = fmemopen (content, contentsize, "r"); + } + + /* No need for locking. */ + __fsetlocking (ldin, FSETLOCKING_BYCALLER); + + if (ldin == NULL) + error (EXIT_FAILURE, errno, gettext ("cannot open '%s'"), + fileinfo->rfname); + + /* Parse the file. If it is a linker script no problems will be + reported. */ + ld_state.srcfiles = NULL; + ldlineno = 1; + ld_scan_version_script = 0; + ldin_fname = fileinfo->rfname; + res = ldparse (); + + fclose (ldin); + if (fileinfo->fd != -1 && !fileinfo->fd_passed) + { + /* We won't need the file descriptor again. */ + close (fileinfo->fd); + fileinfo->fd = -1; + } + + elf_end (elf); + + if (unlikely (res != 0)) + /* Something went wrong during parsing. */ + return 1; + + /* This is no ELF file. */ + fileinfo->elf = NULL; + + /* Now we have to handle eventual INPUT and GROUP statements in + the script. Read the files mentioned. */ + fnames = ld_state.srcfiles; + if (fnames != NULL) + { + struct filename_list *oldp; + + /* Convert the list into a normal single-linked list. */ + oldp = fnames; + fnames = fnames->next; + oldp->next = NULL; + + /* Remove the list from the state structure. */ + ld_state.srcfiles = NULL; + + if (unlikely (ld_handle_filename_list (fnames) != 0)) + return 1; + } + + return 0; + } + + /* Store the file info. */ + fileinfo->elf = elf; + + /* The file is ready for action. */ + fileinfo->status = opened; + + return 0; +} + + +static int +add_whole_archive (struct usedfiles *fileinfo) +{ + Elf *arelf; + Elf_Cmd cmd = ELF_C_READ_MMAP_PRIVATE; + int res = 0; + + while ((arelf = elf_begin (fileinfo->fd, cmd, fileinfo->elf)) != NULL) + { + Elf_Arhdr *arhdr = elf_getarhdr (arelf); + struct usedfiles *newp; + + if (arhdr == NULL) + abort (); + + /* Just to be sure; since these are no files in the archive + these names should never be returned. */ + assert (strcmp (arhdr->ar_name, "/") != 0); + assert (strcmp (arhdr->ar_name, "//") != 0); + + newp = ld_new_inputfile (arhdr->ar_name, relocatable_file_type); + newp->archive_file = fileinfo; + + if (unlikely (ld_state.trace_files)) + print_file_name (stdout, newp, 1, 1); + + /* This shows that this file is contained in an archive. */ + newp->fd = -1; + /* Store the ELF descriptor. */ + newp->elf = arelf; + /* Show that we are open for business. */ + newp->status = opened; + + /* Proces the file, add all the symbols etc. */ + res = file_process2 (newp); + if (unlikely (res != 0)) + break; + + /* Advance to the next archive element. */ + cmd = elf_next (arelf); + } + + return res; +} + + +static int +extract_from_archive (struct usedfiles *fileinfo) +{ + static int archive_seq; + int res = 0; + + 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 : ld_state.nunresolved) == 0) + return 0; + + Elf_Arsym *syms; + size_t nsyms; + + /* Get all the symbols. */ + syms = elf_getarsym (fileinfo->elf, &nsyms); + if (syms == NULL) + { + cannot_read_archive: + error (0, 0, gettext ("cannot read archive `%s': %s"), + fileinfo->rfname, elf_errmsg (-1)); + + /* We cannot use this archive anymore. */ + fileinfo->status = closed; + + return 1; + } + + /* Now add all the symbols to the hash table. Note that there + can potentially be duplicate definitions. We'll always use + the first definition. */ + // XXX Is this a compatible behavior? + bool any_used; + do + { + any_used = false; + + size_t cnt; + for (cnt = 0; cnt < nsyms; ++cnt) + { + struct symbol search = { .name = syms[cnt].as_name }; + struct symbol *sym = ld_symbol_tab_find (&ld_state.symbol_tab, + syms[cnt].as_hash, &search); + if (sym != NULL && ! sym->defined) + { + /* The symbol is referenced and not defined. */ + Elf *arelf; + Elf_Arhdr *arhdr; + struct usedfiles *newp; + + /* Find the archive member for this symbol. */ + if (unlikely (elf_rand (fileinfo->elf, syms[cnt].as_off) + != syms[cnt].as_off)) + goto cannot_read_archive; + + /* Note: no test of a failing 'elf_begin' call. That's fine + since 'elf'getarhdr' will report the problem. */ + arelf = elf_begin (fileinfo->fd, ELF_C_READ_MMAP_PRIVATE, + fileinfo->elf); + arhdr = elf_getarhdr (arelf); + if (arhdr == NULL) + goto cannot_read_archive; + + /* We have all the information and an ELF handle for the + archive member. Create the normal data structure for + a file now. */ + newp = ld_new_inputfile (obstack_strdup (&ld_state.smem, + arhdr->ar_name), + relocatable_file_type); + newp->archive_file = fileinfo; + + if (unlikely (ld_state.trace_files)) + print_file_name (stdout, newp, 1, 1); + + /* This shows that this file is contained in an archive. */ + newp->fd = -1; + /* Store the ELF descriptor. */ + newp->elf = arelf; + /* Show that we are open for business. */ + newp->status = in_archive; + + /* Now read the file and add all the symbols. */ + res = file_process2 (newp); + if (unlikely (res != 0)) + return res; + + any_used = true; + } + } + + if (any_used) + { + /* This is an archive therefore it must have a number. */ + assert (fileinfo->archive_seq != 0); + ld_state.last_archive_used = fileinfo->archive_seq; + } + } + while (any_used); + + return res; +} + + +static int +file_process2 (struct usedfiles *fileinfo) +{ + int res; + + if (likely (elf_kind (fileinfo->elf) == ELF_K_ELF)) + { + /* The first time we get here we read the ELF header. */ +#if NATIVE_ELF != 0 + if (likely (fileinfo->ehdr == NULL)) +#else + if (likely (FILEINFO_EHDR (fileinfo->ehdr).e_type == ET_NONE)) +#endif + { + XElf_Ehdr *ehdr; +#if NATIVE_ELF != 0 + ehdr = xelf_getehdr (fileinfo->elf, fileinfo->ehdr); +#else + xelf_getehdr_copy (fileinfo->elf, ehdr, fileinfo->ehdr); +#endif + if (ehdr == NULL) + { + fprintf (stderr, gettext ("%s: invalid ELF file (%s:%d)\n"), + fileinfo->rfname, __FILE__, __LINE__); + fileinfo->status = closed; + return 1; + } + + if (FILEINFO_EHDR (fileinfo->ehdr).e_type != ET_REL + && unlikely (FILEINFO_EHDR (fileinfo->ehdr).e_type != ET_DYN)) + /* XXX Add ebl* function to query types which are allowed + to link in. */ + { + char buf[64]; + + print_file_name (stderr, fileinfo, 1, 0); + fprintf (stderr, + gettext ("file of type %s cannot be linked in\n"), + ebl_object_type_name (ld_state.ebl, + FILEINFO_EHDR (fileinfo->ehdr).e_type, + buf, sizeof (buf))); + fileinfo->status = closed; + 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_getshdrstrndx (fileinfo->elf, &fileinfo->shstrndx) + < 0)) + { + fprintf (stderr, gettext ("\ +%s: cannot get section header string table index: %s\n"), + fileinfo->rfname, elf_errmsg (-1)); + fileinfo->status = closed; + return 1; + } + } + + /* Now handle the different types of files. */ + if (FILEINFO_EHDR (fileinfo->ehdr).e_type == ET_REL) + { + /* Add all the symbol. Relocatable files have symbol + tables. */ + res = add_relocatable_file (fileinfo, SHT_SYMTAB); + } + else + { + bool has_l_name = fileinfo->file_type == archive_file_type; + + assert (FILEINFO_EHDR (fileinfo->ehdr).e_type == ET_DYN); + + /* If the file is a DT_NEEDED dependency then the type is + already correctly specified. */ + if (fileinfo->file_type != dso_needed_file_type) + fileinfo->file_type = dso_file_type; + + /* We cannot use DSOs when generating relocatable objects. */ + if (ld_state.file_type == relocatable_file_type) + { + error (0, 0, gettext ("\ +cannot use DSO '%s' when generating relocatable object file"), + fileinfo->fname); + return 1; + } + + /* Add all the symbols. For DSOs we are looking at the + dynamic symbol table. */ + res = add_relocatable_file (fileinfo, SHT_DYNSYM); + + /* We always have to have a dynamic section. */ + assert (fileinfo->dynscn != NULL); + + /* We have to remember the dependencies for this object. It + is necessary to look them up. */ + XElf_Shdr_vardef (dynshdr); + xelf_getshdr (fileinfo->dynscn, dynshdr); + + Elf_Data *dyndata = elf_getdata (fileinfo->dynscn, NULL); + /* XXX Should we flag the failure to get the dynamic section? */ + if (dynshdr != NULL) + { + int cnt = dynshdr->sh_size / dynshdr->sh_entsize; + XElf_Dyn_vardef (dyn); + + while (--cnt >= 0) + { + xelf_getdyn (dyndata, cnt, dyn); + if (dyn != NULL) + { + if(dyn->d_tag == DT_NEEDED) + { + struct usedfiles *newp; + + newp = ld_new_inputfile (elf_strptr (fileinfo->elf, + dynshdr->sh_link, + dyn->d_un.d_val), + dso_needed_file_type); + + /* Enqueue the newly found dependencies. */ + // XXX Check that there not already a file with the + // same name. + CSNGL_LIST_ADD_REAR (ld_state.needed, newp); + } + else if (dyn->d_tag == DT_SONAME) + { + /* We use the DT_SONAME (this is what's there + for). */ + fileinfo->soname = elf_strptr (fileinfo->elf, + dynshdr->sh_link, + dyn->d_un.d_val); + has_l_name = false; + } + } + } + } + + /* Construct the file name if the DSO has no SONAME and the + file name comes from a -lXX parameter on the comment + line. */ + if (unlikely (has_l_name)) + { + /* The FNAME is the parameter the user specified on the + command line. We prepend "lib" and append ".so". */ + size_t len = strlen (fileinfo->fname) + 7; + char *newp; + + newp = (char *) obstack_alloc (&ld_state.smem, len); + strcpy (stpcpy (stpcpy (newp, "lib"), fileinfo->fname), ".so"); + + fileinfo->soname = newp; + } + } + } + else if (likely (elf_kind (fileinfo->elf) == ELF_K_AR)) + { + if (unlikely (ld_state.extract_rule == allextract)) + /* Which this option enabled we have to add all the object + files in the archive. */ + res = add_whole_archive (fileinfo); + else if (ld_state.file_type == relocatable_file_type) + { + /* When generating a relocatable object we don't find files + in archives. */ + if (verbose) + error (0, 0, gettext ("input file '%s' ignored"), fileinfo->fname); + + res = 0; + } + else + { + 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. */ + abort (); + + return res; +} + + +/* Process a given file. The first parameter is a file descriptor for + the file which can be -1 to indicate the file has not yet been + found. The second parameter describes the file to be opened, the + last one is the state of the linker which among other information + contain the paths we look at. */ +static int +ld_generic_file_process (int fd, struct usedfiles *fileinfo, + struct ld_state *statep, struct usedfiles **nextp) +{ + int res = 0; + + /* By default we go to the next file in the list. */ + *nextp = fileinfo->next; + + /* Set the flag to signal we are looking for a group start. */ + if (unlikely (fileinfo->group_start)) + { + ld_state.group_start_requested = true; + fileinfo->group_start = false; + } + + /* If the file isn't open yet, open it now. */ + if (likely (fileinfo->status == not_opened)) + { + bool fd_passed = true; + + if (likely (fd == -1)) + { + /* Find the file ourselves. */ + int err = open_along_path (fileinfo); + if (unlikely (err != 0)) + /* We allow libraries and DSOs to be named more than once. + Don't report an error to the caller. */ + return err == EAGAIN ? 0 : err; + + fd_passed = false; + } + else + fileinfo->fd = fd; + + /* Remember where we got the descriptor from. */ + fileinfo->fd_passed = fd_passed; + + /* We found the file. Now test whether it is a file type we can + handle. + + 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, + is_dso_p (fileinfo->fd) + ? ELF_C_READ_MMAP + : ELF_C_READ_MMAP_PRIVATE, NULL)); + if (unlikely (res != 0)) + return res; + } + + /* Now that we have opened the file start processing it. */ + if (likely (fileinfo->status != closed)) + res = file_process2 (fileinfo); + + /* Determine which file to look at next. */ + if (unlikely (fileinfo->group_backref != NULL)) + { + /* We only go back if an archive other than the one we would go + back to has been used in the last round. */ + if (ld_state.last_archive_used > fileinfo->group_backref->archive_seq) + { + *nextp = fileinfo->group_backref; + ld_state.last_archive_used = 0; + } + else + { + /* If we come here this means that the archives we read so + far are not needed anymore. We can free some of the data + now. */ + struct usedfiles *runp = ld_state.archives; + + do + { + /* We don't need the ELF descriptor anymore. Unless there + are no files from the archive used this will not free + the whole file but only some data structures. */ + elf_end (runp->elf); + runp->elf = NULL; + + 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 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_archive != ld_state.tailarchives) + /* The loop includes more than one archive, add the pointer. */ + { + *nextp = ld_state.tailarchives->group_backref = + ld_state.group_start_archive; + ld_state.last_archive_used = 0; + } + else + /* We might still have to go back to the beginning of the + group if since the last archive other files have been + added. But we go back exactly once. */ + if (ld_state.tailarchives != fileinfo) + { + *nextp = ld_state.group_start_archive; + ld_state.last_archive_used = 0; + } + } + + /* Clear the flags. */ + ld_state.group_start_requested = false; + ld_state.group_start_archive = NULL; + fileinfo->group_end = false; + } + + return res; +} + + +/* Library names passed to the linker as -lXX represent files named + libXX.YY. The YY part can have different forms, depending on the + platform. The generic set is .so and .a (in this order). */ +static const char ** +ld_generic_lib_extensions (struct ld_state *statep __attribute__ ((__unused__))) +{ + static const char *exts[] = + { + ".so", ".a", NULL + }; + + return exts; +} + + +/* Flag unresolved symbols. */ +static int +ld_generic_flag_unresolved (struct ld_state *statep) +{ + int retval = 0; + + if (ld_state.nunresolved_nonweak > 0) + { + /* Go through the list and determine the unresolved symbols. */ + struct symbol *first; + struct symbol *s; + + s = first = ld_state.unresolved->next; + do + { + if (! s->defined && ! s->weak) + { + /* Two special symbol we recognize: the symbol for the + GOT and the dynamic section. */ + if (strcmp (s->name, "_GLOBAL_OFFSET_TABLE_") == 0 + || strcmp (s->name, "_DYNAMIC") == 0) + { + /* We will have to fill in more information later. */ + ld_state.need_got = true; + + /* Remember that we found it. */ + if (s->name[1] == 'G') + ld_state.got_symbol = s; + else + ld_state.dyn_symbol = s; + } + else if (ld_state.file_type != dso_file_type || !ld_state.nodefs) + { + /* XXX The error message should get better. It should use + the debugging information if present to tell where in the + sources the undefined reference is. */ + error (0, 0, gettext ("undefined symbol `%s' in %s"), + s->name, s->file->fname); + + retval = 1; + } + } + + /* We cannot decide here what to do with undefined + references which will come from DSO since we do not know + what kind of symbol we expect. Only when looking at the + relocations we can see whether we need a PLT entry or + only a GOT entry. */ + + s = s->next; + } + while (s != first); + } + + return retval; +} + + +/* Close the given file. */ +static int +ld_generic_file_close (struct usedfiles *fileinfo, struct ld_state *statep) +{ + /* Close the ELF descriptor. */ + elf_end (fileinfo->elf); + + /* If we have opened the file descriptor close it. But we might + have done this already in which case FD is -1. */ + if (!fileinfo->fd_passed && fileinfo->fd != -1) + close (fileinfo->fd); + + /* We allocated the resolved file name. */ + if (fileinfo->fname != fileinfo->rfname) + free ((char *) fileinfo->rfname); + + return 0; +} + + +static void +new_generated_scn (enum scn_kind kind, const char *name, int type, int flags, + int entsize, int align) +{ + struct scnhead *newp; + + newp = (struct scnhead *) obstack_calloc (&ld_state.smem, + sizeof (struct scnhead)); + newp->kind = kind; + newp->name = name; + newp->nameent = ebl_strtabadd (ld_state.shstrtab, name, 0); + newp->type = type; + newp->flags = flags; + newp->entsize = entsize; + newp->align = align; + newp->grp_signature = NULL; + newp->used = true; + + /* All is well. Create now the data for the section and insert it + into the section table. */ + ld_section_tab_insert (&ld_state.section_tab, elf_hash (name), newp); +} + + +/* Create the sections which are generated by the linker and are not + present in the input file. */ +static void +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 + other information the ELF loader (kernel or wherever) has to look + at. We put it as the first section in the file. + + We also have to create the dynamic segment which is a special + section the dynamic linker locates through an entry in the + program header. */ + if (dynamically_linked_p ()) + { + /* Use any versioning (defined or required)? */ + bool use_versioning = false; + /* Use version requirements? */ + bool need_version = false; + + /* First the .interp section. */ + 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, + DYNAMIC_SECTION_FLAGS (&ld_state), + xelf_fsize (ld_state.outelf, ELF_T_DYN, 1), + xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1)); + + /* We will need in any case the dynamic symbol table (even in + the unlikely case that no symbol is exported or referenced + from a DSO). */ + ld_state.need_dynsym = true; + new_generated_scn (scn_dot_dynsym, ".dynsym", SHT_DYNSYM, SHF_ALLOC, + xelf_fsize (ld_state.outelf, ELF_T_SYM, 1), + xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1)); + /* It comes with a string table. */ + new_generated_scn (scn_dot_dynstr, ".dynstr", SHT_STRTAB, SHF_ALLOC, + 0, 1); + /* And a hashing table. */ + // XXX For Linux/Alpha we need other sizes unless they change... + 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) + { + /* Create the .plt section. */ + /* XXX We might need a function which returns the section flags. */ + new_generated_scn (scn_dot_plt, ".plt", SHT_PROGBITS, + SHF_ALLOC | SHF_EXECINSTR, + /* XXX Is the size correct? */ + xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1), + xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1)); + + /* Create the relocation section for the .plt. This is always + separate even if the other relocation sections are combined. */ + new_generated_scn (scn_dot_pltrel, ".rel.plt", rel_type, SHF_ALLOC, + rel_type == SHT_REL + ? xelf_fsize (ld_state.outelf, ELF_T_REL, 1) + : xelf_fsize (ld_state.outelf, ELF_T_RELA, 1), + xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1)); + + /* 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. */ + if (ld_state.from_dso != NULL) + { + struct symbol *srunp = ld_state.from_dso; + + do + { + srunp->file->used = true; + + if (srunp->file->verdefdata != NULL) + { + XElf_Versym versym; + + /* The input DSO uses versioning. */ + use_versioning = true; + /* We reference versions. */ + need_version = true; + + if (xelf_getversym_copy (srunp->file->versymdata, + srunp->symidx, versym) == NULL) + assert (! "xelf_getversym failed"); + + /* We cannot link explicitly with an older + version of a symbol. */ + assert ((versym & 0x8000) == 0); + /* We cannot reference local (index 0) or plain + global (index 1) versions. */ + assert (versym > 1); + + /* Check whether we have already seen the + version and if not add it to the referenced + versions in the output file. */ + if (! srunp->file->verdefused[versym]) + { + srunp->file->verdefused[versym] = 1; + + if (++srunp->file->nverdefused == 1) + /* Count the file if it is using versioning. */ + ++ld_state.nverdeffile; + ++ld_state.nverdefused; + } + } + } + while ((srunp = srunp->next) != ld_state.from_dso); + } + + /* Create the sections used to record version dependencies. */ + if (need_version) + 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. */ + int ndt_needed = 0; + if (ld_state.ndsofiles > 0) + { + struct usedfiles *frunp = 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) + new_generated_scn (scn_dot_version, ".gnu.version", SHT_GNU_versym, + SHF_ALLOC, + xelf_fsize (ld_state.outelf, ELF_T_HALF, 1), + xelf_fsize (ld_state.outelf, ELF_T_HALF, 1)); + + /* We need some entries all the time. */ + ld_state.ndynamic = (7 + (ld_state.runpath != NULL + || ld_state.rpath != NULL) + + ndt_needed + + (ld_state.init_symbol != NULL ? 1 : 0) + + (ld_state.fini_symbol != NULL ? 1 : 0) + + (use_versioning ? 1 : 0) + + (need_version ? 2 : 0) + + (ld_state.nplt > 0 ? 4 : 0) + + (ld_state.relsize_total > 0 ? 3 : 0)); + } + + /* When creating a relocatable file or when we are not stripping the + output file we create a symbol table. */ + ld_state.need_symtab = (ld_state.file_type == relocatable_file_type + || ld_state.strip == strip_none); + + /* Add the .got section if needed. */ + if (ld_state.need_got) + /* XXX We might need a function which returns the section flags. */ + new_generated_scn (scn_dot_got, ".got", SHT_PROGBITS, + SHF_ALLOC | SHF_WRITE, + xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1), + xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1)); + + /* Add the .rel.dyn section. */ + if (ld_state.relsize_total > 0) + new_generated_scn (scn_dot_dynrel, ".rel.dyn", rel_type, SHF_ALLOC, + rel_type == SHT_REL + ? xelf_fsize (ld_state.outelf, ELF_T_REL, 1) + : xelf_fsize (ld_state.outelf, ELF_T_RELA, 1), + xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1)); +} + + +/* Callback function registered with on_exit to make sure the temporary + files gets removed if something goes wrong. */ +static void +remove_tempfile (int status, void *arg) +{ + if (status != 0 && ld_state.tempfname != NULL) + unlink (ld_state.tempfname); +} + + +/* Create the output file. The file name is given or "a.out". We + create as much of the ELF structure as possible. */ +static int +ld_generic_open_outfile (struct ld_state *statep, int machine, int klass, + int data) +{ + /* We do not create the new file right away with the final name. + This would destroy an existing file with this name before a + replacement is finalized. We create instead a temporary file in + the same directory. */ + if (ld_state.outfname == NULL) + ld_state.outfname = "a.out"; + + size_t outfname_len = strlen (ld_state.outfname); + char *tempfname = (char *) obstack_alloc (&ld_state.smem, + outfname_len + sizeof (".XXXXXX")); + ld_state.tempfname = tempfname; + + int fd; + int try = 0; + while (1) + { + strcpy (mempcpy (tempfname, ld_state.outfname, outfname_len), ".XXXXXX"); + + /* 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. */ + if (mktemp (tempfname) != NULL + && (fd = open (tempfname, O_RDWR | O_EXCL | O_CREAT | O_NOFOLLOW, + ld_state.file_type == relocatable_file_type + ? DEFFILEMODE : ACCESSPERMS)) != -1) + break; + + /* Failed this round. We keep trying a number of times. */ + if (++try >= 10) + error (EXIT_FAILURE, errno, gettext ("cannot create output file")); + } + ld_state.outfd = fd; + + /* Make sure we remove the temporary file in case something goes + wrong. */ + on_exit (remove_tempfile, NULL); + + /* Create the ELF file data for the output file. */ + Elf *elf = ld_state.outelf = elf_begin (fd, + conserve_memory + ? ELF_C_WRITE : ELF_C_WRITE_MMAP, + NULL); + if (elf == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create ELF descriptor for output file: %s"), + elf_errmsg (-1)); + + /* Create the basic data structures. */ + if (! xelf_newehdr (elf, klass)) + /* Couldn't create the ELF header. Very bad. */ + error (EXIT_FAILURE, 0, + gettext ("could not create ELF header for output file: %s"), + elf_errmsg (-1)); + + /* And get the current header so that we can modify it. */ + XElf_Ehdr_vardef (ehdr); + xelf_getehdr (elf, ehdr); + assert (ehdr != NULL); + + /* Set the machine type. */ + ehdr->e_machine = machine; + + /* Modify it according to the info we have here and now. */ + if (ld_state.file_type == executable_file_type) + ehdr->e_type = ET_EXEC; + else if (ld_state.file_type == dso_file_type) + ehdr->e_type = ET_DYN; + else + { + assert (ld_state.file_type == relocatable_file_type); + ehdr->e_type = ET_REL; + } + + /* Set the ELF version. */ + ehdr->e_version = EV_CURRENT; + + /* Set the endianness. */ + ehdr->e_ident[EI_DATA] = data; + + /* Write the ELF header information back. */ + (void) xelf_update_ehdr (elf, ehdr); + + return 0; +} + + +/* We compute the offsets of the various copied objects and the total + size of the memory needed. */ +// XXX The method used here is simple: go from front to back and pack +// the objects in this order. A more space efficient way would +// actually trying to pack the objects as dense as possible. But this +// is more expensive. +static void +compute_copy_reloc_offset (XElf_Shdr *shdr) +{ + struct symbol *runp = ld_state.from_dso; + assert (runp != NULL); + + XElf_Off maxalign = 1; + XElf_Off offset = 0; + + do + if (runp->need_copy) + { + /* Determine alignment for the symbol. */ + // XXX The question is how? The symbol record itself does not + // have the information. So we have to be conservative and + // assume the alignment of the section the symbol is in. + + // XXX We can be more precise. Use the offset from the beginning + // of the section and determine the largest power of two with + // module zero. + XElf_Off symalign = MAX (SCNINFO_SHDR (runp->file->scninfo[runp->scndx].shdr).sh_addralign, 1); + /* Keep track of the maximum alignment requirement. */ + maxalign = MAX (maxalign, symalign); + + /* Align current position. */ + offset = (offset + symalign - 1) & ~(symalign - 1); + + runp->merge.value = offset; + + offset += runp->size; + } + while ((runp = runp->next) != ld_state.from_dso); + + shdr->sh_type = SHT_NOBITS; + shdr->sh_size = offset; + shdr->sh_addralign = maxalign; +} + + +static void +compute_common_symbol_offset (XElf_Shdr *shdr) +{ + struct symbol *runp = ld_state.common_syms; + assert (runp != NULL); + + XElf_Off maxalign = 1; + XElf_Off offset = 0; + + do + { + /* Determine alignment for the symbol. */ + XElf_Off symalign = runp->merge.value; + + /* Keep track of the maximum alignment requirement. */ + maxalign = MAX (maxalign, symalign); + + /* Align current position. */ + offset = (offset + symalign - 1) & ~(symalign - 1); + + runp->merge.value = offset; + + offset += runp->size; + } + while ((runp = runp->next) != ld_state.common_syms); + + shdr->sh_type = SHT_NOBITS; + shdr->sh_size = offset; + shdr->sh_addralign = maxalign; +} + + +static void +sort_sections_generic (void) +{ + /* XXX TBI */ + abort (); +} + + +static int +match_section (const char *osectname, struct filemask_section_name *sectmask, + struct scnhead **scnhead, bool new_section, size_t segment_nr) +{ + struct scninfo *prevp; + struct scninfo *runp; + struct scninfo *notused; + + if (fnmatch (sectmask->section_name->name, (*scnhead)->name, 0) != 0) + /* The section name does not match. */ + return new_section; + + /* If this is a section generated by the linker it doesn't contain + the regular information (i.e., input section data etc) and must + be handle special. */ + if ((*scnhead)->kind != scn_normal) + { + (*scnhead)->name = osectname; + (*scnhead)->segment_nr = segment_nr; + + /* We have to count note section since they get their own + program header entry. */ + if ((*scnhead)->type == SHT_NOTE) + ++ld_state.nnotesections; + + ld_state.allsections[ld_state.nallsections++] = (*scnhead); + return true; + } + + /* Now we have to match the file names of the input files. Some of + the sections here might not match. */ + runp = (*scnhead)->last->next; + prevp = (*scnhead)->last; + notused = NULL; + + do + { + /* Base of the file name the section comes from. */ + 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 + list, ignore the section. */ + if (!runp->used + || (sectmask->filemask != NULL + && fnmatch (sectmask->filemask, brfname, 0) != 0) + || (sectmask->excludemask != NULL + && fnmatch (sectmask->excludemask, brfname, 0) == 0)) + { + /* This file does not match the file name masks. */ + if (notused == NULL) + notused = runp; + + prevp = runp; + runp = runp->next; + if (runp == notused) + runp = NULL; + } + /* The section fulfills all requirements, add it to the output + file with the correct section name etc. */ + else + { + struct scninfo *found = runp; + + /* Remove this input section data buffer from the list. */ + if (prevp != runp) + runp = prevp->next = runp->next; + else + { + free (*scnhead); + *scnhead = NULL; + runp = NULL; + } + + /* Create a new section for the output file if the 'new_section' + flag says so. Otherwise append the buffer to the last + section which we created in one of the last calls. */ + if (new_section) + { + struct scnhead *newp; + + newp = (struct scnhead *) obstack_calloc (&ld_state.smem, + sizeof (*newp)); + newp->kind = scn_normal; + newp->name = osectname; + newp->type = SCNINFO_SHDR (found->shdr).sh_type; + /* 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; + newp->relsize = found->relsize; + newp->entsize = SCNINFO_SHDR (found->shdr).sh_entsize; + + /* We have to count note section since they get their own + program header entry. */ + if (newp->type == SHT_NOTE) + ++ld_state.nnotesections; + + ld_state.allsections[ld_state.nallsections++] = newp; + new_section = false; + } + else + { + struct scnhead *queued; + + queued = ld_state.allsections[ld_state.nallsections - 1]; + + found->next = queued->last->next; + queued->last = queued->last->next = found; + + /* If the linker script forces us to add incompatible + sections together do so. But reflect this in the + type and flags of the resulting file. */ + if (queued->type != SCNINFO_SHDR (found->shdr).sh_type) + /* 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 + & ~SHF_GROUP); + + /* Accumulate the relocation section size. */ + queued->relsize += found->relsize; + } + } + } + while (runp != NULL); + + return new_section; +} + + +static void +sort_sections_lscript (void) +{ + struct scnhead *temp[ld_state.nallsections]; + + /* Make a copy of the section head pointer array. */ + memcpy (temp, ld_state.allsections, + ld_state.nallsections * sizeof (temp[0])); + size_t nallsections = ld_state.nallsections; + + /* Convert the output segment list in a single-linked list. */ + struct output_segment *segment = ld_state.output_segments->next; + ld_state.output_segments->next = NULL; + ld_state.output_segments = segment; + + /* Put the sections in the correct order in the array in the state + structure. This might involve merging of sections and also + renaming the containing section in the output file. */ + ld_state.nallsections = 0; + size_t segment_nr; + size_t last_writable = ~0ul; + for (segment_nr = 0; segment != NULL; segment = segment->next, ++segment_nr) + { + struct output_rule *orule; + + for (orule = segment->output_rules; orule != NULL; orule = orule->next) + if (orule->tag == output_section) + { + struct input_rule *irule; + bool new_section = true; + + for (irule = orule->val.section.input; irule != NULL; + irule = irule->next) + if (irule->tag == input_section) + { + size_t cnt; + + for (cnt = 0; cnt < nallsections; ++cnt) + if (temp[cnt] != NULL) + new_section = + match_section (orule->val.section.name, + irule->val.section, &temp[cnt], + new_section, segment_nr); + } + } + + if ((segment->mode & PF_W) != 0) + last_writable = ld_state.nallsections - 1; + } + + /* In case we have to create copy relocations or we have common + symbols, find the last writable segment and add one more data + block. It will be a NOBITS block and take up no disk space. + This is why it is important to get the last block. */ + if (ld_state.ncopy > 0 || ld_state.common_syms != NULL) + { + if (last_writable == ~0ul) + error (EXIT_FAILURE, 0, "no writable segment"); + + if (ld_state.allsections[last_writable]->type != SHT_NOBITS) + { + /* Make room in the ALLSECTIONS array for a new section. + There is guaranteed room in the array. We add the new + entry after the last writable section. */ + ++last_writable; + memmove (&ld_state.allsections[last_writable + 1], + &ld_state.allsections[last_writable], + (ld_state.nallsections - last_writable) + * sizeof (ld_state.allsections[0])); + + ld_state.allsections[last_writable] = (struct scnhead *) + obstack_calloc (&ld_state.smem, sizeof (struct scnhead)); + + /* Name for the new section. */ + ld_state.allsections[last_writable]->name = ".bss"; + /* Type: NOBITS. */ + ld_state.allsections[last_writable]->type = SHT_NOBITS; + /* Same segment as the last writable section. */ + ld_state.allsections[last_writable]->segment_nr + = ld_state.allsections[last_writable - 1]->segment_nr; + } + } + + /* Create common symbol data block. */ + if (ld_state.ncopy > 0) + { +#if NATIVE_ELF + struct scninfo *si = (struct scninfo *) + obstack_calloc (&ld_state.smem, sizeof (*si) + sizeof (XElf_Shdr)); + si->shdr = (XElf_Shdr *) (si + 1); +#else + struct scninfo *si = (struct scninfo *) obstack_calloc (&ld_state.smem, + sizeof (*si)); +#endif + + /* Get the information regarding the symbols with copy relocations. */ + compute_copy_reloc_offset (&SCNINFO_SHDR (si->shdr)); + + /* This section is needed. */ + si->used = true; + /* Remember for later the section data structure. */ + ld_state.copy_section = si; + + if (likely (ld_state.allsections[last_writable]->last != NULL)) + { + si->next = ld_state.allsections[last_writable]->last->next; + ld_state.allsections[last_writable]->last->next = si; + ld_state.allsections[last_writable]->last = si; + } + else + ld_state.allsections[last_writable]->last = si->next = si; + } + + /* Create common symbol data block. */ + if (ld_state.common_syms != NULL) + { +#if NATIVE_ELF + struct scninfo *si = (struct scninfo *) + obstack_calloc (&ld_state.smem, sizeof (*si) + sizeof (XElf_Shdr)); + si->shdr = (XElf_Shdr *) (si + 1); +#else + struct scninfo *si = (struct scninfo *) obstack_calloc (&ld_state.smem, + sizeof (*si)); +#endif + + /* Get the information regarding the symbols with copy relocations. */ + compute_common_symbol_offset (&SCNINFO_SHDR (si->shdr)); + + /* This section is needed. */ + si->used = true; + /* Remember for later the section data structure. */ + ld_state.common_section = si; + + if (likely (ld_state.allsections[last_writable]->last != NULL)) + { + si->next = ld_state.allsections[last_writable]->last->next; + ld_state.allsections[last_writable]->last->next = si; + ld_state.allsections[last_writable]->last = si; + } + else + ld_state.allsections[last_writable]->last = si->next = si; + } +} + + +/* Create the output sections now. This requires knowledge about all + the sections we will need. It may be necessary to sort sections in + the order they are supposed to appear in the executable. The + sorting use many different kinds of information to optimize the + resulting binary. Important is to respect segment boundaries and + the needed alignment. The mode of the segments will be determined + afterwards automatically by the output routines. + + The generic sorting routines work in one of two possible ways: + + - if a linker script specifies the sections to be used in the + output and assigns them to a segment this information is used; + + - otherwise the linker will order the sections based on permissions + and some special knowledge about section names.*/ +static void +ld_generic_create_sections (struct ld_state *statep) +{ + struct scngroup *groups; + size_t cnt; + + /* For relocatable object we don't have to bother sorting the + sections and we do want to preserve the relocation sections as + they appear in the input files. */ + if (ld_state.file_type != relocatable_file_type) + { + /* Collect all the relocation sections. They are handled + separately. */ + struct scninfo *list = NULL; + for (cnt = 0; cnt < ld_state.nallsections; ++cnt) + if ((ld_state.allsections[cnt]->type == SHT_REL + || ld_state.allsections[cnt]->type == SHT_RELA) + /* The generated relocation sections are not of any + interest here. */ + && ld_state.allsections[cnt]->last != NULL) + { + if (list == NULL) + list = ld_state.allsections[cnt]->last; + else + { + /* Merge the sections list. */ + struct scninfo *first = list->next; + list->next = ld_state.allsections[cnt]->last->next; + ld_state.allsections[cnt]->last->next = first; + list = ld_state.allsections[cnt]->last; + } + + /* Remove the entry from the section list. */ + ld_state.allsections[cnt] = NULL; + } + ld_state.rellist = list; + + if (ld_state.output_segments == NULL) + /* Sort using builtin rules. */ + sort_sections_generic (); + else + sort_sections_lscript (); + } + + /* Now iterate over the input sections and create the sections in the + order they are required in the output file. */ + for (cnt = 0; cnt < ld_state.nallsections; ++cnt) + { + struct scnhead *head = ld_state.allsections[cnt]; + Elf_Scn *scn; + XElf_Shdr_vardef (shdr); + + /* Don't handle unused sections. */ + if (!head->used) + continue; + + /* We first have to create the section group if necessary. + Section group sections must come (in section index order) + before any of the section contained. This all is necessary + only for relocatable object as other object types are not + allowed to contain section groups. */ + if (ld_state.file_type == relocatable_file_type + && unlikely (head->flags & SHF_GROUP)) + { + /* There is at least one section which is contained in a + section group in the input file. This means we must + create a section group here as well. The only problem is + that not all input files have to have to same kind of + partitioning of the sections. I.e., sections A and B in + one input file and sections B and C in another input file + can be in one group. That will result in a group + containing the sections A, B, and C in the output + file. */ + struct scninfo *runp; + Elf32_Word here_groupidx = 0; + struct scngroup *here_group; + struct member *newp; + + /* First check whether any section is already in a group. + In this case we have to add this output section, too. */ + runp = head->last; + do + { + assert (runp->grpid != 0); + + here_groupidx = runp->fileinfo->scninfo[runp->grpid].outscnndx; + if (here_groupidx != 0) + break; + } + while ((runp = runp->next) != head->last); + + if (here_groupidx == 0) + { + /* We need a new section group section. */ + scn = elf_newscn (ld_state.outelf); + xelf_getshdr (scn, shdr); + if (shdr == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create section for output file: %s"), + elf_errmsg (-1)); + + here_group = (struct scngroup *) xmalloc (sizeof (*here_group)); + here_group->outscnidx = here_groupidx = elf_ndxscn (scn); + here_group->nscns = 0; + here_group->member = NULL; + here_group->next = ld_state.groups; + /* Pick a name for the section. To keep it meaningful + we use a name used in the input files. If the + section group in the output file should contain + section which were in section groups of different + names in the input files this is the users + problem. */ + here_group->nameent + = ebl_strtabadd (ld_state.shstrtab, + elf_strptr (runp->fileinfo->elf, + runp->fileinfo->shstrndx, + SCNINFO_SHDR (runp->shdr).sh_name), + 0); + /* Signature symbol. */ + here_group->symbol + = runp->fileinfo->scninfo[runp->grpid].symbols; + + ld_state.groups = here_group; + } + else + { + /* Search for the group with this index. */ + here_group = ld_state.groups; + while (here_group->outscnidx != here_groupidx) + here_group = here_group->next; + } + + /* Add the new output section. */ + newp = (struct member *) alloca (sizeof (*newp)); + newp->scn = head; +#ifndef NDT_NEEDED + newp->next = NULL; +#endif + CSNGL_LIST_ADD_REAR (here_group->member, newp); + ++here_group->nscns; + + /* Store the section group index in all input files. */ + runp = head->last; + do + { + assert (runp->grpid != 0); + + if (runp->fileinfo->scninfo[runp->grpid].outscnndx == 0) + runp->fileinfo->scninfo[runp->grpid].outscnndx = here_groupidx; + else + assert (runp->fileinfo->scninfo[runp->grpid].outscnndx + == here_groupidx); + } + while ((runp = runp->next) != head->last); + } + + /* We'll use this section so get it's name in the section header + string table. */ + if (head->kind == scn_normal) + head->nameent = ebl_strtabadd (ld_state.shstrtab, head->name, 0); + + /* Create a new section in the output file and add all data + from all the sections we read. */ + scn = elf_newscn (ld_state.outelf); + head->scnidx = elf_ndxscn (scn); + xelf_getshdr (scn, shdr); + if (shdr == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create section for output file: %s"), + elf_errmsg (-1)); + + assert (head->type != SHT_NULL); + assert (head->type != SHT_SYMTAB); + assert (head->type != SHT_DYNSYM || head->kind != scn_normal); + assert (head->type != SHT_STRTAB || head->kind != scn_normal); + assert (head->type != SHT_GROUP); + shdr->sh_type = head->type; + shdr->sh_flags = head->flags; + shdr->sh_addralign = head->align; + shdr->sh_entsize = head->entsize; + assert (shdr->sh_entsize != 0 || (shdr->sh_flags & SHF_MERGE) == 0); + (void) xelf_update_shdr (scn, shdr); + + /* We have to know the section index of the dynamic symbol table + right away. */ + if (head->kind == scn_dot_dynsym) + ld_state.dynsymscnidx = elf_ndxscn (scn); + } + + /* Actually create the section group sections. */ + groups = ld_state.groups; + while (groups != NULL) + { + Elf_Scn *scn; + Elf_Data *data; + Elf32_Word *grpdata; + struct member *runp; + + scn = elf_getscn (ld_state.outelf, groups->outscnidx); + assert (scn != NULL); + + data = elf_newdata (scn); + if (data == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create section for output file: %s"), + elf_errmsg (-1)); + + data->d_size = (groups->nscns + 1) * sizeof (Elf32_Word); + data->d_buf = grpdata = (Elf32_Word *) xmalloc (data->d_size); + data->d_type = ELF_T_WORD; + data->d_version = EV_CURRENT; + data->d_off = 0; + /* XXX What better to use? */ + data->d_align = sizeof (Elf32_Word); + + /* The first word in the section is the flag word. */ + /* XXX Set COMDATA flag is necessary. */ + grpdata[0] = 0; + + runp = groups->member->next; + cnt = 1; + do + /* Fill in the index of the section. */ + grpdata[cnt++] = runp->scn->scnidx; + while ((runp = runp->next) != groups->member->next); + + groups = groups->next; + } +} + + +static bool +reduce_symbol_p (XElf_Sym *sym, struct Ebl_Strent *strent) +{ + const char *str; + const char *version; + struct id_list search; + struct id_list *verp; + bool result = ld_state.default_bind_local; + + if (XELF_ST_BIND (sym->st_info) == STB_LOCAL || sym->st_shndx == SHN_UNDEF) + /* We don't have to do anything to local symbols here. */ + /* XXX Any section value in [SHN_LORESERVER,SHN_XINDEX) need + special treatment? */ + return false; + + /* XXX Handle other symbol bindings. */ + assert (XELF_ST_BIND (sym->st_info) == STB_GLOBAL + || XELF_ST_BIND (sym->st_info) == STB_WEAK); + + str = ebl_string (strent); + version = strchr (str, VER_CHR); + if (version != NULL) + { + search.id = strndupa (str, version - str); + if (*++version == VER_CHR) + /* Skip the second '@' signaling a default definition. */ + ++version; + } + else + { + search.id = str; + version = ""; + } + + verp = ld_version_str_tab_find (&ld_state.version_str_tab, + elf_hash (search.id), &search); + while (verp != NULL) + { + /* We have this symbol in the version hash table. Now match the + version name. */ + if (strcmp (verp->u.s.versionname, version) == 0) + /* Match! */ + return verp->u.s.local; + + verp = verp->next; + } + + /* XXX Add test for wildcard version symbols. */ + + return result; +} + + +static XElf_Addr +eval_expression (struct expression *expr, XElf_Addr addr) +{ + XElf_Addr val = ~((XElf_Addr) 0); + + switch (expr->tag) + { + case exp_num: + val = expr->val.num; + break; + + case exp_sizeof_headers: + { + /* The 'elf_update' call determine the offset of the first + section. The the size of the header. */ + XElf_Shdr_vardef (shdr); + + xelf_getshdr (elf_getscn (ld_state.outelf, 1), shdr); + assert (shdr != NULL); + + val = shdr->sh_offset; + } + break; + + case exp_pagesize: + val = ld_state.pagesize; + break; + + case exp_id: + /* We are here computing only address expressions. It seems not + to be necessary to handle any variable but ".". Let's avoid + the complication. If it turns up to be needed we can add + it. */ + if (strcmp (expr->val.str, ".") != 0) + error (EXIT_FAILURE, 0, gettext ("\ +address computation expression contains variable '%s'"), + expr->val.str); + + val = addr; + break; + + case exp_mult: + val = (eval_expression (expr->val.binary.left, addr) + * eval_expression (expr->val.binary.right, addr)); + break; + + case exp_div: + val = (eval_expression (expr->val.binary.left, addr) + / eval_expression (expr->val.binary.right, addr)); + break; + + case exp_mod: + val = (eval_expression (expr->val.binary.left, addr) + % eval_expression (expr->val.binary.right, addr)); + break; + + case exp_plus: + val = (eval_expression (expr->val.binary.left, addr) + + eval_expression (expr->val.binary.right, addr)); + break; + + case exp_minus: + val = (eval_expression (expr->val.binary.left, addr) + - eval_expression (expr->val.binary.right, addr)); + break; + + case exp_and: + val = (eval_expression (expr->val.binary.left, addr) + & eval_expression (expr->val.binary.right, addr)); + break; + + case exp_or: + val = (eval_expression (expr->val.binary.left, addr) + | eval_expression (expr->val.binary.right, addr)); + break; + + case exp_align: + val = eval_expression (expr->val.child, addr); + if ((val & (val - 1)) != 0) + error (EXIT_FAILURE, 0, gettext ("argument '%" PRIuMAX "' of ALIGN in address computation expression is no power of two"), + (uintmax_t) val); + val = (addr + val - 1) & ~(val - 1); + break; + } + + return val; +} + + +/* Find a good as possible size for the hash table so that all the + non-zero entries in HASHCODES don't collide too much and the table + isn't too large. There is no exact formular for this so we use a + heuristic. Depending on the optimization level the search is + longer or shorter. */ +static size_t +optimal_bucket_size (Elf32_Word *hashcodes, size_t maxcnt, int optlevel) +{ + size_t minsize; + size_t maxsize; + size_t bestsize; + uint64_t bestcost; + size_t size; + uint32_t *counts; + uint32_t *lengths; + + if (maxcnt == 0) + return 0; + + /* When we are not optimizing we run only very few tests. */ + if (optlevel <= 0) + { + minsize = maxcnt; + maxsize = maxcnt + 10000 / maxcnt; + } + else + { + /* Does not make much sense to start with a smaller table than + one which has at least four collisions. */ + minsize = MAX (1, maxcnt / 4); + /* We look for a best fit in the range of up to eigth times the + number of elements. */ + maxsize = 2 * maxcnt + (6 * MIN (optlevel, 100) * maxcnt) / 100; + } + bestsize = maxcnt; + bestcost = UINT_MAX; + + /* Array for counting the collisions and chain lengths. */ + counts = (uint32_t *) xmalloc ((maxcnt + 1 + maxsize) * sizeof (uint32_t)); + lengths = &counts[maxcnt + 1]; + + for (size = minsize; size <= maxsize; ++size) + { + size_t inner; + uint64_t cost; + uint32_t maxlength; + uint64_t success; + uint32_t acc; + double factor; + + memset (lengths, '\0', size * sizeof (uint32_t)); + memset (counts, '\0', (maxcnt + 1) * sizeof (uint32_t)); + + /* Determine how often each hash bucket is used. */ + assert (hashcodes[0] == 0); + for (inner = 1; inner < maxcnt; ++inner) + ++lengths[hashcodes[inner] % size]; + + /* Determine the lengths. */ + maxlength = 0; + for (inner = 0; inner < size; ++inner) + { + ++counts[lengths[inner]]; + + if (lengths[inner] > maxlength) + maxlength = lengths[inner]; + } + + /* Determine successful lookup length. */ + acc = 0; + success = 0; + for (inner = 0; inner <= maxlength; ++inner) + { + acc += inner; + success += counts[inner] * acc; + } + + /* We can compute two factors now: the average length of a + positive search and the average length of a negative search. + We count the number of comparisons which have to look at the + names themselves. Recognizing that the chain ended is not + accounted for since it's almost for free. + + Which lookup is more important depends on the kind of DSO. + If it is a system DSO like libc it is expected that most + lookups succeed. Otherwise most lookups fail. */ + if (ld_state.is_system_library) + factor = (1.0 * (double) success / (double) maxcnt + + 0.3 * (double) maxcnt / (double) size); + else + factor = (0.3 * (double) success / (double) maxcnt + + 1.0 * (double) maxcnt / (double) size); + + /* Combine the lookup cost factor. The 1/16th addend adds + penalties for too large table sizes. */ + cost = (2 + maxcnt + size) * (factor + 1.0 / 16.0); + +#if 0 + printf ("maxcnt = %d, size = %d, cost = %Ld, success = %g, fail = %g, factor = %g\n", + maxcnt, size, cost, (double) success / (double) maxcnt, (double) maxcnt / (double) size, factor); +#endif + + /* Compare with current best results. */ + if (cost < bestcost) + { + bestcost = cost; + bestsize = size; + } + } + + free (counts); + + return bestsize; +} + + +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) +{ + XElf_Addr result; + + if (ld_state.entry != NULL) + { + struct symbol search = { .name = ld_state.entry }; + struct symbol *syment; + + syment = ld_symbol_tab_find (&ld_state.symbol_tab, + elf_hash (ld_state.entry), &search); + if (syment != NULL && syment->defined) + { + /* We found the symbol. */ + Elf_Data *data = elf_getdata (elf_getscn (ld_state.outelf, + ld_state.symscnidx), NULL); + + XElf_Sym_vardef (sym); + + sym = NULL; + if (data != NULL) + xelf_getsym (data, ld_state.dblindirect[syment->outsymidx], sym); + + if (sym == NULL && ld_state.need_dynsym && syment->outdynsymidx != 0) + { + /* Use the dynamic symbol table if available. */ + data = elf_getdata (elf_getscn (ld_state.outelf, + ld_state.dynsymscnidx), NULL); + + sym = NULL; + if (data != NULL) + xelf_getsym (data, syment->outdynsymidx, sym); + } + + if (sym != NULL) + return sym->st_value; + + /* XXX What to do if the output has no non-dynamic symbol + table and the dynamic symbol table does not contain the + symbol? */ + assert (ld_state.need_symtab); + assert (ld_state.symscnidx != 0); + } + } + + /* We couldn't find the symbol or none was given. Use the first + address of the ".text" section then. */ + + + result = 0; + + /* In DSOs this is no fatal error. They usually have no entry + points. In this case we set the entry point to zero, which makes + sure it will always fail. */ + if (ld_state.file_type == executable_file_type) + { + if (ld_state.entry != NULL) + error (0, 0, gettext ("\ +cannot find entry symbol '%s': defaulting to %#0*" PRIx64), + ld_state.entry, + xelf_getclass (ld_state.outelf) == ELFCLASS32 ? 10 : 18, + (uint64_t) result); + else + error (0, 0, gettext ("\ +no entry symbol specified: defaulting to %#0*" PRIx64), + xelf_getclass (ld_state.outelf) == ELFCLASS32 ? 10 : 18, + (uint64_t) result); + } + + return result; +} + + +static void +fillin_special_symbol (struct symbol *symst, size_t scnidx, size_t nsym, + Elf_Data *symdata, struct Ebl_Strtab *strtab) +{ + assert (ld_state.file_type != relocatable_file_type); + + XElf_Sym_vardef (sym); + xelf_getsym_ptr (symdata, nsym, sym); + + /* The name offset will be filled in later. */ + sym->st_name = 0; + /* Traditionally: globally visible. */ + 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. */ + assert (scnidx != 0); + assert (scnidx < SHN_LORESERVE || scnidx == SHN_ABS); + 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) + { + Elf_Data *data = elf_getdata (elf_getscn (ld_state.outelf, scnidx), + NULL); + assert (data != NULL); + sym->st_size = data->d_size; + /* Make sure there is no second data block. */ + assert (elf_getdata (elf_getscn (ld_state.outelf, scnidx), data) + == NULL); + } + + /* Insert symbol into the symbol table. Note that we do not have to + use xelf_update_symshdx. */ + (void) xelf_update_sym (symdata, nsym, sym); + + /* Cross-references. */ + ndxtosym[nsym] = symst; + symst->outsymidx = nsym; + + /* Add the name to the string table. */ + symstrent[nsym] = ebl_strtabadd (strtab, symst->name, 0); +} + + +static void +new_dynamic_entry (Elf_Data *data, int idx, XElf_Sxword tag, XElf_Addr val) +{ + XElf_Dyn_vardef (dyn); + xelf_getdyn_ptr (data, idx, dyn); + dyn->d_tag = tag; + dyn->d_un.d_ptr = val; + (void) xelf_update_dyn (data, idx, dyn); +} + + +static void +allocate_version_names (struct usedfiles *runp, struct Ebl_Strtab *dynstrtab) +{ + /* If this DSO has no versions skip it. */ + if (runp->status != opened || runp->verdefdata == NULL) + return; + + /* Add the object name. */ + int offset = 0; + while (1) + { + XElf_Verdef_vardef (def); + XElf_Verdaux_vardef (aux); + + /* Get data at the next offset. */ + xelf_getverdef (runp->verdefdata, offset, def); + assert (def != NULL); + xelf_getverdaux (runp->verdefdata, offset + def->vd_aux, aux); + assert (aux != NULL); + + assert (def->vd_ndx <= runp->nverdef); + if (def->vd_ndx == 1 || runp->verdefused[def->vd_ndx] != 0) + { + runp->verdefent[def->vd_ndx] + = ebl_strtabadd (dynstrtab, elf_strptr (runp->elf, + runp->dynsymstridx, + aux->vda_name), 0); + + if (def->vd_ndx > 1) + runp->verdefused[def->vd_ndx] = ld_state.nextveridx++; + } + + if (def->vd_next == 0) + /* That were all versions. */ + break; + + offset += def->vd_next; + } +} + + +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); + + 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]; + + 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_getshdrstrndx (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'); + } +} + + +/* Create the output file. + + For relocatable files what basically has to happen is that all + sections from all input files are written into the output file. + Sections with the same name are combined (offsets adjusted + accordingly). The symbol tables are combined in one single table. + When stripping certain symbol table entries are omitted. + + 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. */ +static int +ld_generic_create_outfile (struct ld_state *statep) +{ + struct scnlist + { + size_t scnidx; + struct scninfo *scninfo; + struct scnlist *next; + }; + struct scnlist *rellist = NULL; + size_t cnt; + Elf_Scn *symscn = NULL; + Elf_Scn *xndxscn = NULL; + Elf_Scn *strscn = NULL; + struct Ebl_Strtab *strtab = NULL; + struct Ebl_Strtab *dynstrtab = NULL; + XElf_Shdr_vardef (shdr); + Elf_Data *data; + Elf_Data *symdata = NULL; + Elf_Data *xndxdata = NULL; + struct usedfiles *file; + size_t nsym; + size_t nsym_local; + size_t nsym_allocated; + size_t nsym_dyn = 0; + Elf32_Word *dblindirect = NULL; +#ifndef NDEBUG + bool need_xndx; +#endif + Elf_Scn *shstrtab_scn; + size_t shstrtab_ndx; + XElf_Ehdr_vardef (ehdr); + struct Ebl_Strent *symtab_ent = NULL; + struct Ebl_Strent *xndx_ent = NULL; + struct Ebl_Strent *strtab_ent = NULL; + struct Ebl_Strent *shstrtab_ent; + struct scngroup *groups; + Elf_Scn *dynsymscn = NULL; + 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; + + if (ld_state.need_symtab) + { + /* First create the symbol table. We need the symbol section itself + and the string table for it. */ + symscn = elf_newscn (ld_state.outelf); + ld_state.symscnidx = elf_ndxscn (symscn); + symdata = elf_newdata (symscn); + if (symdata == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create symbol table for output file: %s"), + elf_errmsg (-1)); + + symdata->d_type = ELF_T_SYM; + /* This is an estimated size, but it will definitely cap the real value. + We might have to adjust the number later. */ + nsym_allocated = (1 + ld_state.nsymtab + ld_state.nplt + ld_state.ngot + + ld_state.nusedsections + ld_state.nlscript_syms); + symdata->d_size = xelf_fsize (ld_state.outelf, ELF_T_SYM, + nsym_allocated); + + /* Optionally the extended section table. */ + /* XXX Is SHN_LORESERVE correct? Do we need some other sections? */ + if (unlikely (ld_state.nusedsections >= SHN_LORESERVE)) + { + xndxscn = elf_newscn (ld_state.outelf); + ld_state.xndxscnidx = elf_ndxscn (xndxscn); + + xndxdata = elf_newdata (xndxscn); + if (xndxdata == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create symbol table for output file: %s"), + elf_errmsg (-1)); + + /* The following relies on the fact that Elf32_Word and Elf64_Word + have the same size. */ + xndxdata->d_type = ELF_T_WORD; + /* This is an estimated size, but it will definitely cap the + real value. we might have to adjust the number later. */ + xndxdata->d_size = xelf_fsize (ld_state.outelf, ELF_T_WORD, + nsym_allocated); + /* The first entry is left empty, clear it here and now. */ + xndxdata->d_buf = memset (xmalloc (xndxdata->d_size), '\0', + xelf_fsize (ld_state.outelf, ELF_T_WORD, + 1)); + xndxdata->d_off = 0; + /* XXX Should use an ebl function. */ + xndxdata->d_align = sizeof (Elf32_Word); + } + } + else + { + assert (ld_state.need_dynsym); + + /* First create the symbol table. We need the symbol section itself + and the string table for it. */ + symscn = elf_getscn (ld_state.outelf, ld_state.dynsymscnidx); + symdata = elf_newdata (symscn); + if (symdata == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create symbol table for output file: %s"), + elf_errmsg (-1)); + + symdata->d_version = EV_CURRENT; + symdata->d_type = ELF_T_SYM; + /* This is an estimated size, but it will definitely cap the real value. + We might have to adjust the number later. */ + nsym_allocated = (1 + ld_state.nsymtab + ld_state.nplt + ld_state.ngot + - ld_state.nlocalsymbols + ld_state.nlscript_syms); + symdata->d_size = xelf_fsize (ld_state.outelf, ELF_T_SYM, + nsym_allocated); + } + + /* The first entry is left empty, clear it here and now. */ + symdata->d_buf = memset (xmalloc (symdata->d_size), '\0', + xelf_fsize (ld_state.outelf, ELF_T_SYM, 1)); + symdata->d_off = 0; + /* XXX This is ugly but how else can it be done. */ + symdata->d_align = xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1); + + /* Allocate another array to keep track of the handles for the symbol + names. */ + symstrent = (struct Ebl_Strent **) xcalloc (nsym_allocated, + sizeof (struct Ebl_Strent *)); + + /* By starting at 1 we effectively add a null entry. */ + nsym = 1; + + /* Iteration over all sections. */ + for (cnt = 0; cnt < ld_state.nallsections; ++cnt) + { + struct scnhead *head = ld_state.allsections[cnt]; + Elf_Scn *scn; + struct scninfo *runp; + XElf_Off offset; + Elf32_Word xndx; + + /* Don't handle unused sections at all. */ + if (!head->used) + continue; + + /* Get the section handle. */ + scn = elf_getscn (ld_state.outelf, head->scnidx); + + if (unlikely (head->kind == scn_dot_interp)) + { + Elf_Data *outdata = elf_newdata (scn); + if (outdata == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create section for output file: %s"), + elf_errmsg (-1)); + + /* This is the string we'll put in the section. */ + const char *interp = ld_state.interp ?: "/lib/ld.so.1"; + + /* Create the section data. */ + outdata->d_buf = (void *) interp; + outdata->d_size = strlen (interp) + 1; + outdata->d_type = ELF_T_BYTE; + outdata->d_off = 0; + outdata->d_align = 1; + outdata->d_version = EV_CURRENT; + + /* Remember the index of this section. */ + ld_state.interpscnidx = head->scnidx; + + continue; + } + + if (unlikely (head->kind == scn_dot_got)) + { + /* Remember the index of this section. */ + ld_state.gotscnidx = elf_ndxscn (scn); + + /* Give the backend the change to initialize the section. */ + INITIALIZE_GOT (&ld_state, scn); + + 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; + + outdata = elf_newdata (scn); + if (outdata == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create section for output file: %s"), + elf_errmsg (-1)); + + outdata->d_size = ld_state.relsize_total; + outdata->d_buf = xmalloc (outdata->d_size); + outdata->d_type = (REL_TYPE (&ld_state) == DT_REL + ? ELF_T_REL : ELF_T_RELA); + outdata->d_off = 0; + outdata->d_align = xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1); + + /* Remember the index of this section. */ + ld_state.reldynscnidx = elf_ndxscn (scn); + + continue; + } + + if (unlikely (head->kind == scn_dot_dynamic)) + { + /* Only create the data for now. */ + Elf_Data *outdata; + + /* Account for a few more entries we have to add. */ + if (ld_state.dt_flags != 0) + ++ld_state.ndynamic; + if (ld_state.dt_flags_1 != 0) + ++ld_state.ndynamic; + if (ld_state.dt_feature_1 != 0) + ++ld_state.ndynamic; + + outdata = elf_newdata (scn); + if (outdata == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create section for output file: %s"), + elf_errmsg (-1)); + + /* Create the section data. */ + outdata->d_size = xelf_fsize (ld_state.outelf, ELF_T_DYN, + ld_state.ndynamic); + outdata->d_buf = xcalloc (1, outdata->d_size); + outdata->d_type = ELF_T_DYN; + outdata->d_off = 0; + outdata->d_align = xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1); + + /* Remember the index of this section. */ + ld_state.dynamicscnidx = elf_ndxscn (scn); + + continue; + } + + if (unlikely (head->kind == scn_dot_dynsym)) + { + /* We already know the section index. */ + assert (ld_state.dynsymscnidx == elf_ndxscn (scn)); + + continue; + } + + if (unlikely (head->kind == scn_dot_dynstr)) + { + /* Remember the index of this section. */ + ld_state.dynstrscnidx = elf_ndxscn (scn); + + /* Create the string table. */ + dynstrtab = ebl_strtabinit (true); + + /* XXX TBI + We have to add all the strings which are needed in the + dynamic section here. This means DT_FILTER, + DT_AUXILIARY, ... entries. */ + if (ld_state.ndsofiles > 0) + { + struct usedfiles *frunp = ld_state.dsofiles; + + do + if (! frunp->as_needed || frunp->used) + frunp->sonameent = ebl_strtabadd (dynstrtab, frunp->soname, + 0); + while ((frunp = frunp->next) != ld_state.dsofiles); + } + + + /* Add the runtime path information. The strings are stored + in the .dynstr section. If both rpath and runpath are defined + the runpath information is used. */ + if (ld_state.runpath != NULL || ld_state.rpath != NULL) + { + struct pathelement *startp; + struct pathelement *prunp; + int tag; + size_t len; + char *str; + char *cp; + + if (ld_state.runpath != NULL) + { + startp = ld_state.runpath; + tag = DT_RUNPATH; + } + else + { + startp = ld_state.rpath; + tag = DT_RPATH; + } + + /* Determine how long the string will be. */ + for (len = 0, prunp = startp; prunp != NULL; prunp = prunp->next) + len += strlen (prunp->pname) + 1; + + cp = str = (char *) obstack_alloc (&ld_state.smem, len); + /* Copy the string. */ + for (prunp = startp; prunp != NULL; prunp = prunp->next) + { + cp = stpcpy (cp, prunp->pname); + *cp++ = ':'; + } + /* Remove the last colon. */ + cp[-1] = '\0'; + + /* Remember the values until we can generate the dynamic + section. */ + ld_state.rxxpath_strent = ebl_strtabadd (dynstrtab, str, len); + ld_state.rxxpath_tag = tag; + } + + continue; + } + + if (unlikely (head->kind == scn_dot_hash)) + { + /* Remember the index of this section. */ + ld_state.hashscnidx = elf_ndxscn (scn); + + 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. */ + ld_state.pltscnidx = elf_ndxscn (scn); + + /* Give the backend the change to initialize the section. */ + INITIALIZE_PLT (&ld_state, scn); + + continue; + } + + if (unlikely (head->kind == scn_dot_pltrel)) + { + /* Remember the index of this section. */ + ld_state.pltrelscnidx = elf_ndxscn (scn); + + /* Give the backend the change to initialize the section. */ + INITIALIZE_PLTREL (&ld_state, scn); + + continue; + } + + if (unlikely (head->kind == scn_dot_version)) + { + /* Remember the index of this section. */ + ld_state.versymscnidx = elf_ndxscn (scn); + + continue; + } + + if (unlikely (head->kind == scn_dot_version_r)) + { + /* Remember the index of this section. */ + ld_state.verneedscnidx = elf_ndxscn (scn); + + 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); + + /* Create an STT_SECTION entry in the symbol table. But not for + the symbolic symbol table. */ + if (ld_state.need_symtab) + { + /* XXX Can we be cleverer and do this only if needed? */ + XElf_Sym_vardef (sym); + + /* Optimization ahead: in the native linker we get a pointer + to the final location so that the following code writes + directly in the correct place. Otherwise we write into + the local variable first. */ + xelf_getsym_ptr (symdata, nsym, sym); + + /* Usual section symbol: local, no specific information, + except the section index. The offset here is zero, the + start address will later be added. */ + sym->st_name = 0; + sym->st_info = XELF_ST_INFO (STB_LOCAL, STT_SECTION); + sym->st_other = 0; + sym->st_value = 0; + sym->st_size = 0; + /* In relocatable files the section index can be too big for + the ElfXX_Sym struct. we have to deal with the extended + symbol table. */ + if (likely (head->scnidx < SHN_LORESERVE)) + { + sym->st_shndx = head->scnidx; + xndx = 0; + } + else + { + sym->st_shndx = SHN_XINDEX; + xndx = head->scnidx; + } + /* Commit the change. See the optimization above, this does + not change the symbol table entry. But the extended + section index table entry is always written, if there is + such a table. */ + assert (nsym < nsym_allocated); + xelf_update_symshndx (symdata, xndxdata, nsym, sym, xndx, 0); + + /* Remember the symbol's index in the symbol table. */ + head->scnsymidx = nsym++; + } + + if (head->type == SHT_REL || head->type == SHT_RELA) + { + /* Remember that we have to fill in the symbol table section + index. */ + if (ld_state.file_type == relocatable_file_type) + { + struct scnlist *newp; + + newp = (struct scnlist *) alloca (sizeof (*newp)); + newp->scnidx = head->scnidx; + newp->scninfo = head->last->next; +#ifndef NDEBUG + newp->next = NULL; +#endif + SNGL_LIST_PUSH (rellist, newp); + } + else + { + /* When we create an executable or a DSO we don't simply + copy the existing relocations. Instead many will be + resolved, others will be converted. Create a data buffer + large enough to contain the contents which we will fill + in later. */ + int type = head->type == SHT_REL ? ELF_T_REL : ELF_T_RELA; + + data = elf_newdata (scn); + if (data == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create section for output file: %s"), + elf_errmsg (-1)); + + data->d_size = xelf_fsize (ld_state.outelf, type, head->relsize); + data->d_buf = xcalloc (data->d_size, 1); + data->d_type = type; + data->d_align = xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1); + data->d_off = 0; + + continue; + } + } + + /* Recognize string and merge flag and handle them. */ + if (head->flags & SHF_MERGE) + { + /* We merge the contents of the sections. For this we do + not look at the contents of section directly. Instead we + look at the symbols of the section. */ + Elf_Data *outdata; + + /* Concatenate the lists of symbols for all sections. + + XXX In case any input section has no symbols associated + (this happens for debug sections) we cannot use this + method. Implement parsing the other debug sections and + find the string pointers. For now we don't merge. */ + runp = head->last->next; + if (runp->symbols == NULL) + { + head->flags &= ~SHF_MERGE; + goto no_merge; + } + head->symbols = runp->symbols; + + while ((runp = runp->next) != head->last->next) + { + if (runp->symbols == NULL) + { + head->flags &= ~SHF_MERGE; + head->symbols = NULL; + goto no_merge; + } + + struct symbol *oldhead = head->symbols->next_in_scn; + + head->symbols->next_in_scn = runp->symbols->next_in_scn; + runp->symbols->next_in_scn = oldhead; + head->symbols = runp->symbols; + } + + /* Create the output section. */ + outdata = elf_newdata (scn); + if (outdata == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create section for output file: %s"), + elf_errmsg (-1)); + + /* We use different merging algorithms for performance + reasons. We can easily handle single-byte and + wchar_t-wide character strings. All other cases (which + really should happen in real life) are handled by the + generic code. */ + if (SCNINFO_SHDR (head->last->shdr).sh_entsize == 1 + && (head->flags & SHF_STRINGS)) + { + /* Simple, single-byte string matching. */ + struct Ebl_Strtab *mergestrtab; + struct symbol *symrunp; + Elf_Data *locsymdata = NULL; + Elf_Data *locdata = NULL; + + mergestrtab = ebl_strtabinit (false); + + symrunp = head->symbols->next_in_scn; + file = NULL; + do + { + /* Accelarate the loop. We cache the file + information since it might very well be the case + that the previous entry was from the same + file. */ + if (symrunp->file != file) + { + /* Remember the file. */ + file = symrunp->file; + /* Symbol table data from that file. */ + locsymdata = file->symtabdata; + /* String section data. */ + locdata = elf_rawdata (file->scninfo[symrunp->scndx].scn, + NULL); + assert (locdata != NULL); + /* While we are at it, remember the output + section. If we don't access the string data + section the section won't be in the output + file. So it is sufficient to do the work + here. */ + file->scninfo[symrunp->scndx].outscnndx = head->scnidx; + } + + /* Get the symbol information. This provides us the + offset into the string data section. */ + XElf_Sym_vardef (sym); + xelf_getsym (locsymdata, symrunp->symidx, sym); + assert (sym != NULL); + + /* Get the data from the file. Note that we access + the raw section data; no endian-ness issues with + single-byte strings. */ + symrunp->merge.handle + = ebl_strtabadd (mergestrtab, + (char *) locdata->d_buf + sym->st_value, + 0); + } + while ((symrunp = symrunp->next_in_scn) + != head->symbols->next_in_scn); + + /* All strings have been added. Create the final table. */ + ebl_strtabfinalize (mergestrtab, outdata); + + /* Compute the final offsets in the section. */ + symrunp = runp->symbols; + do + { + symrunp->merge.value + = ebl_strtaboffset (symrunp->merge.handle); + symrunp->merged = 1; + } + while ((symrunp = symrunp->next_in_scn) != runp->symbols); + + /* We don't need the string table anymore. */ + ebl_strtabfree (mergestrtab); + } + else if (likely (SCNINFO_SHDR (head->last->shdr).sh_entsize + == sizeof (wchar_t)) + && likely (head->flags & SHF_STRINGS)) + { + /* Simple, wchar_t string merging. */ + struct Ebl_WStrtab *mergestrtab; + struct symbol *symrunp; + Elf_Data *locsymdata = NULL; + Elf_Data *locdata = NULL; + + mergestrtab = ebl_wstrtabinit (false); + + symrunp = runp->symbols; + file = NULL; + do + { + /* Accelarate the loop. We cache the file + information since it might very well be the case + that the previous entry was from the same + file. */ + if (symrunp->file != file) + { + /* Remember the file. */ + file = symrunp->file; + /* Symbol table data from that file. */ + locsymdata = file->symtabdata; + /* String section data. */ + locdata = elf_rawdata (file->scninfo[symrunp->scndx].scn, + NULL); + assert (locdata != NULL); + + /* While we are at it, remember the output + section. If we don't access the string data + section the section won't be in the output + file. So it is sufficient to do the work + here. */ + file->scninfo[symrunp->scndx].outscnndx = head->scnidx; + } + + /* Get the symbol information. This provides us the + offset into the string data section. */ + XElf_Sym_vardef (sym); + xelf_getsym (locsymdata, symrunp->symidx, sym); + assert (sym != NULL); + + /* Get the data from the file. Using the raw + section data here is possible since we don't + interpret the string themselves except for + looking for the wide NUL character. The NUL + character has fortunately the same representation + regardless of the byte order. */ + symrunp->merge.handle + = ebl_wstrtabadd (mergestrtab, + (wchar_t *) ((char *) locdata->d_buf + + sym->st_value), 0); + } + while ((symrunp = symrunp->next_in_scn) != runp->symbols); + + /* All strings have been added. Create the final table. */ + ebl_wstrtabfinalize (mergestrtab, outdata); + + /* Compute the final offsets in the section. */ + symrunp = runp->symbols; + do + { + symrunp->merge.value + = ebl_wstrtaboffset (symrunp->merge.handle); + symrunp->merged = 1; + } + while ((symrunp = symrunp->next_in_scn) != runp->symbols); + + /* We don't need the string table anymore. */ + ebl_wstrtabfree (mergestrtab); + } + else + { + /* Non-standard merging. */ + struct Ebl_GStrtab *mergestrtab; + struct symbol *symrunp; + Elf_Data *locsymdata = NULL; + Elf_Data *locdata = NULL; + /* If this is no string section the length of each "string" + is always one. */ + unsigned int len = (head->flags & SHF_STRINGS) ? 0 : 1; + + /* This is the generic string table functionality. Much + slower than the specialized code. */ + mergestrtab + = ebl_gstrtabinit (SCNINFO_SHDR (head->last->shdr).sh_entsize, + false); + + symrunp = runp->symbols; + file = NULL; + do + { + /* Accelarate the loop. We cache the file + information since it might very well be the case + that the previous entry was from the same + file. */ + if (symrunp->file != file) + { + /* Remember the file. */ + file = symrunp->file; + /* Symbol table data from that file. */ + locsymdata = file->symtabdata; + /* String section data. */ + locdata = elf_rawdata (file->scninfo[symrunp->scndx].scn, + NULL); + assert (locdata != NULL); + + /* While we are at it, remember the output + section. If we don't access the string data + section the section won't be in the output + file. So it is sufficient to do the work + here. */ + file->scninfo[symrunp->scndx].outscnndx = head->scnidx; + } + + /* Get the symbol information. This provides us the + offset into the string data section. */ + XElf_Sym_vardef (sym); + xelf_getsym (locsymdata, symrunp->symidx, sym); + assert (sym != NULL); + + /* Get the data from the file. Using the raw + section data here is possible since we don't + interpret the string themselves except for + looking for the wide NUL character. The NUL + character has fortunately the same representation + regardless of the byte order. */ + symrunp->merge.handle + = ebl_gstrtabadd (mergestrtab, + (char *) locdata->d_buf + sym->st_value, + len); + } + while ((symrunp = symrunp->next_in_scn) != runp->symbols); + + /* Create the final table. */ + ebl_gstrtabfinalize (mergestrtab, outdata); + + /* Compute the final offsets in the section. */ + symrunp = runp->symbols; + do + { + symrunp->merge.value + = ebl_gstrtaboffset (symrunp->merge.handle); + symrunp->merged = 1; + } + while ((symrunp = symrunp->next_in_scn) != runp->symbols); + + /* We don't need the string table anymore. */ + ebl_gstrtabfree (mergestrtab); + } + } + else + { + no_merge: + assert (head->scnidx == elf_ndxscn (scn)); + + /* It is important to start with the first list entry (and + not just any one) to add the sections in the correct + order. */ + runp = head->last->next; + offset = 0; + do + { + Elf_Data *outdata = elf_newdata (scn); + if (outdata == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create section for output file: %s"), + elf_errmsg (-1)); + + /* Exceptional case: if we synthesize a data block SCN + is NULL and the sectio header info must be for a + SHT_NOBITS block and the size and alignment are + filled in. */ + if (likely (runp->scn != NULL)) + { + data = elf_getdata (runp->scn, NULL); + assert (data != NULL); + + /* We reuse the data buffer in the input file. */ + *outdata = *data; + + /* Given that we read the input file from disk we know there + cannot be another data part. */ + assert (elf_getdata (runp->scn, data) == NULL); + } + else + { + /* Must be a NOBITS section. */ + assert (SCNINFO_SHDR (runp->shdr).sh_type == SHT_NOBITS); + + outdata->d_buf = NULL; /* Not needed. */ + outdata->d_type = ELF_T_BYTE; + outdata->d_version = EV_CURRENT; + outdata->d_size = SCNINFO_SHDR (runp->shdr).sh_size; + outdata->d_align = SCNINFO_SHDR (runp->shdr).sh_addralign; + } + + XElf_Off align = MAX (1, outdata->d_align); + assert (powerof2 (align)); + offset = ((offset + align - 1) & ~(align - 1)); + + runp->offset = offset; + runp->outscnndx = head->scnidx; + runp->allsectionsidx = cnt; + + outdata->d_off = offset; + + offset += outdata->d_size; + } + while ((runp = runp->next) != head->last->next); + + /* If necessary add the additional line to the .comment section. */ + if (ld_state.add_ld_comment + && head->flags == 0 + && head->type == SHT_PROGBITS + && strcmp (head->name, ".comment") == 0 + && head->entsize == 0) + { + Elf_Data *outdata = elf_newdata (scn); + + if (outdata == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create section for output file: %s"), + elf_errmsg (-1)); + + 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; + outdata->d_align = 1; + } + /* XXX We should create a .comment section if none exists. + This requires that we early on detect that no such + section exists. This should probably be implemented + together with some merging of the section contents. + Currently identical entries are not merged. */ + } + } + + /* The table we collect the strings in. */ + strtab = ebl_strtabinit (true); + if (strtab == NULL) + error (EXIT_FAILURE, errno, gettext ("cannot create string table")); + + +#ifndef NDEBUG + /* Keep track of the use of the XINDEX. */ + need_xndx = false; +#endif + + /* We we generate a normal symbol table for an executable and the + --export-dynamic option is not given, we need an extra table + which keeps track of the symbol entry belonging to the symbol + table entry. Note that EXPORT_ALL_DYNAMIC is always set if we + generate a DSO so we do not have to test this separately. */ + ndxtosym = (struct symbol **) xcalloc (nsym_allocated, + sizeof (struct symbol)); + + /* Create the special symbol for the GOT section. */ + if (ld_state.got_symbol != NULL) + { + assert (nsym < nsym_allocated); + // 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); + } + + /* Similarly for the dynamic section symbol. */ + if (ld_state.dyn_symbol != NULL) + { + assert (nsym < nsym_allocated); + fillin_special_symbol (ld_state.dyn_symbol, ld_state.dynamicscnidx, + nsym++, symdata, strtab); + } + + /* Create symbol table entries for the symbols defined in the linker + script. */ + if (ld_state.lscript_syms != NULL) + { + struct symbol *rsym = ld_state.lscript_syms; + do + { + assert (nsym < nsym_allocated); + fillin_special_symbol (rsym, SHN_ABS, nsym++, symdata, strtab); + } + while ((rsym = rsym->next) != NULL); + } + + /* Iterate over all input files to collect the symbols. */ + file = ld_state.relfiles->next; + symdata = elf_getdata (elf_getscn (ld_state.outelf, ld_state.symscnidx), + NULL); + + do + { + size_t maxcnt; + Elf_Data *insymdata; + Elf_Data *inxndxdata; + + /* There must be no dynamic symbol table when creating + relocatable files. */ + assert (ld_state.file_type != relocatable_file_type + || file->dynsymtabdata == NULL); + + insymdata = file->symtabdata; + assert (insymdata != NULL); + inxndxdata = file->xndxdata; + + maxcnt = file->nsymtab; + + file->symindirect = (Elf32_Word *) xcalloc (maxcnt, sizeof (Elf32_Word)); + + /* The dynamic symbol table does not contain local symbols. So + we skip those entries. */ + for (cnt = ld_state.need_symtab ? 1 : file->nlocalsymbols; cnt < maxcnt; + ++cnt) + { + XElf_Sym_vardef (sym); + Elf32_Word xndx; + struct symbol *defp = NULL; + + xelf_getsymshndx (insymdata, inxndxdata, cnt, sym, xndx); + assert (sym != NULL); + + if (unlikely (XELF_ST_TYPE (sym->st_info) == STT_SECTION)) + { + /* Section symbols should always be local but who knows... */ + if (ld_state.need_symtab) + { + /* Determine the real section index in the source file. + Use the XINDEX section content if necessary. We don't + add this information to the dynamic symbol table. */ + if (sym->st_shndx != SHN_XINDEX) + xndx = sym->st_shndx; + + assert (file->scninfo[xndx].allsectionsidx + < ld_state.nallsections); + file->symindirect[cnt] = ld_state.allsections[file->scninfo[xndx].allsectionsidx]->scnsymidx; + /* Note that the resulting index can be zero here. There is + no guarantee that the output file will contain all the + sections the input file did. */ + } + continue; + } + + if ((ld_state.strip >= strip_all || !ld_state.need_symtab) + /* XXX Do we need these entries? */ + && XELF_ST_TYPE (sym->st_info) == STT_FILE) + continue; + +#if NATIVE_ELF != 0 + /* 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 + && (sym->st_shndx < SHN_LORESERVE + || sym->st_shndx == SHN_XINDEX)) + { + /* If we are creating an executable with no normal + symbol table and we do not export all symbols and + this symbol is not defined in a DSO as well, ignore + it. */ + if (!ld_state.export_all_dynamic && !ld_state.need_symtab) + { + assert (cnt >= file->nlocalsymbols); + defp = file->symref[cnt]; + assert (defp != NULL); + + if (!defp->in_dso) + /* Ignore it. */ + continue; + } + + /* Determine the real section index in the source file. Use + the XINDEX section content if necessary. */ + if (sym->st_shndx != SHN_XINDEX) + xndx = sym->st_shndx; + + sym->st_value += file->scninfo[xndx].offset; + + assert (file->scninfo[xndx].outscnndx < SHN_LORESERVE + || file->scninfo[xndx].outscnndx > SHN_HIRESERVE); + if (unlikely (file->scninfo[xndx].outscnndx > SHN_LORESERVE)) + { + /* It is not possible to have an extended section index + table for the dynamic symbol table. */ + if (!ld_state.need_symtab) + error (EXIT_FAILURE, 0, gettext ("\ +section index too large in dynamic symbol table")); + + assert (xndxdata != NULL); + sym->st_shndx = SHN_XINDEX; + xndx = file->scninfo[xndx].outscnndx; +#ifndef NDEBUG + need_xndx = true; +#endif + } + else + { + sym->st_shndx = file->scninfo[xndx].outscnndx; + xndx = 0; + } + } + else if (sym->st_shndx == SHN_COMMON || sym->st_shndx == SHN_UNDEF) + { + /* Check whether we have a (real) definition for this + symbol. If this is the case we skip this symbol + table entry. */ + assert (cnt >= file->nlocalsymbols); + defp = file->symref[cnt]; + assert (defp != NULL); + + assert (sym->st_shndx != SHN_COMMON || defp->defined); + + if ((sym->st_shndx == SHN_COMMON && !defp->common) + || (sym->st_shndx == SHN_UNDEF && defp->defined) + || defp->added) + /* Ignore this symbol table entry, there is a + "better" one or we already added it. */ + continue; + + /* Remember that we already added this symbol. */ + defp->added = 1; + + /* Adjust the section number for common symbols. */ + if (sym->st_shndx == SHN_COMMON) + { + sym->st_value = (ld_state.common_section->offset + + file->symref[cnt]->merge.value); + assert (ld_state.common_section->outscnndx < SHN_LORESERVE); + sym->st_shndx = ld_state.common_section->outscnndx; + xndx = 0; + } + } + else if (unlikely (sym->st_shndx != SHN_ABS)) + { + if (SPECIAL_SECTION_NUMBER_P (&ld_state, sym->st_shndx)) + /* XXX Add code to handle machine specific special + sections. */ + abort (); + } + + /* Add the symbol name to the string table. If the user + chooses the highest level of stripping avoid adding names + for local symbols in the string table. */ + if (sym->st_name != 0 + && (ld_state.strip < strip_everything + || XELF_ST_BIND (sym->st_info) != STB_LOCAL)) + symstrent[nsym] = ebl_strtabadd (strtab, + elf_strptr (file->elf, + file->symstridx, + sym->st_name), 0); + + /* 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 + offset for the symbol. */ + if (file->has_merge_sections && file->symref[cnt] != NULL + && file->symref[cnt]->merged) + sym->st_value = file->symref[cnt]->merge.value; + + /* Create the record in the output sections. */ + assert (nsym < nsym_allocated); + 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 + not need the information for local symbols. */ + if (defp == NULL && cnt >= file->nlocalsymbols) + { + defp = file->symref[cnt]; + + 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. */ + ndxtosym[nsym] = defp; + + /* One more entry finished. */ + if (cnt >= file->nlocalsymbols) + { + assert (file->symref[cnt]->outsymidx == 0); + file->symref[cnt]->outsymidx = nsym; + } + file->symindirect[cnt] = nsym++; + } + } + while ((file = file->next) != ld_state.relfiles->next); + /* Make sure we didn't create the extended section index table for + nothing. */ + assert (xndxdata == NULL || need_xndx); + + /* Create the version related sections. */ + if (ld_state.verneedscnidx != 0) + { + /* We know the number of input files and total number of + referenced versions. This allows us to allocate the memory + and then we iterate over the DSOs to get the version + information. */ + struct usedfiles *runp; + + runp = ld_state.dsofiles->next; + do + allocate_version_names (runp, dynstrtab); + while ((runp = runp->next) != ld_state.dsofiles->next); + + if (ld_state.needed != NULL) + { + runp = ld_state.needed->next; + do + allocate_version_names (runp, dynstrtab); + while ((runp = runp->next) != ld_state.needed->next); + } + } + + /* At this point we should hide symbols and so on. */ + if (ld_state.default_bind_local || ld_state.version_str_tab.filled > 0) + /* XXX Add one more test when handling of wildcard symbol names + is supported. */ + { + /* Check all non-local symbols whether they are on the export list. */ + bool any_reduced = false; + + for (cnt = 1; cnt < nsym; ++cnt) + { + XElf_Sym_vardef (sym); + + /* Note that we don't have to use 'xelf_getsymshndx' since we + only need the binding and the symbol name. */ + xelf_getsym (symdata, cnt, sym); + assert (sym != NULL); + + 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); + + /* Show that we don't need this string anymore. */ + if (ld_state.strip == strip_everything) + { + symstrent[cnt] = NULL; + any_reduced = true; + } + } + } + + if (unlikely (any_reduced)) + { + /* Since we will not write names of local symbols in the + output file and we have reduced the binding of some + symbols the string table previously constructed contains + too many string. Correct it. */ + struct Ebl_Strtab *newp = ebl_strtabinit (true); + + for (cnt = 1; cnt < nsym; ++cnt) + if (symstrent[cnt] != NULL) + symstrent[cnt] = ebl_strtabadd (newp, + ebl_string (symstrent[cnt]), 0); + + ebl_strtabfree (strtab); + strtab = newp; + } + } + + /* Add the references to DSOs. We can add these entries this late + (after sorting out versioning) because references to DSOs are not + effected. */ + if (ld_state.from_dso != NULL) + { + struct symbol *runp; + size_t plt_base = nsym + ld_state.nfrom_dso - ld_state.nplt; + size_t plt_idx = 0; + size_t obj_idx = 0; + + assert (ld_state.nfrom_dso >= ld_state.nplt); + runp = ld_state.from_dso; + do + { + // XXX What about functions which are only referenced via + // pointers and not PLT entries? Can we distinguish such uses? + size_t idx; + if (runp->type == STT_FUNC) + { + /* Store the PLT entry number. */ + runp->merge.value = plt_idx + 1; + idx = plt_base + plt_idx++; + } + else + idx = nsym + obj_idx++; + + XElf_Sym_vardef (sym); + xelf_getsym_ptr (symdata, idx, sym); + + sym->st_value = 0; + sym->st_size = runp->size; + sym->st_info = XELF_ST_INFO (runp->weak ? STB_WEAK : STB_GLOBAL, + runp->type); + sym->st_other = STV_DEFAULT; + sym->st_shndx = SHN_UNDEF; + + /* Create the record in the output sections. */ + xelf_update_symshndx (symdata, xndxdata, idx, sym, 0, 0); + + const char *name = runp->name; + size_t namelen = 0; + + if (runp->file->verdefdata != NULL) + { + // XXX Is it useful to add the versym value to struct symbol? + XElf_Versym versym; + + (void) xelf_getversym_copy (runp->file->versymdata, runp->symidx, + versym); + + /* One can only link with the default version. */ + assert ((versym & 0x8000) == 0); + + const char *versname + = ebl_string (runp->file->verdefent[versym]); + + size_t versname_len = strlen (versname) + 1; + namelen = strlen (name) + versname_len + 2; + char *newp = (char *) obstack_alloc (&ld_state.smem, namelen); + memcpy (stpcpy (stpcpy (newp, name), "@@"), + versname, versname_len); + name = newp; + } + + symstrent[idx] = ebl_strtabadd (strtab, name, namelen); + + /* Record the initial index in the symbol table. */ + runp->outsymidx = idx; + + /* Remember the symbol record this ELF symbol came from. */ + ndxtosym[idx] = runp; + } + while ((runp = runp->next) != ld_state.from_dso); + + assert (nsym + obj_idx == plt_base); + assert (plt_idx == ld_state.nplt); + nsym = plt_base + plt_idx; + } + + /* Now we know how many symbols will be in the output file. Adjust + the count in the section data. */ + symdata->d_size = xelf_fsize (ld_state.outelf, ELF_T_SYM, nsym); + if (unlikely (xndxdata != NULL)) + xndxdata->d_size = xelf_fsize (ld_state.outelf, ELF_T_WORD, nsym); + + /* Create the symbol string table section. */ + strscn = elf_newscn (ld_state.outelf); + ld_state.strscnidx = elf_ndxscn (strscn); + data = elf_newdata (strscn); + xelf_getshdr (strscn, shdr); + if (data == NULL || shdr == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create section for output file: %s"), + elf_errmsg (-1)); + + /* Create a compact string table, allocate the memory for it, and + fill in the section data information. */ + ebl_strtabfinalize (strtab, data); + + shdr->sh_type = SHT_STRTAB; + assert (shdr->sh_entsize == 0); + + if (unlikely (xelf_update_shdr (strscn, shdr) == 0)) + error (EXIT_FAILURE, 0, + gettext ("cannot create section for output file: %s"), + elf_errmsg (-1)); + + /* Fill in the offsets of the symbol names. */ + for (cnt = 1; cnt < nsym; ++cnt) + if (symstrent[cnt] != NULL) + { + XElf_Sym_vardef (sym); + + /* Note that we don't have to use 'xelf_getsymshndx' since we don't + modify the section index. */ + xelf_getsym (symdata, cnt, sym); + /* This better worked, we did it before. */ + assert (sym != NULL); + sym->st_name = ebl_strtaboffset (symstrent[cnt]); + (void) xelf_update_sym (symdata, cnt, sym); + } + + /* Since we are going to reorder the symbol table but still have to + be able to find the new position based on the old one (since the + latter is stored in 'symindirect' information of the input file + data structure) we have to create yet another indirection + table. */ + ld_state.dblindirect = dblindirect + = (Elf32_Word *) xmalloc (nsym * sizeof (Elf32_Word)); + + /* Sort the symbol table so that the local symbols come first. */ + /* XXX We don't use stable sorting here. It seems not necessary and + would be more expensive. If it turns out to be necessary this can + be fixed easily. */ + nsym_local = 1; + cnt = nsym - 1; + while (nsym_local < cnt) + { + XElf_Sym_vardef (locsym); + Elf32_Word locxndx; + XElf_Sym_vardef (globsym); + Elf32_Word globxndx; + + do + { + xelf_getsymshndx (symdata, xndxdata, nsym_local, locsym, locxndx); + /* This better works. */ + assert (locsym != NULL); + + if (XELF_ST_BIND (locsym->st_info) != STB_LOCAL + && (ld_state.need_symtab || ld_state.export_all_dynamic)) + { + do + { + xelf_getsymshndx (symdata, xndxdata, cnt, globsym, globxndx); + /* This better works. */ + assert (globsym != NULL); + + if (unlikely (XELF_ST_BIND (globsym->st_info) == STB_LOCAL)) + { + /* We swap the two entries. */ +#if NATIVE_ELF != 0 + /* Since we directly modify the data in the ELF + data structure we have to make a copy of one + of the entries. */ + XElf_Sym locsym_copy = *locsym; + locsym = &locsym_copy; +#endif + xelf_update_symshndx (symdata, xndxdata, nsym_local, + globsym, globxndx, 1); + xelf_update_symshndx (symdata, xndxdata, cnt, + locsym, locxndx, 1); + + /* Also swap the cross references. */ + dblindirect[nsym_local] = cnt; + dblindirect[cnt] = nsym_local; + + /* And the entries for the symbol names. */ + struct Ebl_Strent *strtmp = symstrent[nsym_local]; + symstrent[nsym_local] = symstrent[cnt]; + symstrent[cnt] = strtmp; + + /* And the mapping from symbol table entry to + struct symbol record. */ + struct symbol *symtmp = ndxtosym[nsym_local]; + ndxtosym[nsym_local] = ndxtosym[cnt]; + ndxtosym[cnt] = symtmp; + + /* Go to the next entry. */ + ++nsym_local; + --cnt; + + break; + } + + dblindirect[cnt] = cnt; + } + while (nsym_local < --cnt); + + break; + } + + dblindirect[nsym_local] = nsym_local; + } + while (++nsym_local < cnt); + } + + /* The symbol 'nsym_local' is currently pointing to might be local, + too. Check and increment the variable if this is the case. */ + if (likely (nsym_local < nsym)) + { + XElf_Sym_vardef (locsym); + + /* This entry isn't moved. */ + dblindirect[nsym_local] = nsym_local; + + /* Note that it is OK to not use 'xelf_getsymshndx' here. */ + xelf_getsym (symdata, nsym_local, locsym); + /* This better works. */ + assert (locsym != NULL); + + if (XELF_ST_BIND (locsym->st_info) == STB_LOCAL) + ++nsym_local; + } + + + /* We need the versym array right away to keep track of the version + symbols. */ + if (ld_state.versymscnidx != 0) + { + /* We allocate more memory than we need since the array is morroring + the dynamic symbol table and not the normal symbol table. I.e., + no local symbols are present. */ + versymscn = elf_getscn (ld_state.outelf, ld_state.versymscnidx); + versymdata = elf_newdata (versymscn); + if (versymdata == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create versioning section: %s"), + elf_errmsg (-1)); + + versymdata->d_size = xelf_fsize (ld_state.outelf, ELF_T_HALF, + nsym - nsym_local + 1); + versymdata->d_buf = xcalloc (1, versymdata->d_size); + versymdata->d_align = xelf_fsize (ld_state.outelf, ELF_T_HALF, 1); + versymdata->d_off = 0; + versymdata->d_type = ELF_T_HALF; + } + + + /* If we have to construct the dynamic symbol table we must not include + the local symbols. If the normal symbol has to be emitted as well + we haven't done anything else yet and we can construct it from + scratch now. */ + if (unlikely (!ld_state.need_symtab)) + { + /* Note that the following code works even if there is no entry + to remove since the zeroth entry is always local. */ + size_t reduce = xelf_fsize (ld_state.outelf, ELF_T_SYM, nsym_local - 1); + + XElf_Sym_vardef (nullsym); + xelf_getsym_ptr (symdata, nsym_local - 1, nullsym); + + /* Note that we don't have to use 'xelf_update_symshndx' since + this is the dynamic symbol table we write. */ + (void) xelf_update_sym (symdata, nsym_local - 1, + memset (nullsym, '\0', sizeof (*nullsym))); + + /* Update the buffer pointer and size in the output data. */ + symdata->d_buf = (char *) symdata->d_buf + reduce; + symdata->d_size -= reduce; + + /* Add the version symbol information. */ + if (versymdata != NULL) + { + nsym_dyn = 1; + for (cnt = nsym_local; cnt < nsym; ++cnt, ++nsym_dyn) + { + struct symbol *symp = ndxtosym[cnt]; + + if (symp->file->versymdata != NULL) + { + GElf_Versym versym; + + gelf_getversym (symp->file->versymdata, symp->symidx, + &versym); + + (void) gelf_update_versym (versymdata, symp->outdynsymidx, + &symp->file->verdefused[versym]); + } + } + } + + /* Since we only created the dynamic symbol table the number of + dynamic symbols is the total number of symbols. */ + nsym_dyn = nsym - nsym_local + 1; + + /* XXX TBI. Create whatever data structure is missing. */ + abort (); + } + else if (ld_state.need_dynsym) + { + /* Create the dynamic symbol table section data along with the + string table. We look at all non-local symbols we found for + the normal symbol table and add those. */ + dynsymscn = elf_getscn (ld_state.outelf, ld_state.dynsymscnidx); + dynsymdata = elf_newdata (dynsymscn); + + dynstrdata = elf_newdata (elf_getscn (ld_state.outelf, + ld_state.dynstrscnidx)); + if (dynsymdata == NULL || dynstrdata == NULL) + error (EXIT_FAILURE, 0, gettext ("\ +cannot create dynamic symbol table for output file: %s"), + elf_errmsg (-1)); + + nsym_dyn_allocated = nsym - nsym_local + 1; + dynsymdata->d_size = xelf_fsize (ld_state.outelf, ELF_T_SYM, + nsym_dyn_allocated); + dynsymdata->d_buf = memset (xmalloc (dynsymdata->d_size), '\0', + xelf_fsize (ld_state.outelf, ELF_T_SYM, 1)); + dynsymdata->d_type = ELF_T_SYM; + dynsymdata->d_off = 0; + dynsymdata->d_align = xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1); + + /* We need one more array which contains the hash codes of the + symbol names. */ + 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; + + /* Populate the table. */ + for (cnt = nsym_local; cnt < nsym; ++cnt) + { + XElf_Sym_vardef (sym); + + xelf_getsym (symdata, cnt, sym); + assert (sym != NULL); + + if (sym->st_shndx == SHN_XINDEX) + error (EXIT_FAILURE, 0, gettext ("\ +section index too large in dynamic symbol table")); + + /* We do not add the symbol to the dynamic symbol table if + + - the symbol is for a file + - it is not externally visible (internal, hidden) + - export_all_dynamic is not set and the symbol is only defined + in the executable (i.e., it is defined, but not (also) in DSO) + + Set symstrent[cnt] to NULL in case an entry is ignored. */ + 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 + || (!ld_state.export_all_dynamic + && !ndxtosym[cnt]->in_dso && ndxtosym[cnt]->defined)) + { + symstrent[cnt] = NULL; + continue; + } + + /* 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) + for (cnt = nsym_local; cnt < nsym; ++cnt) + if (symstrent[cnt] != NULL) + { + struct symbol *symp = ndxtosym[cnt]; + + /* 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, symp->outdynsymidx, + &symp->file->verdefused[versym]); + } + else + { + /* XXX Add support for version definitions. */ + GElf_Versym global = VER_NDX_GLOBAL; + (void) gelf_update_versym (versymdata, nsym_dyn, &global); + } + } + + /* Update the information about the symbol section. */ + if (versymdata != NULL) + { + /* Correct the size now that we know how many entries the + dynamic symbol table has. */ + versymdata->d_size = xelf_fsize (ld_state.outelf, ELF_T_HALF, + nsym_dyn); + + /* Add the reference to the symbol table. */ + xelf_getshdr (versymscn, shdr); + assert (shdr != NULL); + + shdr->sh_link = ld_state.dynsymscnidx; + + (void) xelf_update_shdr (versymscn, shdr); + } + } + + if (ld_state.file_type != relocatable_file_type) + { + /* Now put the names in. */ + for (cnt = nsym_local; cnt < nsym; ++cnt) + if (symstrent[cnt] != NULL) + { + XElf_Sym_vardef (sym); + size_t dynidx = ndxtosym[cnt]->outdynsymidx; + +#if NATIVE_ELF != 0 + XElf_Sym *osym; + memcpy (xelf_getsym (dynsymdata, dynidx, sym), + xelf_getsym (symdata, cnt, osym), + sizeof (XElf_Sym)); +#else + xelf_getsym (symdata, cnt, sym); + assert (sym != NULL); +#endif + + sym->st_name = ebl_strtaboffset (symstrent[cnt]); + + (void) xelf_update_sym (dynsymdata, dynidx, sym); + } + + free (hashcodes); + + /* Create the required version section. */ + if (ld_state.verneedscnidx != 0) + { + Elf_Scn *verneedscn; + Elf_Data *verneeddata; + struct usedfiles *runp; + 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); + size_t offset; + int ntotal; + + verneedscn = elf_getscn (ld_state.outelf, ld_state.verneedscnidx); + xelf_getshdr (verneedscn, shdr); + verneeddata = elf_newdata (verneedscn); + if (shdr == NULL || verneeddata == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create versioning data: %s"), + elf_errmsg (-1)); + + verneeddata->d_size = (ld_state.nverdeffile * verneed_size + + ld_state.nverdefused * vernaux_size); + verneeddata->d_buf = xmalloc (verneeddata->d_size); + verneeddata->d_type = ELF_T_VNEED; + verneeddata->d_align = xelf_fsize (ld_state.outelf, ELF_T_WORD, 1); + verneeddata->d_off = 0; + + offset = 0; + ntotal = ld_state.nverdeffile; + runp = ld_state.dsofiles->next; + do + { + offset = create_verneed_data (offset, verneeddata, runp, + &ntotal); + runp = runp->next; + } + while (ntotal > 0 && runp != ld_state.dsofiles->next); + + if (ntotal > 0) + { + runp = ld_state.needed->next; + do + { + offset = create_verneed_data (offset, verneeddata, runp, + &ntotal); + runp = runp->next; + } + while (ntotal > 0 && runp != ld_state.needed->next); + } + + assert (offset == verneeddata->d_size); + + /* Add the needed information to the section header. */ + shdr->sh_link = ld_state.dynstrscnidx; + shdr->sh_info = ld_state.nverdeffile; + (void) xelf_update_shdr (verneedscn, shdr); + } + + /* Adjust the section size. */ + dynsymdata->d_size = xelf_fsize (ld_state.outelf, ELF_T_SYM, nsym_dyn); + if (versymdata != NULL) + versymdata->d_size = xelf_fsize (ld_state.outelf, ELF_T_HALF, + nsym_dyn); + + /* Add the remaining information to the section header. */ + xelf_getshdr (dynsymscn, shdr); + /* There is always exactly one local symbol. */ + shdr->sh_info = 1; + /* Reference the string table. */ + shdr->sh_link = ld_state.dynstrscnidx; + /* Write the updated info back. */ + (void) xelf_update_shdr (dynsymscn, shdr); + } + + /* We don't need the string table anymore. */ + free (symstrent); + + /* Remember the total number of symbols in the dynamic symbol table. */ + ld_state.ndynsym = nsym_dyn; + + /* Fill in the section header information. */ + symscn = elf_getscn (ld_state.outelf, ld_state.symscnidx); + xelf_getshdr (symscn, shdr); + if (shdr == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create symbol table for output file: %s"), + elf_errmsg (-1)); + + shdr->sh_type = SHT_SYMTAB; + shdr->sh_link = ld_state.strscnidx; + shdr->sh_info = nsym_local; + shdr->sh_entsize = xelf_fsize (ld_state.outelf, ELF_T_SYM, 1); + + (void) xelf_update_shdr (symscn, shdr); + + + /* Add names for the generated sections. */ + if (ld_state.symscnidx != 0) + symtab_ent = ebl_strtabadd (ld_state.shstrtab, ".symtab", 8); + if (ld_state.xndxscnidx != 0) + xndx_ent = ebl_strtabadd (ld_state.shstrtab, ".symtab_shndx", 14); + if (ld_state.strscnidx != 0) + 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 + 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 + quite a bit of code. */ + + + /* Finally create the section for the section header string table. */ + shstrtab_scn = elf_newscn (ld_state.outelf); + shstrtab_ndx = elf_ndxscn (shstrtab_scn); + if (unlikely (shstrtab_ndx == SHN_UNDEF)) + error (EXIT_FAILURE, 0, + gettext ("cannot create section header string section: %s"), + elf_errmsg (-1)); + + /* Add the name of the section to the string table. */ + shstrtab_ent = ebl_strtabadd (ld_state.shstrtab, ".shstrtab", 10); + if (unlikely (shstrtab_ent == NULL)) + error (EXIT_FAILURE, errno, + gettext ("cannot create section header string section")); + + /* Finalize the section header string table. */ + data = elf_newdata (shstrtab_scn); + if (data == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create section header string section: %s"), + elf_errmsg (-1)); + ebl_strtabfinalize (ld_state.shstrtab, data); + + /* Now we know the string offsets for all section names. */ + for (cnt = 0; cnt < ld_state.nallsections; ++cnt) + if (ld_state.allsections[cnt]->scnidx != 0) + { + Elf_Scn *scn; + + scn = elf_getscn (ld_state.outelf, ld_state.allsections[cnt]->scnidx); + + xelf_getshdr (scn, shdr); + assert (shdr != NULL); + + shdr->sh_name = ebl_strtaboffset (ld_state.allsections[cnt]->nameent); + + if (xelf_update_shdr (scn, shdr) == 0) + assert (0); + } + + /* Add the names for the generated sections to the respective + section headers. */ + if (symtab_ent != NULL) + { + Elf_Scn *scn = elf_getscn (ld_state.outelf, ld_state.symscnidx); + + xelf_getshdr (scn, shdr); + /* This cannot fail, we already accessed the header before. */ + assert (shdr != NULL); + + shdr->sh_name = ebl_strtaboffset (symtab_ent); + + (void) xelf_update_shdr (scn, shdr); + } + if (xndx_ent != NULL) + { + Elf_Scn *scn = elf_getscn (ld_state.outelf, ld_state.xndxscnidx); + + xelf_getshdr (scn, shdr); + /* This cannot fail, we already accessed the header before. */ + assert (shdr != NULL); + + shdr->sh_name = ebl_strtaboffset (xndx_ent); + + (void) xelf_update_shdr (scn, shdr); + } + if (strtab_ent != NULL) + { + Elf_Scn *scn = elf_getscn (ld_state.outelf, ld_state.strscnidx); + + xelf_getshdr (scn, shdr); + /* This cannot fail, we already accessed the header before. */ + assert (shdr != NULL); + + shdr->sh_name = ebl_strtaboffset (strtab_ent); + + (void) xelf_update_shdr (scn, shdr); + } + + /* And the section header table section itself. */ + xelf_getshdr (shstrtab_scn, shdr); + if (shdr == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create section header string section: %s"), + elf_errmsg (-1)); + + shdr->sh_name = ebl_strtaboffset (shstrtab_ent); + shdr->sh_type = SHT_STRTAB; + + if (unlikely (xelf_update_shdr (shstrtab_scn, shdr) == 0)) + error (EXIT_FAILURE, 0, + gettext ("cannot create section header string section: %s"), + elf_errmsg (-1)); + + + /* Add the correct section header info to the section group sections. */ + groups = ld_state.groups; + while (groups != NULL) + { + Elf_Scn *scn = elf_getscn (ld_state.outelf, groups->outscnidx); + xelf_getshdr (scn, shdr); + assert (shdr != NULL); + + shdr->sh_name = ebl_strtaboffset (groups->nameent); + shdr->sh_type = SHT_GROUP; + shdr->sh_flags = 0; + shdr->sh_link = ld_state.symscnidx; + shdr->sh_entsize = sizeof (Elf32_Word); + + /* Determine the index for the signature symbol. */ + Elf32_Word si + = groups->symbol->file->symindirect[groups->symbol->symidx]; + if (si == 0) + { + assert (groups->symbol->file->symref[groups->symbol->symidx] + != NULL); + si = groups->symbol->file->symref[groups->symbol->symidx]->outsymidx; + assert (si != 0); + } + shdr->sh_info = ld_state.dblindirect[si]; + + (void) xelf_update_shdr (scn, shdr); + + struct scngroup *oldp = groups; + groups = groups->next; + free (oldp); + } + + + if (ld_state.file_type != relocatable_file_type) + { + /* 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 + one for the program header, the interpreter, and the dynamic + section. First count the number of segments. + + XXX Determine whether the segment is non-empty. */ + 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; + segment = segment->next; + } + + /* 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 + at least one more entry: DYNAMIC. If an interpreter is + specified we add PHDR and INTERP, too. */ + if (dynamically_linked_p ()) + { + ++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)); + + + /* Determine the section sizes and offsets. We have to do this + to be able to determine the memory layout (which normally + differs from the file layout). */ + if (elf_update (ld_state.outelf, ELF_C_NULL) == -1) + error (EXIT_FAILURE, 0, gettext ("while determining file layout: %s"), + elf_errmsg (-1)); + + + /* Now determine the memory addresses of all the sections and + segments. */ + 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. */ + 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 = 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) + { + struct output_rule *orule; + bool first_section = true; + XElf_Off nobits_size = 0; + XElf_Off memsize = 0; + + /* 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) + { + /* See whether this output rule corresponds to the next + section. Yes, this is a pointer comparison. */ + if (ld_state.allsections[nsec]->name + != orule->val.section.name) + /* No, ignore this output rule. */ + continue; + + /* We assign addresses only in segments which are actually + loaded. */ + if (segment->mode != 0) + { + /* Adjust the offset of the input sections. */ + struct scninfo *isect; + struct scninfo *first; + + isect = first = ld_state.allsections[nsec]->last; + if (isect != NULL) + do + isect->offset += addr; + while ((isect = isect->next) != first); + + /* Set the address of current section. */ + shdr->sh_addr = addr; + + /* Write the result back. */ + (void) xelf_update_shdr (scn, shdr); + + /* 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) + { + /* The first segment starts at offset zero. */ + if (segment == ld_state.output_segments) + { + segment->offset = 0; + segment->addr = addr - shdr->sh_offset; + } + else + { + segment->offset = shdr->sh_offset; + segment->addr = addr; + } + + /* Determine the maximum alignment requirement. */ + segment->align = MAX (segment->align, shdr->sh_addralign); + + first_section = false; + } + + /* 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 + that this assumes that the sections following each + other in the section header table are also + consecutive in the file. This is true here because + libelf constructs files this way. */ + XElf_Off oldoff = shdr->sh_offset; + + if (++nsec >= ld_state.nallsections) + break; + + scn = elf_getscn (ld_state.outelf, + ld_state.allsections[nsec]->scnidx); + xelf_getshdr (scn, shdr); + assert (shdr != NULL); + + /* This is the new address resulting from the offsets + in the file. */ + assert (oldoff <= shdr->sh_offset); + addr += shdr->sh_offset - oldoff; + } + else + { + assert (orule->tag == output_assignment); + + if (strcmp (orule->val.assignment->variable, ".") == 0) + /* This is a change of the address. */ + addr = eval_expression (orule->val.assignment->expression, + addr); + else if (orule->val.assignment->sym != NULL) + { + /* This symbol is used. Update the symbol table + entry. */ + XElf_Sym_vardef (sym); + size_t idx; + + /* Note that we do not have to use + xelf_getsymshndx since we only update the + symbol address, not the section + information. */ + idx = dblindirect[orule->val.assignment->sym->outsymidx]; + xelf_getsym (symdata, idx, sym); + sym->st_value = addr; + (void) xelf_update_sym (symdata, idx, sym); + + idx = orule->val.assignment->sym->outdynsymidx; + if (idx != 0) + { + assert (dynsymdata != NULL); + xelf_getsym (dynsymdata, idx, sym); + sym->st_value = addr; + (void) xelf_update_sym (dynsymdata, idx, sym); + } + } + } + + /* Store the segment parameter for loadable segments. */ + if (segment->mode != 0) + { + xelf_getphdr_ptr (ld_state.outelf, nphdr, phdr); + + phdr->p_type = PT_LOAD; + phdr->p_offset = segment->offset; + phdr->p_vaddr = segment->addr; + phdr->p_paddr = phdr->p_vaddr; + phdr->p_filesz = memsize - nobits_size; + phdr->p_memsz = memsize; + phdr->p_flags = segment->mode; + phdr->p_align = segment->align; + + (void) xelf_update_phdr (ld_state.outelf, nphdr, phdr); + ++nphdr; + } + + segment = segment->next; + } + + /* Create the other program header entries. */ + xelf_getehdr (ld_state.outelf, ehdr); + assert (ehdr != NULL); + + /* 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, nphdr, phdr); + ++nphdr; + + + /* 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) + { + XElf_Sym_vardef (sym); + Elf32_Word shndx; + + xelf_getsymshndx (symdata, xndxdata, cnt, sym, shndx); + assert (sym != NULL); + + if (sym->st_shndx != SHN_XINDEX) + shndx = sym->st_shndx; + + if ((shndx > SHN_UNDEF && shndx < SHN_LORESERVE) + || shndx > SHN_HIRESERVE) + { + /* Note we subtract 1 from the section index since ALLSECTIONS + does not store the dummy section with offset zero. */ + sym->st_value += ld_state.allsections[shndx - 1]->addr; + + /* We don't have to use 'xelf_update_symshndx' since the + section number doesn't change. */ + (void) xelf_update_sym (symdata, cnt, sym); + } + } + + if (ld_state.need_dynsym) + for (cnt = 1; cnt < nsym_dyn; ++cnt) + { + XElf_Sym_vardef (sym); + + xelf_getsym (dynsymdata, cnt, sym); + assert (sym != NULL); + + if (sym->st_shndx > SHN_UNDEF && sym->st_shndx < SHN_LORESERVE) + { + /* Note we subtract 1 from the section index since ALLSECTIONS + does not store the dummy section with offset zero. */ + sym->st_value += ld_state.allsections[sym->st_shndx - 1]->addr; + + /* We don't have to use 'xelf_update_symshndx' since the + section number doesn't change. */ + (void) xelf_update_sym (dynsymdata, cnt, sym); + } + } + + /* 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 + // returns all symbols in DSOs. + struct symbol *se; + void *p = NULL; + while ((se = ld_symbol_tab_iterate (&ld_state.symbol_tab, &p)) != NULL) + if (! se->in_dso) + { + XElf_Sym_vardef (sym); + + addr = 0; + + if (se->outdynsymidx != 0) + { + xelf_getsym (dynsymdata, se->outdynsymidx, sym); + assert (sym != NULL); + addr = sym->st_value; + } + else if (se->outsymidx != 0) + { + assert (dblindirect[se->outsymidx] != 0); + xelf_getsym (symdata, dblindirect[se->outsymidx], sym); + assert (sym != NULL); + addr = sym->st_value; + } + else + abort (); + + se->merge.value = addr; + } + + /* Complete the header of the .rel.dyn/.rela.dyn section. Point + to the symbol table. The sh_info field is left zero since + there is no specific section the contained relocations are + for. */ + if (ld_state.reldynscnidx != 0) + { + assert (ld_state.dynsymscnidx != 0); + scn = elf_getscn (ld_state.outelf, ld_state.reldynscnidx); + xelf_getshdr (scn, shdr); + assert (shdr != NULL); + + shdr->sh_link = ld_state.dynsymscnidx; + + (void) xelf_update_shdr (scn, shdr); + } + + /* Fill in the dynamic segment/section. */ + if (dynamically_linked_p ()) + { + Elf_Scn *outscn; + + 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); + + 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, idx, phdr); + ++idx; + } + + /* The pointer to the dynamic section. We this we need to + get the information for the dynamic section first. */ + assert (ld_state.dynamicscnidx); + outscn = elf_getscn (ld_state.outelf, ld_state.dynamicscnidx); + xelf_getshdr (outscn, shdr); + assert (shdr != NULL); + + 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; + 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 = shdr->sh_addralign; + + (void) xelf_update_phdr (ld_state.outelf, idx, phdr); + + /* Fill in the reference to the .dynstr section. */ + assert (ld_state.dynstrscnidx != 0); + shdr->sh_link = ld_state.dynstrscnidx; + (void) xelf_update_shdr (outscn, shdr); + + /* And fill the remaining entries. */ + Elf_Data *dyndata = elf_getdata (outscn, NULL); + assert (dyndata != NULL); + + /* Add the DT_NEEDED entries. */ + if (ld_state.ndsofiles > 0) + { + struct usedfiles *runp = ld_state.dsofiles->next; + + do + if (runp->used || !runp->as_needed) + { + /* Add the position-dependent flag if necessary. */ + if (runp->lazyload) + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + DT_POSFLAG_1, DF_P1_LAZYLOAD); + + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + DT_NEEDED, + ebl_strtaboffset (runp->sonameent)); + } + while ((runp = runp->next) != ld_state.dsofiles->next); + } + + /* We can finish the DT_RUNPATH/DT_RPATH entries now. */ + if (ld_state.rxxpath_strent != NULL) + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + ld_state.rxxpath_tag, + ebl_strtaboffset (ld_state.rxxpath_strent)); + + /* Reference to initialization and finalization functions. */ + // XXX This code depends on symbol table being relocated. + if (ld_state.init_symbol != NULL) + { + XElf_Sym_vardef (sym); + + if (ld_state.need_symtab) + xelf_getsym (symdata, + dblindirect[ld_state.init_symbol->outsymidx], + sym); + else + xelf_getsym (dynsymdata, ld_state.init_symbol->outdynsymidx, + sym); + assert (sym != NULL); + + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + DT_INIT, sym->st_value); + } + if (ld_state.fini_symbol != NULL) + { + XElf_Sym_vardef (sym); + + if (ld_state.need_symtab) + xelf_getsym (symdata, + dblindirect[ld_state.fini_symbol->outsymidx], + sym); + else + xelf_getsym (dynsymdata, ld_state.fini_symbol->outdynsymidx, + sym); + assert (sym != NULL); + + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + DT_FINI, sym->st_value); + } + // XXX Support init,fini,preinit arrays + + /* The hash table which comes with dynamic symbol table. */ + xelf_getshdr (elf_getscn (ld_state.outelf, ld_state.hashscnidx), + shdr); + assert (shdr != NULL); + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, DT_HASH, + shdr->sh_addr); + + /* Reference to the symbol table section. */ + assert (ld_state.dynsymscnidx != 0); + xelf_getshdr (elf_getscn (ld_state.outelf, ld_state.dynsymscnidx), + shdr); + assert (shdr != NULL); + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, DT_SYMTAB, + shdr->sh_addr); + + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, DT_SYMENT, + xelf_fsize (ld_state.outelf, ELF_T_SYM, 1)); + + /* And the string table which comes with it. */ + xelf_getshdr (elf_getscn (ld_state.outelf, ld_state.dynstrscnidx), + shdr); + assert (shdr != NULL); + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, DT_STRTAB, + shdr->sh_addr); + + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, DT_STRSZ, + shdr->sh_size); + + /* Add the entries related to the .plt. */ + if (ld_state.nplt > 0) + { + // 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 + // dependent. + DT_PLTGOT, shdr->sh_addr); + + xelf_getshdr (elf_getscn (ld_state.outelf, + ld_state.pltrelscnidx), shdr); + assert (shdr != NULL); + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + DT_PLTRELSZ, shdr->sh_size); + + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + DT_JMPREL, shdr->sh_addr); + + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + DT_PLTREL, REL_TYPE (statep)); + } + + if (ld_state.relsize_total > 0) + { + int rel = REL_TYPE (statep); + xelf_getshdr (elf_getscn (ld_state.outelf, + ld_state.reldynscnidx), shdr); + assert (shdr != NULL); + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + rel, shdr->sh_addr); + + /* Trick ahead. Use arithmetic to get the right tag. + We check the validity of this assumption in the asserts. */ + assert (DT_RELASZ - DT_RELA == 1); + assert (DT_RELSZ - DT_REL == 1); + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + rel + 1, shdr->sh_size); + + /* Similar for the entry size tag. */ + assert (DT_RELAENT - DT_RELA == 2); + assert (DT_RELENT - DT_REL == 2); + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + rel + 2, + rel == DT_REL + ? xelf_fsize (ld_state.outelf, ELF_T_REL, 1) + : xelf_fsize (ld_state.outelf, ELF_T_RELA, + 1)); + } + + if (ld_state.verneedscnidx != 0) + { + xelf_getshdr (elf_getscn (ld_state.outelf, + ld_state.verneedscnidx), shdr); + assert (shdr != NULL); + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + DT_VERNEED, shdr->sh_addr); + + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + DT_VERNEEDNUM, ld_state.nverdeffile); + } + + if (ld_state.versymscnidx != 0) + { + xelf_getshdr (elf_getscn (ld_state.outelf, + ld_state.versymscnidx), shdr); + assert (shdr != NULL); + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + DT_VERSYM, shdr->sh_addr); + } + + /* We always create the DT_DEBUG entry. */ + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, DT_DEBUG, 0); + assert (ld_state.ndynamic_filled < ld_state.ndynamic); + + /* Add the flag words if necessary. */ + if (ld_state.dt_flags != 0) + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, DT_FLAGS, + ld_state.dt_flags); + + /* Create entry for the DT_FLAGS_1 flag. */ + if (ld_state.dt_flags_1 != 0) + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + DT_FLAGS_1, ld_state.dt_flags_1); + + /* Create entry for the DT_FEATURE_1 flag. */ + if (ld_state.dt_feature_1 != 0) + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + DT_FEATURE_1, ld_state.dt_feature_1); + + assert (ld_state.ndynamic_filled <= ld_state.ndynamic); + } + } + + + // XXX The following code isn't nice. We use two different + // mechanisms to handle relocations, one for relocatable files, one + // for executables and DSOs. Maybe this is the best method but also + // maybe it can be somewhat unified. + + /* Now that we created the symbol table we can add the reference to + it in the sh_link field of the section headers of the relocation + sections. */ + while (rellist != NULL) + { + assert (ld_state.file_type == relocatable_file_type); + Elf_Scn *outscn; + + outscn = elf_getscn (ld_state.outelf, rellist->scnidx); + xelf_getshdr (outscn, shdr); + /* This must not fail since we did it before. */ + assert (shdr != NULL); + + /* Remember the symbol table which belongs to the relocation section. */ + shdr->sh_link = ld_state.symscnidx; + + /* And the reference to the section which is relocated by this + relocation section. We use the info from the first input + section but all records should have the same information. */ + shdr->sh_info = + rellist->scninfo->fileinfo->scninfo[SCNINFO_SHDR (rellist->scninfo->shdr).sh_info].outscnndx; + + + /* Perform the actual relocations. We only have to adjust + offsets and symbol indices. */ + RELOCATE_SECTION (statep, outscn, rellist->scninfo, dblindirect); + + /* Store the changes. */ + (void) xelf_update_shdr (outscn, shdr); + + /* Up to the next relocation section. */ + rellist = rellist->next; + } + + if (ld_state.rellist != NULL) + { + assert (ld_state.file_type != relocatable_file_type); + /* Create the relocations for the output file. */ + CREATE_RELOCATIONS (statep, dblindirect); + } + + + /* We need the ELF header once more. */ + xelf_getehdr (ld_state.outelf, ehdr); + assert (ehdr != NULL); + + /* Set the section header string table index. */ + if (likely (shstrtab_ndx < SHN_HIRESERVE) + && likely (shstrtab_ndx != SHN_XINDEX)) + ehdr->e_shstrndx = shstrtab_ndx; + else + { + /* We have to put the section index in the sh_link field of the + zeroth section header. */ + Elf_Scn *scn = elf_getscn (ld_state.outelf, 0); + + xelf_getshdr (scn, shdr); + if (unlikely (shdr == NULL)) + error (EXIT_FAILURE, 0, + gettext ("cannot get header of 0th section: %s"), + elf_errmsg (-1)); + + shdr->sh_link = shstrtab_ndx; + + (void) xelf_update_shdr (scn, shdr); + + ehdr->e_shstrndx = SHN_XINDEX; + } + + if (ld_state.file_type != relocatable_file_type) + /* DSOs and executables have to define the entry point symbol. */ + ehdr->e_entry = find_entry_point (); + + if (unlikely (xelf_update_ehdr (ld_state.outelf, ehdr) == 0)) + error (EXIT_FAILURE, 0, + gettext ("cannot update ELF header: %s"), + elf_errmsg (-1)); + + + /* Free the data which we don't need anymore. */ + free (ld_state.dblindirect); + + + /* 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; +} + + +/* This is a function which must be specified in all backends. */ +static void +ld_generic_relocate_section (struct ld_state *statep, Elf_Scn *outscn, + struct scninfo *firstp, + const Elf32_Word *dblindirect) +{ + error (EXIT_FAILURE, 0, gettext ("\ +linker backend didn't specify function to relocate section")); + /* NOTREACHED */ +} + + +/* Finalize the output file. */ +static int +ld_generic_finalize (struct ld_state *statep) +{ + /* Write out the ELF file data. */ + if (elf_update (ld_state.outelf, ELF_C_WRITE) == -1) + error (EXIT_FAILURE, 0, gettext ("while writing output file: %s"), + elf_errmsg (-1)); + + /* Free the resources. */ + if (elf_end (ld_state.outelf) != 0) + error (EXIT_FAILURE, 0, gettext ("while finishing output file: %s"), + elf_errmsg (-1)); + + /* Get the file status of the temporary file. */ + struct stat temp_st; + if (fstat (ld_state.outfd, &temp_st) != 0) + error (EXIT_FAILURE, errno, gettext ("cannot stat output file")); + + /* Now it's time to rename the file. Remove an old existing file + first. */ + if (rename (ld_state.tempfname, ld_state.outfname) != 0) + /* Something went wrong. */ + error (EXIT_FAILURE, errno, gettext ("cannot rename output file")); + + /* Make sure the output file is really the one we created. */ + struct stat new_st; + if (stat (ld_state.outfname, &new_st) != 0 + || new_st.st_ino != temp_st.st_ino + || new_st.st_dev != temp_st.st_dev) + { + /* Wow, somebody overwrote the output file, probably some intruder. */ + unlink (ld_state.outfname); + error (EXIT_FAILURE, 0, gettext ("\ +WARNING: temporary output file overwritten before linking finished")); + } + + /* Close the file descriptor. */ + (void) close (ld_state.outfd); + + /* Signal the cleanup handler that the file is correctly created. */ + ld_state.tempfname = NULL; + + return 0; +} + + +static bool +ld_generic_special_section_number_p (struct ld_state *statep, size_t number) +{ + /* There are no special section numbers in the gABI. */ + return false; +} + + +static bool +ld_generic_section_type_p (struct ld_state *statep, GElf_Word type) +{ + if (type < SHT_NUM + /* XXX Enable the following two when implemented. */ + // || type == SHT_GNU_LIBLIST + // || type == SHT_CHECKSUM + /* XXX Eventually include SHT_SUNW_move, SHT_SUNW_COMDAT, and + SHT_SUNW_syminfo. */ + || (type >= SHT_GNU_verdef && type <= SHT_GNU_versym)) + return true; + + return false; +} + + +static XElf_Xword +ld_generic_dynamic_section_flags (struct ld_state *statep) +{ + /* By default the .dynamic section is writable (and is of course + loaded). Few architecture differ from this. */ + return SHF_ALLOC | SHF_WRITE; +} + + +static void +ld_generic_initialize_plt (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_plt"); +} + + +static void +ld_generic_initialize_pltrel (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_pltrel"); +} + + +static void +ld_generic_initialize_got (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_got"); +} + + +static void +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. */ +} + + +static int +ld_generic_rel_type (struct ld_state *statep) +{ + /* 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"), + "rel_type"); + /* Just to keep the compiler calm. */ + return 0; +} + + +static void +ld_generic_count_relocations (struct ld_state *statep, struct scninfo *scninfo) +{ + /* 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"), + "count_relocations"); +} + + +static void +ld_generic_create_relocations (struct ld_state *statep, + const Elf32_Word *dblindirect) +{ + /* 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"), + "create_relocations"); +} diff --git a/0.153/src/ldlex.c b/0.153/src/ldlex.c new file mode 100644 index 00000000..e1b5b4e9 --- /dev/null +++ b/0.153/src/ldlex.c @@ -0,0 +1,2933 @@ +#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_flex_debug ld_flex_debug +#define yy_init_buffer ld_init_buffer +#define yy_flush_buffer ld_flush_buffer +#define yy_load_buffer_state ld_load_buffer_state +#define yy_switch_to_buffer ld_switch_to_buffer +#define yyin ldin +#define yyleng ldleng +#define yylex ldlex +#define yylineno ldlineno +#define yyout ldout +#define yyrestart ldrestart +#define yytext ldtext +#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 <string.h> +#include <errno.h> +#include <stdlib.h> + +/* end standard C headers. */ + +/* 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 */ + +/* 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 + +#endif /* ! FLEXINT_H */ + +#ifdef __cplusplus + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +/* C99 requires __STDC__ to be defined as 1. */ +#if defined (__STDC__) + +#define YY_USE_CONST + +#endif /* defined (__STDC__) */ +#endif /* ! __cplusplus */ + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#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 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 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 ldleng; + +extern FILE *ldin, *ldout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + /* 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 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 + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up ldtext again */ \ + } \ + while ( 0 ) + +#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; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + 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 + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (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 */ + +/* 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_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 ldtext is formed. */ +static char yy_hold_char; +static int yy_n_chars; /* number of characters read into yy_ch_buf */ +int ldleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = (char *) 0; +static int yy_init = 0; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* 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 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 ); + +#define YY_FLUSH_BUFFER ld_flush_buffer(YY_CURRENT_BUFFER ) + +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 ); + +void *ldalloc (yy_size_t ); +void *ldrealloc (void *,yy_size_t ); +void ldfree (void * ); + +#define yy_new_buffer ld_create_buffer + +#define yy_set_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 ){\ + 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_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +#define ldwrap(n) 1 +#define YY_SKIP_YYWRAP + +typedef unsigned char YY_CHAR; + +FILE *ldin = (FILE *) 0, *ldout = (FILE *) 0; + +typedef int yy_state_type; + +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 ldtext. + */ +#define YY_DO_BEFORE_ACTION \ + (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 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, + 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 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, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 1, 4, 1, 5, 6, 1, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 16, 16, + 16, 16, 16, 16, 16, 17, 17, 18, 19, 1, + 20, 1, 21, 1, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 13, 31, 32, 33, 34, 35, 36, + 13, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 1, 49, 1, 50, 51, 52, 53, + + 54, 55, 56, 13, 57, 13, 58, 59, 58, 60, + 61, 13, 13, 13, 62, 13, 13, 13, 13, 63, + 13, 13, 64, 65, 66, 47, 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, 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, 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, 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, + 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 + } ; + +static yyconst flex_int32_t yy_meta[67] = + { 0, + 1, 2, 2, 1, 1, 1, 2, 2, 3, 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, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 2, 1, 2 + } ; + +static yyconst flex_int16_t yy_base[223] = + { 0, + 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 flex_int16_t yy_def[223] = + { 0, + 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, 19, 218, 218, 19, + 19, 218, 218, 218, 218, 52, 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, 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, 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 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, + 24, 25, 17, 17, 17, 26, 17, 27, 17, 28, + 29, 17, 17, 17, 30, 31, 17, 32, 17, 17, + 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, 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 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, + 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, 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, 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 + } ; + +/* 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 *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. + + 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 <ctype.h> +#include <elf.h> +#include <error.h> +#include <inttypes.h> +#include <libintl.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> + +#include <system.h> +#include <ld.h> +#include "ldscript.h" + +/* We sure use no threads to read the stream, so use the _unlocked + variants of the functions. */ +#undef getc +#define getc(s) getc_unlocked (s) +#undef ferror +#define ferror(s) ferror_unlocked (s) +#undef fread +#define fread(b, m, n, s) fread_unlocked (b, m, n, s) +#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; + +#define MAX_PREPDEPTH 20 +static enum prepstate +{ + prep_normal, + skip_if, + skip_to_endif +} prepstate[MAX_PREPDEPTH]; +static int prepdepth; + +static void eat_comment (void); +static void eat_to_eol (bool empty); +static int attrib_convert (int c); +static void push_state (enum prepstate); +static int pop_state (void); +static int handle_ifdef (void); +static void invalid_char (int ch); + +#line 1157 "ldlex.c" + +#define INITIAL 0 +#define IGNORE 1 + +#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. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int ldwrap (void ); +#else +extern int ldwrap (void ); +#endif +#endif + + static void yyunput (int c,char *buf_ptr ); + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ); +#endif + +#ifndef YY_NO_INPUT + +#ifdef __cplusplus +static int yyinput (void ); +#else +static int input (void ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#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 do { if (fwrite( ldtext, ldleng, 1, ldout )) {} } while (0) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + unsigned n; \ + for ( n = 0; n < max_size && \ + (c = getc( ldin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( ldin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + 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();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#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_IS_OURS 1 + +extern int ldlex (void); + +#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 +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + 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, *yy_bp; + register int yy_act; + +#line 96 "ldlex.l" + + if (unlikely (ld_scan_version_script)) + { + ld_scan_version_script = -1; + return kVERSION_SCRIPT; + } + +#line 1351 "ldlex.c" + + if ( !(yy_init) ) + { + (yy_init) = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! (yy_start) ) + (yy_start) = 1; /* first start state */ + + if ( ! ldin ) + ldin = stdin; + + if ( ! ldout ) + ldout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + ldensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + ld_create_buffer(ldin,YY_BUF_SIZE ); + } + + ld_load_buffer_state( ); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = (yy_c_buf_p); + + /* 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_AT_BOL(); +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 >= 219 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + ++yy_cp; + } + while ( yy_current_state != 218 ); + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + + YY_DO_BEFORE_ACTION; + + if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] ) + { + int yyl; + 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: +/* 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 103 "ldlex.l" +{ BEGIN (handle_ifdef ()); } + YY_BREAK +case 2: +/* 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 104 "ldlex.l" +{ eat_to_eol (true); + push_state (skip_to_endif); + BEGIN (IGNORE); } + YY_BREAK +case 3: +/* 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 107 "ldlex.l" +{ eat_to_eol (false); + push_state (skip_to_endif); + BEGIN (IGNORE); } + YY_BREAK +case 4: +/* 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 110 "ldlex.l" +{ eat_to_eol (true) ; } + YY_BREAK +case 5: +/* 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 112 "ldlex.l" +{ eat_to_eol (false); + push_state (skip_to_endif); } + YY_BREAK +case 6: +/* 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 114 "ldlex.l" +{ eat_to_eol (true); + assert (prepdepth > 0); + if (prepstate[prepdepth - 1] == skip_if) + { + /* Back to normal processing. */ + assert (prepdepth == 1); + BEGIN (pop_state ()); + } + } + YY_BREAK +case 7: +/* 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 123 "ldlex.l" +{ assert (prepdepth > 0); + if (prepstate[prepdepth - 1] == skip_if) + { + /* Maybe this symbol is defined. */ + pop_state (); + BEGIN (handle_ifdef ()); + } + } + YY_BREAK +case 8: +/* 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 131 "ldlex.l" +{ eat_to_eol (true); + BEGIN (pop_state ()); } + YY_BREAK +case 9: +/* rule 9 can match eol */ +YY_RULE_SETUP +#line 133 "ldlex.l" +{ /* nothing */ } + YY_BREAK +case 10: +YY_RULE_SETUP +#line 136 "ldlex.l" +{ eat_comment (); } + YY_BREAK +case 11: +YY_RULE_SETUP +#line 138 "ldlex.l" +{ return kALIGN; } + YY_BREAK +case 12: +YY_RULE_SETUP +#line 139 "ldlex.l" +{ return kAS_NEEDED; } + YY_BREAK +case 13: +YY_RULE_SETUP +#line 140 "ldlex.l" +{ return kENTRY; } + YY_BREAK +case 14: +YY_RULE_SETUP +#line 141 "ldlex.l" +{ return kEXCLUDE_FILE; } + YY_BREAK +case 15: +YY_RULE_SETUP +#line 142 "ldlex.l" +{ return kGLOBAL; } + YY_BREAK +case 16: +YY_RULE_SETUP +#line 143 "ldlex.l" +{ return kGROUP; } + YY_BREAK +case 17: +YY_RULE_SETUP +#line 144 "ldlex.l" +{ return kINPUT; } + YY_BREAK +case 18: +YY_RULE_SETUP +#line 145 "ldlex.l" +{ return kINTERP; } + YY_BREAK +case 19: +YY_RULE_SETUP +#line 146 "ldlex.l" +{ return kKEEP; } + YY_BREAK +case 20: +YY_RULE_SETUP +#line 147 "ldlex.l" +{ return kLOCAL; } + YY_BREAK +case 21: +YY_RULE_SETUP +#line 148 "ldlex.l" +{ return kOUTPUT_FORMAT; } + YY_BREAK +case 22: +YY_RULE_SETUP +#line 149 "ldlex.l" +{ return kPAGESIZE; } + YY_BREAK +case 23: +YY_RULE_SETUP +#line 150 "ldlex.l" +{ return kPROVIDE; } + YY_BREAK +case 24: +YY_RULE_SETUP +#line 151 "ldlex.l" +{ return kSEARCH_DIR; } + YY_BREAK +case 25: +YY_RULE_SETUP +#line 152 "ldlex.l" +{ return kSEGMENT; } + YY_BREAK +case 26: +YY_RULE_SETUP +#line 153 "ldlex.l" +{ return kSIZEOF_HEADERS; } + YY_BREAK +case 27: +YY_RULE_SETUP +#line 154 "ldlex.l" +{ return kSORT; } + YY_BREAK +case 28: +YY_RULE_SETUP +#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 < ldleng - 1) + ldlval.num |= attrib_convert (ldtext[cnt++]); + return kMODE; } + YY_BREAK +case 30: +YY_RULE_SETUP +#line 163 "ldlex.l" +{ return '{'; } + YY_BREAK +case 31: +YY_RULE_SETUP +#line 164 "ldlex.l" +{ return '}'; } + YY_BREAK +case 32: +YY_RULE_SETUP +#line 165 "ldlex.l" +{ return '('; } + YY_BREAK +case 33: +YY_RULE_SETUP +#line 166 "ldlex.l" +{ return ')'; } + YY_BREAK +case 34: +YY_RULE_SETUP +#line 167 "ldlex.l" +{ return ':'; } + YY_BREAK +case 35: +YY_RULE_SETUP +#line 168 "ldlex.l" +{ return ';'; } + YY_BREAK +case 36: +YY_RULE_SETUP +#line 169 "ldlex.l" +{ return '='; } + YY_BREAK +case 37: +YY_RULE_SETUP +#line 170 "ldlex.l" +{ ldlval.op = exp_plus; return kADD_OP; } + YY_BREAK +case 38: +YY_RULE_SETUP +#line 171 "ldlex.l" +{ ldlval.op = exp_minus; return kADD_OP; } + YY_BREAK +case 39: +YY_RULE_SETUP +#line 172 "ldlex.l" +{ return '*'; } + YY_BREAK +case 40: +YY_RULE_SETUP +#line 173 "ldlex.l" +{ ldlval.op = exp_div; return kMUL_OP; } + YY_BREAK +case 41: +YY_RULE_SETUP +#line 174 "ldlex.l" +{ ldlval.op = exp_mod; return kMUL_OP; } + YY_BREAK +case 42: +YY_RULE_SETUP +#line 175 "ldlex.l" +{ return '&'; } + YY_BREAK +case 43: +YY_RULE_SETUP +#line 176 "ldlex.l" +{ return '|'; } + YY_BREAK +case 44: +YY_RULE_SETUP +#line 178 "ldlex.l" +{ return ','; } + YY_BREAK +case 45: +YY_RULE_SETUP +#line 180 "ldlex.l" +{ char *endp; + ldlval.num = strtoumax (ldtext, &endp, 0); + if (*endp != '\0') + { + if (tolower (*endp) == 'k') + ldlval.num *= 1024; + else + { + assert (tolower (*endp) == 'm'); + ldlval.num *= 1024 * 1024; + } + } + return kNUM; } + YY_BREAK +case 46: +YY_RULE_SETUP +#line 194 "ldlex.l" +{ ldlval.str = obstack_strndup (&ld_state.smem, + ldtext, ldleng); + return kID; } + YY_BREAK +case 47: +YY_RULE_SETUP +#line 198 "ldlex.l" +{ ldlval.str = obstack_strndup (&ld_state.smem, + ldtext, ldleng); + return kFILENAME; } + YY_BREAK +case 48: +/* rule 48 can match eol */ +YY_RULE_SETUP +#line 202 "ldlex.l" +{ /* IGNORE */ } + YY_BREAK +case 49: +YY_RULE_SETUP +#line 204 "ldlex.l" +{ invalid_char (*ldtext); } + YY_BREAK +case 50: +YY_RULE_SETUP +#line 206 "ldlex.l" +ECHO; + YY_BREAK +#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; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = (yy_hold_char); + YY_RESTORE_YY_MORE_OFFSET + + 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 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_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 + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + 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_current_state = yy_get_previous_state( ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++(yy_c_buf_p); + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_END_OF_FILE: + { + (yy_did_buffer_switch_on_eof) = 0; + + if ( ldwrap( ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have 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_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + 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_current_state = yy_get_previous_state( ); + + 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_LVALUE->yy_ch_buf[(yy_n_chars)]; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ +} /* end of ldlex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +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_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + 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 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + 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_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_LVALUE->yy_n_chars = (yy_n_chars) = 0; + + else + { + 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. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER; + + int yy_c_buf_p_offset = + (int) ((yy_c_buf_p) - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + ldrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + (yy_n_chars), (size_t) num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + if ( (yy_n_chars) == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + ldrestart(ldin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + 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()" ); + } + + (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; + + (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 (void) +{ + register yy_state_type yy_current_state; + register char *yy_cp; + + yy_current_state = (yy_start); + yy_current_state += YY_AT_BOL(); + + 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 >= 219 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + } + + 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 ); + */ + 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 >= 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 == 218); + + return yy_is_jam ? 0 : yy_current_state; +} + + static void yyunput (int c, register char * yy_bp ) +{ + register char *yy_cp; + + yy_cp = (yy_c_buf_p); + + /* undo effects of setting up ldtext */ + *yy_cp = (yy_hold_char); + + 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_LVALUE->yy_ch_buf[ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; + register char *source = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; + + while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size; + + 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' ){ + --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 (void) +#else + static int input (void) +#endif + +{ + int c; + + *(yy_c_buf_p) = (yy_hold_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_LVALUE->yy_ch_buf[(yy_n_chars)] ) + /* This was really a NUL. */ + *(yy_c_buf_p) = '\0'; + + else + { /* need more input */ + int offset = (yy_c_buf_p) - (yytext_ptr); + ++(yy_c_buf_p); + + switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + ldrestart(ldin ); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( ldwrap( ) ) + return EOF; + + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + (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 ldtext */ + (yy_hold_char) = *++(yy_c_buf_p); + + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = (c == '\n'); + if ( YY_CURRENT_BUFFER_LVALUE->yy_at_bol ) + + ldlineno++; +; + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** 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( ); +} + +/** 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 ) + { + /* 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); + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + ld_load_buffer_state( ); + + /* We don't actually know whether we did this switch during + * 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; +} + +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); +} + +/** 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) ldalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + 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 *) ldalloc(b->yy_buf_size + 2 ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in ld_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + ld_init_buffer(b,file ); + + return b; +} + +/** 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 ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + ldfree((void *) b->yy_ch_buf ); + + 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 ) + +{ + int oerrno = errno; + + ld_flush_buffer(b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* 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; + } + + b->yy_is_interactive = 0; + + errno = oerrno; +} + +/** 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; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + 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){ + + /* 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) ldalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + 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; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + ld_switch_to_buffer(b ); + + return b; +} + +/** 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) ); +} + +/** 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 = _yybytes_len + 2; + buf = (char *) ldalloc(n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in ld_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = ld_scan_buffer(buf,n ); + if ( ! b ) + 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. + */ + 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 ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#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 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the current line number. + * + */ +int ldget_lineno (void) +{ + + return ldlineno; +} + +/** Get the input stream. + * + */ +FILE *ldget_in (void) +{ + return ldin; +} + +/** Get the output stream. + * + */ +FILE *ldget_out (void) +{ + return ldout; +} + +/** Get the length of the current token. + * + */ +int ldget_leng (void) +{ + return ldleng; +} + +/** Get the current token. + * + */ + +char *ldget_text (void) +{ + return ldtext; +} + +/** 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 ; +} + +void ldset_out (FILE * out_str ) +{ + ldout = out_str ; +} + +int ldget_debug (void) +{ + return ld_flex_debug; +} + +void ldset_debug (int bdebug ) +{ + ld_flex_debug = bdebug ; +} + +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 + ldin = (FILE *) 0; + ldout = (FILE *) 0; +#endif + + /* 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(); + } + + /* Destroy the stack itself. */ + ldfree((yy_buffer_stack) ); + (yy_buffer_stack) = NULL; + + /* 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. + */ + +#ifndef yytext_ptr +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 +static int yy_flex_strlen (yyconst char * s ) +{ + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *ldalloc (yy_size_t size ) +{ + return (void *) malloc( size ); +} + +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 + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); +} + +void ldfree (void * ptr ) +{ + free( (char *) ptr ); /* see ldrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 206 "ldlex.l" + + + +static void +eat_comment (void) +{ + while (1) + { + int c = input (); + + while (c != '*' && c != EOF) + c = input (); + + if (c == '*') + { + c = input (); + while (c == '*') + c = input (); + if (c == '/') + break; + } + + if (c == EOF) + { + /* XXX Use the setjmp buffer and signal EOF in comment */ + error (0, 0, gettext ("EOF in comment")); + break; + } + } +} + + +static void +eat_to_eol (bool empty) +{ + bool warned = false; + + while (1) + { + int c = input (); + + if (c == EOF) + break; + if (c == '\n') + { + ++ldlineno; + break; + } + + if (empty && ! isspace (c) && ! warned) + { + error (0, 0, gettext ("%d: garbage at end of line"), ldlineno); + warned = true; + } + } +} + + +static int +attrib_convert (int c) +{ + if (c == 'X') + return PF_X; + if (c == 'W') + return PF_W; + assert (c == 'R'); + return PF_R; +} + + +static void +push_state (enum prepstate state) +{ + if (prepdepth >= MAX_PREPDEPTH) + error (EXIT_FAILURE, 0, gettext ("%d: conditionals nested too deep"), + ldlineno); + + prepstate[prepdepth++] = state; +} + + +static int +pop_state (void) +{ + if (prepdepth == 0) + error (0, 0, gettext ("%d: unexpected #endif"), ldlineno); + else + --prepdepth; + + return prepdepth == 0 ? INITIAL : IGNORE; +} + + +static int +handle_ifdef (void) +{ + char idbuf[50]; + char *id = idbuf; + size_t idlen = 0; + size_t idmax = sizeof (idbuf); + bool ignore_ws = true; + bool defined = false; + int result; + + while (1) + { + int c = input (); + + if (isspace (c) && ignore_ws) + continue; + + if (c != '_' && (c < 'a' || c > 'z') && (c < 'A' || c > 'Z') + && (idlen == 0 || c < '0' || c > '9')) + { + unput (c); + break; + } + + if (idlen == idmax) + { + char *newp = (char *) alloca (idmax *= 2); + id = memcpy (newp, id, idlen); + } + + id[idlen++] = c; + ignore_ws = false; + } + + /* XXX Compare in a better way. */ + if (idlen == 6 && strncmp (id, "SHARED", 6) == 0) + defined = ld_state.file_type == dso_file_type; + + if (defined) + result = INITIAL; + else + { + push_state (skip_if); + result = IGNORE; + } + + return result; +} + + +static void +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, ldlineno); +} + + +// Local Variables: +// mode: C +// End: + diff --git a/0.153/src/ldlex.l b/0.153/src/ldlex.l new file mode 100644 index 00000000..eb15c7be --- /dev/null +++ b/0.153/src/ldlex.l @@ -0,0 +1,361 @@ +%{ +/* 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. + + 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 <ctype.h> +#include <elf.h> +#include <error.h> +#include <inttypes.h> +#include <libintl.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> + +#include <system.h> +#include <ld.h> +#include "ldscript.h" + +/* We sure use no threads to read the stream, so use the _unlocked + variants of the functions. */ +#undef getc +#define getc(s) getc_unlocked (s) +#undef ferror +#define ferror(s) ferror_unlocked (s) +#undef fread +#define fread(b, m, n, s) fread_unlocked (b, m, n, s) +#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; + +#define MAX_PREPDEPTH 20 +static enum prepstate +{ + prep_normal, + skip_if, + skip_to_endif +} prepstate[MAX_PREPDEPTH]; +static int prepdepth; + +static void eat_comment (void); +static void eat_to_eol (bool empty); +static int attrib_convert (int c); +static void push_state (enum prepstate); +static int pop_state (void); +static int handle_ifdef (void); +static void invalid_char (int ch); +%} + +ID [a-zA-Z0-9_.*?][a-zA-Z0-9_.*?-]* +FILENAMECHAR1 [a-zA-Z0-9_/.\\~] +FILENAMECHAR [^][{}[:space:]():;]+ +HEX 0[xX][0-9a-fA-F]+[kKmM]? +OCT 0[0-7]*[kKmM]? +DEC [0-9]+[kKmM]? +WHITE [[:space:]]+ + +%option yylineno +%option never-interactive +%option noyywrap + +%x IGNORE + +%% + if (unlikely (ld_scan_version_script)) + { + ld_scan_version_script = -1; + return kVERSION_SCRIPT; + } + +^"#"ifdef/[[:space:]] { BEGIN (handle_ifdef ()); } +^"#"else/[[:space:]\n] { eat_to_eol (true); + push_state (skip_to_endif); + BEGIN (IGNORE); } +^"#"elifdef/[[:space:]] { eat_to_eol (false); + push_state (skip_to_endif); + BEGIN (IGNORE); } +^"#"endif/[[:space:]\n] { eat_to_eol (true) ; } + +<IGNORE>^"#"ifdef/[[:space:]\n] { eat_to_eol (false); + push_state (skip_to_endif); } +<IGNORE>^"#"else/[[:space:]\n] { eat_to_eol (true); + assert (prepdepth > 0); + if (prepstate[prepdepth - 1] == skip_if) + { + /* Back to normal processing. */ + assert (prepdepth == 1); + BEGIN (pop_state ()); + } + } +<IGNORE>^"#"elifdef/[[:space:]] { assert (prepdepth > 0); + if (prepstate[prepdepth - 1] == skip_if) + { + /* Maybe this symbol is defined. */ + pop_state (); + BEGIN (handle_ifdef ()); + } + } +<IGNORE>^"#"endif/[[:space:]\n] { eat_to_eol (true); + BEGIN (pop_state ()); } +<IGNORE>.|\n { /* nothing */ } + + +"/*" { eat_comment (); } + +ALIGN { return kALIGN; } +AS_NEEDED { return kAS_NEEDED; } +ENTRY { return kENTRY; } +EXCLUDE_FILE { return kEXCLUDE_FILE; } +"global:" { return kGLOBAL; } +GROUP { return kGROUP; } +INPUT { return kINPUT; } +INTERP { return kINTERP; } +KEEP { return kKEEP; } +"local:" { return kLOCAL; } +OUTPUT_FORMAT { return kOUTPUT_FORMAT; } +PAGESIZE { return kPAGESIZE; } +PROVIDE { return kPROVIDE; } +SEARCH_DIR { return kSEARCH_DIR; } +SEGMENT { return kSEGMENT; } +SIZEOF_HEADERS { return kSIZEOF_HEADERS; } +SORT { return kSORT; } +VERSION { return kVERSION; } + +"["([RWX]){0,3}"]" { int cnt = 1 ; + ldlval.num = 0; + while (cnt < yyleng - 1) + ldlval.num |= attrib_convert (yytext[cnt++]); + return kMODE; } + +"{" { return '{'; } +"}" { return '}'; } +"(" { return '('; } +")" { return ')'; } +":" { return ':'; } +";" { return ';'; } +"=" { return '='; } +"+" { ldlval.op = exp_plus; return kADD_OP; } +"-" { ldlval.op = exp_minus; return kADD_OP; } +"*" { return '*'; } +"/" { ldlval.op = exp_div; return kMUL_OP; } +"%" { ldlval.op = exp_mod; return kMUL_OP; } +"&" { return '&'; } +"|" { return '|'; } + +"," { return ','; } + +{HEX}|{OCT}|{DEC} { char *endp; + ldlval.num = strtoumax (yytext, &endp, 0); + if (*endp != '\0') + { + if (tolower (*endp) == 'k') + ldlval.num *= 1024; + else + { + assert (tolower (*endp) == 'm'); + ldlval.num *= 1024 * 1024; + } + } + return kNUM; } + +{ID} { ldlval.str = obstack_strndup (&ld_state.smem, + yytext, yyleng); + return kID; } + +{FILENAMECHAR1}{FILENAMECHAR} { ldlval.str = obstack_strndup (&ld_state.smem, + yytext, yyleng); + return kFILENAME; } + +{WHITE} { /* IGNORE */ } + +. { invalid_char (*yytext); } + +%% + +static void +eat_comment (void) +{ + while (1) + { + int c = input (); + + while (c != '*' && c != EOF) + c = input (); + + if (c == '*') + { + c = input (); + while (c == '*') + c = input (); + if (c == '/') + break; + } + + if (c == EOF) + { + /* XXX Use the setjmp buffer and signal EOF in comment */ + error (0, 0, gettext ("EOF in comment")); + break; + } + } +} + + +static void +eat_to_eol (bool empty) +{ + bool warned = false; + + while (1) + { + int c = input (); + + if (c == EOF) + break; + if (c == '\n') + { + ++yylineno; + break; + } + + if (empty && ! isspace (c) && ! warned) + { + error (0, 0, gettext ("%d: garbage at end of line"), yylineno); + warned = true; + } + } +} + + +static int +attrib_convert (int c) +{ + if (c == 'X') + return PF_X; + if (c == 'W') + return PF_W; + assert (c == 'R'); + return PF_R; +} + + +static void +push_state (enum prepstate state) +{ + if (prepdepth >= MAX_PREPDEPTH) + error (EXIT_FAILURE, 0, gettext ("%d: conditionals nested too deep"), + yylineno); + + prepstate[prepdepth++] = state; +} + + +static int +pop_state (void) +{ + if (prepdepth == 0) + error (0, 0, gettext ("%d: unexpected #endif"), yylineno); + else + --prepdepth; + + return prepdepth == 0 ? INITIAL : IGNORE; +} + + +static int +handle_ifdef (void) +{ + char idbuf[50]; + char *id = idbuf; + size_t idlen = 0; + size_t idmax = sizeof (idbuf); + bool ignore_ws = true; + bool defined = false; + int result; + + while (1) + { + int c = input (); + + if (isspace (c) && ignore_ws) + continue; + + if (c != '_' && (c < 'a' || c > 'z') && (c < 'A' || c > 'Z') + && (idlen == 0 || c < '0' || c > '9')) + { + unput (c); + break; + } + + if (idlen == idmax) + { + char *newp = (char *) alloca (idmax *= 2); + id = memcpy (newp, id, idlen); + } + + id[idlen++] = c; + ignore_ws = false; + } + + /* XXX Compare in a better way. */ + if (idlen == 6 && strncmp (id, "SHARED", 6) == 0) + defined = ld_state.file_type == dso_file_type; + + if (defined) + result = INITIAL; + else + { + push_state (skip_if); + result = IGNORE; + } + + return result; +} + + +static void +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); +} + + +// Local Variables: +// mode: C +// End: diff --git a/0.153/src/ldscript.c b/0.153/src/ldscript.c new file mode 100644 index 00000000..05a393bb --- /dev/null +++ b/0.153/src/ldscript.c @@ -0,0 +1,2787 @@ +/* A Bison parser, made by GNU Bison 2.5. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2011 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 + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* 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. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* 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 + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.5" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + +/* Using locations. */ +#define YYLSP_NEEDED 0 + +/* Substitute the variable and function names. */ +#define yyparse ldparse +#define yylex ldlex +#define yyerror lderror +#define yylval ldlval +#define yychar ldchar +#define yydebug lddebug +#define yynerrs ldnerrs + + +/* Copy the first part of user declarations. */ + +/* Line 268 of yacc.c */ +#line 1 "ldscript.y" + +/* Parser for linker scripts. + Copyright (C) 2001-2011 Red Hat, Inc. + This file is part of Red Hat elfutils. + Written by Ulrich Drepper <drepper@redhat.com>, 2001. + + 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 <libintl.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <system.h> +#include <ld.h> + +/* The error handler. */ +static void yyerror (const char *s); + +/* Some helper functions we need to construct the data structures + describing information from the file. */ +static struct expression *new_expr (int tag); +static struct input_section_name *new_input_section_name (const char *name, + bool sort_flag); +static struct input_rule *new_input_rule (int tag); +static struct output_rule *new_output_rule (int tag); +static struct assignment *new_assignment (const char *variable, + struct expression *expression, + bool provide_flag); +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, + struct version *two); +static void add_versions (struct version *versions); + +extern int yylex (void); + + +/* Line 268 of yacc.c */ +#line 150 "ldscript.c" + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + kADD_OP = 258, + kALIGN = 259, + 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 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 +typedef union YYSTYPE +{ + +/* Line 293 of yacc.c */ +#line 71 "ldscript.y" + + uintmax_t num; + enum expression_tag op; + char *str; + struct expression *expr; + struct input_section_name *sectionname; + struct filemask_section_name *filemask_section_name; + struct input_rule *input_rule; + struct output_rule *output_rule; + struct assignment *assignment; + struct filename_list *filename_list; + struct version *version; + struct id_list *id_list; + + + +/* Line 293 of yacc.c */ +#line 261 "ldscript.c" +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +#endif + + +/* Copy the second part of user declarations. */ + + +/* Line 343 of yacc.c */ +#line 273 "ldscript.c" + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#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 + +#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 +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if defined YYENABLE_NLS && 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 yyi) +#else +static int +YYID (yyi) + int yyi; +#endif +{ + return yyi; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# 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 EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* 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 EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS && (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 EXIT_SUCCESS && (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 */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 32 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 226 + +/* 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 284 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +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, 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, 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, + 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, 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, 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, 27, 30, 32 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const yytype_uint8 yyprhs[] = +{ + 0, 0, 3, 5, 8, 11, 13, 19, 25, 31, + 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 yytype_int8 yyrhs[] = +{ + 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 yytype_uint16 yyrline[] = +{ + 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 || 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", "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", "filename_id_listelem", "versionlist", + "version", "version_stmt_list", "version_stmt", "filename_id_star_list", + "filename_id", "filename_id_star", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +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, 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 yytype_uint8 yyr1[] = +{ + 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 yytype_uint8 yyr2[] = +{ + 0, 2, 1, 2, 2, 1, 5, 5, 5, 5, + 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 reduction number in state STATE-NUM. + Performed when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 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 yytype_int16 yydefgoto[] = +{ + -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 -86 +static const yytype_int16 yypact[] = +{ + 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 yytype_int16 yypgoto[] = +{ + -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 + positive, shift that token. If negative, reduce the rule which + number is the opposite. If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -1 +static const yytype_uint8 yytable[] = +{ + 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 +}; + +#define yypact_value_is_default(yystate) \ + ((yystate) == (-86)) + +#define yytable_value_is_error(yytable_value) \ + YYID (0) + +static const yytype_int16 yycheck[] = +{ + 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 yytype_uint8 yystos[] = +{ + 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 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. However, + YYFAIL appears to be in use. Nevertheless, it is formally deprecated + in Bison 2.4.2's NEWS entry, where a plan to phase it out is + discussed. */ + +#define YYFAIL goto yyerrlab +#if defined YYFAIL + /* This is here to suppress warnings from the GCC cpp's + -Wunused-macros. Normally we don't worry about that warning, but + some users do, and we want to make it easy for users to remove + YYFAIL uses, which will produce warnings from Bison 2.5. */ +#endif + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (1); \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + + +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* 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) \ + 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 + + +/* This macro is provided for backward compatibility. */ + +#ifndef YY_LOCATION_PRINT +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (YYLEX_PARAM) +#else +# define YYLEX yylex () +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include <stdio.h> /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (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)) + + +/*--------------------------------. +| 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 __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) +#else +static void +yy_stack_print (yybottom, yytop) + yytype_int16 *yybottom; + yytype_int16 *yytop; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, int yyrule) +#else +static void +yy_reduce_print (yyvsp, yyrule) + YYSTYPE *yyvsp; + int yyrule; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + 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++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + ); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + 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 YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# 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 +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# 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 * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# 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 (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, + yytype_int16 *yyssp, int yytoken) +{ + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytoken]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = 0; + /* Arguments of yyformat. */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Number of reported tokens (one for the "unexpected", one per + "expected"). */ + int yycount = 0; + + /* There are many possibilities here to consider: + - Assume YYFAIL is not used. It's too flawed to consider. See + <http://lists.gnu.org/archive/html/bison-patches/2009-12/msg00024.html> + for details. YYERROR is fine as it does not invoke this + function. + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[*yyssp]; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + 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 yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + if (! (yysize <= yysize1 + && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + } + } + + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } + + yysize1 = yysize + yystrlen (yyformat); + if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; + } + + /* 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 = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + yyp++; + yyformat++; + } + } + return 0; +} +#endif /* YYERROR_VERBOSE */ + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) +#else +static void +yydestruct (yymsg, yytype, yyvaluep) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + YYUSE (yyvaluep); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + + default: + break; + } +} + + +/* Prevent warnings from -Wmissing-prototypes. */ +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + +/* The lookahead symbol. */ +int yychar; + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +#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 __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void) +#else +int +yyparse () + +#endif +#endif +{ + int yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + `yyss': related to states. + `yyvs': related to semantic values. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + YYSIZE_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yytoken = 0; + yyss = yyssa; + yyvs = yyvsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + yyssp = yyss; + yyvsp = yyvs; + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + 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. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* 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; + 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 (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token. */ + yychar = YYEMPTY; + + yystate = yyn; + *++yyvsp = yylval; + + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 3: + +/* Line 1806 of yacc.c */ +#line 145 "ldscript.y" + { add_versions ((yyvsp[(2) - (2)].version)); } + break; + + case 6: + +/* Line 1806 of yacc.c */ +#line 153 "ldscript.y" + { + if (likely (ld_state.entry == NULL)) + ld_state.entry = (yyvsp[(3) - (5)].str); + } + break; + + case 7: + +/* Line 1806 of yacc.c */ +#line 158 "ldscript.y" + { + ld_new_searchdir ((yyvsp[(3) - (5)].str)); + } + break; + + case 8: + +/* Line 1806 of yacc.c */ +#line 162 "ldscript.y" + { + if (likely (ld_state.pagesize == 0)) + ld_state.pagesize = (yyvsp[(3) - (5)].num); + } + break; + + case 9: + +/* Line 1806 of yacc.c */ +#line 167 "ldscript.y" + { + if (likely (ld_state.interp == NULL) + && ld_state.file_type != dso_file_type) + ld_state.interp = (yyvsp[(3) - (5)].str); + } + break; + + case 10: + +/* Line 1806 of yacc.c */ +#line 173 "ldscript.y" + { + new_segment ((yyvsp[(2) - (5)].num), (yyvsp[(4) - (5)].output_rule)); + } + break; + + case 11: + +/* Line 1806 of yacc.c */ +#line 177 "ldscript.y" + { + fputs_unlocked (gettext ("mode for segment invalid\n"), + stderr); + new_segment (0, (yyvsp[(4) - (5)].output_rule)); + } + break; + + case 12: + +/* Line 1806 of yacc.c */ +#line 183 "ldscript.y" + { + /* 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; + } + add_inputfiles ((yyvsp[(3) - (4)].filename_list)); + } + break; + + case 13: + +/* Line 1806 of yacc.c */ +#line 194 "ldscript.y" + { add_inputfiles ((yyvsp[(3) - (4)].filename_list)); } + break; + + case 14: + +/* Line 1806 of yacc.c */ +#line 196 "ldscript.y" + { add_inputfiles (mark_as_needed ((yyvsp[(3) - (4)].filename_list))); } + break; + + case 15: + +/* Line 1806 of yacc.c */ +#line 198 "ldscript.y" + { add_versions ((yyvsp[(3) - (4)].version)); } + break; + + case 16: + +/* Line 1806 of yacc.c */ +#line 200 "ldscript.y" + { /* XXX TODO */ } + break; + + case 17: + +/* Line 1806 of yacc.c */ +#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 1806 of yacc.c */ +#line 209 "ldscript.y" + { (yyval.output_rule) = (yyvsp[(1) - (1)].output_rule); } + break; + + case 19: + +/* Line 1806 of yacc.c */ +#line 213 "ldscript.y" + { + (yyval.output_rule) = new_output_rule (output_assignment); + (yyval.output_rule)->val.assignment = (yyvsp[(1) - (2)].assignment); + } + break; + + case 20: + +/* Line 1806 of yacc.c */ +#line 218 "ldscript.y" + { + (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[(1) - (4)].str))) + (yyval.output_rule)->val.section.ignored = true; + else + (yyval.output_rule)->val.section.ignored = false; + (yyvsp[(3) - (4)].input_rule)->next = NULL; + } + break; + + case 21: + +/* Line 1806 of yacc.c */ +#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) - (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) - (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) - (2)].str))) + (yyval.output_rule)->val.section.ignored = true; + else + (yyval.output_rule)->val.section.ignored = false; + } + break; + + case 22: + +/* Line 1806 of yacc.c */ +#line 254 "ldscript.y" + { (yyval.assignment) = new_assignment ((yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].expr), false); } + break; + + case 23: + +/* Line 1806 of yacc.c */ +#line 256 "ldscript.y" + { (yyval.assignment) = new_assignment ((yyvsp[(3) - (6)].str), (yyvsp[(5) - (6)].expr), true); } + break; + + case 24: + +/* Line 1806 of yacc.c */ +#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 1806 of yacc.c */ +#line 265 "ldscript.y" + { (yyval.input_rule) = (yyvsp[(1) - (1)].input_rule); } + break; + + case 26: + +/* Line 1806 of yacc.c */ +#line 269 "ldscript.y" + { + (yyval.input_rule) = new_input_rule (input_section); + (yyval.input_rule)->val.section = (yyvsp[(1) - (1)].filemask_section_name); + } + break; + + case 27: + +/* Line 1806 of yacc.c */ +#line 274 "ldscript.y" + { + (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 1806 of yacc.c */ +#line 281 "ldscript.y" + { + (yyval.input_rule) = new_input_rule (input_assignment); + (yyval.input_rule)->val.assignment = (yyvsp[(1) - (2)].assignment); + } + break; + + case 29: + +/* Line 1806 of yacc.c */ +#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 1806 of yacc.c */ +#line 299 "ldscript.y" + { (yyval.sectionname) = new_input_section_name ((yyvsp[(1) - (1)].str), false); } + break; + + case 31: + +/* Line 1806 of yacc.c */ +#line 301 "ldscript.y" + { (yyval.sectionname) = new_input_section_name ((yyvsp[(3) - (4)].str), true); } + break; + + case 32: + +/* Line 1806 of yacc.c */ +#line 305 "ldscript.y" + { (yyval.str) = (yyvsp[(3) - (4)].str); } + break; + + case 33: + +/* Line 1806 of yacc.c */ +#line 307 "ldscript.y" + { (yyval.str) = NULL; } + break; + + case 34: + +/* Line 1806 of yacc.c */ +#line 311 "ldscript.y" + { + (yyval.expr) = new_expr (exp_align); + (yyval.expr)->val.child = (yyvsp[(3) - (4)].expr); + } + break; + + case 35: + +/* Line 1806 of yacc.c */ +#line 316 "ldscript.y" + { (yyval.expr) = (yyvsp[(2) - (3)].expr); } + break; + + case 36: + +/* Line 1806 of yacc.c */ +#line 318 "ldscript.y" + { + (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 1806 of yacc.c */ +#line 324 "ldscript.y" + { + (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 1806 of yacc.c */ +#line 330 "ldscript.y" + { + (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 1806 of yacc.c */ +#line 336 "ldscript.y" + { + (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 1806 of yacc.c */ +#line 342 "ldscript.y" + { + (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 1806 of yacc.c */ +#line 348 "ldscript.y" + { + (yyval.expr) = new_expr (exp_num); + (yyval.expr)->val.num = (yyvsp[(1) - (1)].num); + } + break; + + case 42: + +/* Line 1806 of yacc.c */ +#line 353 "ldscript.y" + { + (yyval.expr) = new_expr (exp_id); + (yyval.expr)->val.str = (yyvsp[(1) - (1)].str); + } + break; + + case 43: + +/* Line 1806 of yacc.c */ +#line 358 "ldscript.y" + { (yyval.expr) = new_expr (exp_sizeof_headers); } + break; + + case 44: + +/* Line 1806 of yacc.c */ +#line 360 "ldscript.y" + { (yyval.expr) = new_expr (exp_pagesize); } + break; + + case 45: + +/* Line 1806 of yacc.c */ +#line 364 "ldscript.y" + { + (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 46: + +/* Line 1806 of yacc.c */ +#line 369 "ldscript.y" + { (yyval.filename_list) = (yyvsp[(1) - (1)].filename_list); } + break; + + case 49: + +/* Line 1806 of yacc.c */ +#line 377 "ldscript.y" + { + /* 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 1806 of yacc.c */ +#line 388 "ldscript.y" + { (yyval.filename_list) = mark_as_needed ((yyvsp[(3) - (4)].filename_list)); } + break; + + case 51: + +/* Line 1806 of yacc.c */ +#line 390 "ldscript.y" + { (yyval.filename_list) = new_filename_listelem ((yyvsp[(1) - (1)].str)); } + break; + + case 52: + +/* Line 1806 of yacc.c */ +#line 395 "ldscript.y" + { + (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 1806 of yacc.c */ +#line 400 "ldscript.y" + { (yyval.version) = (yyvsp[(1) - (1)].version); } + break; + + case 54: + +/* Line 1806 of yacc.c */ +#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 1806 of yacc.c */ +#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 1806 of yacc.c */ +#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 1806 of yacc.c */ +#line 425 "ldscript.y" + { (yyval.version) = merge_versions ((yyvsp[(1) - (2)].version), (yyvsp[(2) - (2)].version)); } + break; + + case 58: + +/* Line 1806 of yacc.c */ +#line 427 "ldscript.y" + { (yyval.version) = (yyvsp[(1) - (1)].version); } + break; + + case 59: + +/* Line 1806 of yacc.c */ +#line 431 "ldscript.y" + { (yyval.version) = new_version (NULL, (yyvsp[(2) - (2)].id_list)); } + break; + + case 60: + +/* Line 1806 of yacc.c */ +#line 433 "ldscript.y" + { (yyval.version) = new_version ((yyvsp[(2) - (2)].id_list), NULL); } + break; + + case 61: + +/* Line 1806 of yacc.c */ +#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 1806 of yacc.c */ +#line 444 "ldscript.y" + { (yyval.id_list) = new_id_listelem ((yyvsp[(1) - (2)].str)); } + break; + + case 63: + +/* Line 1806 of yacc.c */ +#line 448 "ldscript.y" + { (yyval.str) = (yyvsp[(1) - (1)].str); } + break; + + case 64: + +/* Line 1806 of yacc.c */ +#line 450 "ldscript.y" + { (yyval.str) = (yyvsp[(1) - (1)].str); } + break; + + case 65: + +/* Line 1806 of yacc.c */ +#line 454 "ldscript.y" + { (yyval.str) = (yyvsp[(1) - (1)].str); } + break; + + case 66: + +/* Line 1806 of yacc.c */ +#line 456 "ldscript.y" + { (yyval.str) = NULL; } + break; + + + +/* Line 1806 of yacc.c */ +#line 2203 "ldscript.c" + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (YY_("syntax error")); +#else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) + { + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; + } +# undef YYSYNTAX_ERROR +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* 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; + + /* 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; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + *++yyvsp = yylval; + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#if !defined(yyoverflow) || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + 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 +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} + + + +/* Line 2067 of yacc.c */ +#line 459 "ldscript.y" + + +static void +yyerror (const char *s) +{ + error (0, 0, (ld_scan_version_script + ? gettext ("while reading version script '%s': %s at line %d") + : gettext ("while reading linker script '%s': %s at line %d")), + ldin_fname, gettext (s), ldlineno); +} + + +static struct expression * +new_expr (int tag) +{ + struct expression *newp = (struct expression *) + obstack_alloc (&ld_state.smem, sizeof (*newp)); + + newp->tag = tag; + return newp; +} + + +static struct input_section_name * +new_input_section_name (const char *name, bool sort_flag) +{ + struct input_section_name *newp = (struct input_section_name *) + obstack_alloc (&ld_state.smem, sizeof (*newp)); + + newp->name = name; + newp->sort_flag = sort_flag; + return newp; +} + + +static struct input_rule * +new_input_rule (int tag) +{ + struct input_rule *newp = (struct input_rule *) + obstack_alloc (&ld_state.smem, sizeof (*newp)); + + newp->tag = tag; + newp->next = newp; + return newp; +} + + +static struct output_rule * +new_output_rule (int tag) +{ + struct output_rule *newp = (struct output_rule *) + memset (obstack_alloc (&ld_state.smem, sizeof (*newp)), + '\0', sizeof (*newp)); + + newp->tag = tag; + newp->next = newp; + return newp; +} + + +static struct assignment * +new_assignment (const char *variable, struct expression *expression, + bool provide_flag) +{ + struct assignment *newp = (struct assignment *) + obstack_alloc (&ld_state.smem, sizeof (*newp)); + + newp->variable = variable; + newp->expression = expression; + newp->sym = NULL; + newp->provide_flag = provide_flag; + + /* Insert the symbol into a hash table. We will later have to matc*/ + return newp; +} + + +static void +new_segment (int mode, struct output_rule *output_rule) +{ + struct output_segment *newp; + + newp + = (struct output_segment *) obstack_alloc (&ld_state.smem, sizeof (*newp)); + newp->mode = mode; + newp->next = newp; + + newp->output_rules = output_rule->next; + output_rule->next = NULL; + + /* Enqueue the output segment description. */ + if (ld_state.output_segments == NULL) + ld_state.output_segments = newp; + else + { + newp->next = ld_state.output_segments->next; + ld_state.output_segments = ld_state.output_segments->next = newp; + } + + /* If the output file should be stripped of all symbol set the flag + in the structures of all output sections. */ + if (mode == 0 && ld_state.strip == strip_all) + { + struct output_rule *runp; + + for (runp = newp->output_rules; runp != NULL; runp = runp->next) + if (runp->tag == output_section) + runp->val.section.ignored = true; + } +} + + +static struct filename_list * +new_filename_listelem (const char *string) +{ + struct filename_list *newp; + + /* We use calloc and not the obstack since this object can be freed soon. */ + newp = (struct filename_list *) xcalloc (1, sizeof (*newp)); + newp->name = string; + newp->next = newp; + return newp; +} + + +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) +{ + assert (fnames != NULL); + + if (ld_state.srcfiles == NULL) + ld_state.srcfiles = fnames; + else + { + struct filename_list *first = ld_state.srcfiles->next; + + ld_state.srcfiles->next = fnames->next; + fnames->next = first; + ld_state.srcfiles->next = fnames; + } +} + + +static _Bool +special_char_p (const char *str) +{ + while (*str != '\0') + { + if (__builtin_expect (*str == '*', 0) + || __builtin_expect (*str == '?', 0) + || __builtin_expect (*str == '[', 0)) + return true; + + ++str; + } + + return false; +} + + +static struct id_list * +new_id_listelem (const char *str) +{ + struct id_list *newp; + + newp = (struct id_list *) obstack_alloc (&ld_state.smem, sizeof (*newp)); + if (str == NULL) + newp->u.id_type = id_all; + else if (__builtin_expect (special_char_p (str), false)) + newp->u.id_type = id_wild; + else + newp->u.id_type = id_str; + newp->id = str; + newp->next = newp; + + return newp; +} + + +static struct version * +new_version (struct id_list *local, struct id_list *global) +{ + struct version *newp; + + newp = (struct version *) obstack_alloc (&ld_state.smem, sizeof (*newp)); + newp->next = newp; + newp->local_names = local; + newp->global_names = global; + newp->versionname = NULL; + newp->parentname = NULL; + + return newp; +} + + +static struct version * +merge_versions (struct version *one, struct version *two) +{ + assert (two->local_names == NULL || two->global_names == NULL); + + if (two->local_names != NULL) + { + if (one->local_names == NULL) + one->local_names = two->local_names; + else + { + two->local_names->next = one->local_names->next; + one->local_names = one->local_names->next = two->local_names; + } + } + else + { + if (one->global_names == NULL) + one->global_names = two->global_names; + else + { + two->global_names->next = one->global_names->next; + one->global_names = one->global_names->next = two->global_names; + } + } + + return one; +} + + +static void +add_id_list (const char *versionname, struct id_list *runp, _Bool local) +{ + struct id_list *lastp = runp; + + if (runp == NULL) + /* Nothing to do. */ + return; + + /* Convert into a simple single-linked list. */ + runp = runp->next; + assert (runp != NULL); + lastp->next = NULL; + + do + if (runp->u.id_type == id_str) + { + struct id_list *curp; + struct id_list *defp; + unsigned long int hval = elf_hash (runp->id); + + curp = runp; + runp = runp->next; + + defp = ld_version_str_tab_find (&ld_state.version_str_tab, hval, curp); + if (defp != NULL) + { + /* There is already a version definition for this symbol. */ + while (strcmp (defp->u.s.versionname, versionname) != 0) + { + if (defp->next == NULL) + { + /* No version like this so far. */ + defp->next = curp; + curp->u.s.local = local; + curp->u.s.versionname = versionname; + curp->next = NULL; + defp = NULL; + break; + } + + defp = defp->next; + } + + if (defp != NULL && defp->u.s.local != local) + error (EXIT_FAILURE, 0, versionname[0] == '\0' + ? gettext ("\ +symbol '%s' is declared both local and global for unnamed version") + : gettext ("\ +symbol '%s' is declared both local and global for version '%s'"), + runp->id, versionname); + } + else + { + /* This is the first version definition for this symbol. */ + ld_version_str_tab_insert (&ld_state.version_str_tab, hval, curp); + + curp->u.s.local = local; + curp->u.s.versionname = versionname; + curp->next = NULL; + } + } + else if (runp->u.id_type == id_all) + { + if (local) + { + if (ld_state.default_bind_global) + error (EXIT_FAILURE, 0, + gettext ("default visibility set as local and global")); + ld_state.default_bind_local = true; + } + else + { + if (ld_state.default_bind_local) + error (EXIT_FAILURE, 0, + gettext ("default visibility set as local and global")); + ld_state.default_bind_global = true; + } + + runp = runp->next; + } + else + { + assert (runp->u.id_type == id_wild); + /* XXX TBI */ + abort (); + } + while (runp != NULL); +} + + +static void +add_versions (struct version *versions) +{ + struct version *lastp = versions; + + if (versions == NULL) + return; + + /* Convert into a simple single-linked list. */ + versions = versions->next; + assert (versions != NULL); + lastp->next = NULL; + + do + { + add_id_list (versions->versionname, versions->local_names, true); + add_id_list (versions->versionname, versions->global_names, false); + + versions = versions->next; + } + while (versions != NULL); +} + diff --git a/0.153/src/ldscript.h b/0.153/src/ldscript.h new file mode 100644 index 00000000..e33b803d --- /dev/null +++ b/0.153/src/ldscript.h @@ -0,0 +1,133 @@ +/* A Bison parser, made by GNU Bison 2.5. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2011 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 + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* 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. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + kADD_OP = 258, + kALIGN = 259, + 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 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 +typedef union YYSTYPE +{ + +/* Line 2068 of yacc.c */ +#line 71 "ldscript.y" + + uintmax_t num; + enum expression_tag op; + char *str; + struct expression *expr; + struct input_section_name *sectionname; + struct filemask_section_name *filemask_section_name; + struct input_rule *input_rule; + struct output_rule *output_rule; + struct assignment *assignment; + struct filename_list *filename_list; + struct version *version; + struct id_list *id_list; + + + +/* Line 2068 of yacc.c */ +#line 125 "ldscript.h" +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +#endif + +extern YYSTYPE ldlval; + + diff --git a/0.153/src/ldscript.y b/0.153/src/ldscript.y new file mode 100644 index 00000000..c2f1971a --- /dev/null +++ b/0.153/src/ldscript.y @@ -0,0 +1,811 @@ +%{ +/* Parser for linker scripts. + Copyright (C) 2001-2011 Red Hat, Inc. + This file is part of Red Hat elfutils. + Written by Ulrich Drepper <drepper@redhat.com>, 2001. + + 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 <libintl.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <system.h> +#include <ld.h> + +/* The error handler. */ +static void yyerror (const char *s); + +/* Some helper functions we need to construct the data structures + describing information from the file. */ +static struct expression *new_expr (int tag); +static struct input_section_name *new_input_section_name (const char *name, + bool sort_flag); +static struct input_rule *new_input_rule (int tag); +static struct output_rule *new_output_rule (int tag); +static struct assignment *new_assignment (const char *variable, + struct expression *expression, + bool provide_flag); +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, + struct version *two); +static void add_versions (struct version *versions); + +extern int yylex (void); +%} + +%union { + uintmax_t num; + enum expression_tag op; + char *str; + struct expression *expr; + struct input_section_name *sectionname; + struct filemask_section_name *filemask_section_name; + struct input_rule *input_rule; + struct output_rule *output_rule; + struct assignment *assignment; + struct filename_list *filename_list; + struct version *version; + struct id_list *id_list; +} + +%token kADD_OP +%token kALIGN +%token kAS_NEEDED +%token kENTRY +%token kEXCLUDE_FILE +%token <str> kFILENAME +%token kGLOBAL +%token kGROUP +%token <str> kID +%token kINPUT +%token kINTERP +%token kKEEP +%token kLOCAL +%token <num> kMODE +%token kMUL_OP +%token <num> kNUM +%token kOUTPUT_FORMAT +%token kPAGESIZE +%token kPROVIDE +%token kSEARCH_DIR +%token kSEGMENT +%token kSIZEOF_HEADERS +%token kSORT +%token kVERSION +%token kVERSION_SCRIPT + +%left '|' +%left '&' +%left ADD_OP +%left MUL_OP '*' + +%type <op> kADD_OP +%type <op> kMUL_OP +%type <str> filename_id +%type <str> filename_id_star +%type <str> exclude_opt +%type <expr> expr +%type <sectionname> sort_opt_name +%type <filemask_section_name> sectionname +%type <input_rule> inputsection +%type <input_rule> inputsections +%type <output_rule> outputsection +%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 +%type <version> version_stmt +%type <id_list> filename_id_star_list + +%expect 16 + +%% + +script_or_version: + file + | kVERSION_SCRIPT versionlist + { add_versions ($2); } + ; + +file: file content + | content + ; + +content: kENTRY '(' kID ')' ';' + { + if (likely (ld_state.entry == NULL)) + ld_state.entry = $3; + } + | kSEARCH_DIR '(' filename_id ')' ';' + { + ld_new_searchdir ($3); + } + | kPAGESIZE '(' kNUM ')' ';' + { + if (likely (ld_state.pagesize == 0)) + ld_state.pagesize = $3; + } + | kINTERP '(' filename_id ')' ';' + { + if (likely (ld_state.interp == NULL) + && ld_state.file_type != dso_file_type) + ld_state.interp = $3; + } + | kSEGMENT kMODE '{' outputsections '}' + { + new_segment ($2, $4); + } + | kSEGMENT error '{' outputsections '}' + { + fputs_unlocked (gettext ("mode for segment invalid\n"), + stderr); + new_segment (0, $4); + } + | 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; + } + add_inputfiles ($3); + } + | 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 ')' + { /* XXX TODO */ } + ; + +outputsections: outputsections outputsection + { + $2->next = $1->next; + $$ = $1->next = $2; + } + | outputsection + { $$ = $1; } + ; + +outputsection: assignment ';' + { + $$ = new_output_rule (output_assignment); + $$->val.assignment = $1; + } + | kID '{' inputsections '}' + { + $$ = new_output_rule (output_section); + $$->val.section.name = $1; + $$->val.section.input = $3->next; + if (ld_state.strip == strip_debug + && ebl_debugscn_p (ld_state.ebl, $1)) + $$->val.section.ignored = true; + else + $$->val.section.ignored = false; + $3->next = NULL; + } + | kID ';' + { + /* This is a short cut for "ID { *(ID) }". */ + $$ = new_output_rule (output_section); + $$->val.section.name = $1; + $$->val.section.input = new_input_rule (input_section); + $$->val.section.input->next = NULL; + $$->val.section.input->val.section = + (struct filemask_section_name *) + obstack_alloc (&ld_state.smem, + sizeof (struct filemask_section_name)); + $$->val.section.input->val.section->filemask = NULL; + $$->val.section.input->val.section->excludemask = NULL; + $$->val.section.input->val.section->section_name = + new_input_section_name ($1, false); + $$->val.section.input->val.section->keep_flag = false; + if (ld_state.strip == strip_debug + && ebl_debugscn_p (ld_state.ebl, $1)) + $$->val.section.ignored = true; + else + $$->val.section.ignored = false; + } + ; + +assignment: kID '=' expr + { $$ = new_assignment ($1, $3, false); } + | kPROVIDE '(' kID '=' expr ')' + { $$ = new_assignment ($3, $5, true); } + ; + +inputsections: inputsections inputsection + { + $2->next = $1->next; + $$ = $1->next = $2; + } + | inputsection + { $$ = $1; } + ; + +inputsection: sectionname + { + $$ = new_input_rule (input_section); + $$->val.section = $1; + } + | kKEEP '(' sectionname ')' + { + $3->keep_flag = true; + + $$ = new_input_rule (input_section); + $$->val.section = $3; + } + | assignment ';' + { + $$ = new_input_rule (input_assignment); + $$->val.assignment = $1; + } + ; + +sectionname: filename_id_star '(' exclude_opt sort_opt_name ')' + { + $$ = (struct filemask_section_name *) + obstack_alloc (&ld_state.smem, sizeof (*$$)); + $$->filemask = $1; + $$->excludemask = $3; + $$->section_name = $4; + $$->keep_flag = false; + } + ; + +sort_opt_name: kID + { $$ = new_input_section_name ($1, false); } + | kSORT '(' kID ')' + { $$ = new_input_section_name ($3, true); } + ; + +exclude_opt: kEXCLUDE_FILE '(' filename_id ')' + { $$ = $3; } + | + { $$ = NULL; } + ; + +expr: kALIGN '(' expr ')' + { + $$ = new_expr (exp_align); + $$->val.child = $3; + } + | '(' expr ')' + { $$ = $2; } + | expr '*' expr + { + $$ = new_expr (exp_mult); + $$->val.binary.left = $1; + $$->val.binary.right = $3; + } + | expr kMUL_OP expr + { + $$ = new_expr ($2); + $$->val.binary.left = $1; + $$->val.binary.right = $3; + } + | expr kADD_OP expr + { + $$ = new_expr ($2); + $$->val.binary.left = $1; + $$->val.binary.right = $3; + } + | expr '&' expr + { + $$ = new_expr (exp_and); + $$->val.binary.left = $1; + $$->val.binary.right = $3; + } + | expr '|' expr + { + $$ = new_expr (exp_or); + $$->val.binary.left = $1; + $$->val.binary.right = $3; + } + | kNUM + { + $$ = new_expr (exp_num); + $$->val.num = $1; + } + | kID + { + $$ = new_expr (exp_id); + $$->val.str = $1; + } + | kSIZEOF_HEADERS + { $$ = new_expr (exp_sizeof_headers); } + | kPAGESIZE + { $$ = new_expr (exp_pagesize); } + ; + +filename_id_list: filename_id_list comma_opt filename_id_listelem + { + $3->next = $1->next; + $$ = $1->next = $3; + } + | 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; + $$ = $1->next = $2; + } + | version + { $$ = $1; } + ; + +version: '{' version_stmt_list '}' ';' + { + $2->versionname = ""; + $2->parentname = NULL; + $$ = $2; + } + | filename_id '{' version_stmt_list '}' ';' + { + $3->versionname = $1; + $3->parentname = NULL; + $$ = $3; + } + | filename_id '{' version_stmt_list '}' filename_id ';' + { + $3->versionname = $1; + $3->parentname = $5; + $$ = $3; + } + ; + +version_stmt_list: + version_stmt_list version_stmt + { $$ = merge_versions ($1, $2); } + | version_stmt + { $$ = $1; } + ; + +version_stmt: kGLOBAL filename_id_star_list + { $$ = new_version (NULL, $2); } + | kLOCAL filename_id_star_list + { $$ = new_version ($2, NULL); } + ; + +filename_id_star_list: + filename_id_star_list filename_id_star ';' + { + struct id_list *newp = new_id_listelem ($2); + newp->next = $1->next; + $$ = $1->next = newp; + } + | filename_id_star ';' + { $$ = new_id_listelem ($1); } + ; + +filename_id: kFILENAME + { $$ = $1; } + | kID + { $$ = $1; } + ; + +filename_id_star: filename_id + { $$ = $1; } + | '*' + { $$ = NULL; } + ; + +%% + +static void +yyerror (const char *s) +{ + error (0, 0, (ld_scan_version_script + ? gettext ("while reading version script '%s': %s at line %d") + : gettext ("while reading linker script '%s': %s at line %d")), + ldin_fname, gettext (s), ldlineno); +} + + +static struct expression * +new_expr (int tag) +{ + struct expression *newp = (struct expression *) + obstack_alloc (&ld_state.smem, sizeof (*newp)); + + newp->tag = tag; + return newp; +} + + +static struct input_section_name * +new_input_section_name (const char *name, bool sort_flag) +{ + struct input_section_name *newp = (struct input_section_name *) + obstack_alloc (&ld_state.smem, sizeof (*newp)); + + newp->name = name; + newp->sort_flag = sort_flag; + return newp; +} + + +static struct input_rule * +new_input_rule (int tag) +{ + struct input_rule *newp = (struct input_rule *) + obstack_alloc (&ld_state.smem, sizeof (*newp)); + + newp->tag = tag; + newp->next = newp; + return newp; +} + + +static struct output_rule * +new_output_rule (int tag) +{ + struct output_rule *newp = (struct output_rule *) + memset (obstack_alloc (&ld_state.smem, sizeof (*newp)), + '\0', sizeof (*newp)); + + newp->tag = tag; + newp->next = newp; + return newp; +} + + +static struct assignment * +new_assignment (const char *variable, struct expression *expression, + bool provide_flag) +{ + struct assignment *newp = (struct assignment *) + obstack_alloc (&ld_state.smem, sizeof (*newp)); + + newp->variable = variable; + newp->expression = expression; + newp->sym = NULL; + newp->provide_flag = provide_flag; + + /* Insert the symbol into a hash table. We will later have to matc*/ + return newp; +} + + +static void +new_segment (int mode, struct output_rule *output_rule) +{ + struct output_segment *newp; + + newp + = (struct output_segment *) obstack_alloc (&ld_state.smem, sizeof (*newp)); + newp->mode = mode; + newp->next = newp; + + newp->output_rules = output_rule->next; + output_rule->next = NULL; + + /* Enqueue the output segment description. */ + if (ld_state.output_segments == NULL) + ld_state.output_segments = newp; + else + { + newp->next = ld_state.output_segments->next; + ld_state.output_segments = ld_state.output_segments->next = newp; + } + + /* If the output file should be stripped of all symbol set the flag + in the structures of all output sections. */ + if (mode == 0 && ld_state.strip == strip_all) + { + struct output_rule *runp; + + for (runp = newp->output_rules; runp != NULL; runp = runp->next) + if (runp->tag == output_section) + runp->val.section.ignored = true; + } +} + + +static struct filename_list * +new_filename_listelem (const char *string) +{ + struct filename_list *newp; + + /* We use calloc and not the obstack since this object can be freed soon. */ + newp = (struct filename_list *) xcalloc (1, sizeof (*newp)); + newp->name = string; + newp->next = newp; + return newp; +} + + +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) +{ + assert (fnames != NULL); + + if (ld_state.srcfiles == NULL) + ld_state.srcfiles = fnames; + else + { + struct filename_list *first = ld_state.srcfiles->next; + + ld_state.srcfiles->next = fnames->next; + fnames->next = first; + ld_state.srcfiles->next = fnames; + } +} + + +static _Bool +special_char_p (const char *str) +{ + while (*str != '\0') + { + if (__builtin_expect (*str == '*', 0) + || __builtin_expect (*str == '?', 0) + || __builtin_expect (*str == '[', 0)) + return true; + + ++str; + } + + return false; +} + + +static struct id_list * +new_id_listelem (const char *str) +{ + struct id_list *newp; + + newp = (struct id_list *) obstack_alloc (&ld_state.smem, sizeof (*newp)); + if (str == NULL) + newp->u.id_type = id_all; + else if (__builtin_expect (special_char_p (str), false)) + newp->u.id_type = id_wild; + else + newp->u.id_type = id_str; + newp->id = str; + newp->next = newp; + + return newp; +} + + +static struct version * +new_version (struct id_list *local, struct id_list *global) +{ + struct version *newp; + + newp = (struct version *) obstack_alloc (&ld_state.smem, sizeof (*newp)); + newp->next = newp; + newp->local_names = local; + newp->global_names = global; + newp->versionname = NULL; + newp->parentname = NULL; + + return newp; +} + + +static struct version * +merge_versions (struct version *one, struct version *two) +{ + assert (two->local_names == NULL || two->global_names == NULL); + + if (two->local_names != NULL) + { + if (one->local_names == NULL) + one->local_names = two->local_names; + else + { + two->local_names->next = one->local_names->next; + one->local_names = one->local_names->next = two->local_names; + } + } + else + { + if (one->global_names == NULL) + one->global_names = two->global_names; + else + { + two->global_names->next = one->global_names->next; + one->global_names = one->global_names->next = two->global_names; + } + } + + return one; +} + + +static void +add_id_list (const char *versionname, struct id_list *runp, _Bool local) +{ + struct id_list *lastp = runp; + + if (runp == NULL) + /* Nothing to do. */ + return; + + /* Convert into a simple single-linked list. */ + runp = runp->next; + assert (runp != NULL); + lastp->next = NULL; + + do + if (runp->u.id_type == id_str) + { + struct id_list *curp; + struct id_list *defp; + unsigned long int hval = elf_hash (runp->id); + + curp = runp; + runp = runp->next; + + defp = ld_version_str_tab_find (&ld_state.version_str_tab, hval, curp); + if (defp != NULL) + { + /* There is already a version definition for this symbol. */ + while (strcmp (defp->u.s.versionname, versionname) != 0) + { + if (defp->next == NULL) + { + /* No version like this so far. */ + defp->next = curp; + curp->u.s.local = local; + curp->u.s.versionname = versionname; + curp->next = NULL; + defp = NULL; + break; + } + + defp = defp->next; + } + + if (defp != NULL && defp->u.s.local != local) + error (EXIT_FAILURE, 0, versionname[0] == '\0' + ? gettext ("\ +symbol '%s' is declared both local and global for unnamed version") + : gettext ("\ +symbol '%s' is declared both local and global for version '%s'"), + runp->id, versionname); + } + else + { + /* This is the first version definition for this symbol. */ + ld_version_str_tab_insert (&ld_state.version_str_tab, hval, curp); + + curp->u.s.local = local; + curp->u.s.versionname = versionname; + curp->next = NULL; + } + } + else if (runp->u.id_type == id_all) + { + if (local) + { + if (ld_state.default_bind_global) + error (EXIT_FAILURE, 0, + gettext ("default visibility set as local and global")); + ld_state.default_bind_local = true; + } + else + { + if (ld_state.default_bind_local) + error (EXIT_FAILURE, 0, + gettext ("default visibility set as local and global")); + ld_state.default_bind_global = true; + } + + runp = runp->next; + } + else + { + assert (runp->u.id_type == id_wild); + /* XXX TBI */ + abort (); + } + while (runp != NULL); +} + + +static void +add_versions (struct version *versions) +{ + struct version *lastp = versions; + + if (versions == NULL) + return; + + /* Convert into a simple single-linked list. */ + versions = versions->next; + assert (versions != NULL); + lastp->next = NULL; + + do + { + add_id_list (versions->versionname, versions->local_names, true); + add_id_list (versions->versionname, versions->global_names, false); + + versions = versions->next; + } + while (versions != NULL); +} diff --git a/0.153/src/libld_elf_i386.map b/0.153/src/libld_elf_i386.map new file mode 100644 index 00000000..703af6d8 --- /dev/null +++ b/0.153/src/libld_elf_i386.map @@ -0,0 +1,7 @@ +ELFUTILS_1.0 { + global: + elf_i386_ld_init; + + local: + *; +}; diff --git a/0.153/src/make-debug-archive.in b/0.153/src/make-debug-archive.in new file mode 100644 index 00000000..c3fcbce4 --- /dev/null +++ b/0.153/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 diff --git a/0.153/src/nm.c b/0.153/src/nm.c new file mode 100644 index 00000000..f33302a4 --- /dev/null +++ b/0.153/src/nm.c @@ -0,0 +1,1506 @@ +/* Print symbol information from ELF file in human-readable form. + Copyright (C) 2000-2008, 2009, 2011, 2012 Red Hat, Inc. + This file is part of Red Hat elfutils. + Written by Ulrich Drepper <drepper@redhat.com>, 2000. + + 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 <ctype.h> +#include <dwarf.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <gelf.h> +#include <inttypes.h> +#include <libdw.h> +#include <libintl.h> +#include <locale.h> +#include <mcheck.h> +#include <obstack.h> +#include <search.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); +ARGP_PROGRAM_VERSION_HOOK_DEF = print_version; + +/* Bug report address. */ +ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT; + + +/* Values for the parameters which have no short form. */ +#define OPT_DEFINED 0x100 +#define OPT_MARK_SPECIAL 0x101 + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + { NULL, 0, NULL, 0, N_("Output selection:"), 0 }, + { "debug-syms", 'a', NULL, 0, N_("Display debugger-only symbols"), 0 }, + { "defined-only", OPT_DEFINED, NULL, 0, N_("Display only defined symbols"), + 0 }, + { "dynamic", 'D', NULL, 0, + N_("Display dynamic symbols instead of normal symbols"), 0 }, + { "extern-only", 'g', NULL, 0, N_("Display only external symbols"), 0 }, + { "undefined-only", 'u', NULL, 0, N_("Display only undefined symbols"), 0 }, + { "print-armap", 's', NULL, 0, + N_("Include index for symbols from archive members"), 0 }, + + { NULL, 0, NULL, 0, N_("Output format:"), 0 }, + { "print-file-name", 'A', NULL, 0, + N_("Print name of the input file before every symbol"), 0 }, + { NULL, 'o', NULL, OPTION_HIDDEN, "Same as -A", 0 }, + { "format", 'f', "FORMAT", 0, + N_("Use the output format FORMAT. FORMAT can be `bsd', `sysv' or `posix'. The default is `sysv'"), + 0 }, + { NULL, 'B', NULL, 0, N_("Same as --format=bsd"), 0 }, + { "portability", 'P', NULL, 0, N_("Same as --format=posix"), 0 }, + { "radix", 't', "RADIX", 0, N_("Use RADIX for printing symbol values"), 0 }, + { "mark-special", OPT_MARK_SPECIAL, NULL, 0, N_("Mark special symbols"), 0 }, + { "mark-weak", OPT_MARK_SPECIAL, NULL, OPTION_HIDDEN, "", 0 }, + { "print-size", 'S', NULL, 0, N_("Print size of defined symbols"), 0 }, + + { NULL, 0, NULL, 0, N_("Output options:"), 0 }, + { "numeric-sort", 'n', NULL, 0, N_("Sort symbols numerically by address"), + 0 }, + { "no-sort", 'p', NULL, 0, N_("Do not sort the symbols"), 0 }, + { "reverse-sort", 'r', NULL, 0, N_("Reverse the sense of the sort"), 0 }, +#ifdef USE_DEMANGLE + { "demangle", 'C', NULL, 0, + N_("Decode low-level symbol names into source code names"), 0 }, +#endif + { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 }, + { NULL, 0, NULL, 0, NULL, 0 } +}; + +/* Short description of program. */ +static const char doc[] = N_("List symbols 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); + +/* Parser children. */ +static struct argp_child argp_children[] = + { + { &color_argp, 0, N_("Output formatting"), 2 }, + { NULL, 0, NULL, 0} + }; + +/* Data structure to communicate with argp functions. */ +static struct argp argp = +{ + options, parse_opt, args_doc, doc, argp_children, 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)) + + +/* Internal representation of symbols. */ +typedef struct GElf_SymX +{ + GElf_Sym sym; + Elf32_Word xndx; + char *where; +} GElf_SymX; + + +/* User-selectable options. */ + +/* The selected output format. */ +static enum +{ + format_sysv = 0, + format_bsd, + format_posix +} format; + +/* Print defined, undefined, or both? */ +static bool hide_undefined; +static bool hide_defined; + +/* Print local symbols also? */ +static bool hide_local; + +/* Nonzero if full filename should precede every symbol. */ +static bool print_file_name; + +/* If true print size of defined symbols in BSD format. */ +static bool print_size; + +/* If true print archive index. */ +static bool print_armap; + +/* If true reverse sorting. */ +static bool reverse_sort; + +#ifdef USE_DEMANGLE +/* If true demangle symbols. */ +static bool demangle; +#endif + +/* Type of the section we are printing. */ +static GElf_Word symsec_type = SHT_SYMTAB; + +/* Sorting selection. */ +static enum +{ + sort_name = 0, + sort_numeric, + sort_nosort +} sort; + +/* Radix for printed numbers. */ +static enum +{ + radix_hex = 0, + radix_decimal, + radix_octal +} radix; + +/* If nonzero mark special symbols: + - weak symbols are distinguished from global symbols by adding + a `*' after the identifying letter for the symbol class and type. + - TLS symbols are distinguished from normal symbols by adding + a '@' after the identifying letter for the symbol class and type. */ +static bool mark_special; + + +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 (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. */ + (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL); + + /* Tell the library which version we are expecting. */ + (void) elf_version (EV_CURRENT); + + 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, "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\ +"), "2012"); + 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': + /* XXX */ + break; + +#ifdef USE_DEMANGLE + case 'C': + demangle = true; + break; +#endif + + case 'f': + if (strcmp (arg, "bsd") == 0) + format = format_bsd; + else if (strcmp (arg, "posix") == 0) + format = format_posix; + else + /* Be bug compatible. The BFD implementation also defaulted to + using the SysV format if nothing else matches. */ + format = format_sysv; + break; + + case 'g': + hide_local = true; + break; + + case 'n': + sort = sort_numeric; + break; + + case 'p': + sort = sort_nosort; + break; + + case 't': + if (strcmp (arg, "10") == 0 || strcmp (arg, "d") == 0) + radix = radix_decimal; + else if (strcmp (arg, "8") == 0 || strcmp (arg, "o") == 0) + radix = radix_octal; + else + radix = radix_hex; + break; + + case 'u': + hide_undefined = false; + hide_defined = true; + break; + + case 'A': + case 'o': + print_file_name = true; + break; + + case 'B': + format = format_bsd; + break; + + case 'D': + symsec_type = SHT_DYNSYM; + break; + + case 'P': + format = format_posix; + break; + + case OPT_DEFINED: + hide_undefined = true; + hide_defined = false; + break; + + case OPT_MARK_SPECIAL: + mark_special = true; + break; + + case 'S': + print_size = true; + break; + + case 's': + print_armap = true; + break; + + case 'r': + reverse_sort = true; + break; + + 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 closing '%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 closing '%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, "]"); + + /* First print the archive index if this is wanted. */ + if (print_armap) + { + Elf_Arsym *arsym = elf_getarsym (elf, NULL); + + if (arsym != NULL) + { + Elf_Arhdr *arhdr = NULL; + size_t arhdr_off = 0; /* Note: 0 is no valid offset. */ + + fputs_unlocked (gettext("\nArchive index:\n"), stdout); + + while (arsym->as_off != 0) + { + if (arhdr_off != arsym->as_off + && (elf_rand (elf, arsym->as_off) != arsym->as_off + || (subelf = elf_begin (fd, cmd, elf)) == NULL + || (arhdr = elf_getarhdr (subelf)) == NULL)) + { + error (0, 0, gettext ("invalid offset %zu for symbol %s"), + arsym->as_off, arsym->as_name); + continue; + } + + printf (gettext ("%s in %s\n"), arsym->as_name, arhdr->ar_name); + + ++arsym; + } + + if (elf_rand (elf, SARMAG) != SARMAG) + { + error (0, 0, + gettext ("cannot reset archive offset to beginning")); + return 1; + } + } + } + + /* 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; +} + + +/* Mapping of radix and binary class to length. */ +static const int length_map[2][3] = +{ + [ELFCLASS32 - 1] = + { + [radix_hex] = 8, + [radix_decimal] = 10, + [radix_octal] = 11 + }, + [ELFCLASS64 - 1] = + { + [radix_hex] = 16, + [radix_decimal] = 20, + [radix_octal] = 22 + } +}; + + +static int +global_compare (const void *p1, const void *p2) +{ + const Dwarf_Global *g1 = (const Dwarf_Global *) p1; + const Dwarf_Global *g2 = (const Dwarf_Global *) p2; + + return strcmp (g1->name, g2->name); +} + + +static void *global_root; + + +static int +get_global (Dwarf *dbg __attribute__ ((unused)), Dwarf_Global *global, + void *arg __attribute__ ((unused))) +{ + tsearch (memcpy (xmalloc (sizeof (Dwarf_Global)), global, + sizeof (Dwarf_Global)), + &global_root, global_compare); + + return DWARF_CB_OK; +} + + +struct local_name +{ + const char *name; + const char *file; + Dwarf_Word lineno; + Dwarf_Addr lowpc; + Dwarf_Addr highpc; +}; + + +static int +local_compare (const void *p1, const void *p2) +{ + struct local_name *g1 = (struct local_name *) p1; + struct local_name *g2 = (struct local_name *) p2; + int result; + + result = strcmp (g1->name, g2->name); + if (result == 0) + { + if (g1->lowpc <= g2->lowpc && g1->highpc >= g2->highpc) + { + /* g2 is contained in g1. Update the data. */ + g2->lowpc = g1->lowpc; + g2->highpc = g1->highpc; + result = 0; + } + else if (g2->lowpc <= g1->lowpc && g2->highpc >= g1->highpc) + { + /* g1 is contained in g2. Update the data. */ + g1->lowpc = g2->lowpc; + g1->highpc = g2->highpc; + result = 0; + } + else + result = g1->lowpc < g2->lowpc ? -1 : 1; + } + + return result; +} + + +static int +get_var_range (Dwarf_Die *die, Dwarf_Word *lowpc, Dwarf_Word *highpc) +{ + Dwarf_Attribute locattr_mem; + Dwarf_Attribute *locattr = dwarf_attr (die, DW_AT_location, &locattr_mem); + if (locattr == NULL) + return 1; + + Dwarf_Op *loc; + size_t nloc; + if (dwarf_getlocation (locattr, &loc, &nloc) != 0) + return 1; + + /* Interpret the location expressions. */ + // XXX For now just the simple one: + if (nloc == 1 && loc[0].atom == DW_OP_addr) + { + *lowpc = *highpc = loc[0].number; + return 0; + } + + return 1; +} + + + +static void *local_root; + + +static void +get_local_names (Dwarf *dbg) +{ + Dwarf_Off offset = 0; + Dwarf_Off old_offset; + size_t hsize; + + while (dwarf_nextcu (dbg, old_offset = offset, &offset, &hsize, NULL, NULL, + NULL) == 0) + { + Dwarf_Die cudie_mem; + Dwarf_Die *cudie = dwarf_offdie (dbg, old_offset + hsize, &cudie_mem); + + /* If we cannot get the CU DIE there is no need to go on with + this CU. */ + if (cudie == NULL) + continue; + /* This better be a CU DIE. */ + if (dwarf_tag (cudie) != DW_TAG_compile_unit) + continue; + + /* Get the line information. */ + Dwarf_Files *files; + size_t nfiles; + if (dwarf_getsrcfiles (cudie, &files, &nfiles) != 0) + continue; + + Dwarf_Die die_mem; + Dwarf_Die *die = &die_mem; + if (dwarf_child (cudie, die) == 0) + /* Iterate over all immediate children of the CU DIE. */ + do + { + int tag = dwarf_tag (die); + if (tag != DW_TAG_subprogram && tag != DW_TAG_variable) + continue; + + /* We are interested in five attributes: name, decl_file, + decl_line, low_pc, and high_pc. */ + Dwarf_Attribute attr_mem; + Dwarf_Attribute *attr = dwarf_attr (die, DW_AT_name, &attr_mem); + const char *name = dwarf_formstring (attr); + if (name == NULL) + continue; + + Dwarf_Word fileidx; + attr = dwarf_attr (die, DW_AT_decl_file, &attr_mem); + if (dwarf_formudata (attr, &fileidx) != 0 || fileidx >= nfiles) + continue; + + Dwarf_Word lineno; + attr = dwarf_attr (die, DW_AT_decl_line, &attr_mem); + if (dwarf_formudata (attr, &lineno) != 0 || lineno == 0) + continue; + + Dwarf_Addr lowpc; + Dwarf_Addr highpc; + if (tag == DW_TAG_subprogram) + { + if (dwarf_lowpc (die, &lowpc) != 0 + || dwarf_highpc (die, &highpc) != 0) + continue; + } + else + { + if (get_var_range (die, &lowpc, &highpc) != 0) + continue; + } + + /* We have all the information. Create a record. */ + struct local_name *newp + = (struct local_name *) xmalloc (sizeof (*newp)); + newp->name = name; + newp->file = dwarf_filesrc (files, fileidx, NULL, NULL); + newp->lineno = lineno; + newp->lowpc = lowpc; + newp->highpc = highpc; + + /* Since we cannot deallocate individual memory we do not test + for duplicates in the tree. This should not happen anyway. */ + if (tsearch (newp, &local_root, local_compare) == NULL) + error (EXIT_FAILURE, errno, + gettext ("cannot create search tree")); + } + while (dwarf_siblingof (die, die) == 0); + } +} + +/* 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 +show_symbols_sysv (Ebl *ebl, GElf_Word strndx, const char *fullname, + GElf_SymX *syms, size_t nsyms, int longest_name, + int longest_where) +{ + size_t shnum; + if (elf_getshdrnum (ebl->elf, &shnum) < 0) + INTERNAL_ERROR (fullname); + + bool scnnames_malloced = shnum * sizeof (const char *) > 128 * 1024; + const char **scnnames; + if (scnnames_malloced) + scnnames = (const char **) xmalloc (sizeof (const char *) * shnum); + else + scnnames = (const char **) alloca (sizeof (const char *) * shnum); + /* Get the section header string table index. */ + size_t shstrndx; + if (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + /* Cache the section names. */ + Elf_Scn *scn = NULL; + size_t cnt = 1; + while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) + { + GElf_Shdr shdr_mem; + + assert (elf_ndxscn (scn) == cnt++); + + 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]; + + /* We always print this prolog. */ + printf (gettext ("\n\nSymbols from %s:\n\n"), fullname); + + /* The header line. */ + printf (gettext ("%*s%-*s %-*s Class Type %-*s %*s Section\n\n"), + print_file_name ? (int) strlen (fullname) + 1: 0, "", + longest_name, sgettext ("sysv|Name"), + /* TRANS: the "sysv|" parts makes the string unique. */ + digits, sgettext ("sysv|Value"), + /* TRANS: the "sysv|" parts makes the string unique. */ + digits, sgettext ("sysv|Size"), + /* TRANS: the "sysv|" parts makes the string unique. */ + longest_where, sgettext ("sysv|Line")); + + /* Which format string to use (different radix for numbers). */ + const char *number_fmtstr; + if (radix == radix_hex) + number_fmtstr = "%0*" PRIx64; + else if (radix == radix_decimal) + number_fmtstr = "%0*" PRId64; + else + number_fmtstr = "%0*" PRIo64; + +#ifdef USE_DEMANGLE + size_t demangle_buffer_len = 0; + char *demangle_buffer = NULL; +#endif + + /* Iterate over all symbols. */ + for (cnt = 1; cnt < nsyms; ++cnt) + { + /* In this format SECTION entries are not printed. */ + if (GELF_ST_TYPE (syms[cnt].sym.st_info) == STT_SECTION) + continue; + + char symstrbuf[50]; + const char *symstr = sym_name (ebl->elf, strndx, syms[cnt].sym.st_name, + symstrbuf, sizeof symstrbuf); + +#ifdef USE_DEMANGLE + /* Demangle if necessary. */ + if (demangle) + { + int status = -1; + char *dmsymstr = __cxa_demangle (symstr, demangle_buffer, + &demangle_buffer_len, &status); + + if (status == 0) + symstr = dmsymstr; + } +#endif + + char symbindbuf[50]; + char symtypebuf[50]; + char secnamebuf[1024]; + char addressbuf[(64 + 2) / 3 + 1]; + char sizebuf[(64 + 2) / 3 + 1]; + + /* If we have to precede the line with the file name. */ + if (print_file_name) + { + fputs_unlocked (fullname, stdout); + putchar_unlocked (':'); + } + + /* Covert the address. */ + if (syms[cnt].sym.st_shndx == SHN_UNDEF) + addressbuf[0] = sizebuf[0] = '\0'; + else + { + snprintf (addressbuf, sizeof (addressbuf), number_fmtstr, + digits, syms[cnt].sym.st_value); + snprintf (sizebuf, sizeof (sizebuf), number_fmtstr, + digits, syms[cnt].sym.st_size); + } + + /* Print the actual string. */ + printf ("%-*s|%s|%-6s|%-8s|%s|%*s|%s\n", + longest_name, symstr, addressbuf, + ebl_symbol_binding_name (ebl, + GELF_ST_BIND (syms[cnt].sym.st_info), + symbindbuf, sizeof (symbindbuf)), + ebl_symbol_type_name (ebl, GELF_ST_TYPE (syms[cnt].sym.st_info), + symtypebuf, sizeof (symtypebuf)), + sizebuf, longest_where, syms[cnt].where, + ebl_section_name (ebl, syms[cnt].sym.st_shndx, syms[cnt].xndx, + secnamebuf, sizeof (secnamebuf), scnnames, + shnum)); + } + +#ifdef USE_DEMANGLE + free (demangle_buffer); +#endif + + if (scnnames_malloced) + free (scnnames); +} + + +static char +class_type_char (Elf *elf, const GElf_Ehdr *ehdr, GElf_Sym *sym) +{ + int local_p = GELF_ST_BIND (sym->st_info) == STB_LOCAL; + + /* XXX Add support for architecture specific types and classes. */ + if (sym->st_shndx == SHN_ABS) + return local_p ? 'a' : 'A'; + + if (sym->st_shndx == SHN_UNDEF) + /* Undefined symbols must be global. */ + return 'U'; + + char result = "NDTSFBD "[GELF_ST_TYPE (sym->st_info)]; + + if (result == 'D') + { + /* Special handling: unique data symbols. */ + if (ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX + && GELF_ST_BIND (sym->st_info) == STB_GNU_UNIQUE) + result = 'u'; + else + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (elf_getscn (elf, sym->st_shndx), + &shdr_mem); + if (shdr != NULL) + { + if ((shdr->sh_flags & SHF_WRITE) == 0) + result = 'R'; + else if (shdr->sh_type == SHT_NOBITS) + result = 'B'; + } + } + } + + return local_p ? tolower (result) : result; +} + + +static void +show_symbols_bsd (Elf *elf, const GElf_Ehdr *ehdr, GElf_Word strndx, + const char *prefix, const char *fname, const char *fullname, + GElf_SymX *syms, size_t nsyms) +{ + int digits = length_map[gelf_getclass (elf) - 1][radix]; + + if (prefix != NULL && ! print_file_name) + printf ("\n%s:\n", fname); + + static const char *const fmtstrs[] = + { + [radix_hex] = "%8$s%2$0*1$" PRIx64 "%10$s %9$s%3$c%4$s %5$s", + [radix_decimal] = "%8$s%*" PRId64 "%10$s %9$s%3$c%4$s %5$s", + [radix_octal] = "%8$s%2$0*1$" PRIo64 "%10$s %9$s%3$c%4$s %5$s" + }; + static const char *const sfmtstrs[] = + { + [radix_hex] = "%8$s%2$0*1$" PRIx64 "%10$s %7$0*6$" PRIx64 " %9$s%3$c%4$s %5$s", + [radix_decimal] = "%8$s%2$*1$" PRId64 "%10$s %7$*6$" PRId64 " %9$s%3$c%4$s %5$s", + [radix_octal] = "%8$s%2$0*1$" PRIo64 "%10$s %7$0*6$" PRIo64 " %9$s%3$c%4$s %5$s" + }; + +#ifdef USE_DEMANGLE + size_t demangle_buffer_len = 0; + char *demangle_buffer = NULL; +#endif + + /* Iterate over all symbols. */ + for (size_t cnt = 0; cnt < nsyms; ++cnt) + { + 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 + much information we leave them out. */ + if (symstr[0] == '\0') + continue; + + /* We do not print the entries for files. */ + if (GELF_ST_TYPE (syms[cnt].sym.st_info) == STT_FILE) + continue; + +#ifdef USE_DEMANGLE + /* Demangle if necessary. */ + if (demangle) + { + int status = -1; + char *dmsymstr = __cxa_demangle (symstr, demangle_buffer, + &demangle_buffer_len, &status); + + if (status == 0) + symstr = dmsymstr; + } +#endif + + /* If we have to precede the line with the file name. */ + if (print_file_name) + { + fputs_unlocked (fullname, stdout); + putchar_unlocked (':'); + } + + bool is_tls = GELF_ST_TYPE (syms[cnt].sym.st_info) == STT_TLS; + bool is_weak = GELF_ST_BIND (syms[cnt].sym.st_info) == STB_WEAK; + const char *marker = (mark_special + ? (is_tls ? "@" : (is_weak ? "*" : " ")) : ""); + + if (syms[cnt].sym.st_shndx == SHN_UNDEF) + { + const char *color = ""; + if (color_mode) + { + if (is_tls) + color = color_undef_tls; + else if (is_weak) + color = color_undef_weak; + else + color = color_undef; + } + + printf ("%*s %sU%s %s", digits, "", color, marker, symstr); + } + else + { + const char *color = ""; + if (color_mode) + { + if (is_tls) + color = color_tls; + else if (is_weak) + color = color_weak; + else + color = color_symbol; + } + + printf (print_size && syms[cnt].sym.st_size != 0 + ? sfmtstrs[radix] : fmtstrs[radix], + digits, syms[cnt].sym.st_value, + class_type_char (elf, ehdr, &syms[cnt].sym), marker, + symstr, + digits, (uint64_t) syms[cnt].sym.st_size, + color_mode ? color_address : "", + color, + color_mode ? color_off : ""); + } + + if (color_mode) + fputs_unlocked (color_off, stdout); + putchar_unlocked ('\n'); + } + +#ifdef USE_DEMANGLE + free (demangle_buffer); +#endif +} + + +static void +show_symbols_posix (Elf *elf, const GElf_Ehdr *ehdr, GElf_Word strndx, + const char *prefix, const char *fullname, GElf_SymX *syms, + size_t nsyms) +{ + if (prefix != NULL && ! print_file_name) + printf ("%s:\n", fullname); + + const char *fmtstr; + if (radix == radix_hex) + fmtstr = "%s %c%s %0*" PRIx64 " %0*" PRIx64 "\n"; + else if (radix == radix_decimal) + fmtstr = "%s %c%s %*" PRId64 " %*" PRId64 "\n"; + else + fmtstr = "%s %c%s %0*" PRIo64 " %0*" PRIo64 "\n"; + + int digits = length_map[gelf_getclass (elf) - 1][radix]; + +#ifdef USE_DEMANGLE + size_t demangle_buffer_len = 0; + char *demangle_buffer = NULL; +#endif + + /* Iterate over all symbols. */ + for (size_t cnt = 0; cnt < nsyms; ++cnt) + { + 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 + much information we leave them out. */ + if (symstr[0] == '\0') + continue; + +#ifdef USE_DEMANGLE + /* Demangle if necessary. */ + if (demangle) + { + int status = -1; + char *dmsymstr = __cxa_demangle (symstr, demangle_buffer, + &demangle_buffer_len, &status); + + if (status == 0) + symstr = dmsymstr; + } +#endif + + /* If we have to precede the line with the file name. */ + if (print_file_name) + { + fputs_unlocked (fullname, stdout); + putchar_unlocked (':'); + putchar_unlocked (' '); + } + + printf (fmtstr, + symstr, + class_type_char (elf, ehdr, &syms[cnt].sym), + mark_special + ? (GELF_ST_TYPE (syms[cnt].sym.st_info) == STT_TLS + ? "@" + : (GELF_ST_BIND (syms[cnt].sym.st_info) == STB_WEAK + ? "*" : " ")) + : "", + digits, syms[cnt].sym.st_value, + digits, syms[cnt].sym.st_size); + } + +#ifdef USE_DEMANGLE + free (demangle_buffer); +#endif +} + + +/* Maximum size of memory we allocate on the stack. */ +#define MAX_STACK_ALLOC 65536 + +static int +sort_by_address (const void *p1, const void *p2) +{ + GElf_SymX *s1 = (GElf_SymX *) p1; + GElf_SymX *s2 = (GElf_SymX *) p2; + + 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; +} + +static Elf_Data *sort_by_name_strtab; + +static int +sort_by_name (const void *p1, const void *p2) +{ + GElf_SymX *s1 = (GElf_SymX *) p1; + GElf_SymX *s2 = (GElf_SymX *) p2; + + 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_getshdrstrndx (ebl->elf, &shstrndx) < 0) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + /* The section is that large. */ + size_t size = shdr->sh_size; + /* One entry is this large. */ + size_t entsize = shdr->sh_entsize; + + /* Consistency checks. */ + if (entsize != gelf_fsize (ebl->elf, ELF_T_SYM, 1, ehdr->e_version)) + error (0, 0, + gettext ("%s: entry size in section `%s' is not what we expect"), + fullname, elf_strptr (ebl->elf, shstrndx, shdr->sh_name)); + else if (size % entsize != 0) + error (0, 0, + gettext ("%s: size of section `%s' is not multiple of entry size"), + fullname, elf_strptr (ebl->elf, shstrndx, shdr->sh_name)); + + /* Compute number of entries. Handle buggy entsize values. */ + size_t nentries = size / (entsize ?: 1); + + +#define obstack_chunk_alloc xmalloc +#define obstack_chunk_free free + struct obstack whereob; + obstack_init (&whereob); + + /* Get a DWARF debugging descriptor. It's no problem if this isn't + possible. We just won't print any line number information. */ + Dwarf *dbg = NULL; + if (format == format_sysv) + { + dbg = dwarf_begin_elf (ebl->elf, DWARF_C_READ, NULL); + if (dbg != NULL) + { + (void) dwarf_getpubnames (dbg, get_global, NULL, 0); + + get_local_names (dbg); + } + } + + /* Allocate the memory. + + XXX We can use a dirty trick here. Since GElf_Sym == Elf64_Sym we + can use the data memory instead of copying again if what we read + is a 64 bit file. */ + GElf_SymX *sym_mem; + if (nentries * sizeof (GElf_SymX) < MAX_STACK_ALLOC) + sym_mem = (GElf_SymX *) alloca (nentries * sizeof (GElf_SymX)); + else + sym_mem = (GElf_SymX *) xmalloc (nentries * sizeof (GElf_SymX)); + + /* Get the data of the section. */ + Elf_Data *data = elf_getdata (scn, NULL); + Elf_Data *xndxdata = elf_getdata (xndxscn, NULL); + if (data == NULL || (xndxscn != NULL && xndxdata == NULL)) + INTERNAL_ERROR (fullname); + + /* Iterate over all symbols. */ +#ifdef USE_DEMANGLE + size_t demangle_buffer_len = 0; + char *demangle_buffer = NULL; +#endif + int longest_name = 4; + int longest_where = 4; + size_t nentries_used = 0; + for (size_t cnt = 0; cnt < nentries; ++cnt) + { + GElf_Sym *sym = gelf_getsymshndx (data, xndxdata, cnt, + &sym_mem[nentries_used].sym, + &sym_mem[nentries_used].xndx); + if (sym == NULL) + INTERNAL_ERROR (fullname); + + /* Filter out administrative symbols without a name and those + deselected by the user with command line options. */ + if ((hide_undefined && sym->st_shndx == SHN_UNDEF) + || (hide_defined && sym->st_shndx != SHN_UNDEF) + || (hide_local && GELF_ST_BIND (sym->st_info) == STB_LOCAL)) + continue; + + sym_mem[nentries_used].where = ""; + if (format == format_sysv) + { + const char *symstr = elf_strptr (ebl->elf, shdr->sh_link, + sym->st_name); + if (symstr == NULL) + continue; + +#ifdef USE_DEMANGLE + /* Demangle if necessary. */ + if (demangle) + { + int status = -1; + char *dmsymstr = __cxa_demangle (symstr, demangle_buffer, + &demangle_buffer_len, &status); + + if (status == 0) + symstr = dmsymstr; + } +#endif + + longest_name = MAX ((size_t) longest_name, strlen (symstr)); + + if (sym->st_shndx != SHN_UNDEF + && GELF_ST_BIND (sym->st_info) != STB_LOCAL + && global_root != NULL) + { + Dwarf_Global fake = { .name = symstr }; + Dwarf_Global **found = tfind (&fake, &global_root, + global_compare); + if (found != NULL) + { + Dwarf_Die die_mem; + Dwarf_Die *die = dwarf_offdie (dbg, (*found)->die_offset, + &die_mem); + + Dwarf_Die cudie_mem; + Dwarf_Die *cudie = NULL; + + Dwarf_Addr lowpc; + Dwarf_Addr highpc; + if (die != NULL + && dwarf_lowpc (die, &lowpc) == 0 + && lowpc <= sym->st_value + && dwarf_highpc (die, &highpc) == 0 + && highpc > sym->st_value) + cudie = dwarf_offdie (dbg, (*found)->cu_offset, + &cudie_mem); + if (cudie != NULL) + { + Dwarf_Line *line = dwarf_getsrc_die (cudie, + sym->st_value); + if (line != NULL) + { + /* We found the line. */ + int lineno; + (void) dwarf_lineno (line, &lineno); + int n; + n = obstack_printf (&whereob, "%s:%d%c", + basename (dwarf_linesrc (line, + NULL, + NULL)), + lineno, '\0'); + sym_mem[nentries_used].where + = obstack_finish (&whereob); + + /* The return value of obstack_print included the + NUL byte, so subtract one. */ + if (--n > (int) longest_where) + longest_where = (size_t) n; + } + } + } + } + + /* Try to find the symbol among the local symbols. */ + if (sym_mem[nentries_used].where[0] == '\0') + { + struct local_name fake = + { + .name = symstr, + .lowpc = sym->st_value, + .highpc = sym->st_value, + }; + struct local_name **found = tfind (&fake, &local_root, + local_compare); + if (found != NULL) + { + /* We found the line. */ + int n = obstack_printf (&whereob, "%s:%" PRIu64 "%c", + basename ((*found)->file), + (*found)->lineno, + '\0'); + sym_mem[nentries_used].where = obstack_finish (&whereob); + + /* The return value of obstack_print included the + NUL byte, so subtract one. */ + if (--n > (int) longest_where) + longest_where = (size_t) n; + } + } + } + + /* We use this entry. */ + ++nentries_used; + } +#ifdef USE_DEMANGLE + free (demangle_buffer); +#endif + /* Now we know the exact number. */ + nentries = nentries_used; + + /* Sort the entries according to the users wishes. */ + if (sort == sort_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); + + /* Finally print according to the users selection. */ + switch (format) + { + case format_sysv: + show_symbols_sysv (ebl, shdr->sh_link, fullname, sym_mem, nentries, + longest_name, longest_where); + break; + + case format_bsd: + show_symbols_bsd (ebl->elf, ehdr, 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, fullname, + sym_mem, nentries); + break; + } + + /* Free all memory. */ + if (nentries * sizeof (GElf_Sym) >= MAX_STACK_ALLOC) + free (sym_mem); + + obstack_free (&whereob, NULL); + + if (dbg != NULL) + { + tdestroy (global_root, free); + global_root = NULL; + + tdestroy (local_root, free); + local_root = NULL; + + (void) dwarf_end (dbg); + } +} + + +static int +handle_elf (Elf *elf, const char *prefix, const char *fname, + const char *suffix) +{ + 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; + Elf_Scn *scn = NULL; + int any = 0; + int result = 0; + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr; + Ebl *ebl; + + /* Get the backend for this object file type. */ + ebl = ebl_openbackend (elf); + + /* We need the ELF header in a few places. */ + ehdr = gelf_getehdr (elf, &ehdr_mem); + if (ehdr == NULL) + INTERNAL_ERROR (fullname); + + /* If we are asked to print the dynamic symbol table and this is + executable or dynamic executable, fail. */ + if (symsec_type == SHT_DYNSYM + && ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) + { + /* XXX Add machine specific object file types. */ + error (0, 0, gettext ("%s%s%s%s: Invalid operation"), + prefix ?: "", prefix ? "(" : "", fname, prefix ? ")" : ""); + result = 1; + goto out; + } + + /* Create the full name of the file. */ + 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); + + /* Find the symbol table. + + XXX Can there be more than one? Do we print all? Currently we do. */ + while ((scn = elf_nextscn (elf, scn)) != NULL) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + + if (shdr == NULL) + INTERNAL_ERROR (fullname); + + if (shdr->sh_type == symsec_type) + { + Elf_Scn *xndxscn = NULL; + + /* We have a symbol table. First make sure we remember this. */ + any = 1; + + /* Look for an extended section index table for this section. */ + if (symsec_type == SHT_SYMTAB) + { + size_t scnndx = elf_ndxscn (scn); + + while ((xndxscn = elf_nextscn (elf, xndxscn)) != NULL) + { + GElf_Shdr xndxshdr_mem; + GElf_Shdr *xndxshdr = gelf_getshdr (xndxscn, &xndxshdr_mem); + + if (xndxshdr == NULL) + INTERNAL_ERROR (fullname); + + if (xndxshdr->sh_type == SHT_SYMTAB_SHNDX + && xndxshdr->sh_link == scnndx) + break; + } + } + + show_symbols (ebl, ehdr, scn, xndxscn, shdr, prefix, fname, + fullname); + } + } + + if (! any) + { + error (0, 0, gettext ("%s%s%s: no symbols"), + prefix ?: "", prefix ? ":" : "", fname); + result = 1; + } + + out: + /* Close the ELF backend library descriptor. */ + ebl_closebackend (ebl); + + return result; +} + + +#include "debugpred.h" diff --git a/0.153/src/none_ld.c b/0.153/src/none_ld.c new file mode 100644 index 00000000..fb0f0fb2 --- /dev/null +++ b/0.153/src/none_ld.c @@ -0,0 +1 @@ +/* Nothing here. This is just a testimony of automake inflexibility. */ diff --git a/0.153/src/objdump.c b/0.153/src/objdump.c new file mode 100644 index 00000000..e2fcfbf1 --- /dev/null +++ b/0.153/src/objdump.c @@ -0,0 +1,812 @@ +/* Print information from ELF file in human-readable form. + Copyright (C) 2005, 2006, 2007, 2009, 2011, 2012 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); +ARGP_PROGRAM_VERSION_HOOK_DEF = print_version; + +/* Bug report address. */ +ARGP_PROGRAM_BUG_ADDRESS_DEF = 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 content 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); + +/* Parser children. */ +static struct argp_child argp_children[] = + { + { &color_argp, 0, N_("Output formatting"), 2 }, + { NULL, 0, NULL, 0} + }; + +/* Data structure to communicate with argp functions. */ +static const struct argp argp = +{ + options, parse_opt, args_doc, doc, argp_children, 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\ +"), "2012"); + 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, + program_invocation_short_name); + exit (EXIT_FAILURE); + } + + 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_x (Ebl *ebl, GElf_Shdr *shdr, Elf_Data *symdata, + Elf_Data *xndxdata, size_t symstrndx, size_t shstrndx, + GElf_Addr r_offset, GElf_Xword r_info, GElf_Sxword r_addend) +{ + int elfclass = gelf_getclass (ebl->elf); + char buf[128]; + + printf ("%0*" PRIx64 " %-20s ", + elfclass == ELFCLASS32 ? 8 : 16, r_offset, + ebl_reloc_type_name (ebl, GELF_R_TYPE (r_info), buf, sizeof (buf))); + + Elf32_Word xndx; + GElf_Sym symmem; + GElf_Sym *sym = gelf_getsymshndx (symdata, xndxdata, GELF_R_SYM (r_info), + &symmem, &xndx); + + if (sym == NULL) + printf ("<%s %ld>", + gettext ("INVALID SYMBOL"), (long int) GELF_R_SYM (r_info)); + else if (GELF_ST_TYPE (sym->st_info) != STT_SECTION) + printf ("%s", + 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 ("<%s %ld>", + gettext ("INVALID SECTION"), + (long int) (sym->st_shndx == SHN_XINDEX + ? xndx : sym->st_shndx)); + else + printf ("%s", + elf_strptr (ebl->elf, shstrndx, destshdr->sh_name)); + } + + if (r_addend != 0) + { + char sign = '+'; + if (r_addend < 0) + { + sign = '-'; + r_addend = -r_addend; + } + printf ("%c%#" PRIx64, sign, r_addend); + } + putchar ('\n'); +} + + +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 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) + show_relocs_x (ebl, shdr, symdata, xndxdata, symstrndx, shstrndx, + rel->r_offset, rel->r_info, 0); + } +} + + +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 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) + show_relocs_x (ebl, shdr, symdata, xndxdata, symstrndx, shstrndx, + rel->r_offset, rel->r_info, rel->r_addend); + } +} + + +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 ("\nRELOCATION 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); + + putchar ('\n'); + } + } + + 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; + const char *address_color; + const char *bytes_color; +}; + + +// 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; + + if (info->address_color != NULL) + printf ("%s%8" PRIx64 "%s: ", + info->address_color, (uint64_t) info->addr, color_off); + else + printf ("%8" PRIx64 ": ", (uint64_t) info->addr); + + if (info->bytes_color != NULL) + fputs_unlocked (info->bytes_color, stdout); + size_t cnt; + for (cnt = 0; cnt < (size_t) MIN (info->cur - info->last_end, 8); ++cnt) + printf (" %02" PRIx8, info->last_end[cnt]); + if (info->bytes_color != NULL) + fputs_unlocked (color_off, stdout); + + 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) + { + if (info->address_color != NULL) + printf ("%s%8" PRIx64 "%s: ", + info->address_color, (uint64_t) info->addr, color_off); + else + printf ("%8" PRIx64 ": ", (uint64_t) info->addr); + + if (info->bytes_color != NULL) + fputs_unlocked (info->bytes_color, stdout); + for (; cnt < (size_t) (info->cur - info->last_end); ++cnt) + printf (" %02" PRIx8, info->last_end[cnt]); + if (info->bytes_color != NULL) + fputs_unlocked (color_off, stdout); + 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; + char *fmt; + if (color_mode) + { + info.address_color = color_address; + info.bytes_color = color_bytes; + + if (asprintf (&fmt, "%s%%7m %s%%.1o,%s%%.2o,%s%%.3o%%34a %s%%l", + color_mnemonic ?: "", + color_operand1 ?: "", + color_operand2 ?: "", + color_operand3 ?: "", + color_label ?: "") < 0) + error (EXIT_FAILURE, errno, _("cannot allocate memory")); + } + else + { + info.address_color = info.bytes_color = NULL; + + fmt = "%7m %.1o,%.2o,%.3o%34a %l"; + } + + disasm_cb (ctx, &info.cur, info.cur + data->d_size, info.addr, + fmt, disasm_output, &info, NULL /* XXX */); + + if (color_mode) + free (fmt); + } + } + + (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_getshdrstrndx (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/0.153/src/ranlib.c b/0.153/src/ranlib.c new file mode 100644 index 00000000..d30fc320 --- /dev/null +++ b/0.153/src/ranlib.c @@ -0,0 +1,309 @@ +/* Generate an index to speed access to archives. + Copyright (C) 2005-2012 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); +ARGP_PROGRAM_VERSION_HOOK_DEF = print_version; + +/* Bug report address. */ +ARGP_PROGRAM_BUG_ADDRESS_DEF = 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, arlib_argp_children, 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\ +"), "2012"); + 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/0.153/src/readelf.c b/0.153/src/readelf.c new file mode 100644 index 00000000..88766889 --- /dev/null +++ b/0.153/src/readelf.c @@ -0,0 +1,8499 @@ +/* Print information from ELF file in human-readable form. + Copyright (C) 1999-2012 Red Hat, Inc. + This file is part of Red Hat elfutils. + Written by Ulrich Drepper <drepper@redhat.com>, 1999. + + 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 <dwarf.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <gelf.h> +#include <inttypes.h> +#include <langinfo.h> +#include <libdw.h> +#include <libdwfl.h> +#include <libintl.h> +#include <locale.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <sys/param.h> +#include <sys/stat.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); +ARGP_PROGRAM_VERSION_HOOK_DEF = print_version; + +/* Bug report address. */ +ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT; + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + { NULL, 0, NULL, 0, N_("ELF output selection:"), 0 }, + { "all", 'a', NULL, 0, + N_("All these plus -p .strtab -p .dynstr -p .comment"), 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"), 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' headers"), 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 }, + { "notes", 'n', NULL, 0, N_("Display the ELF notes"), 0 }, + { "arch-specific", 'A', NULL, 0, + N_("Display architecture specific information, if any"), 0 }, + { "exception", 'e', NULL, 0, + N_("Display sections for exception handling"), 0 }, + + { NULL, 0, NULL, 0, N_("Additional output selection:"), 0 }, + { "debug-dump", 'w', "SECTION", OPTION_ARG_OPTIONAL, + N_("Display DWARF section content. SECTION can be one of abbrev, " + "aranges, frame, gdb_index, info, loc, line, ranges, pubnames, str, " + "macinfo, or exception"), 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 }, + { "numeric-addresses", 'N', NULL, 0, + N_("Do not find symbol names for addresses in DWARF data"), 0 }, + { "wide", 'W', NULL, 0, + N_("Ignored for compatibility (lines always wide)"), 0 }, + { NULL, 0, NULL, 0, NULL, 0 } +}; + +/* Short description of program. */ +static const char doc[] = N_("\ +Print information from ELF file in human-readable form."); + +/* 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 +}; + + +/* Flags set by the option controlling the output. */ + +/* True if dynamic segment should be printed. */ +static bool print_dynamic_table; + +/* True if the file header should be printed. */ +static bool print_file_header; + +/* True if the program headers should be printed. */ +static bool print_program_header; + +/* True if relocations should be printed. */ +static bool print_relocations; + +/* True if the section headers should be printed. */ +static bool print_section_header; + +/* True if the symbol table should be printed. */ +static bool print_symbol_table; + +/* True if the version information should be printed. */ +static bool print_version_info; + +/* True if section groups should be printed. */ +static bool print_section_groups; + +/* True if bucket list length histogram should be printed. */ +static bool print_histogram; + +/* True if the architecture specific data should be printed. */ +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; + +/* True if we should print addresses from DWARF in symbolic form. */ +static bool print_address_names = true; + +/* Select printing of debugging sections. */ +static enum section_e +{ + section_abbrev = 1, /* .debug_abbrev */ + section_aranges = 2, /* .debug_aranges */ + section_frame = 4, /* .debug_frame or .eh_frame & al. */ + section_info = 8, /* .debug_info, .debug_types */ + section_types = section_info, + section_line = 16, /* .debug_line */ + section_loc = 32, /* .debug_loc */ + section_pubnames = 64, /* .debug_pubnames */ + section_str = 128, /* .debug_str */ + section_macinfo = 256, /* .debug_macinfo */ + section_ranges = 512, /* .debug_ranges */ + section_exception = 1024, /* .eh_frame & al. */ + section_gdb_index = 2048, /* .gdb_index */ + section_all = (section_abbrev | section_aranges | section_frame + | section_info | section_line | section_loc + | section_pubnames | section_str | section_macinfo + | section_ranges | section_exception | section_gdb_index) +} print_debug_sections, implicit_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; + bool implicit; +}; + +/* Numbers of sections and program headers in the file. */ +static size_t shnum; +static size_t phnum; + + +/* Declarations of local functions. */ +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); +static void print_dynamic (Ebl *ebl); +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, 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 (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); +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[]) +{ + /* Set locale. */ + setlocale (LC_ALL, ""); + + /* Initialize the message catalog. */ + textdomain (PACKAGE_TARNAME); + + /* Parse and process arguments. */ + int remaining; + argp_parse (&argp, argc, argv, 0, &remaining, NULL); + + /* 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. */ + bool only_one = remaining + 1 == argc; + do + { + /* Open the file. */ + int fd = open (argv[remaining], O_RDONLY); + if (fd == -1) + { + error (0, errno, gettext ("cannot open input file")); + continue; + } + + process_file (fd, argv[remaining], only_one); + + close (fd); + } + while (++remaining < argc); + + return error_message_count != 0; +} + + +/* Handle program arguments. */ +static error_t +parse_opt (int key, char *arg, + struct argp_state *state __attribute__ ((unused))) +{ + void add_dump_section (const char *name, bool implicit) + { + struct section_argument *a = xmalloc (sizeof *a); + a->arg = name; + a->next = NULL; + a->implicit = implicit; + struct section_argument ***tailp + = key == 'x' ? &dump_data_sections_tail : &string_sections_tail; + **tailp = a; + *tailp = &a->next; + } + + switch (key) + { + case 'a': + print_file_header = true; + print_program_header = true; + print_relocations = true; + print_section_header = true; + print_symbol_table = true; + print_version_info = true; + print_dynamic_table = true; + print_section_groups = true; + print_histogram = true; + print_arch = true; + print_notes = true; + implicit_debug_sections |= section_exception; + add_dump_section (".strtab", true); + add_dump_section (".dynstr", true); + add_dump_section (".comment", true); + any_control_option = true; + break; + case 'A': + print_arch = true; + any_control_option = true; + break; + case 'd': + print_dynamic_table = true; + any_control_option = true; + break; + case 'e': + print_debug_sections |= section_exception; + any_control_option = true; + break; + case 'g': + print_section_groups = true; + any_control_option = true; + break; + case 'h': + print_file_header = true; + any_control_option = true; + break; + case 'I': + print_histogram = true; + any_control_option = true; + break; + case 'l': + print_program_header = true; + any_control_option = true; + break; + case 'n': + print_notes = true; + any_control_option = true; + break; + case 'r': + print_relocations = true; + any_control_option = true; + break; + case 'S': + print_section_header = true; + any_control_option = true; + break; + case 's': + print_symbol_table = true; + any_control_option = true; + break; + case 'V': + 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; + else if (strcmp (arg, "abbrev") == 0) + 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; + implicit_debug_sections |= section_info; + } + else if (strcmp (arg, "frame") == 0 || strcmp (arg, "frames") == 0) + print_debug_sections |= section_frame; + else if (strcmp (arg, "info") == 0) + print_debug_sections |= section_info; + else if (strcmp (arg, "loc") == 0) + { + print_debug_sections |= section_loc; + implicit_debug_sections |= section_info; + } + else if (strcmp (arg, "line") == 0) + print_debug_sections |= section_line; + else if (strcmp (arg, "pubnames") == 0) + print_debug_sections |= section_pubnames; + else if (strcmp (arg, "str") == 0) + print_debug_sections |= section_str; + else if (strcmp (arg, "macinfo") == 0) + print_debug_sections |= section_macinfo; + else if (strcmp (arg, "exception") == 0) + print_debug_sections |= section_exception; + else if (strcmp (arg, "gdb_index") == 0) + print_debug_sections |= section_gdb_index; + else + { + fprintf (stderr, gettext ("Unknown DWARF debug section `%s'.\n"), + arg); + argp_help (&argp, stderr, ARGP_HELP_SEE, + 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': + add_dump_section (arg, false); + any_control_option = true; + break; + case 'N': + print_address_names = false; + 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, + program_invocation_short_name); + exit (EXIT_FAILURE); + } + break; + case 'W': /* Ignored. */ + 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, "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\ +"), "2012"); + fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); +} + + +/* Check if the file is an archive, and if so dump its index. */ +static void +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 +{ + int fd; + bool only_one; +}; + +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) + { + const char *fname; + dwfl_module_info (dwflmod, NULL, NULL, NULL, NULL, NULL, &fname, NULL); + + printf ("\n%s:\n\n", fname); + } + + process_elf_file (dwflmod, a->fd); + + return DWARF_CB_OK; +} + +/* 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; +} + +/* 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); + + 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 (dwfl_fd, &st) != 0) + error (0, errno, gettext ("cannot stat input file")); + else if (unlikely (st.st_size == 0)) + error (0, 0, gettext ("input file is empty")); + else + error (0, 0, gettext ("failed reading '%s': %s"), + fname, dwfl_errmsg (-1)); + close (dwfl_fd); /* Consumed on success, not on failure. */ + } + 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 ELF file. */ +static void +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); + + if (ehdr == NULL) + { + elf_error: + error (0, 0, gettext ("cannot read ELF header: %s"), elf_errmsg (-1)); + return; + } + + 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 (unlikely (elf_getshdrnum (ebl->elf, &shnum) < 0)) + error (EXIT_FAILURE, 0, + gettext ("cannot determine number of sections: %s"), + elf_errmsg (-1)); + + /* Determine the number of phdrs. */ + if (unlikely (elf_getphdrnum (ebl->elf, &phnum) < 0)) + error (EXIT_FAILURE, 0, + gettext ("cannot determine number of program headers: %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 (pure_ebl, ehdr); + if (print_program_header) + print_phdr (ebl, ehdr); + if (print_section_groups) + print_scngrp (ebl); + if (print_dynamic_table) + print_dynamic (ebl); + if (print_relocations) + print_relocs (pure_ebl, ehdr); + if (print_histogram) + handle_hash (ebl); + if (print_symbol_table) + print_symtab (ebl, SHT_DYNSYM); + if (print_version_info) + print_verinfo (ebl); + if (print_symbol_table) + print_symtab (ebl, SHT_SYMTAB); + if (print_arch) + print_liblist (ebl); + if (print_arch) + print_attributes (ebl, ehdr); + if (dump_data_sections != NULL) + dump_data (pure_ebl); + if (string_sections != NULL) + dump_strings (ebl); + if ((print_debug_sections | implicit_debug_sections) != 0) + print_debug (dwflmod, ebl, ehdr); + if (print_notes) + 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); + } +} + + +/* Print file type. */ +static void +print_file_type (unsigned short int e_type) +{ + if (likely (e_type <= ET_CORE)) + { + static const char *const knowntypes[] = + { + N_("NONE (None)"), + N_("REL (Relocatable file)"), + N_("EXEC (Executable file)"), + N_("DYN (Shared object file)"), + N_("CORE (Core file)") + }; + puts (gettext (knowntypes[e_type])); + } + else if (e_type >= ET_LOOS && e_type <= ET_HIOS) + printf (gettext ("OS Specific: (%x)\n"), e_type); + else if (e_type >= ET_LOPROC /* && e_type <= ET_HIPROC always true */) + printf (gettext ("Processor Specific: (%x)\n"), e_type); + else + puts ("???"); +} + + +/* Print ELF header. */ +static void +print_ehdr (Ebl *ebl, GElf_Ehdr *ehdr) +{ + fputs_unlocked (gettext ("ELF Header:\n Magic: "), stdout); + for (size_t cnt = 0; cnt < EI_NIDENT; ++cnt) + printf (" %02hhx", ehdr->e_ident[cnt]); + + printf (gettext ("\n Class: %s\n"), + ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? "ELF32" + : ehdr->e_ident[EI_CLASS] == ELFCLASS64 ? "ELF64" + : "\?\?\?"); + + printf (gettext (" Data: %s\n"), + ehdr->e_ident[EI_DATA] == ELFDATA2LSB + ? "2's complement, little endian" + : ehdr->e_ident[EI_DATA] == ELFDATA2MSB + ? "2's complement, big endian" : "\?\?\?"); + + 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))); + + printf (gettext (" ABI Version: %hhd\n"), + ehdr->e_ident[EI_ABIVERSION]); + + fputs_unlocked (gettext (" Type: "), stdout); + print_file_type (ehdr->e_type); + + printf (gettext (" Machine: %s\n"), ebl->name); + + printf (gettext (" Version: %d %s\n"), + ehdr->e_version, + ehdr->e_version == EV_CURRENT ? gettext ("(current)") : "(\?\?\?)"); + + printf (gettext (" Entry point address: %#" PRIx64 "\n"), + ehdr->e_entry); + + printf (gettext (" Start of program headers: %" PRId64 " %s\n"), + ehdr->e_phoff, gettext ("(bytes into file)")); + + printf (gettext (" Start of section headers: %" PRId64 " %s\n"), + ehdr->e_shoff, gettext ("(bytes into file)")); + + printf (gettext (" Flags: %s\n"), + ebl_machine_flag_name (ebl, ehdr->e_flags, buf, sizeof (buf))); + + printf (gettext (" Size of this header: %" PRId16 " %s\n"), + ehdr->e_ehsize, gettext ("(bytes)")); + + printf (gettext (" Size of program header entries: %" PRId16 " %s\n"), + ehdr->e_phentsize, gettext ("(bytes)")); + + printf (gettext (" Number of program headers entries: %" PRId16), + ehdr->e_phnum); + if (ehdr->e_phnum == PN_XNUM) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem); + if (shdr != NULL) + printf (gettext (" (%" PRIu32 " in [0].sh_info)"), + (uint32_t) shdr->sh_info); + else + fputs_unlocked (gettext (" ([0] not available)"), stdout); + } + fputc_unlocked ('\n', stdout); + + printf (gettext (" Size of section header entries: %" PRId16 " %s\n"), + ehdr->e_shentsize, gettext ("(bytes)")); + + printf (gettext (" Number of section headers entries: %" PRId16), + ehdr->e_shnum); + if (ehdr->e_shnum == 0) + { + GElf_Shdr 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); + else + fputs_unlocked (gettext (" ([0] not available)"), stdout); + } + fputc_unlocked ('\n', stdout); + + if (unlikely (ehdr->e_shstrndx == SHN_XINDEX)) + { + GElf_Shdr 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)"), + (uint32_t) shdr->sh_link); + else + { + strncpy (buf, gettext (" ([0] not available)"), sizeof (buf)); + buf[sizeof (buf) - 1] = '\0'; + } + + printf (gettext (" Section header string table index: XINDEX%s\n\n"), + buf); + } + else + printf (gettext (" Section header string table index: %" PRId16 "\n\n"), + ehdr->e_shstrndx); +} + + +static const char * +get_visibility_type (int value) +{ + switch (value) + { + case STV_DEFAULT: + return "DEFAULT"; + case STV_INTERNAL: + return "INTERNAL"; + case STV_HIDDEN: + return "HIDDEN"; + case STV_PROTECTED: + return "PROTECTED"; + default: + return "???"; + } +} + + +/* Print the section headers. */ +static void +print_shdr (Ebl *ebl, GElf_Ehdr *ehdr) +{ + size_t cnt; + size_t shstrndx; + + if (! print_file_header) + printf (gettext ("\ +There are %d section headers, starting at offset %#" PRIx64 ":\n\ +\n"), + ehdr->e_shnum, ehdr->e_shoff); + + /* Get the section header string table index. */ + if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0)) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + puts (gettext ("Section Headers:")); + + if (ehdr->e_ident[EI_CLASS] == ELFCLASS32) + puts (gettext ("[Nr] Name Type Addr Off Size ES Flags Lk Inf Al")); + else + puts (gettext ("[Nr] Name Type Addr Off Size ES Flags Lk Inf Al")); + + for (cnt = 0; cnt < shnum; ++cnt) + { + Elf_Scn *scn = elf_getscn (ebl->elf, cnt); + + if (unlikely (scn == NULL)) + error (EXIT_FAILURE, 0, gettext ("cannot get section: %s"), + elf_errmsg (-1)); + + /* Get the section header. */ + 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)); + + char flagbuf[20]; + char *cp = flagbuf; + if (shdr->sh_flags & SHF_WRITE) + *cp++ = 'W'; + if (shdr->sh_flags & SHF_ALLOC) + *cp++ = 'A'; + if (shdr->sh_flags & SHF_EXECINSTR) + *cp++ = 'X'; + if (shdr->sh_flags & SHF_MERGE) + *cp++ = 'M'; + if (shdr->sh_flags & SHF_STRINGS) + *cp++ = 'S'; + if (shdr->sh_flags & SHF_INFO_LINK) + *cp++ = 'I'; + if (shdr->sh_flags & SHF_LINK_ORDER) + *cp++ = 'L'; + if (shdr->sh_flags & SHF_OS_NONCONFORMING) + *cp++ = 'N'; + if (shdr->sh_flags & SHF_GROUP) + *cp++ = 'G'; + if (shdr->sh_flags & SHF_TLS) + *cp++ = 'T'; + if (shdr->sh_flags & SHF_ORDERED) + *cp++ = 'O'; + if (shdr->sh_flags & SHF_EXCLUDE) + *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", + cnt, + elf_strptr (ebl->elf, shstrndx, shdr->sh_name) + ?: "<corrupt>", + ebl_section_type_name (ebl, shdr->sh_type, buf, sizeof (buf)), + ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 8 : 16, shdr->sh_addr, + ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 6 : 8, shdr->sh_offset, + ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 6 : 8, shdr->sh_size, + shdr->sh_entsize, flagbuf, shdr->sh_link, shdr->sh_info, + shdr->sh_addralign); + } + + fputc_unlocked ('\n', stdout); +} + + +/* Print the program header. */ +static void +print_phdr (Ebl *ebl, GElf_Ehdr *ehdr) +{ + if (ehdr->e_phnum == 0) + /* No program header, this is OK in relocatable objects. */ + return; + + puts (gettext ("Program Headers:")); + if (ehdr->e_ident[EI_CLASS] == ELFCLASS32) + puts (gettext ("\ + Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align")); + else + puts (gettext ("\ + Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align")); + + /* Process all program headers. */ + bool has_relro = false; + GElf_Addr relro_from = 0; + GElf_Addr relro_to = 0; + for (size_t cnt = 0; cnt < 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 (unlikely (phdr == NULL)) + { + puts (" ???"); + continue; + } + + printf (" %-14s 0x%06" PRIx64 " 0x%0*" PRIx64 " 0x%0*" PRIx64 + " 0x%06" PRIx64 " 0x%06" PRIx64 " %c%c%c 0x%" PRIx64 "\n", + ebl_segment_type_name (ebl, phdr->p_type, buf, sizeof (buf)), + phdr->p_offset, + ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 8 : 16, phdr->p_vaddr, + ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 8 : 16, phdr->p_paddr, + phdr->p_filesz, + phdr->p_memsz, + phdr->p_flags & PF_R ? 'R' : ' ', + phdr->p_flags & PF_W ? 'W' : ' ', + phdr->p_flags & PF_X ? 'E' : ' ', + phdr->p_align); + + if (phdr->p_type == PT_INTERP) + { + /* We can show the user the name of the interpreter. */ + size_t maxsize; + char *filedata = elf_rawfile (ebl->elf, &maxsize); + + if (filedata != NULL && phdr->p_offset < maxsize) + printf (gettext ("\t[Requesting program interpreter: %s]\n"), + filedata + phdr->p_offset); + } + else if (phdr->p_type == PT_GNU_RELRO) + { + has_relro = true; + relro_from = phdr->p_vaddr; + relro_to = relro_from + phdr->p_memsz; + } + } + + if (ehdr->e_shnum == 0) + /* No sections in the file. Punt. */ + return; + + /* Get the section header string table index. */ + size_t shstrndx; + if (unlikely (elf_getshdrstrndx (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 (size_t cnt = 0; cnt < phnum; ++cnt) + { + /* 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 (unlikely (phdr == NULL)) + error (EXIT_FAILURE, 0, gettext ("cannot get program header: %s"), + elf_errmsg (-1)); + + /* Iterate over the sections. */ + bool in_relro = false; + bool in_ro = false; + for (size_t inner = 1; inner < shnum; ++inner) + { + Elf_Scn *scn = elf_getscn (ebl->elf, inner); + /* This should not happen. */ + if (unlikely (scn == NULL)) + error (EXIT_FAILURE, 0, gettext ("cannot get section: %s"), + elf_errmsg (-1)); + + /* Get the section header. */ + 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)); + + if (shdr->sh_size > 0 + /* Compare allocated sections by VMA, unallocated + sections by file offset. */ + && (shdr->sh_flags & SHF_ALLOC + ? (shdr->sh_addr >= phdr->p_vaddr + && (shdr->sh_addr + shdr->sh_size + <= phdr->p_vaddr + phdr->p_memsz)) + : (shdr->sh_offset >= phdr->p_offset + && (shdr->sh_offset + shdr->sh_size + <= phdr->p_offset + phdr->p_filesz)))) + { + if (has_relro && !in_relro + && shdr->sh_addr >= relro_from + && shdr->sh_addr + shdr->sh_size <= relro_to) + { + fputs_unlocked (" [RELRO:", stdout); + in_relro = true; + } + else if (has_relro && in_relro && shdr->sh_addr >= relro_to) + { + fputs_unlocked ("]", stdout); + in_relro = false; + } + 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 < 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 < 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)); + + /* Signal that this sectin is only partially covered. */ + if (has_relro && in_relro + && shdr->sh_addr + shdr->sh_size > relro_to) + { + fputs_unlocked (">", stdout); + in_relro = false; + } + } + } + if (in_relro || in_ro) + fputs_unlocked ("]", stdout); + + /* Finish the line. */ + fputc_unlocked ('\n', stdout); + } +} + + +static const char * +section_name (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr) +{ + return elf_strptr (ebl->elf, ehdr->e_shstrndx, shdr->sh_name) ?: "???"; +} + + +static void +handle_scngrp (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) +{ + /* Get the data of the section. */ + Elf_Data *data = elf_getdata (scn, 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. */ + size_t shstrndx; + if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0)) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + 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", + "\ +\nCOMDAT section group [%2zu] '%s' with signature '%s' contains %zu entries:\n", + data->d_size / sizeof (Elf32_Word) - 1) + : ngettext ("\ +\nSection group [%2zu] '%s' with signature '%s' contains %zu entry:\n", "\ +\nSection group [%2zu] '%s' with signature '%s' contains %zu entries:\n", + data->d_size / sizeof (Elf32_Word) - 1), + elf_ndxscn (scn), + elf_strptr (ebl->elf, shstrndx, shdr->sh_name), + elf_strptr (ebl->elf, symshdr->sh_link, + gelf_getsym (symdata, shdr->sh_info, &sym_mem)->st_name) + ?: gettext ("<INVALID SYMBOL>"), + data->d_size / sizeof (Elf32_Word) - 1); + + for (size_t cnt = 1; cnt < data->d_size / sizeof (Elf32_Word); ++cnt) + { + GElf_Shdr grpshdr_mem; + 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) +{ + /* Find all relocation sections and handle them. */ + 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 (shdr != NULL && shdr->sh_type == SHT_GROUP) + handle_scngrp (ebl, scn, shdr); + } +} + + +static const struct flags +{ + int mask; + const char *str; +} dt_flags[] = + { + { DF_ORIGIN, "ORIGIN" }, + { DF_SYMBOLIC, "SYMBOLIC" }, + { DF_TEXTREL, "TEXTREL" }, + { DF_BIND_NOW, "BIND_NOW" }, + { DF_STATIC_TLS, "STATIC_TLS" } + }; +static const int ndt_flags = sizeof (dt_flags) / sizeof (dt_flags[0]); + +static const struct flags dt_flags_1[] = + { + { DF_1_NOW, "NOW" }, + { DF_1_GLOBAL, "GLOBAL" }, + { DF_1_GROUP, "GROUP" }, + { DF_1_NODELETE, "NODELETE" }, + { DF_1_LOADFLTR, "LOADFLTR" }, + { DF_1_INITFIRST, "INITFIRST" }, + { DF_1_NOOPEN, "NOOPEN" }, + { DF_1_ORIGIN, "ORIGIN" }, + { DF_1_DIRECT, "DIRECT" }, + { DF_1_TRANS, "TRANS" }, + { DF_1_INTERPOSE, "INTERPOSE" }, + { DF_1_NODEFLIB, "NODEFLIB" }, + { DF_1_NODUMP, "NODUMP" }, + { DF_1_CONFALT, "CONFALT" }, + { DF_1_ENDFILTEE, "ENDFILTEE" }, + { DF_1_DISPRELDNE, "DISPRELDNE" }, + { DF_1_DISPRELPND, "DISPRELPND" }, + }; +static const int ndt_flags_1 = sizeof (dt_flags_1) / sizeof (dt_flags_1[0]); + +static const struct flags dt_feature_1[] = + { + { DTF_1_PARINIT, "PARINIT" }, + { DTF_1_CONFEXP, "CONFEXP" } + }; +static const int ndt_feature_1 = (sizeof (dt_feature_1) + / sizeof (dt_feature_1[0])); + +static const struct flags dt_posflag_1[] = + { + { DF_P1_LAZYLOAD, "LAZYLOAD" }, + { DF_P1_GROUPPERM, "GROUPPERM" } + }; +static const int ndt_posflag_1 = (sizeof (dt_posflag_1) + / sizeof (dt_posflag_1[0])); + + +static void +print_flags (int class, GElf_Xword d_val, const struct flags *flags, + int nflags) +{ + bool first = true; + int cnt; + + for (cnt = 0; cnt < nflags; ++cnt) + if (d_val & flags[cnt].mask) + { + if (!first) + putchar_unlocked (' '); + fputs_unlocked (flags[cnt].str, stdout); + d_val &= ~flags[cnt].mask; + first = false; + } + + if (d_val != 0) + { + if (!first) + putchar_unlocked (' '); + printf ("%#0*" PRIx64, class == ELFCLASS32 ? 10 : 18, d_val); + } + + putchar_unlocked ('\n'); +} + + +static void +print_dt_flags (int class, GElf_Xword d_val) +{ + print_flags (class, d_val, dt_flags, ndt_flags); +} + + +static void +print_dt_flags_1 (int class, GElf_Xword d_val) +{ + print_flags (class, d_val, dt_flags_1, ndt_flags_1); +} + + +static void +print_dt_feature_1 (int class, GElf_Xword d_val) +{ + print_flags (class, d_val, dt_feature_1, ndt_feature_1); +} + + +static void +print_dt_posflag_1 (int class, GElf_Xword d_val) +{ + print_flags (class, d_val, dt_posflag_1, ndt_posflag_1); +} + + +static void +handle_dynamic (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) +{ + int class = gelf_getclass (ebl->elf); + GElf_Shdr glink; + Elf_Data *data; + size_t cnt; + size_t shstrndx; + + /* Get the data of the section. */ + data = elf_getdata (scn, NULL); + if (data == NULL) + return; + + /* Get the section header string table index. */ + if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0)) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + printf (ngettext ("\ +\nDynamic segment contains %lu entry:\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'\n", + "\ +\nDynamic segment contains %lu entries:\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'\n", + shdr->sh_size / shdr->sh_entsize), + (unsigned long int) (shdr->sh_size / shdr->sh_entsize), + class == ELFCLASS32 ? 10 : 18, shdr->sh_addr, + shdr->sh_offset, + (int) shdr->sh_link, + elf_strptr (ebl->elf, shstrndx, + gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), + &glink)->sh_name)); + fputs_unlocked (gettext (" Type Value\n"), stdout); + + for (cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt) + { + GElf_Dyn 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))); + + switch (dyn->d_tag) + { + case DT_NULL: + case DT_DEBUG: + case DT_BIND_NOW: + case DT_TEXTREL: + /* No further output. */ + fputc_unlocked ('\n', stdout); + break; + + case DT_NEEDED: + printf (gettext ("Shared library: [%s]\n"), + elf_strptr (ebl->elf, shdr->sh_link, dyn->d_un.d_val)); + break; + + case DT_SONAME: + printf (gettext ("Library soname: [%s]\n"), + elf_strptr (ebl->elf, shdr->sh_link, dyn->d_un.d_val)); + break; + + case DT_RPATH: + printf (gettext ("Library rpath: [%s]\n"), + elf_strptr (ebl->elf, shdr->sh_link, dyn->d_un.d_val)); + break; + + case DT_RUNPATH: + printf (gettext ("Library runpath: [%s]\n"), + elf_strptr (ebl->elf, shdr->sh_link, dyn->d_un.d_val)); + break; + + case DT_PLTRELSZ: + case DT_RELASZ: + case DT_STRSZ: + case DT_RELSZ: + case DT_RELAENT: + case DT_SYMENT: + case DT_RELENT: + case DT_PLTPADSZ: + case DT_MOVEENT: + case DT_MOVESZ: + case DT_INIT_ARRAYSZ: + case DT_FINI_ARRAYSZ: + case DT_SYMINSZ: + case DT_SYMINENT: + case DT_GNU_CONFLICTSZ: + case DT_GNU_LIBLISTSZ: + printf (gettext ("%" PRId64 " (bytes)\n"), dyn->d_un.d_val); + break; + + case DT_VERDEFNUM: + case DT_VERNEEDNUM: + case DT_RELACOUNT: + case DT_RELCOUNT: + printf ("%" PRId64 "\n", dyn->d_un.d_val); + break; + + case DT_PLTREL:; + const char *tagname = ebl_dynamic_tag_name (ebl, dyn->d_un.d_val, + NULL, 0); + puts (tagname ?: "???"); + break; + + case DT_FLAGS: + print_dt_flags (class, dyn->d_un.d_val); + break; + + case DT_FLAGS_1: + print_dt_flags_1 (class, dyn->d_un.d_val); + break; + + case DT_FEATURE_1: + print_dt_feature_1 (class, dyn->d_un.d_val); + break; + + case DT_POSFLAG_1: + print_dt_posflag_1 (class, dyn->d_un.d_val); + break; + + default: + printf ("%#0*" PRIx64 "\n", + class == ELFCLASS32 ? 10 : 18, dyn->d_un.d_val); + break; + } + } +} + + +/* Print the dynamic segment. */ +static void +print_dynamic (Ebl *ebl) +{ + for (size_t i = 0; i < phnum; ++i) + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = gelf_getphdr (ebl->elf, i, &phdr_mem); + + if (phdr != NULL && phdr->p_type == PT_DYNAMIC) + { + 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; + } + } +} + + +/* Print relocations. */ +static void +print_relocs (Ebl *ebl, GElf_Ehdr *ehdr) +{ + /* Find all relocation sections and handle them. */ + 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_REL) + handle_relocs_rel (ebl, ehdr, scn, shdr); + else if (shdr->sh_type == SHT_RELA) + handle_relocs_rela (ebl, ehdr, scn, shdr); + } + } +} + + +/* Handle a relocation section. */ +static void +handle_relocs_rel (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) +{ + int class = gelf_getclass (ebl->elf); + int nentries = shdr->sh_size / shdr->sh_entsize; + + /* Get the data of the section. */ + Elf_Data *data = elf_getdata (scn, NULL); + if (data == NULL) + return; + + /* 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); + + /* Get the section header of the section the relocations are for. */ + GElf_Shdr destshdr_mem; + GElf_Shdr *destshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_info), + &destshdr_mem); + + if (unlikely (symshdr == NULL || symdata == NULL || destshdr == NULL)) + { + printf (gettext ("\nInvalid symbol table at offset %#0" PRIx64 "\n"), + shdr->sh_offset); + return; + } + + /* Search for the optional extended section index table. */ + 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. */ + size_t shstrndx; + if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0)) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + if (shdr->sh_info != 0) + printf (ngettext ("\ +\nRelocation section [%2zu] '%s' for section [%2u] '%s' at offset %#0" PRIx64 " contains %d entry:\n", + "\ +\nRelocation section [%2zu] '%s' for section [%2u] '%s' at offset %#0" PRIx64 " contains %d entries:\n", + nentries), + elf_ndxscn (scn), + elf_strptr (ebl->elf, shstrndx, shdr->sh_name), + (unsigned int) shdr->sh_info, + elf_strptr (ebl->elf, shstrndx, destshdr->sh_name), + shdr->sh_offset, + nentries); + else + /* The .rel.dyn section does not refer to a specific section but + instead of section index zero. Do not try to print a section + name. */ + printf (ngettext ("\ +\nRelocation section [%2u] '%s' at offset %#0" PRIx64 " contains %d entry:\n", + "\ +\nRelocation section [%2u] '%s' at offset %#0" PRIx64 " contains %d entries:\n", + nentries), + (unsigned int) elf_ndxscn (scn), + elf_strptr (ebl->elf, shstrndx, shdr->sh_name), + shdr->sh_offset, + nentries); + fputs_unlocked (class == ELFCLASS32 + ? gettext ("\ + Offset Type Value Name\n") + : gettext ("\ + Offset Type Value Name\n"), + stdout); + + int is_statically_linked = 0; + for (int cnt = 0; cnt < nentries; ++cnt) + { + GElf_Rel relmem; + GElf_Rel *rel = gelf_getrel (data, cnt, &relmem); + if (likely (rel != NULL)) + { + char buf[128]; + GElf_Sym symmem; + Elf32_Word xndx; + GElf_Sym *sym = gelf_getsymshndx (symdata, xndxdata, + GELF_R_SYM (rel->r_info), + &symmem, &xndx); + if (unlikely (sym == NULL)) + { + /* As a special case we have to handle relocations in static + executables. This only happens for IRELATIVE relocations + (so far). There is no symbol table. */ + if (is_statically_linked == 0) + { + /* Find the program header and look for a PT_INTERP entry. */ + is_statically_linked = -1; + if (ehdr->e_type == ET_EXEC) + { + is_statically_linked = 1; + + for (size_t inner = 0; inner < phnum; ++inner) + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = gelf_getphdr (ebl->elf, inner, + &phdr_mem); + if (phdr != NULL && phdr->p_type == PT_INTERP) + { + is_statically_linked = -1; + break; + } + } + } + } + + if (is_statically_linked > 0 && shdr->sh_link == 0) + printf ("\ + %#0*" PRIx64 " %-20s %*s %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 + information. */ + ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info), + buf, sizeof (buf)) + 2 + : gettext ("<INVALID RELOC>"), + class == ELFCLASS32 ? 10 : 18, "", + elf_strptr (ebl->elf, shstrndx, destshdr->sh_name)); + else + 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)) + /* Avoid the leading R_ which isn't carrying any + information. */ + ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info), + buf, sizeof (buf)) + 2 + : 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 %#0*" PRIx64 " %s\n", + class == ELFCLASS32 ? 10 : 18, rel->r_offset, + 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), + buf, sizeof (buf)) + 2 + : gettext ("<INVALID RELOC>"), + class == ELFCLASS32 ? 10 : 18, sym->st_value, + elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name)); + else + { + destshdr = gelf_getshdr (elf_getscn (ebl->elf, + sym->st_shndx == SHN_XINDEX + ? xndx : sym->st_shndx), + &destshdr_mem); + + 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)) + /* Avoid the leading R_ which isn't carrying any + information. */ + ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info), + buf, sizeof (buf)) + 2 + : gettext ("<INVALID RELOC>"), + gettext ("INVALID SECTION"), + (long int) (sym->st_shndx == SHN_XINDEX + ? xndx : sym->st_shndx)); + else + 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)) + /* Avoid the leading R_ which isn't carrying any + information. */ + ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info), + buf, sizeof (buf)) + 2 + : gettext ("<INVALID RELOC>"), + class == ELFCLASS32 ? 10 : 18, sym->st_value, + elf_strptr (ebl->elf, shstrndx, destshdr->sh_name)); + } + } + } +} + + +/* Handle a relocation section. */ +static void +handle_relocs_rela (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) +{ + int class = gelf_getclass (ebl->elf); + int nentries = shdr->sh_size / shdr->sh_entsize; + + /* Get the data of the section. */ + Elf_Data *data = elf_getdata (scn, NULL); + if (data == NULL) + return; + + /* 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); + + /* Get the section header of the section the relocations are for. */ + GElf_Shdr destshdr_mem; + GElf_Shdr *destshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_info), + &destshdr_mem); + + if (unlikely (symshdr == NULL || symdata == NULL || destshdr == NULL)) + { + printf (gettext ("\nInvalid symbol table at offset %#0" PRIx64 "\n"), + shdr->sh_offset); + return; + } + + /* Search for the optional extended section index table. */ + 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. */ + size_t shstrndx; + if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0)) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + printf (ngettext ("\ +\nRelocation section [%2zu] '%s' for section [%2u] '%s' at offset %#0" PRIx64 " contains %d entry:\n", + "\ +\nRelocation section [%2zu] '%s' for section [%2u] '%s' at offset %#0" PRIx64 " contains %d entries:\n", + nentries), + elf_ndxscn (scn), + elf_strptr (ebl->elf, shstrndx, shdr->sh_name), + (unsigned int) shdr->sh_info, + elf_strptr (ebl->elf, shstrndx, destshdr->sh_name), + shdr->sh_offset, + nentries); + fputs_unlocked (class == ELFCLASS32 + ? gettext ("\ + Offset Type Value Addend Name\n") + : gettext ("\ + Offset Type Value Addend Name\n"), + stdout); + + int is_statically_linked = 0; + for (int cnt = 0; cnt < nentries; ++cnt) + { + GElf_Rela relmem; + GElf_Rela *rel = gelf_getrela (data, cnt, &relmem); + if (likely (rel != NULL)) + { + char buf[64]; + GElf_Sym symmem; + Elf32_Word xndx; + GElf_Sym *sym = gelf_getsymshndx (symdata, xndxdata, + GELF_R_SYM (rel->r_info), + &symmem, &xndx); + + if (unlikely (sym == NULL)) + { + /* As a special case we have to handle relocations in static + executables. This only happens for IRELATIVE relocations + (so far). There is no symbol table. */ + if (is_statically_linked == 0) + { + /* Find the program header and look for a PT_INTERP entry. */ + is_statically_linked = -1; + if (ehdr->e_type == ET_EXEC) + { + is_statically_linked = 1; + + for (size_t inner = 0; inner < phnum; ++inner) + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = gelf_getphdr (ebl->elf, inner, + &phdr_mem); + if (phdr != NULL && phdr->p_type == PT_INTERP) + { + is_statically_linked = -1; + break; + } + } + } + } + + if (is_statically_linked > 0 && shdr->sh_link == 0) + printf ("\ + %#0*" PRIx64 " %-15s %*s %#6" PRIx64 " %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 + information. */ + ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info), + buf, sizeof (buf)) + 2 + : gettext ("<INVALID RELOC>"), + class == ELFCLASS32 ? 10 : 18, "", + rel->r_addend, + elf_strptr (ebl->elf, shstrndx, destshdr->sh_name)); + else + 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)) + /* Avoid the leading R_ which isn't carrying any + information. */ + ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info), + buf, sizeof (buf)) + 2 + : 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 " %-15s %#0*" PRIx64 " %+6" PRId64 " %s\n", + class == ELFCLASS32 ? 10 : 18, rel->r_offset, + 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), + buf, sizeof (buf)) + 2 + : gettext ("<INVALID RELOC>"), + class == ELFCLASS32 ? 10 : 18, sym->st_value, + rel->r_addend, + elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name)); + else + { + destshdr = gelf_getshdr (elf_getscn (ebl->elf, + sym->st_shndx == SHN_XINDEX + ? xndx : sym->st_shndx), + &destshdr_mem); + + 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)) + /* Avoid the leading R_ which isn't carrying any + information. */ + ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info), + buf, sizeof (buf)) + 2 + : gettext ("<INVALID RELOC>"), + gettext ("INVALID SECTION"), + (long int) (sym->st_shndx == SHN_XINDEX + ? xndx : sym->st_shndx)); + else + printf ("\ + %#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 + information. */ + ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info), + buf, sizeof (buf)) + 2 + : gettext ("<INVALID RELOC>"), + class == ELFCLASS32 ? 10 : 18, sym->st_value, + rel->r_addend, + elf_strptr (ebl->elf, shstrndx, destshdr->sh_name)); + } + } + } +} + + +/* Print the program header. */ +static void +print_symtab (Ebl *ebl, int type) +{ + /* Find the symbol table(s). For this we have to search through the + section table. */ + 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 (shdr != NULL && shdr->sh_type == (GElf_Word) type) + handle_symtab (ebl, scn, shdr); + } +} + + +static void +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; + int class = gelf_getclass (ebl->elf); + Elf32_Word verneed_stridx = 0; + Elf32_Word verdef_stridx = 0; + + /* Get the data of the section. */ + Elf_Data *data = elf_getdata (scn, NULL); + if (data == NULL) + return; + + /* Find out whether we have other sections we might need. */ + 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 (likely (runshdr != NULL)) + { + if (runshdr->sh_type == SHT_GNU_versym + && runshdr->sh_link == elf_ndxscn (scn)) + /* Bingo, found the version information. Now get the data. */ + versym_data = elf_getdata (runscn, NULL); + else if (runshdr->sh_type == SHT_GNU_verneed) + { + /* This is the information about the needed versions. */ + verneed_data = elf_getdata (runscn, NULL); + verneed_stridx = runshdr->sh_link; + } + else if (runshdr->sh_type == SHT_GNU_verdef) + { + /* This is the information about the defined versions. */ + verdef_data = elf_getdata (runscn, NULL); + verdef_stridx = runshdr->sh_link; + } + else if (runshdr->sh_type == SHT_SYMTAB_SHNDX + && runshdr->sh_link == elf_ndxscn (scn)) + /* Extended section index. */ + xndx_data = elf_getdata (runscn, NULL); + } + } + + /* Get the section header string table index. */ + size_t shstrndx; + if (unlikely (elf_getshdrstrndx (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. */ + 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), + (unsigned long int) shdr->sh_info, + (unsigned int) shdr->sh_link, + elf_strptr (ebl->elf, shstrndx, + gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), + &glink)->sh_name)); + + fputs_unlocked (class == ELFCLASS32 + ? gettext ("\ + Num: Value Size Type Bind Vis Ndx Name\n") + : gettext ("\ + Num: Value Size Type Bind Vis Ndx Name\n"), + stdout); + + for (unsigned int cnt = 0; cnt < nsyms; ++cnt) + { + char typebuf[64]; + char bindbuf[64]; + char scnbuf[64]; + Elf32_Word xndx; + GElf_Sym sym_mem; + GElf_Sym *sym = gelf_getsymshndx (data, xndx_data, cnt, &sym_mem, &xndx); + + if (unlikely (sym == NULL)) + continue; + + /* Determine the real section index. */ + if (likely (sym->st_shndx != SHN_XINDEX)) + xndx = sym->st_shndx; + + printf (gettext ("\ +%5u: %0*" PRIx64 " %6" PRId64 " %-7s %-6s %-9s %6s %s"), + cnt, + class == ELFCLASS32 ? 8 : 16, + sym->st_value, + sym->st_size, + ebl_symbol_type_name (ebl, GELF_ST_TYPE (sym->st_info), + typebuf, sizeof (typebuf)), + ebl_symbol_binding_name (ebl, GELF_ST_BIND (sym->st_info), + bindbuf, sizeof (bindbuf)), + get_visibility_type (GELF_ST_VISIBILITY (sym->st_other)), + ebl_section_name (ebl, sym->st_shndx, xndx, scnbuf, + sizeof (scnbuf), NULL, shnum), + elf_strptr (ebl->elf, shdr->sh_link, sym->st_name)); + + if (versym_data != NULL) + { + /* Get the version information. */ + GElf_Versym versym_mem; + GElf_Versym *versym = gelf_getversym (versym_data, cnt, &versym_mem); + + if (versym != NULL && ((*versym & 0x8000) != 0 || *versym > 1)) + { + bool is_nobits = false; + bool check_def = xndx != SHN_UNDEF; + + if (xndx < SHN_LORESERVE || sym->st_shndx == SHN_XINDEX) + { + GElf_Shdr symshdr_mem; + GElf_Shdr *symshdr = + gelf_getshdr (elf_getscn (ebl->elf, xndx), &symshdr_mem); + + is_nobits = (symshdr != NULL + && symshdr->sh_type == SHT_NOBITS); + } + + if (is_nobits || ! check_def) + { + /* We must test both. */ + GElf_Vernaux vernaux_mem; + GElf_Vernaux *vernaux = NULL; + size_t vn_offset = 0; + + GElf_Verneed verneed_mem; + GElf_Verneed *verneed = gelf_getverneed (verneed_data, 0, + &verneed_mem); + while (verneed != NULL) + { + size_t vna_offset = vn_offset; + + vernaux = gelf_getvernaux (verneed_data, + vna_offset += verneed->vn_aux, + &vernaux_mem); + while (vernaux != NULL + && vernaux->vna_other != *versym + && vernaux->vna_next != 0) + { + /* Update the offset. */ + vna_offset += vernaux->vna_next; + + vernaux = (vernaux->vna_next == 0 + ? NULL + : gelf_getvernaux (verneed_data, + vna_offset, + &vernaux_mem)); + } + + /* Check whether we found the version. */ + if (vernaux != NULL && vernaux->vna_other == *versym) + /* Found it. */ + break; + + vn_offset += verneed->vn_next; + verneed = (verneed->vn_next == 0 + ? NULL + : gelf_getverneed (verneed_data, vn_offset, + &verneed_mem)); + } + + if (vernaux != NULL && vernaux->vna_other == *versym) + { + printf ("@%s (%u)", + elf_strptr (ebl->elf, verneed_stridx, + vernaux->vna_name), + (unsigned int) vernaux->vna_other); + check_def = 0; + } + else if (unlikely (! is_nobits)) + error (0, 0, gettext ("bad dynamic symbol")); + else + check_def = 1; + } + + if (check_def && *versym != 0x8001) + { + /* We must test both. */ + size_t vd_offset = 0; + + GElf_Verdef verdef_mem; + GElf_Verdef *verdef = gelf_getverdef (verdef_data, 0, + &verdef_mem); + while (verdef != NULL) + { + if (verdef->vd_ndx == (*versym & 0x7fff)) + /* Found the definition. */ + break; + + vd_offset += verdef->vd_next; + verdef = (verdef->vd_next == 0 + ? NULL + : gelf_getverdef (verdef_data, vd_offset, + &verdef_mem)); + } + + if (verdef != NULL) + { + GElf_Verdaux verdaux_mem; + GElf_Verdaux *verdaux + = gelf_getverdaux (verdef_data, + vd_offset + verdef->vd_aux, + &verdaux_mem); + + if (verdaux != NULL) + printf ((*versym & 0x8000) ? "@%s" : "@@%s", + elf_strptr (ebl->elf, verdef_stridx, + verdaux->vda_name)); + } + } + } + } + + putchar_unlocked ('\n'); + } +} + + +/* Print version information. */ +static void +print_verinfo (Ebl *ebl) +{ + /* Find the version information sections. For this we have to + search through the section table. */ + 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 (likely (shdr != NULL)) + { + if (shdr->sh_type == SHT_GNU_verneed) + handle_verneed (ebl, scn, shdr); + else if (shdr->sh_type == SHT_GNU_verdef) + handle_verdef (ebl, scn, shdr); + else if (shdr->sh_type == SHT_GNU_versym) + handle_versym (ebl, scn, shdr); + } + } +} + + +static const char * +get_ver_flags (unsigned int flags) +{ + static char buf[32]; + char *endp; + + if (flags == 0) + return gettext ("none"); + + if (flags & VER_FLG_BASE) + endp = stpcpy (buf, "BASE "); + else + endp = buf; + + if (flags & VER_FLG_WEAK) + { + if (endp != buf) + endp = stpcpy (endp, "| "); + + endp = stpcpy (endp, "WEAK "); + } + + if (unlikely (flags & ~(VER_FLG_BASE | VER_FLG_WEAK))) + { + strncpy (endp, gettext ("| <unknown>"), buf + sizeof (buf) - endp); + buf[sizeof (buf) - 1] = '\0'; + } + + return buf; +} + + +static void +handle_verneed (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) +{ + int class = gelf_getclass (ebl->elf); + + /* Get the data of the section. */ + Elf_Data *data = elf_getdata (scn, NULL); + if (data == NULL) + return; + + /* Get the section header string table index. */ + size_t shstrndx; + if (unlikely (elf_getshdrstrndx (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", + "\ +\nVersion needs section [%2u] '%s' contains %d entries:\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'\n", + shdr->sh_info), + (unsigned int) elf_ndxscn (scn), + elf_strptr (ebl->elf, shstrndx, shdr->sh_name), shdr->sh_info, + class == 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)); + + 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 (unlikely (need == NULL)) + break; + + printf (gettext (" %#06x: Version: %hu File: %s Cnt: %hu\n"), + offset, (unsigned short int) need->vn_version, + elf_strptr (ebl->elf, shdr->sh_link, need->vn_file), + (unsigned short int) need->vn_cnt); + + unsigned int auxoffset = offset + need->vn_aux; + for (int cnt2 = need->vn_cnt; --cnt2 >= 0; ) + { + GElf_Vernaux auxmem; + GElf_Vernaux *aux = gelf_getvernaux (data, auxoffset, &auxmem); + if (unlikely (aux == NULL)) + break; + + printf (gettext (" %#06x: Name: %s Flags: %s Version: %hu\n"), + auxoffset, + elf_strptr (ebl->elf, shdr->sh_link, aux->vna_name), + get_ver_flags (aux->vna_flags), + (unsigned short int) aux->vna_other); + + auxoffset += aux->vna_next; + } + + /* Find the next offset. */ + offset += need->vn_next; + } +} + + +static void +handle_verdef (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) +{ + /* Get the data of the section. */ + Elf_Data *data = elf_getdata (scn, NULL); + if (data == NULL) + return; + + /* Get the section header string table index. */ + size_t shstrndx; + if (unlikely (elf_getshdrstrndx (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", + "\ +\nVersion definition section [%2u] '%s' contains %d entries:\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'\n", + shdr->sh_info), + (unsigned int) elf_ndxscn (scn), + elf_strptr (ebl->elf, shstrndx, shdr->sh_name), + shdr->sh_info, + class == 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)); + + 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 (unlikely (def == NULL)) + break; + + 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 ("\ + %#06x: Version: %hd Flags: %s Index: %hd Cnt: %hd Name: %s\n"), + offset, def->vd_version, + get_ver_flags (def->vd_flags), + def->vd_ndx, + def->vd_cnt, + elf_strptr (ebl->elf, shdr->sh_link, aux->vda_name)); + + auxoffset += aux->vda_next; + for (int cnt2 = 1; cnt2 < def->vd_cnt; ++cnt2) + { + aux = gelf_getverdaux (data, auxoffset, &auxmem); + if (unlikely (aux == NULL)) + break; + + printf (gettext (" %#06x: Parent %d: %s\n"), + auxoffset, cnt2, + elf_strptr (ebl->elf, shdr->sh_link, aux->vda_name)); + + auxoffset += aux->vda_next; + } + + /* Find the next offset. */ + offset += def->vd_next; + } +} + + +static void +handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) +{ + int class = gelf_getclass (ebl->elf); + const char **vername; + const char **filename; + + /* Get the data of the section. */ + Elf_Data *data = elf_getdata (scn, NULL); + if (data == NULL) + return; + + /* Get the section header string table index. */ + size_t shstrndx; + if (unlikely (elf_getshdrstrndx (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. */ + Elf_Scn *defscn = NULL; + Elf_Scn *needscn = 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 (likely (vershdr != NULL)) + { + if (vershdr->sh_type == SHT_GNU_verdef) + defscn = verscn; + else if (vershdr->sh_type == SHT_GNU_verneed) + needscn = verscn; + } + } + + size_t nvername; + if (defscn != NULL || needscn != NULL) + { + /* We have a version information (better should have). Now get + the version names. First find the maximum version number. */ + nvername = 0; + if (defscn != NULL) + { + /* Run through the version definitions and find the highest + index. */ + unsigned int offset = 0; + Elf_Data *defdata; + GElf_Shdr defshdrmem; + GElf_Shdr *defshdr; + + defdata = elf_getdata (defscn, NULL); + if (unlikely (defdata == NULL)) + return; + + defshdr = gelf_getshdr (defscn, &defshdrmem); + if (unlikely (defshdr == NULL)) + return; + + 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 (unlikely (def == NULL)) + break; + + nvername = MAX (nvername, (size_t) (def->vd_ndx & 0x7fff)); + + offset += def->vd_next; + } + } + if (needscn != NULL) + { + unsigned int offset = 0; + Elf_Data *needdata; + GElf_Shdr needshdrmem; + GElf_Shdr *needshdr; + + needdata = elf_getdata (needscn, NULL); + if (unlikely (needdata == NULL)) + return; + + needshdr = gelf_getshdr (needscn, &needshdrmem); + if (unlikely (needshdr == NULL)) + return; + + 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 (unlikely (need == NULL)) + break; + + /* Run through the auxiliary entries. */ + auxoffset = offset + need->vn_aux; + for (cnt2 = need->vn_cnt; --cnt2 >= 0; ) + { + GElf_Vernaux auxmem; + GElf_Vernaux *aux; + + aux = gelf_getvernaux (needdata, auxoffset, &auxmem); + if (unlikely (aux == NULL)) + break; + + nvername = MAX (nvername, + (size_t) (aux->vna_other & 0x7fff)); + + auxoffset += aux->vna_next; + } + + offset += need->vn_next; + } + } + + /* This is the number of versions we know about. */ + ++nvername; + + /* Allocate the array. */ + vername = (const char **) alloca (nvername * sizeof (const char *)); + filename = (const char **) alloca (nvername * sizeof (const char *)); + + /* Run through the data structures again and collect the strings. */ + if (defscn != NULL) + { + /* Run through the version definitions and find the highest + index. */ + unsigned int offset = 0; + Elf_Data *defdata; + GElf_Shdr defshdrmem; + GElf_Shdr *defshdr; + + defdata = elf_getdata (defscn, NULL); + if (unlikely (defdata == NULL)) + return; + + defshdr = gelf_getshdr (defscn, &defshdrmem); + if (unlikely (defshdr == NULL)) + return; + + for (unsigned int cnt = 0; cnt < defshdr->sh_info; ++cnt) + { + + /* Get the data at the next offset. */ + 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] + = elf_strptr (ebl->elf, defshdr->sh_link, aux->vda_name); + filename[def->vd_ndx & 0x7fff] = NULL; + + offset += def->vd_next; + } + } + if (needscn != NULL) + { + unsigned int offset = 0; + + 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 (unsigned int cnt = 0; cnt < needshdr->sh_info; ++cnt) + { + /* Get the data at the next offset. */ + GElf_Verneed needmem; + GElf_Verneed *need = gelf_getverneed (needdata, offset, + &needmem); + if (unlikely (need == NULL)) + break; + + /* Run through the auxiliary entries. */ + unsigned int auxoffset = offset + need->vn_aux; + for (int cnt2 = need->vn_cnt; --cnt2 >= 0; ) + { + GElf_Vernaux auxmem; + GElf_Vernaux *aux = gelf_getvernaux (needdata, auxoffset, + &auxmem); + if (unlikely (aux == NULL)) + break; + + vername[aux->vna_other & 0x7fff] + = elf_strptr (ebl->elf, needshdr->sh_link, aux->vna_name); + filename[aux->vna_other & 0x7fff] + = elf_strptr (ebl->elf, needshdr->sh_link, need->vn_file); + + auxoffset += aux->vna_next; + } + + offset += need->vn_next; + } + } + } + else + { + vername = NULL; + nvername = 1; + filename = NULL; + } + + /* 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'", + "\ +\nVersion symbols section [%2u] '%s' contains %d entries:\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'", + shdr->sh_size / shdr->sh_entsize), + (unsigned int) elf_ndxscn (scn), + elf_strptr (ebl->elf, shstrndx, shdr->sh_name), + (int) (shdr->sh_size / shdr->sh_entsize), + class == 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)); + + /* Now we can finally look at the actual contents of this section. */ + for (unsigned int cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt) + { + if (cnt % 2 == 0) + printf ("\n %4d:", cnt); + + 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); + break; + + case 1: + fputs_unlocked (gettext (" 1 *global* "), + stdout); + break; + + default: + n = printf ("%4d%c%s", + *sym & 0x7fff, *sym & 0x8000 ? 'h' : ' ', + (unsigned int) (*sym & 0x7fff) < nvername + ? vername[*sym & 0x7fff] : "???"); + if ((unsigned int) (*sym & 0x7fff) < nvername + && filename[*sym & 0x7fff] != NULL) + n += printf ("(%s)", filename[*sym & 0x7fff]); + printf ("%*s", MAX (0, 33 - (int) n), " "); + break; + } + } + putchar_unlocked ('\n'); +} + + +static void +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) +{ + uint32_t *counts = (uint32_t *) xcalloc (maxlength + 1, sizeof (uint32_t)); + + for (Elf32_Word cnt = 0; cnt < nbucket; ++cnt) + ++counts[lengths[cnt]]; + + 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)) + { + uint64_t success = 0; + + /* xgettext:no-c-format */ + 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); + + uint64_t nzero_counts = 0; + for (Elf32_Word 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); + } + + 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); + } + + free (counts); +} + + +/* 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; + } + + 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]; + + uint32_t *lengths = (uint32_t *) xcalloc (nbucket, sizeof (uint32_t)); + + 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; + + inner = chain[inner]; + } + } + + print_hash_info (ebl, scn, shdr, shstrndx, maxlength, nbucket, nsyms, + lengths, NULL); + + free (lengths); +} + + +/* 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; + } + + 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]; + + uint32_t *lengths = (uint32_t *) xcalloc (nbucket, sizeof (uint32_t)); + + 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 +handle_hash (Ebl *ebl) +{ + /* Get the section header string table index. */ + size_t shstrndx; + if (unlikely (elf_getshdrstrndx (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. */ + Elf_Scn *scn = NULL; + + /* Get the section header string table index. */ + size_t shstrndx; + if (unlikely (elf_getshdrstrndx (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_LIBLIST) + { + int nentries = shdr->sh_size / shdr->sh_entsize; + printf (ngettext ("\ +\nLibrary list section [%2zu] '%s' at offset %#0" PRIx64 " contains %d entry:\n", + "\ +\nLibrary list section [%2zu] '%s' at offset %#0" PRIx64 " contains %d entries:\n", + nentries), + elf_ndxscn (scn), + elf_strptr (ebl->elf, shstrndx, shdr->sh_name), + shdr->sh_offset, + nentries); + + Elf_Data *data = elf_getdata (scn, NULL); + if (data == NULL) + return; + + puts (gettext ("\ + Library Time Stamp Checksum Version Flags")); + + for (int cnt = 0; cnt < nentries; ++cnt) + { + GElf_Lib lib_mem; + GElf_Lib *lib = gelf_getlib (data, cnt, &lib_mem); + if (unlikely (lib == NULL)) + continue; + + time_t t = (time_t) lib->l_time_stamp; + struct tm *tm = gmtime (&t); + if (unlikely (tm == NULL)) + continue; + + printf (" [%2d] %-29s %04u-%02u-%02uT%02u:%02u:%02u %08x %-7u %u\n", + cnt, elf_strptr (ebl->elf, shdr->sh_link, lib->l_name), + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec, + (unsigned int) lib->l_checksum, + (unsigned int) lib->l_version, + (unsigned int) lib->l_flags); + } + } + } +} + +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_getshdrstrndx (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 + && (shdr->sh_type != SHT_ARM_ATTRIBUTES + || ehdr->e_machine != EM_ARM))) + 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 (shdr->sh_type != SHT_GNU_ATTRIBUTES + || (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 = print_address_names + ? dwfl_module_addrsym (dwflmod, address, &sym, NULL) : 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 *const known_tags[] = + { + [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_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", + [DW_TAG_type_unit] = "type_unit", + [DW_TAG_rvalue_reference_type] = "rvalue_reference_type", + [DW_TAG_template_alias] = "template_alias", + }; + const unsigned int nknown_tags = (sizeof (known_tags) + / sizeof (known_tags[0])); + static char buf[40]; + const char *result = NULL; + + if (likely (tag < nknown_tags)) + result = known_tags[tag]; + + if (unlikely (result == NULL)) + /* There are a few known extensions. */ + switch (tag) + { + case DW_TAG_MIPS_loop: + result = "MIPS_loop"; + break; + + case DW_TAG_format_label: + result = "format_label"; + break; + + case DW_TAG_function_template: + result = "function_template"; + break; + + case DW_TAG_class_template: + result = "class_template"; + break; + + case DW_TAG_GNU_BINCL: + result = "GNU_BINCL"; + break; + + case DW_TAG_GNU_EINCL: + result = "GNU_EINCL"; + break; + + case DW_TAG_GNU_template_template_param: + result = "GNU_template_template_param"; + break; + + case DW_TAG_GNU_template_parameter_pack: + result = "GNU_template_parameter_pack"; + break; + + case DW_TAG_GNU_formal_parameter_pack: + result = "GNU_formal_parameter_pack"; + break; + + case DW_TAG_GNU_call_site: + result = "GNU_call_site"; + break; + + case DW_TAG_GNU_call_site_parameter: + result = "GNU_call_site_parameter"; + break; + + default: + if (tag < DW_TAG_lo_user) + snprintf (buf, sizeof buf, gettext ("unknown tag %hx"), tag); + else + snprintf (buf, sizeof buf, gettext ("unknown user tag %hx"), tag); + result = buf; + break; + } + + return result; +} + + +static const char * +dwarf_attr_string (unsigned int attrnum) +{ + static const char *const known_attrs[] = + { + [DW_AT_sibling] = "sibling", + [DW_AT_location] = "location", + [DW_AT_name] = "name", + [DW_AT_ordering] = "ordering", + [DW_AT_subscr_data] = "subscr_data", + [DW_AT_byte_size] = "byte_size", + [DW_AT_bit_offset] = "bit_offset", + [DW_AT_bit_size] = "bit_size", + [DW_AT_element_list] = "element_list", + [DW_AT_stmt_list] = "stmt_list", + [DW_AT_low_pc] = "low_pc", + [DW_AT_high_pc] = "high_pc", + [DW_AT_language] = "language", + [DW_AT_member] = "member", + [DW_AT_discr] = "discr", + [DW_AT_discr_value] = "discr_value", + [DW_AT_visibility] = "visibility", + [DW_AT_import] = "import", + [DW_AT_string_length] = "string_length", + [DW_AT_common_reference] = "common_reference", + [DW_AT_comp_dir] = "comp_dir", + [DW_AT_const_value] = "const_value", + [DW_AT_containing_type] = "containing_type", + [DW_AT_default_value] = "default_value", + [DW_AT_inline] = "inline", + [DW_AT_is_optional] = "is_optional", + [DW_AT_lower_bound] = "lower_bound", + [DW_AT_producer] = "producer", + [DW_AT_prototyped] = "prototyped", + [DW_AT_return_addr] = "return_addr", + [DW_AT_start_scope] = "start_scope", + [DW_AT_bit_stride] = "bit_stride", + [DW_AT_upper_bound] = "upper_bound", + [DW_AT_abstract_origin] = "abstract_origin", + [DW_AT_accessibility] = "accessibility", + [DW_AT_address_class] = "address_class", + [DW_AT_artificial] = "artificial", + [DW_AT_base_types] = "base_types", + [DW_AT_calling_convention] = "calling_convention", + [DW_AT_count] = "count", + [DW_AT_data_member_location] = "data_member_location", + [DW_AT_decl_column] = "decl_column", + [DW_AT_decl_file] = "decl_file", + [DW_AT_decl_line] = "decl_line", + [DW_AT_declaration] = "declaration", + [DW_AT_discr_list] = "discr_list", + [DW_AT_encoding] = "encoding", + [DW_AT_external] = "external", + [DW_AT_frame_base] = "frame_base", + [DW_AT_friend] = "friend", + [DW_AT_identifier_case] = "identifier_case", + [DW_AT_macro_info] = "macro_info", + [DW_AT_namelist_item] = "namelist_item", + [DW_AT_priority] = "priority", + [DW_AT_segment] = "segment", + [DW_AT_specification] = "specification", + [DW_AT_static_link] = "static_link", + [DW_AT_type] = "type", + [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_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", + [DW_AT_signature] = "signature", + [DW_AT_main_subprogram] = "main_subprogram", + [DW_AT_data_bit_offset] = "data_bit_offset", + [DW_AT_const_expr] = "const_expr", + [DW_AT_enum_class] = "enum_class", + [DW_AT_linkage_name] = "linkage_name", + }; + const unsigned int nknown_attrs = (sizeof (known_attrs) + / sizeof (known_attrs[0])); + static char buf[40]; + const char *result = NULL; + + if (likely (attrnum < nknown_attrs)) + result = known_attrs[attrnum]; + + if (unlikely (result == NULL)) + /* There are a few known extensions. */ + switch (attrnum) + { + case DW_AT_MIPS_fde: + result = "MIPS_fde"; + break; + + case DW_AT_MIPS_loop_begin: + result = "MIPS_loop_begin"; + break; + + case DW_AT_MIPS_tail_loop_begin: + result = "MIPS_tail_loop_begin"; + break; + + case DW_AT_MIPS_epilog_begin: + result = "MIPS_epilog_begin"; + break; + + case DW_AT_MIPS_loop_unroll_factor: + result = "MIPS_loop_unroll_factor"; + break; + + case DW_AT_MIPS_software_pipeline_depth: + result = "MIPS_software_pipeline_depth"; + break; + + case DW_AT_MIPS_linkage_name: + result = "MIPS_linkage_name"; + break; + + case DW_AT_MIPS_stride: + result = "MIPS_stride"; + break; + + case DW_AT_MIPS_abstract_name: + result = "MIPS_abstract_name"; + break; + + case DW_AT_MIPS_clone_origin: + result = "MIPS_clone_origin"; + break; + + case DW_AT_MIPS_has_inlines: + result = "MIPS_has_inlines"; + break; + + case DW_AT_MIPS_stride_byte: + result = "MIPS_stride_byte"; + break; + + case DW_AT_MIPS_stride_elem: + result = "MIPS_stride_elem"; + break; + + case DW_AT_MIPS_ptr_dopetype: + result = "MIPS_ptr_dopetype"; + break; + + case DW_AT_MIPS_allocatable_dopetype: + result = "MIPS_allocatable_dopetype"; + break; + + case DW_AT_MIPS_assumed_shape_dopetype: + result = "MIPS_assumed_shape_dopetype"; + break; + + case DW_AT_MIPS_assumed_size: + result = "MIPS_assumed_size"; + break; + + case DW_AT_sf_names: + result = "sf_names"; + break; + + case DW_AT_src_info: + result = "src_info"; + break; + + case DW_AT_mac_info: + result = "mac_info"; + break; + + case DW_AT_src_coords: + result = "src_coords"; + break; + + case DW_AT_body_begin: + result = "body_begin"; + break; + + case DW_AT_body_end: + result = "body_end"; + break; + + case DW_AT_GNU_vector: + result = "GNU_vector"; + break; + + case DW_AT_GNU_guarded_by: + result = "GNU_guarded_by"; + break; + + case DW_AT_GNU_pt_guarded_by: + result = "GNU_pt_guarded_by"; + break; + + case DW_AT_GNU_guarded: + result = "GNU_guarded"; + break; + + case DW_AT_GNU_pt_guarded: + result = "GNU_pt_guarded"; + break; + + case DW_AT_GNU_locks_excluded: + result = "GNU_locks_excluded"; + break; + + case DW_AT_GNU_exclusive_locks_required: + result = "GNU_exclusive_locks_required"; + break; + + case DW_AT_GNU_shared_locks_required: + result = "GNU_shared_locks_required"; + break; + + case DW_AT_GNU_odr_signature: + result = "GNU_odr_signature"; + break; + + case DW_AT_GNU_template_name: + result = "GNU_template_name"; + break; + + case DW_AT_GNU_call_site_value: + result = "GNU_call_site_value"; + break; + + case DW_AT_GNU_call_site_data_value: + result = "GNU_call_site_data_value"; + break; + + case DW_AT_GNU_call_site_target: + result = "GNU_call_site_target"; + break; + + case DW_AT_GNU_call_site_target_clobbered: + result = "GNU_call_site_target_clobbered"; + break; + + case DW_AT_GNU_tail_call: + result = "GNU_tail_call"; + break; + + case DW_AT_GNU_all_tail_call_sites: + result = "GNU_all_tail_call_sites"; + break; + + case DW_AT_GNU_all_call_sites: + result = "GNU_all_call_sites"; + break; + + case DW_AT_GNU_all_source_call_sites: + result = "GNU_all_source_call_sites"; + break; + + default: + if (attrnum < DW_AT_lo_user) + snprintf (buf, sizeof buf, gettext ("unknown attribute %hx"), + attrnum); + else + snprintf (buf, sizeof buf, gettext ("unknown user attribute %hx"), + attrnum); + result = buf; + break; + } + + return result; +} + + +static const char * +dwarf_form_string (unsigned int form) +{ + static const char *const known_forms[] = + { + [DW_FORM_addr] = "addr", + [DW_FORM_block2] = "block2", + [DW_FORM_block4] = "block4", + [DW_FORM_data2] = "data2", + [DW_FORM_data4] = "data4", + [DW_FORM_data8] = "data8", + [DW_FORM_string] = "string", + [DW_FORM_block] = "block", + [DW_FORM_block1] = "block1", + [DW_FORM_data1] = "data1", + [DW_FORM_flag] = "flag", + [DW_FORM_sdata] = "sdata", + [DW_FORM_strp] = "strp", + [DW_FORM_udata] = "udata", + [DW_FORM_ref_addr] = "ref_addr", + [DW_FORM_ref1] = "ref1", + [DW_FORM_ref2] = "ref2", + [DW_FORM_ref4] = "ref4", + [DW_FORM_ref8] = "ref8", + [DW_FORM_ref_udata] = "ref_udata", + [DW_FORM_indirect] = "indirect", + [DW_FORM_sec_offset] = "sec_offset", + [DW_FORM_exprloc] = "exprloc", + [DW_FORM_flag_present] = "flag_present", + [DW_FORM_ref_sig8] = "ref_sig8", + }; + const unsigned int nknown_forms = (sizeof (known_forms) + / sizeof (known_forms[0])); + static char buf[40]; + const char *result = NULL; + + if (likely (form < nknown_forms)) + result = known_forms[form]; + + if (unlikely (result == NULL)) + { + snprintf (buf, sizeof buf, gettext ("unknown form %#" PRIx64), + (uint64_t) form); + result = buf; + } + + return result; +} + + +static const char * +dwarf_lang_string (unsigned int lang) +{ + 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_Cobol74] = "Cobol74", + [DW_LANG_Cobol85] = "Cobol85", + [DW_LANG_Fortran77] = "Fortran77", + [DW_LANG_Fortran90] = "Fortran90", + [DW_LANG_Pascal83] = "Pascal83", + [DW_LANG_Modula2] = "Modula2", + [DW_LANG_Java] = "Java", + [DW_LANG_C99] = "ISO C99", + [DW_LANG_Ada95] = "Ada95", + [DW_LANG_Fortran95] = "Fortran95", + [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 (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. */ + return "Assembler"; + + if (lang >= DW_LANG_lo_user && lang <= DW_LANG_hi_user) + { + static char buf[30]; + snprintf (buf, sizeof (buf), "lo_user+%u", lang - DW_LANG_lo_user); + return buf; + } + + return "???"; +} + + +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_block (size_t n, const void *block) +{ + if (n == 0) + puts (_("empty block")); + else + { + printf (_("%zu byte block:"), n); + const unsigned char *data = block; + do + printf (" %02x", *data++); + while (--n > 0); + putchar ('\n'); + } +} + +static void +print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, + unsigned int vers, unsigned int addrsize, unsigned int offset_size, + Dwarf_Word len, const unsigned char *data) +{ + const unsigned int ref_size = vers < 3 ? addrsize : offset_size; + + static const char *const known[] = + { + [DW_OP_addr] = "addr", + [DW_OP_deref] = "deref", + [DW_OP_const1u] = "const1u", + [DW_OP_const1s] = "const1s", + [DW_OP_const2u] = "const2u", + [DW_OP_const2s] = "const2s", + [DW_OP_const4u] = "const4u", + [DW_OP_const4s] = "const4s", + [DW_OP_const8u] = "const8u", + [DW_OP_const8s] = "const8s", + [DW_OP_constu] = "constu", + [DW_OP_consts] = "consts", + [DW_OP_dup] = "dup", + [DW_OP_drop] = "drop", + [DW_OP_over] = "over", + [DW_OP_pick] = "pick", + [DW_OP_swap] = "swap", + [DW_OP_rot] = "rot", + [DW_OP_xderef] = "xderef", + [DW_OP_abs] = "abs", + [DW_OP_and] = "and", + [DW_OP_div] = "div", + [DW_OP_minus] = "minus", + [DW_OP_mod] = "mod", + [DW_OP_mul] = "mul", + [DW_OP_neg] = "neg", + [DW_OP_not] = "not", + [DW_OP_or] = "or", + [DW_OP_plus] = "plus", + [DW_OP_plus_uconst] = "plus_uconst", + [DW_OP_shl] = "shl", + [DW_OP_shr] = "shr", + [DW_OP_shra] = "shra", + [DW_OP_xor] = "xor", + [DW_OP_bra] = "bra", + [DW_OP_eq] = "eq", + [DW_OP_ge] = "ge", + [DW_OP_gt] = "gt", + [DW_OP_le] = "le", + [DW_OP_lt] = "lt", + [DW_OP_ne] = "ne", + [DW_OP_skip] = "skip", + [DW_OP_lit0] = "lit0", + [DW_OP_lit1] = "lit1", + [DW_OP_lit2] = "lit2", + [DW_OP_lit3] = "lit3", + [DW_OP_lit4] = "lit4", + [DW_OP_lit5] = "lit5", + [DW_OP_lit6] = "lit6", + [DW_OP_lit7] = "lit7", + [DW_OP_lit8] = "lit8", + [DW_OP_lit9] = "lit9", + [DW_OP_lit10] = "lit10", + [DW_OP_lit11] = "lit11", + [DW_OP_lit12] = "lit12", + [DW_OP_lit13] = "lit13", + [DW_OP_lit14] = "lit14", + [DW_OP_lit15] = "lit15", + [DW_OP_lit16] = "lit16", + [DW_OP_lit17] = "lit17", + [DW_OP_lit18] = "lit18", + [DW_OP_lit19] = "lit19", + [DW_OP_lit20] = "lit20", + [DW_OP_lit21] = "lit21", + [DW_OP_lit22] = "lit22", + [DW_OP_lit23] = "lit23", + [DW_OP_lit24] = "lit24", + [DW_OP_lit25] = "lit25", + [DW_OP_lit26] = "lit26", + [DW_OP_lit27] = "lit27", + [DW_OP_lit28] = "lit28", + [DW_OP_lit29] = "lit29", + [DW_OP_lit30] = "lit30", + [DW_OP_lit31] = "lit31", + [DW_OP_reg0] = "reg0", + [DW_OP_reg1] = "reg1", + [DW_OP_reg2] = "reg2", + [DW_OP_reg3] = "reg3", + [DW_OP_reg4] = "reg4", + [DW_OP_reg5] = "reg5", + [DW_OP_reg6] = "reg6", + [DW_OP_reg7] = "reg7", + [DW_OP_reg8] = "reg8", + [DW_OP_reg9] = "reg9", + [DW_OP_reg10] = "reg10", + [DW_OP_reg11] = "reg11", + [DW_OP_reg12] = "reg12", + [DW_OP_reg13] = "reg13", + [DW_OP_reg14] = "reg14", + [DW_OP_reg15] = "reg15", + [DW_OP_reg16] = "reg16", + [DW_OP_reg17] = "reg17", + [DW_OP_reg18] = "reg18", + [DW_OP_reg19] = "reg19", + [DW_OP_reg20] = "reg20", + [DW_OP_reg21] = "reg21", + [DW_OP_reg22] = "reg22", + [DW_OP_reg23] = "reg23", + [DW_OP_reg24] = "reg24", + [DW_OP_reg25] = "reg25", + [DW_OP_reg26] = "reg26", + [DW_OP_reg27] = "reg27", + [DW_OP_reg28] = "reg28", + [DW_OP_reg29] = "reg29", + [DW_OP_reg30] = "reg30", + [DW_OP_reg31] = "reg31", + [DW_OP_breg0] = "breg0", + [DW_OP_breg1] = "breg1", + [DW_OP_breg2] = "breg2", + [DW_OP_breg3] = "breg3", + [DW_OP_breg4] = "breg4", + [DW_OP_breg5] = "breg5", + [DW_OP_breg6] = "breg6", + [DW_OP_breg7] = "breg7", + [DW_OP_breg8] = "breg8", + [DW_OP_breg9] = "breg9", + [DW_OP_breg10] = "breg10", + [DW_OP_breg11] = "breg11", + [DW_OP_breg12] = "breg12", + [DW_OP_breg13] = "breg13", + [DW_OP_breg14] = "breg14", + [DW_OP_breg15] = "breg15", + [DW_OP_breg16] = "breg16", + [DW_OP_breg17] = "breg17", + [DW_OP_breg18] = "breg18", + [DW_OP_breg19] = "breg19", + [DW_OP_breg20] = "breg20", + [DW_OP_breg21] = "breg21", + [DW_OP_breg22] = "breg22", + [DW_OP_breg23] = "breg23", + [DW_OP_breg24] = "breg24", + [DW_OP_breg25] = "breg25", + [DW_OP_breg26] = "breg26", + [DW_OP_breg27] = "breg27", + [DW_OP_breg28] = "breg28", + [DW_OP_breg29] = "breg29", + [DW_OP_breg30] = "breg30", + [DW_OP_breg31] = "breg31", + [DW_OP_regx] = "regx", + [DW_OP_fbreg] = "fbreg", + [DW_OP_bregx] = "bregx", + [DW_OP_piece] = "piece", + [DW_OP_deref_size] = "deref_size", + [DW_OP_xderef_size] = "xderef_size", + [DW_OP_nop] = "nop", + [DW_OP_push_object_address] = "push_object_address", + [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", + [DW_OP_implicit_value] = "implicit_value", + [DW_OP_stack_value] = "stack_value", + [DW_OP_GNU_implicit_pointer] = "GNU_implicit_pointer", + [DW_OP_GNU_entry_value] = "GNU_entry_value", + [DW_OP_GNU_const_type] = "GNU_const_type", + [DW_OP_GNU_regval_type] = "GNU_regval_type", + [DW_OP_GNU_deref_type] = "GNU_deref_type", + [DW_OP_GNU_convert] = "GNU_convert", + [DW_OP_GNU_reinterpret] = "GNU_reinterpret", + }; + + if (len == 0) + { + printf ("%*s(empty)\n", indent, ""); + return; + } + +#define NEED(n) if (len < (Dwarf_Word) (n)) goto invalid +#define CONSUME(n) NEED (n); else len -= (n) + + Dwarf_Word offset = 0; + while (len-- > 0) + { + uint_fast8_t op = *data++; + + switch (op) + { + case DW_OP_addr:; + /* Address operand. */ + Dwarf_Word addr; + NEED (addrsize); + if (addrsize == 4) + addr = read_4ubyte_unaligned (dbg, data); + else + { + assert (addrsize == 8); + addr = read_8ubyte_unaligned (dbg, data); + } + data += addrsize; + CONSUME (addrsize); + + char *a = format_dwarf_addr (dwflmod, 0, addr); + printf ("%*s[%4" PRIuMAX "] %s %s\n", + indent, "", (uintmax_t) offset, known[op], a); + free (a); + + offset += 1 + addrsize; + break; + + case DW_OP_call_ref: + /* Offset operand. */ + NEED (ref_size); + if (ref_size == 4) + addr = read_4ubyte_unaligned (dbg, data); + else + { + assert (ref_size == 8); + addr = read_8ubyte_unaligned (dbg, data); + } + data += ref_size; + CONSUME (ref_size); + + printf ("%*s[%4" PRIuMAX "] %s %#" PRIxMAX "\n", + indent, "", (uintmax_t) offset, + known[op], (uintmax_t) addr); + offset += 1 + ref_size; + break; + + case DW_OP_deref_size: + case DW_OP_xderef_size: + case DW_OP_pick: + case DW_OP_const1u: + // XXX value might be modified by relocation + NEED (1); + printf ("%*s[%4" PRIuMAX "] %s %" PRIu8 "\n", + indent, "", (uintmax_t) offset, + known[op], *((uint8_t *) data)); + ++data; + --len; + offset += 2; + break; + + case DW_OP_const2u: + NEED (2); + // XXX value might be modified by relocation + printf ("%*s[%4" PRIuMAX "] %s %" PRIu16 "\n", + indent, "", (uintmax_t) offset, + known[op], read_2ubyte_unaligned (dbg, data)); + CONSUME (2); + data += 2; + offset += 3; + break; + + case DW_OP_const4u: + NEED (4); + // XXX value might be modified by relocation + printf ("%*s[%4" PRIuMAX "] %s %" PRIu32 "\n", + indent, "", (uintmax_t) offset, + known[op], read_4ubyte_unaligned (dbg, data)); + CONSUME (4); + data += 4; + offset += 5; + break; + + case DW_OP_const8u: + NEED (8); + // XXX value might be modified by relocation + printf ("%*s[%4" PRIuMAX "] %s %" PRIu64 "\n", + indent, "", (uintmax_t) offset, + known[op], read_8ubyte_unaligned (dbg, data)); + CONSUME (8); + data += 8; + offset += 9; + break; + + case DW_OP_const1s: + NEED (1); + // XXX value might be modified by relocation + printf ("%*s[%4" PRIuMAX "] %s %" PRId8 "\n", + indent, "", (uintmax_t) offset, + known[op], *((int8_t *) data)); + ++data; + --len; + offset += 2; + break; + + case DW_OP_const2s: + NEED (2); + // XXX value might be modified by relocation + printf ("%*s[%4" PRIuMAX "] %s %" PRId16 "\n", + indent, "", (uintmax_t) offset, + known[op], read_2sbyte_unaligned (dbg, data)); + CONSUME (2); + data += 2; + offset += 3; + break; + + case DW_OP_const4s: + NEED (4); + // XXX value might be modified by relocation + printf ("%*s[%4" PRIuMAX "] %s %" PRId32 "\n", + indent, "", (uintmax_t) offset, + known[op], read_4sbyte_unaligned (dbg, data)); + CONSUME (4); + data += 4; + offset += 5; + break; + + case DW_OP_const8s: + NEED (8); + // XXX value might be modified by relocation + printf ("%*s[%4" PRIuMAX "] %s %" PRId64 "\n", + indent, "", (uintmax_t) offset, + known[op], read_8sbyte_unaligned (dbg, data)); + CONSUME (8); + data += 8; + offset += 9; + break; + + case DW_OP_piece: + case DW_OP_regx: + case DW_OP_plus_uconst: + case DW_OP_constu:; + const unsigned char *start = data; + uint64_t uleb; + NEED (1); + get_uleb128 (uleb, data); /* XXX check overrun */ + printf ("%*s[%4" PRIuMAX "] %s %" PRIu64 "\n", + indent, "", (uintmax_t) offset, known[op], uleb); + CONSUME (data - start); + offset += 1 + (data - start); + break; + + case DW_OP_bit_piece: + start = data; + uint64_t uleb2; + NEED (2); + get_uleb128 (uleb, data); /* XXX check overrun */ + get_uleb128 (uleb2, data); /* XXX check overrun */ + printf ("%*s[%4" PRIuMAX "] %s %" PRIu64 ", %" PRIu64 "\n", + indent, "", (uintmax_t) offset, known[op], uleb, uleb2); + CONSUME (data - start); + offset += 1 + (data - start); + break; + + case DW_OP_fbreg: + case DW_OP_breg0 ... DW_OP_breg31: + case DW_OP_consts: + start = data; + int64_t sleb; + NEED (1); + get_sleb128 (sleb, data); /* XXX check overrun */ + printf ("%*s[%4" PRIuMAX "] %s %" PRId64 "\n", + indent, "", (uintmax_t) offset, known[op], sleb); + CONSUME (data - start); + offset += 1 + (data - start); + break; + + case DW_OP_bregx: + start = data; + NEED (2); + get_uleb128 (uleb, data); /* XXX check overrun */ + get_sleb128 (sleb, data); /* XXX check overrun */ + printf ("%*s[%4" PRIuMAX "] %s %" PRIu64 " %" PRId64 "\n", + indent, "", (uintmax_t) offset, known[op], uleb, sleb); + CONSUME (data - start); + offset += 1 + (data - start); + break; + + case DW_OP_call2: + NEED (2); + printf ("%*s[%4" PRIuMAX "] %s %" PRIu16 "\n", + indent, "", (uintmax_t) offset, known[op], + read_2ubyte_unaligned (dbg, data)); + CONSUME (2); + offset += 3; + break; + + case DW_OP_call4: + NEED (4); + printf ("%*s[%4" PRIuMAX "] %s %" PRIu32 "\n", + indent, "", (uintmax_t) offset, known[op], + read_4ubyte_unaligned (dbg, data)); + CONSUME (4); + offset += 5; + break; + + case DW_OP_skip: + case DW_OP_bra: + NEED (2); + printf ("%*s[%4" PRIuMAX "] %s %" PRIuMAX "\n", + indent, "", (uintmax_t) offset, known[op], + (uintmax_t) (offset + read_2sbyte_unaligned (dbg, data))); + CONSUME (2); + data += 2; + offset += 3; + break; + + case DW_OP_implicit_value: + start = data; + NEED (1); + get_uleb128 (uleb, data); /* XXX check overrun */ + printf ("%*s[%4" PRIuMAX "] %s: ", + indent, "", (uintmax_t) offset, known[op]); + NEED (uleb); + print_block (uleb, data); + data += uleb; + CONSUME (data - start); + offset += 1 + (data - start); + break; + + case DW_OP_GNU_implicit_pointer: + /* DIE offset operand. */ + start = data; + NEED (ref_size + 1); + if (ref_size == 4) + addr = read_4ubyte_unaligned (dbg, data); + else + { + assert (ref_size == 8); + addr = read_8ubyte_unaligned (dbg, data); + } + data += ref_size; + /* Byte offset operand. */ + get_sleb128 (sleb, data); /* XXX check overrun */ + + printf ("%*s[%4" PRIuMAX "] %s %#" PRIxMAX ", %+" PRId64 "\n", + indent, "", (intmax_t) offset, + known[op], (uintmax_t) addr, sleb); + CONSUME (data - start); + offset += 1 + (data - start); + break; + + case DW_OP_GNU_entry_value: + /* Size plus expression block. */ + start = data; + NEED (1); + get_uleb128 (uleb, data); /* XXX check overrun */ + printf ("%*s[%4" PRIuMAX "] %s:\n", + indent, "", (uintmax_t) offset, known[op]); + NEED (uleb); + print_ops (dwflmod, dbg, indent + 6, indent + 6, vers, + addrsize, offset_size, uleb, data); + data += uleb; + CONSUME (data - start); + offset += 1 + (data - start); + break; + + case DW_OP_GNU_const_type: + /* DIE offset, size plus block. */ + start = data; + NEED (2); + get_uleb128 (uleb, data); /* XXX check overrun */ + uint8_t usize = *(uint8_t *) data++; + NEED (usize); + printf ("%*s[%4" PRIuMAX "] %s [%6" PRIxMAX "] ", + indent, "", (uintmax_t) offset, known[op], uleb); + print_block (usize, data); + data += usize; + CONSUME (data - start); + offset += 1 + (data - start); + break; + + case DW_OP_GNU_regval_type: + start = data; + NEED (2); + get_uleb128 (uleb, data); /* XXX check overrun */ + get_uleb128 (uleb2, data); /* XXX check overrun */ + printf ("%*s[%4" PRIuMAX "] %s %" PRIu64 " %#" PRIx64 "\n", + indent, "", (uintmax_t) offset, known[op], uleb, uleb2); + CONSUME (data - start); + offset += 1 + (data - start); + break; + + case DW_OP_GNU_deref_type: + start = data; + NEED (2); + usize = *(uint8_t *) data++; + get_uleb128 (uleb, data); /* XXX check overrun */ + printf ("%*s[%4" PRIuMAX "] %s %" PRIu8 " [%6" PRIxMAX "]\n", + indent, "", (uintmax_t) offset, + known[op], usize, uleb); + CONSUME (data - start); + offset += 1 + (data - start); + break; + + case DW_OP_GNU_convert: + case DW_OP_GNU_reinterpret: + start = data; + NEED (1); + get_uleb128 (uleb, data); /* XXX check overrun */ + printf ("%*s[%4" PRIuMAX "] %s [%6" PRIxMAX "]\n", + indent, "", (uintmax_t) offset, known[op], uleb); + CONSUME (data - start); + offset += 1 + (data - start); + break; + + default: + /* No Operand. */ + 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; + continue; + + invalid: + printf (gettext ("%*s[%4" PRIuMAX "] %s <TRUNCATED>\n"), + indent, "", (uintmax_t) offset, known[op]); + break; + } +} + + +struct listptr +{ + Dwarf_Off offset:(64 - 3); + bool addr64:1; + bool dwarf64:1; + bool warned:1; +}; + +#define listptr_offset_size(p) ((p)->dwarf64 ? 8 : 4) +#define listptr_address_size(p) ((p)->addr64 ? 8 : 4) + +static int +compare_listptr (const void *a, const void *b, void *arg) +{ + const char *name = arg; + struct listptr *p1 = (void *) a; + struct listptr *p2 = (void *) b; + + if (p1->offset < p2->offset) + return -1; + if (p1->offset > p2->offset) + return 1; + + if (!p1->warned && !p2->warned) + { + if (p1->addr64 != p2->addr64) + { + p1->warned = p2->warned = true; + error (0, 0, + gettext ("%s %#" PRIx64 " used with different address sizes"), + name, (uint64_t) p1->offset); + } + if (p1->dwarf64 != p2->dwarf64) + { + p1->warned = p2->warned = true; + error (0, 0, + gettext ("%s %#" PRIx64 " used with different offset sizes"), + name, (uint64_t) p1->offset); + } + } + + return 0; +} + +struct listptr_table +{ + size_t n; + size_t alloc; + struct listptr *table; +}; + +static struct listptr_table known_loclistptr; +static struct listptr_table known_rangelistptr; + +static void +reset_listptr (struct listptr_table *table) +{ + free (table->table); + table->table = NULL; + table->n = table->alloc = 0; +} + +static void +notice_listptr (enum section_e section, struct listptr_table *table, + uint_fast8_t address_size, uint_fast8_t offset_size, + Dwarf_Off offset) +{ + if (print_debug_sections & section) + { + if (table->n == table->alloc) + { + if (table->alloc == 0) + table->alloc = 128; + else + table->alloc *= 2; + table->table = xrealloc (table->table, + table->alloc * sizeof table->table[0]); + } + + struct listptr *p = &table->table[table->n++]; + + *p = (struct listptr) + { + .addr64 = address_size == 8, + .dwarf64 = offset_size == 8, + .offset = offset + }; + assert (p->offset == offset); + } +} + +static void +sort_listptr (struct listptr_table *table, const char *name) +{ + if (table->n > 0) + qsort_r (table->table, table->n, sizeof table->table[0], + &compare_listptr, (void *) name); +} + +static bool +skip_listptr_hole (struct listptr_table *table, size_t *idxp, + uint_fast8_t *address_sizep, uint_fast8_t *offset_sizep, + ptrdiff_t offset, unsigned char **readp, unsigned char *endp) +{ + if (table->n == 0) + return false; + + while (*idxp < table->n && table->table[*idxp].offset < (Dwarf_Off) offset) + ++*idxp; + + struct listptr *p = &table->table[*idxp]; + + if (*idxp == table->n + || p->offset >= (Dwarf_Off) (endp - *readp + offset)) + { + *readp = endp; + printf (gettext (" [%6tx] <UNUSED GARBAGE IN REST OF SECTION>\n"), + offset); + return true; + } + + if (p->offset != (Dwarf_Off) offset) + { + *readp += p->offset - offset; + printf (gettext (" [%6tx] <UNUSED GARBAGE> ... %" PRIu64 " bytes ...\n"), + offset, (Dwarf_Off) p->offset - offset); + return true; + } + + if (address_sizep != NULL) + *address_sizep = listptr_address_size (p); + if (offset_sizep != NULL) + *offset_sizep = listptr_offset_size (p); + + return false; +} + + +static void +print_debug_abbrev_section (Dwfl_Module *dwflmod __attribute__ ((unused)), + Ebl *ebl, GElf_Ehdr *ehdr, + Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) +{ + printf (gettext ("\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n" + " [ Code]\n"), + elf_ndxscn (scn), section_name (ebl, ehdr, shdr), + (uint64_t) shdr->sh_offset); + + Dwarf_Off offset = 0; + while (offset < dbg->sectiondata[IDX_debug_abbrev]->d_size) + { + printf (gettext ("\nAbbreviation section at offset %" PRIu64 ":\n"), + offset); + + while (1) + { + size_t length; + Dwarf_Abbrev abbrev; + + 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; + } + + /* This is the NUL byte at the end of the section. */ + ++offset; + break; + } + + /* 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); + + ++cnt; + } + + offset += length; + } + } +} + + +/* Print content of DWARF .debug_aranges section. We fortunately do + not have to know a bit about the structure of the section, libdwarf + takes care of it. */ +static void +print_debug_aranges_section (Dwfl_Module *dwflmod __attribute__ ((unused)), + Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, + GElf_Shdr *shdr, Dwarf *dbg) +{ + Dwarf_Aranges *aranges; + size_t cnt; + if (unlikely (dwarf_getaranges (dbg, &aranges, &cnt) != 0)) + { + error (0, 0, gettext ("cannot get .debug_aranges content: %s"), + dwarf_errmsg (-1)); + return; + } + + printf (ngettext ("\ +\nDWARF section [%2zu] '%s' at offset %#" PRIx64 " contains %zu entry:\n", + "\ +\nDWARF section [%2zu] '%s' at offset %#" PRIx64 " contains %zu entries:\n", + cnt), + elf_ndxscn (scn), section_name (ebl, ehdr, shdr), + (uint64_t) shdr->sh_offset, cnt); + + /* Compute floor(log16(cnt)). */ + size_t tmp = cnt; + int digits = 1; + while (tmp >= 16) + { + ++digits; + tmp >>= 4; + } + + for (size_t n = 0; n < cnt; ++n) + { + Dwarf_Arange *runp = dwarf_onearange (aranges, n); + if (unlikely (runp == NULL)) + { + printf ("cannot get arange %zu: %s\n", n, dwarf_errmsg (-1)); + return; + } + + Dwarf_Addr start; + Dwarf_Word length; + Dwarf_Off offset; + + if (unlikely (dwarf_getarangeinfo (runp, &start, &length, &offset) != 0)) + printf (gettext (" [%*zu] ???\n"), digits, n); + else + printf (gettext (" [%*zu] start: %0#*" PRIx64 + ", length: %5" PRIu64 ", CU DIE offset: %6" + PRId64 "\n"), + digits, n, ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 10 : 18, + (uint64_t) start, (uint64_t) length, (int64_t) offset); + } +} + +/* Print content of DWARF .debug_ranges section. */ +static void +print_debug_ranges_section (Dwfl_Module *dwflmod, + Ebl *ebl, 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 [%2zu] '%s' at offset %#" PRIx64 ":\n"), + elf_ndxscn (scn), section_name (ebl, ehdr, shdr), + (uint64_t) shdr->sh_offset); + + sort_listptr (&known_rangelistptr, "rangelistptr"); + size_t listptr_idx = 0; + + uint_fast8_t address_size = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8; + + bool first = true; + unsigned char *const endp = (unsigned char *) data->d_buf + data->d_size; + unsigned char *readp = data->d_buf; + while (readp < endp) + { + ptrdiff_t offset = readp - (unsigned char *) data->d_buf; + + if (first && skip_listptr_hole (&known_rangelistptr, &listptr_idx, + &address_size, NULL, + offset, &readp, endp)) + continue; + + 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. */ + { + if (first) + printf (gettext (" [%6tx] empty list\n"), offset); + 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; + } + } +} + +#define REGNAMESZ 16 +static const char * +register_info (Ebl *ebl, unsigned int regno, const Ebl_Register_Location *loc, + char name[REGNAMESZ], int *bits, int *type) +{ + const char *set; + const char *pfx; + int ignore; + ssize_t n = ebl_register_info (ebl, regno, name, REGNAMESZ, &pfx, &set, + bits ?: &ignore, type ?: &ignore); + if (n <= 0) + { + snprintf (name, REGNAMESZ, "reg%u", loc->regno); + if (bits != NULL) + *bits = loc->bits; + if (type != NULL) + *type = DW_ATE_unsigned; + set = "??? unrecognized"; + } + else + { + if (bits != NULL && *bits <= 0) + *bits = loc->bits; + if (type != NULL && *type == DW_ATE_void) + *type = DW_ATE_unsigned; + + } + return set; +} + +static void +print_cfa_program (const unsigned char *readp, const unsigned char *const endp, + Dwarf_Word vma_base, unsigned int code_align, + int data_align, + unsigned int version, unsigned int ptr_size, + Dwfl_Module *dwflmod, Ebl *ebl, Dwarf *dbg) +{ + char regnamebuf[REGNAMESZ]; + const char *regname (unsigned int regno) + { + register_info (ebl, regno, NULL, regnamebuf, NULL, NULL); + return regnamebuf; + } + + puts ("\n Program:"); + Dwarf_Word pc = vma_base; + while (readp < endp) + { + unsigned int opcode = *readp++; + + if (opcode < DW_CFA_advance_loc) + /* Extended opcode. */ + switch (opcode) + { + uint64_t op1; + int64_t sop1; + uint64_t op2; + int64_t sop2; + + case DW_CFA_nop: + puts (" nop"); + break; + case DW_CFA_set_loc: + // XXX overflow check + get_uleb128 (op1, readp); + op1 += vma_base; + printf (" set_loc %" PRIu64 "\n", op1 * code_align); + break; + case DW_CFA_advance_loc1: + printf (" advance_loc1 %u to %#" PRIx64 "\n", + *readp, pc += *readp * code_align); + ++readp; + break; + case DW_CFA_advance_loc2: + op1 = read_2ubyte_unaligned_inc (dbg, readp); + printf (" advance_loc2 %" PRIu64 " to %#" PRIx64 "\n", + op1, pc += op1 * code_align); + break; + case DW_CFA_advance_loc4: + op1 = read_4ubyte_unaligned_inc (dbg, readp); + printf (" advance_loc4 %" PRIu64 " to %#" PRIx64 "\n", + op1, pc += op1 * code_align); + break; + case DW_CFA_offset_extended: + // XXX overflow check + get_uleb128 (op1, readp); + get_uleb128 (op2, readp); + printf (" offset_extended r%" PRIu64 " (%s) at cfa%+" PRId64 + "\n", + op1, regname (op1), op2 * data_align); + break; + case DW_CFA_restore_extended: + // XXX overflow check + get_uleb128 (op1, readp); + printf (" restore_extended r%" PRIu64 " (%s)\n", + op1, regname (op1)); + break; + case DW_CFA_undefined: + // XXX overflow check + get_uleb128 (op1, readp); + printf (" undefined r%" PRIu64 " (%s)\n", op1, regname (op1)); + break; + case DW_CFA_same_value: + // XXX overflow check + get_uleb128 (op1, readp); + printf (" same_value r%" PRIu64 " (%s)\n", op1, regname (op1)); + break; + case DW_CFA_register: + // XXX overflow check + get_uleb128 (op1, readp); + get_uleb128 (op2, readp); + printf (" register r%" PRIu64 " (%s) in r%" PRIu64 " (%s)\n", + op1, regname (op1), op2, regname (op2)); + break; + case DW_CFA_remember_state: + puts (" remember_state"); + break; + case DW_CFA_restore_state: + puts (" restore_state"); + break; + case DW_CFA_def_cfa: + // XXX overflow check + get_uleb128 (op1, readp); + get_uleb128 (op2, readp); + printf (" def_cfa r%" PRIu64 " (%s) at offset %" PRIu64 "\n", + op1, regname (op1), op2); + break; + case DW_CFA_def_cfa_register: + // XXX overflow check + get_uleb128 (op1, readp); + printf (" def_cfa_register r%" PRIu64 " (%s)\n", + op1, regname (op1)); + break; + case DW_CFA_def_cfa_offset: + // XXX overflow check + get_uleb128 (op1, readp); + printf (" def_cfa_offset %" PRIu64 "\n", op1); + break; + case DW_CFA_def_cfa_expression: + // XXX overflow check + get_uleb128 (op1, readp); /* Length of DW_FORM_block. */ + printf (" def_cfa_expression %" PRIu64 "\n", op1); + print_ops (dwflmod, dbg, 10, 10, version, ptr_size, 0, op1, readp); + readp += op1; + break; + case DW_CFA_expression: + // XXX overflow check + get_uleb128 (op1, readp); + get_uleb128 (op2, readp); /* Length of DW_FORM_block. */ + printf (" expression r%" PRIu64 " (%s) \n", + op1, regname (op1)); + print_ops (dwflmod, dbg, 10, 10, version, ptr_size, 0, op2, readp); + readp += op2; + break; + case DW_CFA_offset_extended_sf: + // XXX overflow check + get_uleb128 (op1, readp); + get_sleb128 (sop2, readp); + printf (" offset_extended_sf r%" PRIu64 " (%s) at cfa%+" + PRId64 "\n", + op1, regname (op1), sop2 * data_align); + break; + case DW_CFA_def_cfa_sf: + // XXX overflow check + get_uleb128 (op1, readp); + get_sleb128 (sop2, readp); + printf (" def_cfa_sf r%" PRIu64 " (%s) at offset %" PRId64 "\n", + op1, regname (op1), sop2 * data_align); + break; + case DW_CFA_def_cfa_offset_sf: + // XXX overflow check + get_sleb128 (sop1, readp); + printf (" def_cfa_offset_sf %" PRId64 "\n", sop1 * data_align); + break; + case DW_CFA_val_offset: + // XXX overflow check + get_uleb128 (op1, readp); + get_uleb128 (op2, readp); + printf (" val_offset %" PRIu64 " at offset %" PRIu64 "\n", + op1, op2 * data_align); + break; + case DW_CFA_val_offset_sf: + // XXX overflow check + get_uleb128 (op1, readp); + get_sleb128 (sop2, readp); + printf (" val_offset_sf %" PRIu64 " at offset %" PRId64 "\n", + op1, sop2 * data_align); + break; + case DW_CFA_val_expression: + // XXX overflow check + get_uleb128 (op1, readp); + get_uleb128 (op2, readp); /* Length of DW_FORM_block. */ + printf (" val_expression r%" PRIu64 " (%s)\n", + op1, regname (op1)); + print_ops (dwflmod, dbg, 10, 10, version, ptr_size, 0, op2, readp); + readp += op2; + break; + case DW_CFA_MIPS_advance_loc8: + op1 = read_8ubyte_unaligned_inc (dbg, readp); + printf (" MIPS_advance_loc8 %" PRIu64 " to %#" PRIx64 "\n", + op1, pc += op1 * code_align); + break; + case DW_CFA_GNU_window_save: + puts (" GNU_window_save"); + break; + case DW_CFA_GNU_args_size: + // XXX overflow check + get_uleb128 (op1, readp); + printf (" args_size %" PRIu64 "\n", op1); + break; + default: + printf (" ??? (%u)\n", opcode); + break; + } + else if (opcode < DW_CFA_offset) + printf (" advance_loc %u to %#" PRIx64 "\n", + opcode & 0x3f, pc += (opcode & 0x3f) * code_align); + else if (opcode < DW_CFA_restore) + { + uint64_t offset; + // XXX overflow check + get_uleb128 (offset, readp); + printf (" offset r%u (%s) at cfa%+" PRId64 "\n", + opcode & 0x3f, regname (opcode & 0x3f), offset * data_align); + } + else + printf (" restore r%u (%s)\n", + opcode & 0x3f, regname (opcode & 0x3f)); + } +} + + +static unsigned int +encoded_ptr_size (int encoding, unsigned int ptr_size) +{ + switch (encoding & 7) + { + case 2: + return 2; + case 3: + return 4; + case 4: + return 8; + default: + return ptr_size; + } +} + + +static unsigned int +print_encoding (unsigned int val) +{ + switch (val & 0xf) + { + case DW_EH_PE_absptr: + fputs ("absptr", stdout); + break; + case DW_EH_PE_uleb128: + fputs ("uleb128", stdout); + break; + case DW_EH_PE_udata2: + fputs ("udata2", stdout); + break; + case DW_EH_PE_udata4: + fputs ("udata4", stdout); + break; + case DW_EH_PE_udata8: + fputs ("udata8", stdout); + break; + case DW_EH_PE_sleb128: + fputs ("sleb128", stdout); + break; + case DW_EH_PE_sdata2: + fputs ("sdata2", stdout); + break; + case DW_EH_PE_sdata4: + fputs ("sdata4", stdout); + break; + case DW_EH_PE_sdata8: + fputs ("sdata8", stdout); + break; + default: + /* We did not use any of the bits after all. */ + return val; + } + + return val & ~0xf; +} + + +static unsigned int +print_relinfo (unsigned int val) +{ + switch (val & 0x70) + { + case DW_EH_PE_pcrel: + fputs ("pcrel", stdout); + break; + case DW_EH_PE_textrel: + fputs ("textrel", stdout); + break; + case DW_EH_PE_datarel: + fputs ("datarel", stdout); + break; + case DW_EH_PE_funcrel: + fputs ("funcrel", stdout); + break; + case DW_EH_PE_aligned: + fputs ("aligned", stdout); + break; + default: + return val; + } + + return val & ~0x70; +} + + +static void +print_encoding_base (const char *pfx, unsigned int fde_encoding) +{ + printf ("(%s", pfx); + + if (fde_encoding == DW_EH_PE_omit) + puts ("omit)"); + else + { + unsigned int w = fde_encoding; + + w = print_encoding (w); + + if (w & 0x70) + { + if (w != fde_encoding) + fputc_unlocked (' ', stdout); + + w = print_relinfo (w); + } + + if (w != 0) + printf ("%s%x", w != fde_encoding ? " " : "", w); + + puts (")"); + } +} + + +static const unsigned char * +read_encoded (unsigned int encoding, const unsigned char *readp, + const unsigned char *const endp, uint64_t *res, Dwarf *dbg) +{ + if ((encoding & 0xf) == DW_EH_PE_absptr) + encoding = gelf_getclass (dbg->elf) == ELFCLASS32 + ? DW_EH_PE_udata4 : DW_EH_PE_udata8; + + switch (encoding & 0xf) + { + case DW_EH_PE_uleb128: + // XXX buffer overrun check + get_uleb128 (*res, readp); + break; + case DW_EH_PE_sleb128: + // XXX buffer overrun check + get_sleb128 (*res, readp); + break; + case DW_EH_PE_udata2: + if (readp + 2 > endp) + goto invalid; + *res = read_2ubyte_unaligned_inc (dbg, readp); + break; + case DW_EH_PE_udata4: + if (readp + 4 > endp) + goto invalid; + *res = read_4ubyte_unaligned_inc (dbg, readp); + break; + case DW_EH_PE_udata8: + if (readp + 8 > endp) + goto invalid; + *res = read_8ubyte_unaligned_inc (dbg, readp); + break; + case DW_EH_PE_sdata2: + if (readp + 2 > endp) + goto invalid; + *res = read_2sbyte_unaligned_inc (dbg, readp); + break; + case DW_EH_PE_sdata4: + if (readp + 4 > endp) + goto invalid; + *res = read_4sbyte_unaligned_inc (dbg, readp); + break; + case DW_EH_PE_sdata8: + if (readp + 8 > endp) + goto invalid; + *res = read_8sbyte_unaligned_inc (dbg, readp); + break; + default: + invalid: + error (1, 0, + gettext ("invalid encoding")); + } + + return readp; +} + + +static void +print_debug_frame_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, + Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) +{ + size_t shstrndx; + /* We know this call will succeed since it did in the caller. */ + (void) elf_getshdrstrndx (ebl->elf, &shstrndx); + const char *scnname = elf_strptr (ebl->elf, shstrndx, shdr->sh_name); + + Elf_Data *data = elf_rawdata (scn, NULL); + + if (unlikely (data == NULL)) + { + error (0, 0, gettext ("cannot get %s content: %s"), + scnname, elf_errmsg (-1)); + return; + } + bool is_eh_frame = strcmp (scnname, ".eh_frame") == 0; + + if (is_eh_frame) + printf (gettext ("\ +\nCall frame information section [%2zu] '%s' at offset %#" PRIx64 ":\n"), + elf_ndxscn (scn), scnname, (uint64_t) shdr->sh_offset); + else + printf (gettext ("\ +\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"), + elf_ndxscn (scn), scnname, (uint64_t) shdr->sh_offset); + + struct cieinfo + { + ptrdiff_t cie_offset; + const char *augmentation; + unsigned int code_alignment_factor; + unsigned int data_alignment_factor; + uint8_t address_size; + uint8_t fde_encoding; + uint8_t lsda_encoding; + struct cieinfo *next; + } *cies = NULL; + + const unsigned char *readp = data->d_buf; + const unsigned char *const dataend = ((unsigned char *) data->d_buf + + data->d_size); + while (readp < dataend) + { + if (unlikely (readp + 4 > dataend)) + { + invalid_data: + error (0, 0, gettext ("invalid data in section [%zu] '%s'"), + elf_ndxscn (scn), scnname); + return; + } + + /* At the beginning there must be a CIE. There can be multiple, + hence we test tis in a loop. */ + ptrdiff_t offset = readp - (unsigned char *) data->d_buf; + + Dwarf_Word unit_length = read_4ubyte_unaligned_inc (dbg, readp); + unsigned int length = 4; + if (unlikely (unit_length == 0xffffffff)) + { + if (unlikely (readp + 8 > dataend)) + goto invalid_data; + + unit_length = read_8ubyte_unaligned_inc (dbg, readp); + length = 8; + } + + if (unlikely (unit_length == 0)) + { + printf (gettext ("\n [%6tx] Zero terminator\n"), offset); + continue; + } + + unsigned int ptr_size = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8; + + ptrdiff_t start = readp - (unsigned char *) data->d_buf; + const unsigned char *const cieend = readp + unit_length; + if (unlikely (cieend > dataend || readp + 8 > dataend)) + goto invalid_data; + + Dwarf_Off cie_id; + if (length == 4) + { + cie_id = read_4ubyte_unaligned_inc (dbg, readp); + if (!is_eh_frame && cie_id == DW_CIE_ID_32) + cie_id = DW_CIE_ID_64; + } + else + cie_id = read_8ubyte_unaligned_inc (dbg, readp); + + uint_fast8_t version = 2; + unsigned int code_alignment_factor; + int data_alignment_factor; + unsigned int fde_encoding = 0; + unsigned int lsda_encoding = 0; + Dwarf_Word initial_location = 0; + Dwarf_Word vma_base = 0; + + if (cie_id == (is_eh_frame ? 0 : DW_CIE_ID_64)) + { + version = *readp++; + const char *const augmentation = (const char *) readp; + readp = memchr (readp, '\0', cieend - readp); + if (unlikely (readp == NULL)) + goto invalid_data; + ++readp; + + uint_fast8_t segment_size = 0; + if (version >= 4) + { + if (cieend - readp < 5) + goto invalid_data; + ptr_size = *readp++; + segment_size = *readp++; + } + + // XXX Check overflow + get_uleb128 (code_alignment_factor, readp); + // XXX Check overflow + get_sleb128 (data_alignment_factor, readp); + + /* In some variant for unwind data there is another field. */ + if (strcmp (augmentation, "eh") == 0) + readp += ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8; + + unsigned int return_address_register; + if (unlikely (version == 1)) + return_address_register = *readp++; + else + // XXX Check overflow + get_uleb128 (return_address_register, readp); + + printf ("\n [%6tx] CIE length=%" PRIu64 "\n" + " CIE_id: %" PRIu64 "\n" + " version: %u\n" + " augmentation: \"%s\"\n", + offset, (uint64_t) unit_length, (uint64_t) cie_id, + version, augmentation); + if (version >= 4) + printf (" address_size: %u\n" + " segment_size: %u\n", + ptr_size, segment_size); + printf (" code_alignment_factor: %u\n" + " data_alignment_factor: %d\n" + " return_address_register: %u\n", + code_alignment_factor, + data_alignment_factor, return_address_register); + + if (augmentation[0] == 'z') + { + unsigned int augmentationlen; + get_uleb128 (augmentationlen, readp); + + if (augmentationlen > (size_t) (dataend - readp)) + error (1, 0, gettext ("invalid augmentation length")); + + const char *hdr = "Augmentation data:"; + const char *cp = augmentation + 1; + while (*cp != '\0') + { + printf (" %-26s%#x ", hdr, *readp); + hdr = ""; + + if (*cp == 'R') + { + fde_encoding = *readp++; + print_encoding_base (gettext ("FDE address encoding: "), + fde_encoding); + } + else if (*cp == 'L') + { + lsda_encoding = *readp++; + print_encoding_base (gettext ("LSDA pointer encoding: "), + lsda_encoding); + } + else if (*cp == 'P') + { + /* Personality. This field usually has a relocation + attached pointing to __gcc_personality_v0. */ + const unsigned char *startp = readp; + unsigned int encoding = *readp++; + uint64_t val = 0; + readp = read_encoded (encoding, readp, + readp - 1 + augmentationlen, + &val, dbg); + + while (++startp < readp) + printf ("%#x ", *startp); + + putchar ('('); + print_encoding (encoding); + putchar (' '); + switch (encoding & 0xf) + { + case DW_EH_PE_sleb128: + case DW_EH_PE_sdata2: + case DW_EH_PE_sdata4: + printf ("%" PRId64 ")\n", val); + break; + default: + printf ("%#" PRIx64 ")\n", val); + break; + } + } + else + printf ("(%x)\n", *readp++); + + ++cp; + } + } + + if (likely (ptr_size == 4 || ptr_size == 8)) + { + struct cieinfo *newp = alloca (sizeof (*newp)); + newp->cie_offset = offset; + newp->augmentation = augmentation; + newp->fde_encoding = fde_encoding; + newp->lsda_encoding = lsda_encoding; + newp->address_size = ptr_size; + newp->code_alignment_factor = code_alignment_factor; + newp->data_alignment_factor = data_alignment_factor; + newp->next = cies; + cies = newp; + } + } + else + { + struct cieinfo *cie = cies; + while (cie != NULL) + if (is_eh_frame + ? start - (ptrdiff_t) cie_id == cie->cie_offset + : (ptrdiff_t) cie_id == cie->cie_offset) + break; + else + cie = cie->next; + if (unlikely (cie == NULL)) + { + puts ("invalid CIE reference in FDE"); + return; + } + + /* Initialize from CIE data. */ + fde_encoding = cie->fde_encoding; + lsda_encoding = cie->lsda_encoding; + ptr_size = encoded_ptr_size (fde_encoding, cie->address_size); + code_alignment_factor = cie->code_alignment_factor; + data_alignment_factor = cie->data_alignment_factor; + + const unsigned char *base = readp; + // XXX There are sometimes relocations for this value + initial_location = read_ubyte_unaligned_inc (ptr_size, dbg, readp); + Dwarf_Word address_range + = read_ubyte_unaligned_inc (ptr_size, dbg, readp); + + char *a = format_dwarf_addr (dwflmod, cie->address_size, + initial_location); + printf ("\n [%6tx] FDE length=%" PRIu64 " cie=[%6tx]\n" + " CIE_pointer: %" PRIu64 "\n" + " initial_location: %s", + offset, (uint64_t) unit_length, + cie->cie_offset, (uint64_t) cie_id, a); + free (a); + if ((fde_encoding & 0x70) == DW_EH_PE_pcrel) + { + vma_base = (((uint64_t) shdr->sh_offset + + (base - (const unsigned char *) data->d_buf) + + (uint64_t) initial_location) + & (ptr_size == 4 + ? UINT64_C (0xffffffff) + : UINT64_C (0xffffffffffffffff))); + printf (gettext (" (offset: %#" PRIx64 ")"), + (uint64_t) vma_base); + } + + printf ("\n address_range: %#" PRIx64, + (uint64_t) address_range); + if ((fde_encoding & 0x70) == DW_EH_PE_pcrel) + printf (gettext (" (end offset: %#" PRIx64 ")"), + ((uint64_t) vma_base + (uint64_t) address_range) + & (ptr_size == 4 + ? UINT64_C (0xffffffff) + : UINT64_C (0xffffffffffffffff))); + putchar ('\n'); + + if (cie->augmentation[0] == 'z') + { + unsigned int augmentationlen; + get_uleb128 (augmentationlen, readp); + + if (augmentationlen > 0) + { + const char *hdr = "Augmentation data:"; + const char *cp = cie->augmentation + 1; + unsigned int u = 0; + while (*cp != '\0') + { + if (*cp == 'L') + { + uint64_t lsda_pointer; + const unsigned char *p + = read_encoded (lsda_encoding, &readp[u], + &readp[augmentationlen], + &lsda_pointer, dbg); + u = p - readp; + printf (gettext ("\ + %-26sLSDA pointer: %#" PRIx64 "\n"), + hdr, lsda_pointer); + hdr = ""; + } + ++cp; + } + + while (u < augmentationlen) + { + printf (" %-26s%#x\n", hdr, readp[u++]); + hdr = ""; + } + } + + readp += augmentationlen; + } + } + + /* Handle the initialization instructions. */ + print_cfa_program (readp, cieend, vma_base, code_alignment_factor, + data_alignment_factor, version, ptr_size, + dwflmod, ebl, dbg); + readp = cieend; + } +} + + +struct attrcb_args +{ + Dwfl_Module *dwflmod; + Dwarf *dbg; + int level; + bool silent; + unsigned int version; + unsigned int addrsize; + unsigned int offset_size; + Dwarf_Off cu_offset; +}; + + +static int +attr_callback (Dwarf_Attribute *attrp, void *arg) +{ + struct attrcb_args *cbargs = (struct attrcb_args *) arg; + const int level = cbargs->level; + + unsigned int attr = dwarf_whatattr (attrp); + if (unlikely (attr == 0)) + { + if (!cbargs->silent) + error (0, 0, gettext ("cannot get attribute code: %s"), + dwarf_errmsg (-1)); + return DWARF_CB_ABORT; + } + + unsigned int form = dwarf_whatform (attrp); + if (unlikely (form == 0)) + { + if (!cbargs->silent) + error (0, 0, gettext ("cannot get attribute form: %s"), + dwarf_errmsg (-1)); + return DWARF_CB_ABORT; + } + + switch (form) + { + case DW_FORM_addr: + if (!cbargs->silent) + { + Dwarf_Addr addr; + if (unlikely (dwarf_formaddr (attrp, &addr) != 0)) + { + attrval_out: + if (!cbargs->silent) + 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) %s\n", + (int) (level * 2), "", dwarf_attr_string (attr), + dwarf_form_string (form), a); + free (a); + } + break; + + case DW_FORM_indirect: + case DW_FORM_strp: + case DW_FORM_string: + if (cbargs->silent) + break; + const char *str = dwarf_formstring (attrp); + if (unlikely (str == NULL)) + goto attrval_out; + printf (" %*s%-20s (%s) \"%s\"\n", + (int) (level * 2), "", dwarf_attr_string (attr), + dwarf_form_string (form), str); + break; + + case DW_FORM_ref_addr: + case DW_FORM_ref_udata: + case DW_FORM_ref8: + case DW_FORM_ref4: + case DW_FORM_ref2: + case DW_FORM_ref1:; + if (cbargs->silent) + break; + Dwarf_Die ref; + if (unlikely (dwarf_formref_die (attrp, &ref) == NULL)) + goto attrval_out; + + printf (" %*s%-20s (%s) [%6" PRIxMAX "]\n", + (int) (level * 2), "", dwarf_attr_string (attr), + dwarf_form_string (form), (uintmax_t) dwarf_dieoffset (&ref)); + break; + + case DW_FORM_ref_sig8: + if (cbargs->silent) + break; + printf (" %*s%-20s (%s) {%6" PRIx64 "}\n", + (int) (level * 2), "", dwarf_attr_string (attr), + dwarf_form_string (form), + read_8ubyte_unaligned (attrp->cu->dbg, attrp->valp)); + break; + + case DW_FORM_sec_offset: + case DW_FORM_udata: + case DW_FORM_sdata: + case DW_FORM_data8: + case DW_FORM_data4: + case DW_FORM_data2: + case DW_FORM_data1:; + Dwarf_Word num; + if (unlikely (dwarf_formudata (attrp, &num) != 0)) + goto attrval_out; + + const char *valuestr = NULL; + switch (attr) + { + /* This case can take either a constant or a loclistptr. */ + case DW_AT_data_member_location: + if (form != DW_FORM_sec_offset + && (cbargs->version >= 4 + || (form != DW_FORM_data4 && form != DW_FORM_data8))) + { + if (!cbargs->silent) + printf (" %*s%-20s (%s) %" PRIxMAX "\n", + (int) (level * 2), "", dwarf_attr_string (attr), + dwarf_form_string (form), (uintmax_t) num); + return DWARF_CB_OK; + } + /* else fallthrough */ + + /* These cases always take a loclistptr and no constant. */ + case DW_AT_location: + case DW_AT_data_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_GNU_call_site_value: + case DW_AT_GNU_call_site_data_value: + case DW_AT_GNU_call_site_target: + case DW_AT_GNU_call_site_target_clobbered: + notice_listptr (section_loc, &known_loclistptr, + cbargs->addrsize, cbargs->offset_size, num); + if (!cbargs->silent) + printf (" %*s%-20s (%s) location list [%6" PRIxMAX "]\n", + (int) (level * 2), "", dwarf_attr_string (attr), + dwarf_form_string (form), (uintmax_t) num); + return DWARF_CB_OK; + + case DW_AT_ranges: + notice_listptr (section_ranges, &known_rangelistptr, + cbargs->addrsize, cbargs->offset_size, num); + if (!cbargs->silent) + printf (" %*s%-20s (%s) range list [%6" PRIxMAX "]\n", + (int) (level * 2), "", dwarf_attr_string (attr), + dwarf_form_string (form), (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; + } + + if (cbargs->silent) + break; + + if (valuestr == NULL) + printf (" %*s%-20s (%s) %" PRIuMAX "\n", + (int) (level * 2), "", dwarf_attr_string (attr), + dwarf_form_string (form), (uintmax_t) num); + else + printf (" %*s%-20s (%s) %s (%" PRIuMAX ")\n", + (int) (level * 2), "", dwarf_attr_string (attr), + dwarf_form_string (form), valuestr, (uintmax_t) num); + break; + + case DW_FORM_flag: + if (cbargs->silent) + break; + bool flag; + if (unlikely (dwarf_formflag (attrp, &flag) != 0)) + goto attrval_out; + + printf (" %*s%-20s (%s) %s\n", + (int) (level * 2), "", dwarf_attr_string (attr), + dwarf_form_string (form), nl_langinfo (flag ? YESSTR : NOSTR)); + break; + + case DW_FORM_flag_present: + if (cbargs->silent) + break; + printf (" %*s%-20s (%s) %s\n", + (int) (level * 2), "", dwarf_attr_string (attr), + dwarf_form_string (form), nl_langinfo (YESSTR)); + break; + + case DW_FORM_exprloc: + case DW_FORM_block4: + case DW_FORM_block2: + case DW_FORM_block1: + case DW_FORM_block: + if (cbargs->silent) + break; + Dwarf_Block block; + if (unlikely (dwarf_formblock (attrp, &block) != 0)) + goto attrval_out; + + printf (" %*s%-20s (%s) ", + (int) (level * 2), "", dwarf_attr_string (attr), + dwarf_form_string (form)); + + switch (attr) + { + default: + if (form != DW_FORM_exprloc) + { + print_block (block.length, block.data); + break; + } + /* Fall through. */ + + 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: + case DW_AT_GNU_call_site_value: + case DW_AT_GNU_call_site_data_value: + case DW_AT_GNU_call_site_target: + case DW_AT_GNU_call_site_target_clobbered: + putchar ('\n'); + print_ops (cbargs->dwflmod, cbargs->dbg, + 12 + level * 2, 12 + level * 2, + cbargs->version, cbargs->addrsize, cbargs->offset_size, + block.length, block.data); + break; + } + break; + + default: + if (cbargs->silent) + break; + printf (" %*s%-20s (form: %#x) ???\n", + (int) (level * 2), "", dwarf_attr_string (attr), + (int) form); + break; + } + + return DWARF_CB_OK; +} + +static void +print_debug_units (Dwfl_Module *dwflmod, + Ebl *ebl, GElf_Ehdr *ehdr, + Elf_Scn *scn, GElf_Shdr *shdr, + Dwarf *dbg, bool debug_types) +{ + const bool silent = !(print_debug_sections & section_info); + const char *secname = section_name (ebl, ehdr, shdr); + + if (!silent) + printf (gettext ("\ +\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n [Offset]\n"), + elf_ndxscn (scn), secname, (uint64_t) shdr->sh_offset); + + /* If the section is empty we don't have to do anything. */ + if (!silent && shdr->sh_size == 0) + return; + + int maxdies = 20; + Dwarf_Die *dies = (Dwarf_Die *) xmalloc (maxdies * sizeof (Dwarf_Die)); + + Dwarf_Off offset = 0; + + /* New compilation unit. */ + size_t cuhl; + Dwarf_Half version; + Dwarf_Off abbroffset; + uint8_t addrsize; + uint8_t offsize; + Dwarf_Off nextcu; + uint64_t typesig; + Dwarf_Off typeoff; + next_cu: + if (dwarf_next_unit (dbg, offset, &nextcu, &cuhl, &version, + &abbroffset, &addrsize, &offsize, + debug_types ? &typesig : NULL, + debug_types ? &typeoff : NULL) != 0) + goto do_return; + + if (!silent) + { + if (debug_types) + printf (gettext (" Type unit at offset %" PRIu64 ":\n" + " Version: %" PRIu16 ", Abbreviation section offset: %" + PRIu64 ", Address size: %" PRIu8 + ", Offset size: %" PRIu8 + "\n Type signature: %#" PRIx64 + ", Type offset: %#" PRIx64 "\n"), + (uint64_t) offset, version, abbroffset, addrsize, offsize, + typesig, (uint64_t) typeoff); + else + printf (gettext (" Compilation unit at offset %" PRIu64 ":\n" + " Version: %" PRIu16 ", Abbreviation section offset: %" + PRIu64 ", Address size: %" PRIu8 + ", Offset size: %" PRIu8 "\n"), + (uint64_t) offset, version, abbroffset, addrsize, offsize); + } + + struct attrcb_args args = + { + .dwflmod = dwflmod, + .dbg = dbg, + .silent = silent, + .version = version, + .addrsize = addrsize, + .offset_size = offsize, + .cu_offset = offset + }; + + offset += cuhl; + + int level = 0; + + if (unlikely ((debug_types ? dwarf_offdie_types : dwarf_offdie) + (dbg, offset, &dies[level]) == NULL)) + { + if (!silent) + error (0, 0, gettext ("cannot get DIE at offset %" PRIu64 + " in section '%s': %s"), + (uint64_t) offset, secname, dwarf_errmsg (-1)); + goto do_return; + } + + do + { + offset = dwarf_dieoffset (&dies[level]); + if (unlikely (offset == ~0ul)) + { + if (!silent) + error (0, 0, gettext ("cannot get DIE offset: %s"), + dwarf_errmsg (-1)); + goto do_return; + } + + int tag = dwarf_tag (&dies[level]); + if (unlikely (tag == DW_TAG_invalid)) + { + if (!silent) + error (0, 0, gettext ("cannot get tag of DIE at offset %" PRIu64 + " in section '%s': %s"), + (uint64_t) offset, secname, dwarf_errmsg (-1)); + goto do_return; + } + + if (!silent) + printf (" [%6" PRIx64 "] %*s%s\n", + (uint64_t) offset, (int) (level * 2), "", + dwarf_tag_string (tag)); + + /* Print the attribute values. */ + args.level = level; + (void) dwarf_getattrs (&dies[level], attr_callback, &args, 0); + + /* Make room for the next level's DIE. */ + if (level + 1 == maxdies) + dies = (Dwarf_Die *) xrealloc (dies, + (maxdies += 10) + * sizeof (Dwarf_Die)); + + int res = dwarf_child (&dies[level], &dies[level + 1]); + if (res > 0) + { + while ((res = dwarf_siblingof (&dies[level], &dies[level])) == 1) + if (level-- == 0) + break; + + if (unlikely (res == -1)) + { + if (!silent) + error (0, 0, gettext ("cannot get next DIE: %s\n"), + dwarf_errmsg (-1)); + goto do_return; + } + } + else if (unlikely (res < 0)) + { + if (!silent) + error (0, 0, gettext ("cannot get next DIE: %s"), + dwarf_errmsg (-1)); + goto do_return; + } + else + ++level; + } + while (level >= 0); + + offset = nextcu; + if (offset != 0) + goto next_cu; + + do_return: + free (dies); +} + +static void +print_debug_info_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, + Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) +{ + print_debug_units (dwflmod, ebl, ehdr, scn, shdr, dbg, false); +} + +static void +print_debug_types_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, + Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) +{ + print_debug_units (dwflmod, ebl, ehdr, scn, shdr, dbg, true); +} + + +static void +print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, + Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) +{ + printf (gettext ("\ +\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"), + elf_ndxscn (scn), section_name (ebl, ehdr, shdr), + (uint64_t) shdr->sh_offset); + + if (shdr->sh_size == 0) + return; + + /* 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 (unlikely (data == NULL || data->d_buf == NULL)) + { + error (0, 0, gettext ("cannot get line data section data: %s"), + elf_errmsg (-1)); + return; + } + + const unsigned char *linep = (const unsigned char *) data->d_buf; + const unsigned char *lineendp; + + while (linep + < (lineendp = (const unsigned char *) data->d_buf + data->d_size)) + { + 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)) + { + if (unlikely (linep + 8 > lineendp)) + { + invalid_data: + error (0, 0, gettext ("invalid data in section [%zu] '%s'"), + elf_ndxscn (scn), section_name (ebl, ehdr, shdr)); + return; + } + unit_length = read_8ubyte_unaligned_inc (dbg, linep); + length = 8; + } + + /* Check whether we have enough room in the section. */ + if (unit_length < 2 + length + 5 * 1 + || unlikely (linep + unit_length > lineendp)) + goto invalid_data; + lineendp = linep + unit_length; + + /* The next element of the header is the version identifier. */ + uint_fast16_t version = read_2ubyte_unaligned_inc (dbg, linep); + + /* Next comes the header length. */ + Dwarf_Word header_length; + if (length == 4) + header_length = read_4ubyte_unaligned_inc (dbg, linep); + else + header_length = read_8ubyte_unaligned_inc (dbg, linep); + //const unsigned char *header_start = linep; + + /* Next the minimum instruction length. */ + uint_fast8_t minimum_instr_len = *linep++; + + /* Next the maximum operations per instruction, in version 4 format. */ + uint_fast8_t max_ops_per_instr = version < 4 ? 1 : *linep++; + + /* Then the flag determining the default value of the is_stmt + register. */ + uint_fast8_t default_is_stmt = *linep++; + + /* Now the line base. */ + int_fast8_t line_base = *((const int_fast8_t *) linep); + ++linep; + + /* And the line range. */ + uint_fast8_t line_range = *linep++; + + /* The opcode base. */ + uint_fast8_t opcode_base = *linep++; + + /* Print what we got so far. */ + printf (gettext ("\n" + " Length: %" PRIu64 "\n" + " DWARF version: %" PRIuFAST16 "\n" + " Prologue length: %" PRIu64 "\n" + " Minimum instruction length: %" PRIuFAST8 "\n" + " Maximum operations per instruction: %" PRIuFAST8 "\n" + " Initial value if '%s': %" PRIuFAST8 "\n" + " Line base: %" PRIdFAST8 "\n" + " Line range: %" PRIuFAST8 "\n" + " Opcode base: %" PRIuFAST8 "\n" + "\n" + "Opcodes:\n"), + (uint64_t) unit_length, version, (uint64_t) header_length, + minimum_instr_len, max_ops_per_instr, + "is_stmt", default_is_stmt, line_base, + line_range, opcode_base); + + if (unlikely (linep + opcode_base - 1 >= lineendp)) + { + 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), section_name (ebl, ehdr, shdr)); + linep = lineendp; + continue; + } + int opcode_base_l10 = 1; + unsigned int tmp = opcode_base; + while (tmp > 10) + { + tmp /= 10; + ++opcode_base_l10; + } + const uint8_t *standard_opcode_lengths = linep - 1; + for (uint_fast8_t cnt = 1; cnt < opcode_base; ++cnt) + printf (ngettext (" [%*" PRIuFAST8 "] %hhu argument\n", + " [%*" PRIuFAST8 "] %hhu arguments\n", + (int) linep[cnt - 1]), + opcode_base_l10, cnt, linep[cnt - 1]); + linep += opcode_base - 1; + if (unlikely (linep >= lineendp)) + goto invalid_unit; + + puts (gettext ("\nDirectory table:")); + while (*linep != 0) + { + unsigned char *endp = memchr (linep, '\0', lineendp - linep); + if (unlikely (endp == NULL)) + goto invalid_unit; + + printf (" %s\n", (char *) linep); + + linep = endp + 1; + } + /* Skip the final NUL byte. */ + ++linep; + + if (unlikely (linep >= lineendp)) + goto invalid_unit; + puts (gettext ("\nFile name table:\n" + " Entry Dir Time Size Name")); + for (unsigned int cnt = 1; *linep != 0; ++cnt) + { + /* First comes the file name. */ + char *fname = (char *) linep; + unsigned char *endp = memchr (fname, '\0', lineendp - linep); + if (unlikely (endp == NULL)) + goto invalid_unit; + linep = endp + 1; + + /* Then the index. */ + unsigned int diridx; + get_uleb128 (diridx, linep); + + /* Next comes the modification time. */ + unsigned int mtime; + get_uleb128 (mtime, linep); + + /* Finally the length of the file. */ + unsigned int fsize; + get_uleb128 (fsize, linep); + + printf (" %-5u %-5u %-9u %-9u %s\n", + cnt, diridx, mtime, fsize, fname); + } + /* Skip the final NUL byte. */ + ++linep; + + puts (gettext ("\nLine number statements:")); + Dwarf_Word address = 0; + unsigned int op_index = 0; + size_t line = 1; + uint_fast8_t is_stmt = default_is_stmt; + + /* Default address value, in case we do not find the CU. */ + size_t address_size + = elf_getident (ebl->elf, NULL)[EI_CLASS] == ELFCLASS32 ? 4 : 8; + + /* Determine the CU this block is for. */ + Dwarf_Off cuoffset; + Dwarf_Off ncuoffset = 0; + size_t hsize; + while (dwarf_nextcu (dbg, cuoffset = ncuoffset, &ncuoffset, &hsize, + NULL, NULL, NULL) == 0) + { + Dwarf_Die cudie; + if (dwarf_offdie (dbg, cuoffset + hsize, &cudie) == NULL) + continue; + Dwarf_Attribute stmt_list; + if (dwarf_attr (&cudie, DW_AT_stmt_list, &stmt_list) == NULL) + continue; + Dwarf_Word lineoff; + if (dwarf_formudata (&stmt_list, &lineoff) != 0) + continue; + if (lineoff == start_offset) + { + /* Found the CU. */ + address_size = cudie.cu->address_size; + break; + } + } + + /* Apply the "operation advance" from a special opcode + or DW_LNS_advance_pc (as per DWARF4 6.2.5.1). */ + unsigned int op_addr_advance; + bool show_op_index; + inline void advance_pc (unsigned int op_advance) + { + op_addr_advance = minimum_instr_len * ((op_index + op_advance) + / max_ops_per_instr); + address += op_advance; + show_op_index = (op_index > 0 || + (op_index + op_advance) % max_ops_per_instr > 0); + op_index = (op_index + op_advance) % max_ops_per_instr; + } + + while (linep < lineendp) + { + size_t offset = linep - (const unsigned char *) data->d_buf; + unsigned int u128; + int s128; + + /* Read the opcode. */ + unsigned int opcode = *linep++; + + printf (" [%6" PRIx64 "]", (uint64_t)offset); + /* Is this a special opcode? */ + if (likely (opcode >= opcode_base)) + { + /* Yes. Handling this is quite easy since the opcode value + is computed with + + opcode = (desired line increment - line_base) + + (line_range * address advance) + opcode_base + */ + int line_increment = (line_base + + (opcode - opcode_base) % line_range); + + /* Perform the increments. */ + line += line_increment; + advance_pc ((opcode - opcode_base) / line_range); + + char *a = format_dwarf_addr (dwflmod, 0, address); + if (show_op_index) + printf (gettext ("\ + special opcode %u: address+%u = %s, op_index = %u, line%+d = %zu\n"), + opcode, op_addr_advance, a, op_index, + line_increment, line); + else + printf (gettext ("\ + special opcode %u: address+%u = %s, line%+d = %zu\n"), + opcode, op_addr_advance, a, line_increment, line); + free (a); + } + else if (opcode == 0) + { + /* This an extended opcode. */ + if (unlikely (linep + 2 > lineendp)) + goto invalid_unit; + + /* The length. */ + unsigned int len = *linep++; + + if (unlikely (linep + len > lineendp)) + goto invalid_unit; + + /* The sub-opcode. */ + opcode = *linep++; + + printf (gettext (" extended opcode %u: "), opcode); + + switch (opcode) + { + case DW_LNE_end_sequence: + puts (gettext (" end of sequence")); + + /* Reset the registers we care about. */ + address = 0; + op_index = 0; + line = 1; + is_stmt = default_is_stmt; + break; + + case DW_LNE_set_address: + op_index = 0; + if (address_size == 4) + address = read_4ubyte_unaligned_inc (dbg, linep); + else + address = read_8ubyte_unaligned_inc (dbg, linep); + { + char *a = format_dwarf_addr (dwflmod, 0, address); + printf (gettext (" set address to %s\n"), a); + free (a); + } + break; + + case DW_LNE_define_file: + { + char *fname = (char *) linep; + unsigned char *endp = memchr (linep, '\0', + lineendp - linep); + if (unlikely (endp == NULL)) + goto invalid_unit; + linep = endp + 1; + + unsigned int diridx; + get_uleb128 (diridx, linep); + Dwarf_Word mtime; + get_uleb128 (mtime, linep); + Dwarf_Word filelength; + get_uleb128 (filelength, linep); + + printf (gettext ("\ + define new file: dir=%u, mtime=%" PRIu64 ", length=%" PRIu64 ", name=%s\n"), + diridx, (uint64_t) mtime, (uint64_t) filelength, + fname); + } + break; + + case DW_LNE_set_discriminator: + /* Takes one ULEB128 parameter, the discriminator. */ + if (unlikely (standard_opcode_lengths[opcode] != 1)) + goto invalid_unit; + + get_uleb128 (u128, linep); + printf (gettext (" set discriminator to %u\n"), u128); + break; + + default: + /* Unknown, ignore it. */ + puts (gettext (" unknown opcode")); + linep += len - 1; + break; + } + } + else if (opcode <= DW_LNS_set_isa) + { + /* This is a known standard opcode. */ + switch (opcode) + { + case DW_LNS_copy: + /* Takes no argument. */ + puts (gettext (" copy")); + break; + + case DW_LNS_advance_pc: + /* Takes one uleb128 parameter which is added to the + address. */ + get_uleb128 (u128, linep); + advance_pc (u128); + { + char *a = format_dwarf_addr (dwflmod, 0, address); + if (show_op_index) + printf (gettext ("\ + advance address by %u to %s, op_index to %u\n"), + op_addr_advance, a, op_index); + else + printf (gettext (" advance address by %u to %s\n"), + op_addr_advance, a); + free (a); + } + break; + + case DW_LNS_advance_line: + /* Takes one sleb128 parameter which is added to the + line. */ + get_sleb128 (s128, linep); + line += s128; + printf (gettext ("\ + advance line by constant %d to %" PRId64 "\n"), + s128, (int64_t) line); + break; + + case DW_LNS_set_file: + /* Takes one uleb128 parameter which is stored in file. */ + get_uleb128 (u128, linep); + printf (gettext (" set file to %" PRIu64 "\n"), + (uint64_t) u128); + break; + + case DW_LNS_set_column: + /* Takes one uleb128 parameter which is stored in column. */ + if (unlikely (standard_opcode_lengths[opcode] != 1)) + goto invalid_unit; + + get_uleb128 (u128, linep); + printf (gettext (" set column to %" PRIu64 "\n"), + (uint64_t) u128); + break; + + case DW_LNS_negate_stmt: + /* Takes no argument. */ + is_stmt = 1 - is_stmt; + printf (gettext (" set '%s' to %" PRIuFAST8 "\n"), + "is_stmt", is_stmt); + break; + + case DW_LNS_set_basic_block: + /* Takes no argument. */ + puts (gettext (" set basic block flag")); + break; + + case DW_LNS_const_add_pc: + /* Takes no argument. */ + advance_pc ((255 - opcode_base) / line_range); + { + char *a = format_dwarf_addr (dwflmod, 0, address); + if (show_op_index) + printf (gettext ("\ + advance address by constant %u to %s, op_index to %u\n"), + op_addr_advance, a, op_index); + else + printf (gettext ("\ + advance address by constant %u to %s\n"), + op_addr_advance, 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_unit; + + u128 = read_2ubyte_unaligned_inc (dbg, linep); + address += u128; + op_index = 0; + { + 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: + /* Takes no argument. */ + puts (gettext (" set prologue end flag")); + break; + + case DW_LNS_set_epilogue_begin: + /* Takes no argument. */ + puts (gettext (" set epilogue begin flag")); + break; + + case DW_LNS_set_isa: + /* Takes one uleb128 parameter which is stored in isa. */ + if (unlikely (standard_opcode_lengths[opcode] != 1)) + goto invalid_unit; + + get_uleb128 (u128, linep); + printf (gettext (" set isa to %u\n"), u128); + break; + } + } + else + { + /* This is a new opcode the generator but not we know about. + Read the parameters associated with it but then discard + everything. Read all the parameters for this opcode. */ + printf (ngettext (" unknown opcode with %" PRIu8 " parameter:", + " unknown opcode with %" PRIu8 " parameters:", + standard_opcode_lengths[opcode]), + standard_opcode_lengths[opcode]); + for (int n = standard_opcode_lengths[opcode]; n > 0; --n) + { + get_uleb128 (u128, linep); + if (n != standard_opcode_lengths[opcode]) + putc_unlocked (',', stdout); + printf (" %u", u128); + } + + /* Next round, ignore this opcode. */ + continue; + } + } + } + + /* There must only be one data block. */ + assert (elf_getdata (scn, data) == NULL); +} + + +static void +print_debug_loc_section (Dwfl_Module *dwflmod, + Ebl *ebl, 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_loc content: %s"), + elf_errmsg (-1)); + return; + } + + printf (gettext ("\ +\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"), + elf_ndxscn (scn), section_name (ebl, ehdr, shdr), + (uint64_t) shdr->sh_offset); + + sort_listptr (&known_loclistptr, "loclistptr"); + size_t listptr_idx = 0; + + uint_fast8_t address_size = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8; + uint_fast8_t offset_size = 4; + + bool first = true; + unsigned char *readp = data->d_buf; + unsigned char *const endp = (unsigned char *) data->d_buf + data->d_size; + while (readp < endp) + { + ptrdiff_t offset = readp - (unsigned char *) data->d_buf; + + if (first && skip_listptr_hole (&known_loclistptr, &listptr_idx, + &address_size, &offset_size, + offset, &readp, endp)) + continue; + + 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. */ + { + if (first) + printf (gettext (" [%6tx] empty list\n"), offset); + 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); + + if (endp - readp <= (ptrdiff_t) len) + { + fputs (gettext (" <INVALID DATA>\n"), stdout); + break; + } + + print_ops (dwflmod, dbg, 1, 18 + (address_size * 4), + 3 /*XXX*/, address_size, offset_size, len, readp); + + first = false; + readp += len; + } + } +} + +struct mac_culist +{ + Dwarf_Die die; + Dwarf_Off offset; + Dwarf_Files *files; + struct mac_culist *next; +}; + + +static int +mac_compare (const void *p1, const void *p2) +{ + struct mac_culist *m1 = (struct mac_culist *) p1; + struct mac_culist *m2 = (struct mac_culist *) p2; + + if (m1->offset < m2->offset) + return -1; + if (m1->offset > m2->offset) + return 1; + return 0; +} + + +static void +print_debug_macinfo_section (Dwfl_Module *dwflmod __attribute__ ((unused)), + Ebl *ebl, GElf_Ehdr *ehdr, + Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) +{ + printf (gettext ("\ +\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"), + elf_ndxscn (scn), section_name (ebl, ehdr, shdr), + (uint64_t) shdr->sh_offset); + putc_unlocked ('\n', stdout); + + /* 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 (unlikely (data == NULL || data->d_buf == NULL)) + { + error (0, 0, gettext ("cannot get macro information section data: %s"), + elf_errmsg (-1)); + return; + } + + /* Get the source file information for all CUs. */ + Dwarf_Off offset; + Dwarf_Off ncu = 0; + size_t hsize; + struct mac_culist *culist = NULL; + size_t nculist = 0; + while (dwarf_nextcu (dbg, offset = ncu, &ncu, &hsize, NULL, NULL, NULL) == 0) + { + Dwarf_Die cudie; + if (dwarf_offdie (dbg, offset + hsize, &cudie) == NULL) + continue; + + Dwarf_Attribute attr; + if (dwarf_attr (&cudie, DW_AT_macro_info, &attr) == NULL) + continue; + + Dwarf_Word macoff; + if (dwarf_formudata (&attr, &macoff) != 0) + continue; + + struct mac_culist *newp = (struct mac_culist *) alloca (sizeof (*newp)); + newp->die = cudie; + newp->offset = macoff; + newp->files = NULL; + newp->next = culist; + culist = newp; + ++nculist; + } + + /* Convert the list into an array for easier consumption. */ + struct mac_culist *cus = (struct mac_culist *) alloca ((nculist + 1) + * sizeof (*cus)); + /* Add sentinel. */ + cus[nculist].offset = data->d_size; + if (nculist > 0) + { + for (size_t cnt = nculist - 1; culist != NULL; --cnt) + { + assert (cnt < nculist); + cus[cnt] = *culist; + culist = culist->next; + } + + /* Sort the array according to the offset in the .debug_macinfo + section. Note we keep the sentinel at the end. */ + qsort (cus, nculist, sizeof (*cus), mac_compare); + } + + const unsigned char *readp = (const unsigned char *) data->d_buf; + const unsigned char *readendp = readp + data->d_size; + int level = 1; + + while (readp < readendp) + { + unsigned int opcode = *readp++; + unsigned int u128; + unsigned int u128_2; + const unsigned char *endp; + + switch (opcode) + { + case DW_MACINFO_define: + case DW_MACINFO_undef: + case DW_MACINFO_vendor_ext: + /* For the first two opcodes the parameters are + line, string + For the latter + number, string. + We can treat these cases together. */ + get_uleb128 (u128, readp); + + endp = memchr (readp, '\0', readendp - readp); + if (unlikely (endp == NULL)) + { + printf (gettext ("\ +%*s*** non-terminated string at end of section"), + level, ""); + return; + } + + if (opcode == DW_MACINFO_define) + printf ("%*s#define %s, line %u\n", + level, "", (char *) readp, u128); + else if (opcode == DW_MACINFO_undef) + printf ("%*s#undef %s, line %u\n", + level, "", (char *) readp, u128); + else + printf (" #vendor-ext %s, number %u\n", (char *) readp, u128); + + readp = endp + 1; + break; + + case DW_MACINFO_start_file: + /* The two parameters are line and file index, in this order. */ + get_uleb128 (u128, readp); + get_uleb128 (u128_2, readp); + + /* Find the CU DIE for this file. */ + size_t macoff = readp - (const unsigned char *) data->d_buf; + const char *fname = "???"; + if (macoff >= cus[0].offset) + { + while (macoff >= cus[1].offset) + ++cus; + + if (cus[0].files == NULL + && dwarf_getsrcfiles (&cus[0].die, &cus[0].files, NULL) != 0) + cus[0].files = (Dwarf_Files *) -1l; + + if (cus[0].files != (Dwarf_Files *) -1l) + fname = (dwarf_filesrc (cus[0].files, u128_2, NULL, NULL) + ?: "???"); + } + + printf ("%*sstart_file %u, [%u] %s\n", + level, "", u128, u128_2, fname); + ++level; + break; + + case DW_MACINFO_end_file: + --level; + printf ("%*send_file\n", level, ""); + /* Nothing more to do. */ + break; + + default: + // XXX gcc seems to generate files with a trailing zero. + if (unlikely (opcode != 0 || readp != readendp)) + printf ("%*s*** invalid opcode %u\n", level, "", opcode); + break; + } + } +} + + +/* Callback for printing global names. */ +static int +print_pubnames (Dwarf *dbg __attribute__ ((unused)), Dwarf_Global *global, + void *arg) +{ + int *np = (int *) arg; + + printf (gettext (" [%5d] DIE offset: %6" PRId64 + ", CU DIE offset: %6" PRId64 ", name: %s\n"), + (*np)++, global->die_offset, global->cu_offset, global->name); + + return 0; +} + + +/* Print the known exported symbols in the DWARF section '.debug_pubnames'. */ +static void +print_debug_pubnames_section (Dwfl_Module *dwflmod __attribute__ ((unused)), + Ebl *ebl, GElf_Ehdr *ehdr, + Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) +{ + printf (gettext ("\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"), + elf_ndxscn (scn), section_name (ebl, ehdr, shdr), + (uint64_t) shdr->sh_offset); + + int n = 0; + (void) dwarf_getpubnames (dbg, print_pubnames, &n, 0); +} + +/* Print the content of the DWARF string section '.debug_str'. */ +static void +print_debug_str_section (Dwfl_Module *dwflmod __attribute__ ((unused)), + Ebl *ebl, GElf_Ehdr *ehdr, + Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) +{ + const size_t sh_size = dbg->sectiondata[IDX_debug_str]->d_size; + + /* Compute floor(log16(shdr->sh_size)). */ + GElf_Addr tmp = sh_size; + int digits = 1; + while (tmp >= 16) + { + ++digits; + tmp >>= 4; + } + digits = MAX (4, digits); + + printf (gettext ("\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n" + " %*s String\n"), + elf_ndxscn (scn), + section_name (ebl, ehdr, shdr), (uint64_t) shdr->sh_offset, + /* TRANS: the debugstr| prefix makes the string unique. */ + digits + 2, sgettext ("debugstr|Offset")); + + Dwarf_Off offset = 0; + while (offset < sh_size) + { + size_t len; + const char *str = dwarf_getstring (dbg, offset, &len); + if (unlikely (str == NULL)) + { + printf (gettext (" *** error while reading strings: %s\n"), + dwarf_errmsg (-1)); + break; + } + + printf (" [%*" PRIx64 "] \"%s\"\n", digits, (uint64_t) offset, str); + + offset += len + 1; + } +} + + +/* Print the content of the call frame search table section + '.eh_frame_hdr'. */ +static void +print_debug_frame_hdr_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 ("\ +\nCall frame search table section [%2zu] '.eh_frame_hdr':\n"), + elf_ndxscn (scn)); + + Elf_Data *data = elf_rawdata (scn, NULL); + + if (unlikely (data == NULL)) + { + error (0, 0, gettext ("cannot get %s content: %s"), + ".eh_frame_hdr", elf_errmsg (-1)); + return; + } + + const unsigned char *readp = data->d_buf; + const unsigned char *const dataend = ((unsigned char *) data->d_buf + + data->d_size); + + if (unlikely (readp + 4 > dataend)) + { + invalid_data: + error (0, 0, gettext ("invalid data")); + return; + } + + unsigned int version = *readp++; + unsigned int eh_frame_ptr_enc = *readp++; + unsigned int fde_count_enc = *readp++; + unsigned int table_enc = *readp++; + + printf (" version: %u\n" + " eh_frame_ptr_enc: %#x ", + version, eh_frame_ptr_enc); + print_encoding_base ("", eh_frame_ptr_enc); + printf (" fde_count_enc: %#x ", fde_count_enc); + print_encoding_base ("", fde_count_enc); + printf (" table_enc: %#x ", table_enc); + print_encoding_base ("", table_enc); + + uint64_t eh_frame_ptr = 0; + if (eh_frame_ptr_enc != DW_EH_PE_omit) + { + readp = read_encoded (eh_frame_ptr_enc, readp, dataend, &eh_frame_ptr, + dbg); + if (unlikely (readp == NULL)) + goto invalid_data; + + printf (" eh_frame_ptr: %#" PRIx64, eh_frame_ptr); + if ((eh_frame_ptr_enc & 0x70) == DW_EH_PE_pcrel) + printf (" (offset: %#" PRIx64 ")", + /* +4 because of the 4 byte header of the section. */ + (uint64_t) shdr->sh_offset + 4 + eh_frame_ptr); + + putchar_unlocked ('\n'); + } + + uint64_t fde_count = 0; + if (fde_count_enc != DW_EH_PE_omit) + { + readp = read_encoded (fde_count_enc, readp, dataend, &fde_count, dbg); + if (unlikely (readp == NULL)) + goto invalid_data; + + printf (" fde_count: %" PRIu64 "\n", fde_count); + } + + if (fde_count == 0 || table_enc == DW_EH_PE_omit) + return; + + puts (" Table:"); + + /* Optimize for the most common case. */ + if (table_enc == (DW_EH_PE_datarel | DW_EH_PE_sdata4)) + while (fde_count > 0 && readp + 8 <= dataend) + { + int32_t initial_location = read_4sbyte_unaligned_inc (dbg, readp); + uint64_t initial_offset = ((uint64_t) shdr->sh_offset + + (int64_t) initial_location); + int32_t address = read_4sbyte_unaligned_inc (dbg, readp); + // XXX Possibly print symbol name or section offset for initial_offset + printf (" %#" PRIx32 " (offset: %#6" PRIx64 ") -> %#" PRIx32 + " fde=[%6" PRIx64 "]\n", + initial_location, initial_offset, + address, address - (eh_frame_ptr + 4)); + } + else + while (0 && readp < dataend) + { + + } +} + + +/* Print the content of the exception handling table section + '.eh_frame_hdr'. */ +static void +print_debug_exception_table (Dwfl_Module *dwflmod __attribute__ ((unused)), + Ebl *ebl __attribute__ ((unused)), + GElf_Ehdr *ehdr __attribute__ ((unused)), + Elf_Scn *scn, + GElf_Shdr *shdr __attribute__ ((unused)), + Dwarf *dbg __attribute__ ((unused))) +{ + printf (gettext ("\ +\nException handling table section [%2zu] '.gcc_except_table':\n"), + elf_ndxscn (scn)); + + Elf_Data *data = elf_rawdata (scn, NULL); + + if (unlikely (data == NULL)) + { + error (0, 0, gettext ("cannot get %s content: %s"), + ".gcc_except_table", elf_errmsg (-1)); + return; + } + + const unsigned char *readp = data->d_buf; + const unsigned char *const dataend = readp + data->d_size; + + if (unlikely (readp + 1 > dataend)) + { + invalid_data: + error (0, 0, gettext ("invalid data")); + return; + } + unsigned int lpstart_encoding = *readp++; + printf (gettext (" LPStart encoding: %#x "), lpstart_encoding); + print_encoding_base ("", lpstart_encoding); + if (lpstart_encoding != DW_EH_PE_omit) + { + uint64_t lpstart; + readp = read_encoded (lpstart_encoding, readp, dataend, &lpstart, dbg); + printf (" LPStart: %#" PRIx64 "\n", lpstart); + } + + if (unlikely (readp + 1 > dataend)) + goto invalid_data; + unsigned int ttype_encoding = *readp++; + printf (gettext (" TType encoding: %#x "), ttype_encoding); + print_encoding_base ("", ttype_encoding); + const unsigned char *ttype_base = NULL; + if (ttype_encoding != DW_EH_PE_omit) + { + unsigned int ttype_base_offset; + get_uleb128 (ttype_base_offset, readp); + printf (" TType base offset: %#x\n", ttype_base_offset); + ttype_base = readp + ttype_base_offset; + } + + if (unlikely (readp + 1 > dataend)) + goto invalid_data; + unsigned int call_site_encoding = *readp++; + printf (gettext (" Call site encoding: %#x "), call_site_encoding); + print_encoding_base ("", call_site_encoding); + unsigned int call_site_table_len; + get_uleb128 (call_site_table_len, readp); + + const unsigned char *const action_table = readp + call_site_table_len; + if (unlikely (action_table > dataend)) + goto invalid_data; + unsigned int u = 0; + unsigned int max_action = 0; + while (readp < action_table) + { + if (u == 0) + puts (gettext ("\n Call site table:")); + + uint64_t call_site_start; + readp = read_encoded (call_site_encoding, readp, dataend, + &call_site_start, dbg); + uint64_t call_site_length; + readp = read_encoded (call_site_encoding, readp, dataend, + &call_site_length, dbg); + uint64_t landing_pad; + readp = read_encoded (call_site_encoding, readp, dataend, + &landing_pad, dbg); + unsigned int action; + get_uleb128 (action, readp); + max_action = MAX (action, max_action); + printf (gettext (" [%4u] Call site start: %#" PRIx64 "\n" + " Call site length: %" PRIu64 "\n" + " Landing pad: %#" PRIx64 "\n" + " Action: %u\n"), + u++, call_site_start, call_site_length, landing_pad, action); + } + assert (readp == action_table); + + unsigned int max_ar_filter = 0; + if (max_action > 0) + { + puts ("\n Action table:"); + + const unsigned char *const action_table_end + = action_table + max_action + 1; + + u = 0; + do + { + int ar_filter; + get_sleb128 (ar_filter, readp); + if (ar_filter > 0 && (unsigned int) ar_filter > max_ar_filter) + max_ar_filter = ar_filter; + int ar_disp; + get_sleb128 (ar_disp, readp); + + printf (" [%4u] ar_filter: % d\n" + " ar_disp: % -5d", + u, ar_filter, ar_disp); + if (abs (ar_disp) & 1) + printf (" -> [%4u]\n", u + (ar_disp + 1) / 2); + else if (ar_disp != 0) + puts (" -> ???"); + else + putchar_unlocked ('\n'); + ++u; + } + while (readp < action_table_end); + } + + if (max_ar_filter > 0) + { + puts ("\n TType table:"); + + // XXX Not *4, size of encoding; + switch (ttype_encoding & 7) + { + case DW_EH_PE_udata2: + case DW_EH_PE_sdata2: + readp = ttype_base - max_ar_filter * 2; + break; + case DW_EH_PE_udata4: + case DW_EH_PE_sdata4: + readp = ttype_base - max_ar_filter * 4; + break; + case DW_EH_PE_udata8: + case DW_EH_PE_sdata8: + readp = ttype_base - max_ar_filter * 8; + break; + default: + error (1, 0, gettext ("invalid TType encoding")); + } + + do + { + uint64_t ttype; + readp = read_encoded (ttype_encoding, readp, ttype_base, &ttype, + dbg); + printf (" [%4u] %#" PRIx64 "\n", max_ar_filter--, ttype); + } + while (readp < ttype_base); + } +} + +/* Print the content of the '.gdb_index' section. + http://sourceware.org/gdb/current/onlinedocs/gdb/Index-Section-Format.html +*/ +static void +print_gdb_index_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, + Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) +{ + printf (gettext ("\nGDB section [%2zu] '%s' at offset %#" PRIx64 + " contains %" PRId64 " bytes :\n"), + elf_ndxscn (scn), section_name (ebl, ehdr, shdr), + (uint64_t) shdr->sh_offset, (uint64_t) shdr->sh_size); + + Elf_Data *data = elf_rawdata (scn, NULL); + + if (unlikely (data == NULL)) + { + error (0, 0, gettext ("cannot get %s content: %s"), + ".gdb_index", elf_errmsg (-1)); + return; + } + + // .gdb_index is always in little endian. + Dwarf dummy_dbg = { .other_byte_order = MY_ELFDATA != ELFDATA2LSB }; + dbg = &dummy_dbg; + + const unsigned char *readp = data->d_buf; + const unsigned char *const dataend = readp + data->d_size; + + if (unlikely (readp + 4 > dataend)) + { + invalid_data: + error (0, 0, gettext ("invalid data")); + return; + } + + int32_t vers = read_4ubyte_unaligned (dbg, readp); + printf (gettext (" Version: %" PRId32 "\n"), vers); + + // The only difference between version 4 and version 5 is the + // hash used for generating the table. + if (vers < 4 || vers > 5) + { + printf (gettext (" unknown version, cannot parse section\n")); + return; + } + + readp += 4; + if (unlikely (readp + 4 > dataend)) + goto invalid_data; + + uint32_t cu_off = read_4ubyte_unaligned (dbg, readp); + printf (gettext (" CU offset: %#" PRIx32 "\n"), cu_off); + + readp += 4; + if (unlikely (readp + 4 > dataend)) + goto invalid_data; + + uint32_t tu_off = read_4ubyte_unaligned (dbg, readp); + printf (gettext (" TU offset: %#" PRIx32 "\n"), tu_off); + + readp += 4; + if (unlikely (readp + 4 > dataend)) + goto invalid_data; + + uint32_t addr_off = read_4ubyte_unaligned (dbg, readp); + printf (gettext (" address offset: %#" PRIx32 "\n"), addr_off); + + readp += 4; + if (unlikely (readp + 4 > dataend)) + goto invalid_data; + + uint32_t sym_off = read_4ubyte_unaligned (dbg, readp); + printf (gettext (" symbol offset: %#" PRIx32 "\n"), sym_off); + + readp += 4; + if (unlikely (readp + 4 > dataend)) + goto invalid_data; + + uint32_t const_off = read_4ubyte_unaligned (dbg, readp); + printf (gettext (" constant offset: %#" PRIx32 "\n"), const_off); + + readp = data->d_buf + cu_off; + + const unsigned char *nextp = data->d_buf + tu_off; + size_t nr = (nextp - readp) / 16; + + printf (gettext ("\n CU list at offset %#" PRIx32 + " contains %zu entries:\n"), + cu_off, nr); + + size_t n = 0; + while (readp + 16 <= dataend && n < nr) + { + uint64_t off = read_8ubyte_unaligned (dbg, readp); + readp += 8; + + uint64_t len = read_8ubyte_unaligned (dbg, readp); + readp += 8; + + printf (" [%4zu] start: %0#8" PRIx64 + ", length: %5" PRIu64 "\n", n, off, len); + n++; + } + + readp = data->d_buf + tu_off; + nextp = data->d_buf + addr_off; + nr = (nextp - readp) / 24; + + printf (gettext ("\n TU list at offset %#" PRIx32 + " contains %zu entries:\n"), + tu_off, nr); + + n = 0; + while (readp + 24 <= dataend && n < nr) + { + uint64_t off = read_8ubyte_unaligned (dbg, readp); + readp += 8; + + uint64_t type = read_8ubyte_unaligned (dbg, readp); + readp += 8; + + uint64_t sig = read_8ubyte_unaligned (dbg, readp); + readp += 8; + + printf (" [%4zu] CU offset: %5" PRId64 + ", type offset: %5" PRId64 + ", signature: %0#8" PRIx64 "\n", n, off, type, sig); + n++; + } + + readp = data->d_buf + addr_off; + nextp = data->d_buf + sym_off; + nr = (nextp - readp) / 20; + + printf (gettext ("\n Address list at offset %#" PRIx32 + " contains %zu entries:\n"), + addr_off, nr); + + n = 0; + while (readp + 20 <= dataend && n < nr) + { + uint64_t low = read_8ubyte_unaligned (dbg, readp); + readp += 8; + + uint64_t high = read_8ubyte_unaligned (dbg, readp); + readp += 8; + + uint32_t idx = read_4ubyte_unaligned (dbg, readp); + readp += 4; + + char *l = format_dwarf_addr (dwflmod, 8, low); + char *h = format_dwarf_addr (dwflmod, 8, high - 1); + printf (" [%4zu] %s..%s, CU index: %5" PRId32 "\n", + n, l, h, idx); + n++; + } + + readp = data->d_buf + sym_off; + nextp = data->d_buf + const_off; + nr = (nextp - readp) / 8; + + printf (gettext ("\n Symbol table at offset %#" PRIx32 + " contains %zu slots:\n"), + addr_off, nr); + + n = 0; + while (readp + 8 <= dataend && n < nr) + { + uint32_t name = read_4ubyte_unaligned (dbg, readp); + readp += 4; + + uint32_t vector = read_4ubyte_unaligned (dbg, readp); + readp += 4; + + if (name != 0 || vector != 0) + { + const unsigned char *sym = data->d_buf + const_off + name; + if (unlikely (sym > dataend)) + goto invalid_data; + + printf (" [%4zu] symbol: %s, CUs: ", n, sym); + + const unsigned char *readcus = data->d_buf + const_off + vector; + if (unlikely (readcus + 8 > dataend)) + goto invalid_data; + + uint32_t cus = read_4ubyte_unaligned (dbg, readcus); + while (cus--) + { + uint32_t cu; + readcus += 4; + cu = read_4ubyte_unaligned (dbg, readcus); + printf ("%" PRId32 "%s", cu, ((cus > 0) ? ", " : "")); + } + printf ("\n"); + } + n++; + } +} + +static void +print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr) +{ + /* Before we start the real work get a debug context descriptor. */ + Dwarf_Addr dwbias; + Dwarf *dbg = dwfl_module_getdwarf (dwflmod, &dwbias); + Dwarf dummy_dbg = + { + .elf = ebl->elf, + .other_byte_order = MY_ELFDATA != ehdr->e_ident[EI_DATA] + }; + if (dbg == NULL) + { + if ((print_debug_sections & ~section_exception) != 0) + error (0, 0, gettext ("cannot get debug context descriptor: %s"), + dwfl_errmsg (-1)); + if ((print_debug_sections & section_exception) == 0) + return; + dbg = &dummy_dbg; + } + + /* Get the section header string table index. */ + size_t shstrndx; + if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0)) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + /* Look through all the sections for the debugging sections to print. */ + 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_PROGBITS) + { + static const struct + { + const char *name; + enum section_e bitmask; + void (*fp) (Dwfl_Module *, Ebl *, + GElf_Ehdr *, Elf_Scn *, GElf_Shdr *, Dwarf *); + } debug_sections[] = + { +#define NEW_SECTION(name) \ + { ".debug_" #name, section_##name, print_debug_##name##_section } + NEW_SECTION (abbrev), + NEW_SECTION (aranges), + NEW_SECTION (frame), + NEW_SECTION (info), + NEW_SECTION (types), + NEW_SECTION (line), + NEW_SECTION (loc), + NEW_SECTION (pubnames), + NEW_SECTION (str), + NEW_SECTION (macinfo), + NEW_SECTION (ranges), + { ".eh_frame", section_frame | section_exception, + print_debug_frame_section }, + { ".eh_frame_hdr", section_frame | section_exception, + print_debug_frame_hdr_section }, + { ".gcc_except_table", section_frame | section_exception, + print_debug_exception_table }, + { ".gdb_index", section_gdb_index, print_gdb_index_section } + }; + const int ndebug_sections = (sizeof (debug_sections) + / sizeof (debug_sections[0])); + const char *name = elf_strptr (ebl->elf, shstrndx, + shdr->sh_name); + int n; + + for (n = 0; n < ndebug_sections; ++n) + if (strcmp (name, debug_sections[n].name) == 0 +#if USE_ZLIB + || (name[0] == '.' && name[1] == 'z' + && debug_sections[n].name[1] == 'd' + && strcmp (&name[2], &debug_sections[n].name[1]) == 0) +#endif + ) + { + if ((print_debug_sections | implicit_debug_sections) + & debug_sections[n].bitmask) + debug_sections[n].fp (dwflmod, ebl, ehdr, scn, shdr, dbg); + break; + } + } + } + + reset_listptr (&known_loclistptr); + reset_listptr (&known_rangelistptr); +} + + +#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 if (item->count != 0 || item->format != '\n') + *repeated_size -= size; + } + + 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; + unsigned int run = 0; + while (w != 0) + { + int n = ffs (w); + w >>= n; + bit += n; + + if (lastbit + 1 == bit) + ++run; + else + { + if (lastbit == 0) + p += sprintf (p, "%u", bit - bias); + else if (run == 0) + p += sprintf (p, ",%u", bit - bias); + else + p += sprintf (p, "-%u,%u", lastbit - bias, bit - bias); + run = 0; + } + + 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; + + case '\n': + /* This is a list of strings separated by '\n'. */ + assert (item->count == 0); + assert (repeated_size != NULL); + assert (item->name == NULL); + if (unlikely (item->offset >= *repeated_size)) + break; + + const char *s = desc + item->offset; + size = *repeated_size - item->offset; + *repeated_size = 0; + while (size > 0) + { + const char *eol = memchr (s, '\n', size); + int len = size; + if (eol != NULL) + len = eol - s; + printf ("%*s%.*s\n", ITEM_INDENT, "", len, s); + if (eol == NULL) + break; + size -= eol + 1 - s; + s = eol + 1; + } + + colno = ITEM_WRAP_COLUMN; + 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) + { + char name[REGNAMESZ]; + int bits; + int type; + register_info (ebl, reg, regloc, name, &bits, &type); + +#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[REGNAMESZ]; + int regno; + int bits; + int 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) + { + for (size_t i = 0; i < nregloc; ++i) + if (maxnreg < reglocs[i].regno + reglocs[i].count) + maxnreg = reglocs[i].regno + reglocs[i].count; + assert (maxnreg > 0); + } + + 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]; + info->regloc = ®locs[i]; + info->regno = reg; + info->set = register_info (ebl, reg, ®locs[i], + info->name, &info->bits, &info->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; + } + + 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 char *name, 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, name, + ®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, name, 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 + && (nhdr.n_namesz == 4 /* Broken old Linux kernels. */ + || (nhdr.n_namesz == 5 && name[4] == '\0')) + && !memcmp (name, "CORE", 4)) + handle_auxv_note (ebl, ebl->elf, nhdr.n_descsz, + start + desc_offset); + else + handle_core_note (ebl, &nhdr, name, 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) +{ + /* 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_getshdrstrndx (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 (size_t cnt = 0; cnt < phnum; ++cnt) + { + GElf_Phdr mem; + GElf_Phdr *phdr = gelf_getphdr (ebl->elf, cnt, &mem); + + if (phdr == NULL || phdr->p_type != PT_NOTE) + /* Not what we are looking for. */ + continue; + + printf (gettext ("\ +\nNote segment of %" PRIu64 " bytes at offset %#0" PRIx64 ":\n"), + phdr->p_filesz, phdr->p_offset); + + handle_notes_data (ebl, ehdr, phdr->p_offset, + elf_getdata_rawchunk (ebl->elf, + phdr->p_offset, phdr->p_filesz, + ELF_T_NHDR)); + } +} + + +static void +hex_dump (const uint8_t *data, size_t len) +{ + size_t pos = 0; + while (pos < len) + { + printf (" 0x%08Zx ", pos); + + const size_t chunk = MIN (len - pos, 16); + + for (size_t i = 0; i < chunk; ++i) + if (i % 4 == 3) + printf ("%02x ", data[pos + i]); + else + printf ("%02x", data[pos + i]); + + if (chunk < 16) + printf ("%*s", (int) ((16 - chunk) * 2 + (16 - chunk + 3) / 4), ""); + + for (size_t i = 0; i < chunk; ++i) + { + 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); + } + } +} + +static void +print_string_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 strings 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 ("\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)) + { + printf (" [%6Zx]- %.*s\n", + pos, (int) (limit - start), start); + break; + } + printf (" [%6Zx] %s\n", pos, start); + start = end + 1; + } while (start < limit); + } + } +} + +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_getshdrstrndx (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; + bool found = false; + 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)) + { + found = true; + (*dump) (scn, &shdr_mem, name); + } + } + + if (unlikely (!found) && !a->implicit) + error (0, 0, gettext ("\nsection '%s' does not exist"), a->arg); + } + } +} + +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_getshdrstrndx (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); + } + + printf ("\t%s\n", s->as_name); + } +} + +#include "debugpred.h" diff --git a/0.153/src/sectionhash.c b/0.153/src/sectionhash.c new file mode 100644 index 00000000..68d734e1 --- /dev/null +++ b/0.153/src/sectionhash.c @@ -0,0 +1,81 @@ +/* Section hash table implementation. + Copyright (C) 2001, 2002, 2005 Red Hat, Inc. + This file is part of Red Hat elfutils. + Written by Ulrich Drepper <drepper@redhat.com>, 2001. + + 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 <string.h> + +#include <elf-knowledge.h> +#include <ld.h> + + +/* Comparison function for sections. */ +static int +scnhead_compare (struct scnhead *one, struct scnhead *two) +{ + int result = strcmp (one->name, two->name); + + if (result == 0) + { + result = one->type - two->type; + + if (result == 0) + { + GElf_Sxword diff = (SH_FLAGS_IMPORTANT (one->flags) + - SH_FLAGS_IMPORTANT (two->flags)); + result = diff < 0 ? -1 : diff == 0 ? 0 : 1; + + if (result == 0) + { + result = one->entsize - two->entsize; + + if (result == 0) + { + result = (one->grp_signature == NULL + ? (two->grp_signature == NULL ? 0 : -1) + : (two->grp_signature == NULL + ? 1 : strcmp (one->grp_signature, + two->grp_signature))); + + if (result == 0) + result = one->kind - two->kind; + } + } + } + } + + return result; +} + +/* Definitions for the section hash table. */ +#define TYPE struct scnhead * +#define NAME ld_section_tab +#define ITERATE 1 +#define COMPARE(a, b) scnhead_compare (a, b) + +#include "../lib/dynamicsizehash.c" diff --git a/0.153/src/sectionhash.h b/0.153/src/sectionhash.h new file mode 100644 index 00000000..ba41ee8c --- /dev/null +++ b/0.153/src/sectionhash.h @@ -0,0 +1,35 @@ +/* Copyright (C) 2001, 2002 Red Hat, Inc. + This file is part of Red Hat elfutils. + Written by Ulrich Drepper <drepper@redhat.com>, 2001. + + 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 SECTIONHASH_H +#define SECTIONHASH_H 1 + +/* Definitions for the section hash table. */ +#define TYPE struct scnhead * +#define NAME ld_section_tab +#define ITERATE 1 +#include <dynamicsizehash.h> + +#endif /* sectionhash.h */ diff --git a/0.153/src/size.c b/0.153/src/size.c new file mode 100644 index 00000000..14dafc48 --- /dev/null +++ b/0.153/src/size.c @@ -0,0 +1,697 @@ +/* Print size information from ELF file. + Copyright (C) 2000-2007,2009,2012 Red Hat, Inc. + This file is part of Red Hat elfutils. + Written by Ulrich Drepper <drepper@redhat.com>, 2000. + + 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 <gelf.h> +#include <inttypes.h> +#include <libelf.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> + + +/* Name and version of program. */ +static void print_version (FILE *stream, struct argp_state *state); +ARGP_PROGRAM_VERSION_HOOK_DEF = print_version; + +/* Bug report address. */ +ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT; + + +/* Values for the parameters which have no short form. */ +#define OPT_FORMAT 0x100 +#define OPT_RADIX 0x101 + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + { 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. */ +static const char doc[] = N_("\ +List section sizes of 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); + +/* Handle content of archive. */ +static int handle_ar (int fd, Elf *elf, const char *prefix, const char *fname); + +/* Handle ELF file. */ +static void handle_elf (Elf *elf, const char *fullname, const char *fname); + +/* Show total size. */ +static void show_bsd_totals (void); + +#define INTERNAL_ERROR(fname) \ + error (EXIT_FAILURE, 0, gettext ("%s: INTERNAL ERROR %d (%s-%s): %s"), \ + fname, __LINE__, PACKAGE_VERSION, __DATE__, elf_errmsg (-1)) + + +/* User-selectable options. */ + +/* The selected output format. */ +static enum +{ + format_bsd = 0, + format_sysv, + format_sysv_one_line, + format_segments +} format; + +/* Radix for printed numbers. */ +static enum +{ + radix_decimal = 0, + radix_hex, + radix_octal +} radix; + + +/* Mapping of radix and binary class to length. */ +static const int length_map[2][3] = +{ + [ELFCLASS32 - 1] = + { + [radix_hex] = 8, + [radix_decimal] = 10, + [radix_octal] = 11 + }, + [ELFCLASS64 - 1] = + { + [radix_hex] = 16, + [radix_decimal] = 20, + [radix_octal] = 22 + } +}; + +/* True if total sizes should be printed. */ +static bool totals; +/* To print the total sizes in a reasonable format remember the higest + "class" of ELF binaries processed. */ +static int totals_class; + + +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. */ + __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. */ + argp_parse (&argp, argc, argv, 0, &remaining, NULL); + + + /* Tell the library which version we are expecting. */ + elf_version (EV_CURRENT); + + if (remaining == argc) + /* The user didn't specify a name so we use a.out. */ + result = process_file ("a.out"); + else + /* Process all the remaining files. */ + do + result |= process_file (argv[remaining]); + while (++remaining < argc); + + /* Print the total sizes but only if the output format is BSD and at + least one file has been correctly read (i.e., we recognized the + class). */ + if (totals && format == format_bsd && totals_class != 0) + show_bsd_totals (); + + return result; +} + + +/* Print the version information. */ +static void +print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) +{ + 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\ +"), "2012"); + 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 'd': + radix = radix_decimal; + break; + + case 'f': + format = format_sysv_one_line; + break; + + case 'o': + radix = radix_octal; + break; + + case 'x': + radix = radix_hex; + break; + + case 'A': + format = format_sysv; + break; + + case 'B': + format = format_bsd; + break; + + case 'F': + format = format_segments; + break; + + case OPT_FORMAT: + if (strcmp (arg, "bsd") == 0 || strcmp (arg, "berkeley") == 0) + format = format_bsd; + else if (likely (strcmp (arg, "sysv") == 0)) + format = format_sysv; + else + error (EXIT_FAILURE, 0, gettext ("Invalid format: %s"), arg); + break; + + case OPT_RADIX: + if (strcmp (arg, "x") == 0 || strcmp (arg, "16") == 0) + radix = radix_hex; + else if (strcmp (arg, "d") == 0 || strcmp (arg, "10") == 0) + radix = radix_decimal; + else if (strcmp (arg, "o") == 0 || strcmp (arg, "8") == 0) + radix = radix_octal; + else + error (EXIT_FAILURE, 0, gettext ("Invalid radix: %s"), arg); + break; + + case 't': + totals = true; + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + + +/* Open the file and determine the type. */ +static int +process_file (const char *fname) +{ + int fd = open (fname, O_RDONLY); + if (unlikely (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 (likely (elf != NULL)) + { + if (elf_kind (elf) == ELF_K_ELF) + { + handle_elf (elf, NULL, fname); + + if (unlikely (elf_end (elf) != 0)) + INTERNAL_ERROR (fname); + + if (unlikely (close (fd) != 0)) + error (EXIT_FAILURE, errno, gettext ("while closing '%s'"), fname); + + return 0; + } + else if (likely (elf_kind (elf) == ELF_K_AR)) + { + int result = handle_ar (fd, elf, NULL, fname); + + if (unlikely (close (fd) != 0)) + error (EXIT_FAILURE, errno, gettext ("while closing '%s'"), fname); + + return result; + } + + /* We cannot handle this type. Close the descriptor anyway. */ + if (unlikely (elf_end (elf) != 0)) + INTERNAL_ERROR (fname); + } + + if (unlikely (close (fd) != 0)) + error (EXIT_FAILURE, errno, gettext ("while closing '%s'"), fname); + + error (0, 0, gettext ("%s: file format not recognized"), fname); + + return 1; +} + + +/* Print the BSD-style header. This is done exactly once. */ +static void +print_header (Elf *elf) +{ + static int done; + + if (! done) + { + int ddigits = length_map[gelf_getclass (elf) - 1][radix_decimal]; + int xdigits = length_map[gelf_getclass (elf) - 1][radix_hex]; + + printf ("%*s %*s %*s %*s %*s %s\n", + ddigits - 2, sgettext ("bsd|text"), + ddigits - 2, sgettext ("bsd|data"), + ddigits - 2, sgettext ("bsd|bss"), + ddigits - 2, sgettext ("bsd|dec"), + xdigits - 2, sgettext ("bsd|hex"), + sgettext ("bsd|filename")); + + done = 1; + } +} + + +static int +handle_ar (int fd, Elf *elf, const char *prefix, const char *fname) +{ + 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); + + /* 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. */ + Elf_Arhdr *arhdr = elf_getarhdr (subelf); + + if (elf_kind (subelf) == ELF_K_ELF) + handle_elf (subelf, new_prefix, arhdr->ar_name); + 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 (unlikely (elf_end (subelf) != 0)) + INTERNAL_ERROR (fname); + } + + if (unlikely (elf_end (elf) != 0)) + INTERNAL_ERROR (fname); + + return result; +} + + +/* Show sizes in SysV format. */ +static void +show_sysv (Elf *elf, const char *prefix, const char *fname, + const char *fullname) +{ + int maxlen = 10; + const int digits = length_map[gelf_getclass (elf) - 1][radix]; + + /* Get the section header string table index. */ + size_t shstrndx; + if (unlikely (elf_getshdrstrndx (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) + INTERNAL_ERROR (fullname); + + /* Ignore all sections which are not used at runtime. */ + if ((shdr->sh_flags & SHF_ALLOC) != 0) + maxlen = MAX (maxlen, + (int) strlen (elf_strptr (elf, shstrndx, + shdr->sh_name))); + } + + fputs_unlocked (fname, stdout); + if (prefix != NULL) + printf (gettext (" (ex %s)"), prefix); + printf (":\n%-*s %*s %*s\n", + maxlen, sgettext ("sysv|section"), + 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) + fmtstr = "%-*s %*" PRId64 " %*" PRId64 "\n"; + else + 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. */ + if ((shdr->sh_flags & SHF_ALLOC) != 0) + { + printf (fmtstr, + maxlen, elf_strptr (elf, shstrndx, shdr->sh_name), + digits - 2, shdr->sh_size, + digits, shdr->sh_addr); + + total += shdr->sh_size; + } + } + + if (radix == radix_hex) + printf ("%-*s %*" PRIx64 "\n\n\n", maxlen, sgettext ("sysv|Total"), + digits - 2, total); + else if (radix == radix_decimal) + printf ("%-*s %*" PRId64 "\n\n\n", maxlen, sgettext ("sysv|Total"), + digits - 2, total); + else + printf ("%-*s %*" PRIo64 "\n\n\n", maxlen, sgettext ("sysv|Total"), + digits - 2, total); +} + + +/* Show sizes in SysV format in one line. */ +static void +show_sysv_one_line (Elf *elf) +{ + /* Get the section header string table index. */ + size_t shstrndx; + if (unlikely (elf_getshdrstrndx (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) + fmtstr = "%" PRId64 "(%s)"; + else + 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. */ + if ((shdr->sh_flags & SHF_ALLOC) == 0) + continue; + + if (! first) + fputs_unlocked (" + ", stdout); + first = false; + + printf (fmtstr, shdr->sh_size, + elf_strptr (elf, shstrndx, shdr->sh_name)); + + total += shdr->sh_size; + } + + if (radix == radix_hex) + printf (" = %#" PRIx64 "\n", total); + else if (radix == radix_decimal) + printf (" = %" PRId64 "\n", total); + else + printf (" = %" PRIo64 "\n", total); +} + + +/* Variables to add up the sizes of all files. */ +static uintmax_t total_textsize; +static uintmax_t total_datasize; +static uintmax_t total_bsssize; + + +/* Show sizes in BSD format. */ +static void +show_bsd (Elf *elf, const char *prefix, const char *fname, + const char *fullname) +{ + GElf_Off textsize = 0; + GElf_Off datasize = 0; + GElf_Off bsssize = 0; + 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) + INTERNAL_ERROR (fullname); + + /* Ignore all sections which are not marked as loaded. */ + if ((shdr->sh_flags & SHF_ALLOC) == 0) + continue; + + if ((shdr->sh_flags & SHF_WRITE) == 0) + textsize += shdr->sh_size; + else if (shdr->sh_type == SHT_NOBITS) + bsssize += shdr->sh_size; + else + datasize += shdr->sh_size; + } + + printf ("%*" PRId64 " %*" PRId64 " %*" PRId64 " %*" PRId64 " %*" + PRIx64 " %s", + ddigits - 2, textsize, + ddigits - 2, datasize, + ddigits - 2, bsssize, + ddigits - 2, textsize + datasize + bsssize, + xdigits - 2, textsize + datasize + bsssize, + fname); + if (prefix != NULL) + printf (gettext (" (ex %s)"), prefix); + fputs_unlocked ("\n", stdout); + + total_textsize += textsize; + total_datasize += datasize; + total_bsssize += bsssize; + + totals_class = MAX (totals_class, gelf_getclass (elf)); +} + + +/* Show total size. */ +static void +show_bsd_totals (void) +{ + int ddigits = length_map[totals_class - 1][radix_decimal]; + int xdigits = length_map[totals_class - 1][radix_hex]; + + printf ("%*" PRIuMAX " %*" PRIuMAX " %*" PRIuMAX " %*" PRIuMAX " %*" + PRIxMAX " %s", + ddigits - 2, total_textsize, + ddigits - 2, total_datasize, + ddigits - 2, total_bsssize, + ddigits - 2, total_textsize + total_datasize + total_bsssize, + xdigits - 2, total_textsize + total_datasize + total_bsssize, + gettext ("(TOTALS)\n")); +} + + +/* Show size and permission of loadable segments. */ +static void +show_segments (Elf *elf, const char *fullname) +{ + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); + if (ehdr == NULL) + INTERNAL_ERROR (fullname); + + GElf_Off total = 0; + bool first = true; + for (size_t cnt = 0; cnt < ehdr->e_phnum; ++cnt) + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr; + + phdr = gelf_getphdr (elf, cnt, &phdr_mem); + if (phdr == NULL) + INTERNAL_ERROR (fullname); + + if (phdr->p_type != PT_LOAD) + /* Only load segments. */ + continue; + + if (! first) + fputs_unlocked (" + ", stdout); + first = false; + + printf (radix == radix_hex ? "%" PRIx64 "(%c%c%c)" + : (radix == radix_decimal ? "%" PRId64 "(%c%c%c)" + : "%" PRIo64 "(%c%c%c)"), + phdr->p_memsz, + (phdr->p_flags & PF_R) == 0 ? '-' : 'r', + (phdr->p_flags & PF_W) == 0 ? '-' : 'w', + (phdr->p_flags & PF_X) == 0 ? '-' : 'x'); + + total += phdr->p_memsz; + } + + if (radix == radix_hex) + printf (" = %#" PRIx64 "\n", total); + else if (radix == radix_decimal) + printf (" = %" PRId64 "\n", total); + else + printf (" = %" PRIo64 "\n", total); +} + + +static void +handle_elf (Elf *elf, const char *prefix, const char *fname) +{ + size_t prefix_len = prefix == NULL ? 0 : strlen (prefix); + size_t fname_len = strlen (fname) + 1; + char fullname[prefix_len + 1 + fname_len]; + char *cp = fullname; + + /* Create the full name of the file. */ + if (prefix != NULL) + { + cp = mempcpy (cp, prefix, prefix_len); + *cp++ = ':'; + } + memcpy (cp, fname, fname_len); + + if (format == format_sysv) + show_sysv (elf, prefix, fname, fullname); + else if (format == format_sysv_one_line) + show_sysv_one_line (elf); + else if (format == format_segments) + show_segments (elf, fullname); + else + { + print_header (elf); + + show_bsd (elf, prefix, fname, fullname); + } +} + + +#include "debugpred.h" diff --git a/0.153/src/strings.c b/0.153/src/strings.c new file mode 100644 index 00000000..442901e5 --- /dev/null +++ b/0.153/src/strings.c @@ -0,0 +1,744 @@ +/* Print the strings of printable characters in files. + Copyright (C) 2005-2010, 2012 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); +ARGP_PROGRAM_VERSION_HOOK_DEF = print_version; + +/* Bug report address. */ +ARGP_PROGRAM_BUG_ADDRESS_DEF = 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 (STDIN_FILENO, + print_file_name ? "{standard input}" : NULL, + (fstat64 (STDIN_FILENO, &st) == 0 && S_ISREG (st.st_mode)) + ? 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\ +"), "2012"); + 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 characters. */ + nb &= ~(bytes_per_char - 1); + + process_chunk (fname, buf, from + nb, nb, &unprinted); + + /* If the last bytes of the buffer (modulo 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); + ntrailer = to_keep; + 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) +{ + 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 (from != 0 && lseek64 (fd, from, SEEK_SET) != from) + error (EXIT_FAILURE, errno, gettext ("lseek64 failed")); + + return read_block_no_mmap (fd, fname, from, to - from); + } + + assert ((off64_t) min_len_bytes < fdlen); + + 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/0.153/src/strip.c b/0.153/src/strip.c new file mode 100644 index 00000000..78f683a8 --- /dev/null +++ b/0.153/src/strip.c @@ -0,0 +1,2138 @@ +/* Discard section not used at runtime from object files. + Copyright (C) 2000-2012 Red Hat, Inc. + This file is part of Red Hat elfutils. + Written by Ulrich Drepper <drepper@redhat.com>, 2000. + + 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 <byteswap.h> +#include <endian.h> +#include <error.h> +#include <fcntl.h> +#include <gelf.h> +#include <libelf.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 <sys/stat.h> +#include <sys/time.h> + +#include <elf-knowledge.h> +#include <libebl.h> +#include <system.h> + +typedef uint8_t GElf_Byte; + +/* Name and version of program. */ +static void print_version (FILE *stream, struct argp_state *state); +ARGP_PROGRAM_VERSION_HOOK_DEF = print_version; + +/* Bug report address. */ +ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT; + + +/* Values for the parameters which have no short form. */ +#define OPT_REMOVE_COMMENT 0x100 +#define OPT_PERMISSIVE 0x101 +#define OPT_STRIP_SECTIONS 0x102 +#define OPT_RELOC_DEBUG 0x103 + + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + { 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 }, + { "strip-sections", OPT_STRIP_SECTIONS, NULL, 0, + N_("Remove section headers (not recommended)"), 0 }, + { "preserve-dates", 'p', NULL, 0, + N_("Copy modified/access timestamps to the output"), 0 }, + { "reloc-debug-sections", OPT_RELOC_DEBUG, NULL, 0, + N_("Resolve all trivial relocations between debug sections if the removed sections are placed in a debug file (only relevant for ET_REL files, operation is not reversable, needs -f)"), 0 }, + { "remove-comment", OPT_REMOVE_COMMENT, NULL, 0, + 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"), 0 }, + { NULL, 0, NULL, 0, NULL, 0 } +}; + +/* Short description of program. */ +static const char doc[] = N_("Discard symbols from object 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 +}; + + +/* Print symbols in file named FNAME. */ +static int process_file (const char *fname); + +/* Handle one ELF file. */ +static int handle_elf (int fd, Elf *elf, const char *prefix, + const char *fname, mode_t mode, struct timeval tvp[2]); + +/* Handle all files contained in the archive. */ +static int handle_ar (int fd, Elf *elf, const char *prefix, const char *fname, + struct timeval tvp[2]); + +#define INTERNAL_ERROR(fname) \ + error (EXIT_FAILURE, 0, gettext ("%s: INTERNAL ERROR %d (%s-%s): %s"), \ + fname, __LINE__, PACKAGE_VERSION, __DATE__, elf_errmsg (-1)) + + +/* Name of the output file. */ +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; + +/* If true .comment sections will be removed. */ +static bool remove_comment; + +/* If true remove all debug sections. */ +static bool remove_debug; + +/* If true remove all section headers. */ +static bool remove_shdrs; + +/* If true relax some ELF rules for input files. */ +static bool permissive; + +/* If true perform relocations between debug sections. */ +static bool reloc_debug; + + +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. */ + __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. */ + if (argp_parse (&argp, argc, argv, 0, &remaining, NULL) != 0) + return EXIT_FAILURE; + + if (reloc_debug && debug_fname == NULL) + error (EXIT_FAILURE, 0, + gettext ("--reloc-debug-sections used without -f")); + + /* Tell the library which version we are expecting. */ + elf_version (EV_CURRENT); + + if (remaining == argc) + /* The user didn't specify a name so we use a.out. */ + result = process_file ("a.out"); + else + { + /* 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) + error (EXIT_FAILURE, 0, gettext ("\ +Only one input file allowed together with '-o' and '-f'")); + + /* Process all the remaining files. */ + do + result |= process_file (argv[remaining]); + while (++remaining < argc); + } + + return result; +} + + +/* Print the version information. */ +static void +print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) +{ + 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\ +"), "2012"); + 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) +{ + 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; + + case 'p': + preserve_dates = true; + break; + + case OPT_RELOC_DEBUG: + reloc_debug = true; + break; + + case OPT_REMOVE_COMMENT: + 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; + + case OPT_STRIP_SECTIONS: + remove_shdrs = true; + break; + + case OPT_PERMISSIVE: + permissive = true; + break; + + case 's': /* Ignored for compatibility. */ + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + + +static int +process_file (const char *fname) +{ + /* If we have to preserve the modify and access timestamps get them + now. We cannot use fstat() after opening the file since the open + would change the access time. */ + struct stat64 pre_st; + struct timeval tv[2]; + again: + if (preserve_dates) + { + if (stat64 (fname, &pre_st) != 0) + { + error (0, errno, gettext ("cannot stat input file '%s'"), fname); + return 1; + } + + /* If we have to preserve the timestamp, we need it in the + format utimes() understands. */ + TIMESPEC_TO_TIMEVAL (&tv[0], &pre_st.st_atim); + TIMESPEC_TO_TIMEVAL (&tv[1], &pre_st.st_mtim); + } + + /* Open the file. */ + int fd = open (fname, output_fname == NULL ? O_RDWR : O_RDONLY); + if (fd == -1) + { + error (0, errno, gettext ("while opening '%s'"), fname); + return 1; + } + + /* We always use fstat() even if we called stat() before. This is + done to make sure the information returned by stat() is for the + same file. */ + struct stat64 st; + if (fstat64 (fd, &st) != 0) + { + error (0, errno, gettext ("cannot stat input file '%s'"), fname); + return 1; + } + /* Paranoid mode on. */ + if (preserve_dates + && (st.st_ino != pre_st.st_ino || st.st_dev != pre_st.st_dev)) + { + /* We detected a race. Try again. */ + close (fd); + goto again; + } + + /* Now get the ELF descriptor. */ + Elf *elf = elf_begin (fd, output_fname == NULL ? ELF_C_RDWR : ELF_C_READ, + NULL); + int result; + switch (elf_kind (elf)) + { + case ELF_K_ELF: + result = handle_elf (fd, elf, NULL, fname, st.st_mode & ACCESSPERMS, + preserve_dates ? tv : NULL); + break; + + 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 || debug_fname != NULL)) + { + error (0, 0, gettext ("%s: cannot use -o or -f when stripping archive"), + fname); + result = 1; + } + else + result = handle_ar (fd, elf, NULL, fname, preserve_dates ? tv : NULL); + break; + + default: + error (0, 0, gettext ("%s: File format not recognized"), fname); + result = 1; + break; + } + + if (unlikely (elf_end (elf) != 0)) + INTERNAL_ERROR (fname); + + close (fd); + + return result; +} + + +/* Maximum size of array allocated on stack. */ +#define MAX_STACK_ALLOC (400 * 1024) + +static int +handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, + mode_t mode, struct timeval tvp[2]) +{ + size_t prefix_len = prefix == NULL ? 0 : strlen (prefix); + size_t fname_len = strlen (fname) + 1; + char *fullname = alloca (prefix_len + 1 + fname_len); + char *cp = fullname; + Elf *debugelf = NULL; + char *tmp_debug_fname = NULL; + int result = 0; + size_t shdridx = 0; + size_t shstrndx; + 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. */ + Elf32_Word symtab_idx; + Elf32_Word version_idx; + Elf32_Word group_idx; + Elf32_Word group_cnt; + Elf_Scn *newscn; + struct Ebl_Strent *se; + Elf32_Word *newsymidx; + } *shdr_info = NULL; + Elf_Scn *scn; + size_t cnt; + size_t idx; + bool changes; + GElf_Ehdr newehdr_mem; + GElf_Ehdr *newehdr; + GElf_Ehdr debugehdr_mem; + GElf_Ehdr *debugehdr; + struct Ebl_Strtab *shst = NULL; + Elf_Data debuglink_crc_data; + bool any_symtab_changes = false; + Elf_Data *shstrtab_data = NULL; + + /* Create the full name of the file. */ + if (prefix != NULL) + { + cp = mempcpy (cp, prefix, prefix_len); + *cp++ = ':'; + } + memcpy (cp, fname, fname_len); + + /* If we are not replacing the input file open a new file here. */ + if (output_fname != NULL) + { + fd = open (output_fname, O_RDWR | O_CREAT, mode); + if (unlikely (fd == -1)) + { + error (0, errno, gettext ("cannot open '%s'"), output_fname); + return 1; + } + } + + int debug_fd = -1; + + /* Get the EBL handling. Removing all debugging symbols with the -g + option or resolving all relocations between debug sections with + the --reloc-debug-sections option are currently the only reasons + we need EBL so don't open the backend unless necessary. */ + Ebl *ebl = NULL; + if (remove_debug || reloc_debug) + { + ebl = ebl_openbackend (elf); + if (ebl == NULL) + { + error (0, errno, gettext ("cannot open EBL backend")); + result = 1; + goto fail; + } + } + + /* Open the additional file the debug information will be stored in. */ + if (debug_fname != NULL) + { + /* Create a temporary file name. We do not want to overwrite + the debug file if the file would not contain any + information. */ + size_t debug_fname_len = strlen (debug_fname); + tmp_debug_fname = (char *) alloca (debug_fname_len + sizeof (".XXXXXX")); + strcpy (mempcpy (tmp_debug_fname, debug_fname, debug_fname_len), + ".XXXXXX"); + + debug_fd = mkstemp (tmp_debug_fname); + if (unlikely (debug_fd == -1)) + { + error (0, errno, gettext ("cannot open '%s'"), debug_fname); + result = 1; + goto fail; + } + } + + /* Get the information from the old file. */ + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); + if (ehdr == NULL) + INTERNAL_ERROR (fname); + + /* Get the section header string table index. */ + if (unlikely (elf_getshdrstrndx (elf, &shstrndx) < 0)) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + /* 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 + newelf = elf_clone (elf, ELF_C_EMPTY); + + if (unlikely (gelf_newehdr (newelf, gelf_getclass (elf)) == 0) + || (ehdr->e_type != ET_REL + && unlikely (gelf_newphdr (newelf, ehdr->e_phnum) == 0))) + { + error (0, 0, gettext ("cannot create new file '%s': %s"), + output_fname, elf_errmsg (-1)); + goto fail; + } + + /* Copy over the old program header if needed. */ + if (ehdr->e_type != ET_REL) + for (cnt = 0; cnt < ehdr->e_phnum; ++cnt) + { + GElf_Phdr 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); + } + + if (debug_fname != NULL) + { + /* Also create an ELF descriptor for the debug file */ + debugelf = elf_begin (debug_fd, ELF_C_WRITE_MMAP, NULL); + if (unlikely (gelf_newehdr (debugelf, gelf_getclass (elf)) == 0) + || (ehdr->e_type != ET_REL + && unlikely (gelf_newphdr (debugelf, ehdr->e_phnum) == 0))) + { + error (0, 0, gettext ("cannot create new file '%s': %s"), + debug_fname, elf_errmsg (-1)); + goto fail_close; + } + + /* Copy over the old program header if needed. */ + if (ehdr->e_type != ET_REL) + for (cnt = 0; cnt < ehdr->e_phnum; ++cnt) + { + GElf_Phdr 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); + } + } + + /* Number of sections. */ + size_t shnum; + if (unlikely (elf_getshdrnum (elf, &shnum) < 0)) + { + error (0, 0, gettext ("cannot determine number of sections: %s"), + elf_errmsg (-1)); + goto fail_close; + } + + /* Storage for section information. We leave room for two more + entries since we unconditionally create a section header string + table. Maybe some weird tool created an ELF file without one. + The other one is used for the debug link section. */ + if ((shnum + 2) * sizeof (struct shdr_info) > MAX_STACK_ALLOC) + shdr_info = (struct shdr_info *) xcalloc (shnum + 2, + sizeof (struct shdr_info)); + else + { + shdr_info = (struct shdr_info *) alloca ((shnum + 2) + * sizeof (struct shdr_info)); + memset (shdr_info, '\0', (shnum + 2) * sizeof (struct shdr_info)); + } + + /* Prepare section information data structure. */ + scn = NULL; + cnt = 1; + while ((scn = elf_nextscn (elf, scn)) != NULL) + { + /* This should always be true (i.e., there should not be any + holes in the numbering). */ + assert (elf_ndxscn (scn) == cnt); + + shdr_info[cnt].scn = scn; + + /* Get the header. */ + if (gelf_getshdr (scn, &shdr_info[cnt].shdr) == NULL) + INTERNAL_ERROR (fname); + + /* Get the name of the section. */ + shdr_info[cnt].name = elf_strptr (elf, shstrndx, + shdr_info[cnt].shdr.sh_name); + if (shdr_info[cnt].name == NULL) + { + error (0, 0, gettext ("illformed file '%s'"), fname); + goto fail_close; + } + + /* Mark them as present but not yet investigated. */ + shdr_info[cnt].idx = 1; + + /* Remember the shdr.sh_link value. */ + shdr_info[cnt].old_sh_link = shdr_info[cnt].shdr.sh_link; + + /* Sections in files other than relocatable object files which + are not loaded can be freely moved by us. In relocatable + object files everything can be moved. */ + if (ehdr->e_type == ET_REL + || (shdr_info[cnt].shdr.sh_flags & SHF_ALLOC) == 0) + shdr_info[cnt].shdr.sh_offset = 0; + + /* If this is an extended section index table store an + appropriate reference. */ + if (unlikely (shdr_info[cnt].shdr.sh_type == SHT_SYMTAB_SHNDX)) + { + assert (shdr_info[shdr_info[cnt].shdr.sh_link].symtab_idx == 0); + shdr_info[shdr_info[cnt].shdr.sh_link].symtab_idx = cnt; + } + else if (unlikely (shdr_info[cnt].shdr.sh_type == SHT_GROUP)) + { + /* Cross-reference the sections contained in the section + group. */ + shdr_info[cnt].data = elf_getdata (shdr_info[cnt].scn, NULL); + if (shdr_info[cnt].data == NULL) + INTERNAL_ERROR (fname); + + /* XXX Fix for unaligned access. */ + 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) + shdr_info[grpref[inner]].group_idx = cnt; + + if (inner == 1 || (inner == 2 && (grpref[0] & GRP_COMDAT) == 0)) + /* If the section group contains only one element and this + is n COMDAT section we can drop it right away. */ + shdr_info[cnt].idx = 0; + else + shdr_info[cnt].group_cnt = inner - 1; + } + else if (unlikely (shdr_info[cnt].shdr.sh_type == SHT_GNU_versym)) + { + assert (shdr_info[shdr_info[cnt].shdr.sh_link].version_idx == 0); + shdr_info[shdr_info[cnt].shdr.sh_link].version_idx = cnt; + } + + /* If this section is part of a group make sure it is not + discarded right away. */ + if ((shdr_info[cnt].shdr.sh_flags & SHF_GROUP) != 0) + { + assert (shdr_info[cnt].group_idx != 0); + + if (shdr_info[shdr_info[cnt].group_idx].idx == 0) + { + /* The section group section will be removed. */ + shdr_info[cnt].group_idx = 0; + shdr_info[cnt].shdr.sh_flags &= ~SHF_GROUP; + } + } + + /* Increment the counter. */ + ++cnt; + } + + /* Now determine which sections can go away. The general rule is that + all sections which are not used at runtime are stripped out. But + there are a few exceptions: + + - special sections named ".comment" and ".note" are kept + - OS or architecture specific sections are kept since we might not + know how to handle them + - if a section is referred to from a section which is not removed + in the sh_link or sh_info element it cannot be removed either + */ + for (cnt = 1; cnt < shnum; ++cnt) + /* Check whether the section can be removed. */ + if (remove_shdrs ? !(shdr_info[cnt].shdr.sh_flags & SHF_ALLOC) + : 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; + + idx = shdr_info[cnt].group_idx; + while (idx != 0) + { + /* The section group data is already loaded. */ + assert (shdr_info[idx].data != NULL); + + /* 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) + || (is_comdat && shdr_info[idx].group_cnt == 0)) + { + shdr_info[idx].idx = 0; + /* Continue recursively. */ + idx = shdr_info[idx].group_idx; + } + else + break; + } + } + + /* Mark the SHT_NULL section as handled. */ + shdr_info[0].idx = 2; + + + /* Handle exceptions: section groups and cross-references. We might + have to repeat this a few times since the resetting of the flag + might propagate. */ + do + { + changes = false; + + for (cnt = 1; cnt < shnum; ++cnt) + { + if (shdr_info[cnt].idx == 0) + { + /* If a relocation section is marked as being removed make + sure the section it is relocating is removed, too. */ + if ((shdr_info[cnt].shdr.sh_type == SHT_REL + || shdr_info[cnt].shdr.sh_type == SHT_RELA) + && shdr_info[shdr_info[cnt].shdr.sh_info].idx != 0) + shdr_info[cnt].idx = 1; + + /* If a group section is marked as being removed make + sure all the sections it contains are being removed, too. */ + if (shdr_info[cnt].shdr.sh_type == SHT_GROUP) + { + Elf32_Word *grpref; + grpref = (Elf32_Word *) shdr_info[cnt].data->d_buf; + for (size_t in = 1; + in < shdr_info[cnt].data->d_size / sizeof (Elf32_Word); + ++in) + if (shdr_info[grpref[in]].idx != 0) + { + shdr_info[cnt].idx = 1; + break; + } + } + } + + if (shdr_info[cnt].idx == 1) + { + /* 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 (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)) + { + /* Make sure the data is loaded. */ + if (shdr_info[cnt].data == NULL) + { + shdr_info[cnt].data + = elf_getdata (shdr_info[cnt].scn, NULL); + if (shdr_info[cnt].data == NULL) + INTERNAL_ERROR (fname); + } + Elf_Data *symdata = shdr_info[cnt].data; + + /* If there is an extended section index table load it + as well. */ + if (shdr_info[cnt].symtab_idx != 0 + && shdr_info[shdr_info[cnt].symtab_idx].data == NULL) + { + assert (shdr_info[cnt].shdr.sh_type == SHT_SYMTAB); + + shdr_info[shdr_info[cnt].symtab_idx].data + = elf_getdata (shdr_info[shdr_info[cnt].symtab_idx].scn, + NULL); + if (shdr_info[shdr_info[cnt].symtab_idx].data == NULL) + INTERNAL_ERROR (fname); + } + 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. */ + 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; + ++inner) + { + GElf_Sym sym_mem; + Elf32_Word xndx; + GElf_Sym *sym = gelf_getsymshndx (symdata, xndxdata, + inner, &sym_mem, + &xndx); + if (sym == NULL) + INTERNAL_ERROR (fname); + + size_t scnidx = sym->st_shndx; + if (scnidx == SHN_UNDEF || scnidx >= shnum + || (scnidx >= SHN_LORESERVE + && scnidx <= SHN_HIRESERVE + && scnidx != SHN_XINDEX) + /* Don't count in the section symbols. */ + || GELF_ST_TYPE (sym->st_info) == STT_SECTION) + /* This is no section index, leave it alone. */ + continue; + else if (scnidx == SHN_XINDEX) + scnidx = xndx; + + if (shdr_info[scnidx].idx == 0) + /* 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. + - references inside section groups + - specially marked references in sh_info if the SHF_INFO_LINK + flag is set + */ + + if (shdr_info[shdr_info[cnt].shdr.sh_link].idx == 0) + { + shdr_info[shdr_info[cnt].shdr.sh_link].idx = 1; + changes |= shdr_info[cnt].shdr.sh_link < cnt; + } + + /* Handle references through sh_info. */ + if (SH_INFO_LINK_P (&shdr_info[cnt].shdr) + && shdr_info[shdr_info[cnt].shdr.sh_info].idx == 0) + { + shdr_info[shdr_info[cnt].shdr.sh_info].idx = 1; + changes |= shdr_info[cnt].shdr.sh_info < cnt; + } + + /* 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 + && shdr_info[i].debug_data == NULL) + { + 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); + + /* 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) + { + scn = elf_newscn (debugelf); + if (scn == NULL) + error (EXIT_FAILURE, 0, + gettext ("while generating output file: %s"), + elf_errmsg (-1)); + + bool discard_section = (shdr_info[cnt].idx > 0 + && shdr_info[cnt].debug_data == NULL + && shdr_info[cnt].shdr.sh_type != SHT_NOTE + && shdr_info[cnt].shdr.sh_type != SHT_GROUP + && cnt != ehdr->e_shstrndx); + + /* Set the section header in the new file. */ + GElf_Shdr debugshdr = shdr_info[cnt].shdr; + if (discard_section) + debugshdr.sh_type = SHT_NOBITS; + + if (unlikely (gelf_update_shdr (scn, &debugshdr) == 0)) + /* There cannot be any overflows. */ + INTERNAL_ERROR (fname); + + /* Get the data from the old file if necessary. */ + if (shdr_info[cnt].data == NULL) + { + shdr_info[cnt].data = elf_getdata (shdr_info[cnt].scn, NULL); + if (shdr_info[cnt].data == NULL) + INTERNAL_ERROR (fname); + } + + /* Set the data. This is done by copying from the old file. */ + Elf_Data *debugdata = elf_newdata (scn); + if (debugdata == NULL) + INTERNAL_ERROR (fname); + + /* 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 + || shdr_info[cnt].shdr.sh_type == SHT_GROUP) + { + /* 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 + libelf from the old file. */ + debugehdr = gelf_getehdr (debugelf, &debugehdr_mem); + if (debugehdr == NULL) + INTERNAL_ERROR (fname); + + memcpy (debugehdr->e_ident, ehdr->e_ident, EI_NIDENT); + debugehdr->e_type = ehdr->e_type; + debugehdr->e_machine = ehdr->e_machine; + debugehdr->e_version = ehdr->e_version; + debugehdr->e_entry = ehdr->e_entry; + debugehdr->e_flags = ehdr->e_flags; + debugehdr->e_shstrndx = ehdr->e_shstrndx; + + 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; + } + } + + /* Mark the section header string table as unused, we will create + a new one. */ + shdr_info[shstrndx].idx = 0; + + /* 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'"), + output_fname ?: fname); + + /* Assign new section numbers. */ + shdr_info[0].idx = 0; + for (cnt = idx = 1; cnt < shnum; ++cnt) + if (shdr_info[cnt].idx > 0) + { + shdr_info[cnt].idx = idx++; + + /* Create a new section. */ + shdr_info[cnt].newscn = elf_newscn (newelf); + if (shdr_info[cnt].newscn == NULL) + error (EXIT_FAILURE, 0, gettext ("while generating output file: %s"), + elf_errmsg (-1)); + + assert (elf_ndxscn (shdr_info[cnt].newscn) == shdr_info[cnt].idx); + + /* Add this name to the section header string table. */ + shdr_info[cnt].se = ebl_strtabadd (shst, shdr_info[cnt].name, 0); + } + + /* Test whether we are doing anything at all. */ + if (cnt == idx) + /* Nope, all removable sections are already gone. */ + goto fail_close; + + /* Create the reference to the file with the debug info. */ + if (debug_fname != NULL && !remove_shdrs) + { + /* Add the section header string table section name. */ + shdr_info[cnt].se = ebl_strtabadd (shst, ".gnu_debuglink", 15); + shdr_info[cnt].idx = idx++; + + /* Create the section header. */ + shdr_info[cnt].shdr.sh_type = SHT_PROGBITS; + shdr_info[cnt].shdr.sh_flags = 0; + shdr_info[cnt].shdr.sh_addr = 0; + shdr_info[cnt].shdr.sh_link = SHN_UNDEF; + shdr_info[cnt].shdr.sh_info = SHN_UNDEF; + shdr_info[cnt].shdr.sh_entsize = 0; + shdr_info[cnt].shdr.sh_addralign = 4; + /* We set the offset to zero here. Before we write the ELF file the + field must have the correct value. This is done in the final + loop over all section. Then we have all the information needed. */ + shdr_info[cnt].shdr.sh_offset = 0; + + /* Create the section. */ + shdr_info[cnt].newscn = elf_newscn (newelf); + if (shdr_info[cnt].newscn == NULL) + error (EXIT_FAILURE, 0, + gettext ("while create section header section: %s"), + elf_errmsg (-1)); + assert (elf_ndxscn (shdr_info[cnt].newscn) == shdr_info[cnt].idx); + + shdr_info[cnt].data = elf_newdata (shdr_info[cnt].newscn); + if (shdr_info[cnt].data == NULL) + error (EXIT_FAILURE, 0, gettext ("cannot allocate section data: %s"), + elf_errmsg (-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; + + shdr_info[cnt].data->d_align = 4; + shdr_info[cnt].shdr.sh_size = shdr_info[cnt].data->d_size + = crc_offset + 4; + shdr_info[cnt].data->d_buf = xcalloc (1, shdr_info[cnt].data->d_size); + + strcpy (shdr_info[cnt].data->d_buf, debug_basename); + + /* 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. */ + shdridx = cnt; + + /* Add the section header string table section name. */ + shdr_info[cnt].se = ebl_strtabadd (shst, ".shstrtab", 10); + shdr_info[cnt].idx = idx; + + /* Create the section header. */ + shdr_info[cnt].shdr.sh_type = SHT_STRTAB; + shdr_info[cnt].shdr.sh_flags = 0; + shdr_info[cnt].shdr.sh_addr = 0; + shdr_info[cnt].shdr.sh_link = SHN_UNDEF; + shdr_info[cnt].shdr.sh_info = SHN_UNDEF; + shdr_info[cnt].shdr.sh_entsize = 0; + /* We set the offset to zero here. Before we write the ELF file the + field must have the correct value. This is done in the final + loop over all section. Then we have all the information needed. */ + shdr_info[cnt].shdr.sh_offset = 0; + shdr_info[cnt].shdr.sh_addralign = 1; + + /* Create the section. */ + shdr_info[cnt].newscn = elf_newscn (newelf); + if (shdr_info[cnt].newscn == NULL) + error (EXIT_FAILURE, 0, + gettext ("while create section header section: %s"), + elf_errmsg (-1)); + assert (elf_ndxscn (shdr_info[cnt].newscn) == idx); + + /* Finalize the string table and fill in the correct indices in the + section headers. */ + shstrtab_data = elf_newdata (shdr_info[cnt].newscn); + if (shstrtab_data == NULL) + error (EXIT_FAILURE, 0, + gettext ("while create section header string table: %s"), + elf_errmsg (-1)); + ebl_strtabfinalize (shst, shstrtab_data); + + /* We have to set the section size. */ + shdr_info[cnt].shdr.sh_size = shstrtab_data->d_size; + + /* Update the section information. */ + GElf_Off lastoffset = 0; + for (cnt = 1; cnt <= shdridx; ++cnt) + if (shdr_info[cnt].idx > 0) + { + Elf_Data *newdata; + + scn = elf_getscn (newelf, shdr_info[cnt].idx); + assert (scn != NULL); + + /* Update the name. */ + shdr_info[cnt].shdr.sh_name = ebl_strtaboffset (shdr_info[cnt].se); + + /* Update the section header from the input file. Some fields + might be section indeces which now have to be adjusted. */ + if (shdr_info[cnt].shdr.sh_link != 0) + shdr_info[cnt].shdr.sh_link = + shdr_info[shdr_info[cnt].shdr.sh_link].idx; + + if (shdr_info[cnt].shdr.sh_type == SHT_GROUP) + { + assert (shdr_info[cnt].data != NULL); + + Elf32_Word *grpref = (Elf32_Word *) shdr_info[cnt].data->d_buf; + for (size_t inner = 0; + inner < shdr_info[cnt].data->d_size / sizeof (Elf32_Word); + ++inner) + grpref[inner] = shdr_info[grpref[inner]].idx; + } + + /* Handle the SHT_REL, SHT_RELA, and SHF_INFO_LINK flag. */ + if (SH_INFO_LINK_P (&shdr_info[cnt].shdr)) + shdr_info[cnt].shdr.sh_info = + shdr_info[shdr_info[cnt].shdr.sh_info].idx; + + /* Get the data from the old file if necessary. We already + created the data for the section header string table. */ + if (cnt < shnum) + { + if (shdr_info[cnt].data == NULL) + { + shdr_info[cnt].data = elf_getdata (shdr_info[cnt].scn, NULL); + if (shdr_info[cnt].data == NULL) + INTERNAL_ERROR (fname); + } + + /* Set the data. This is done by copying from the old file. */ + newdata = elf_newdata (scn); + if (newdata == NULL) + INTERNAL_ERROR (fname); + + /* Copy the structure. */ + *newdata = *shdr_info[cnt].data; + + /* We know the size. */ + shdr_info[cnt].shdr.sh_size = shdr_info[cnt].data->d_size; + + /* 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) + { + Elf_Data *versiondata = NULL; + Elf_Data *shndxdata = NULL; + + size_t elsize = gelf_fsize (elf, ELF_T_SYM, 1, + ehdr->e_version); + + if (shdr_info[cnt].symtab_idx != 0) + { + assert (shdr_info[cnt].shdr.sh_type == SHT_SYMTAB_SHNDX); + /* This section has extended section information. + We have to modify that information, too. */ + shndxdata = elf_getdata (shdr_info[shdr_info[cnt].symtab_idx].scn, + NULL); + + assert ((versiondata->d_size / sizeof (Elf32_Word)) + >= shdr_info[cnt].data->d_size / elsize); + } + + if (shdr_info[cnt].version_idx != 0) + { + assert (shdr_info[cnt].shdr.sh_type == SHT_DYNSYM); + /* This section has associated version + information. We have to modify that + information, too. */ + versiondata = elf_getdata (shdr_info[shdr_info[cnt].version_idx].scn, + NULL); + + assert ((versiondata->d_size / sizeof (GElf_Versym)) + >= shdr_info[cnt].data->d_size / elsize); + } + + shdr_info[cnt].newsymidx + = (Elf32_Word *) xcalloc (shdr_info[cnt].data->d_size + / elsize, sizeof (Elf32_Word)); + + bool last_was_local = true; + size_t destidx; + size_t inner; + for (destidx = inner = 1; + inner < shdr_info[cnt].data->d_size / elsize; + ++inner) + { + Elf32_Word sec; + GElf_Sym sym_mem; + Elf32_Word xshndx; + GElf_Sym *sym = gelf_getsymshndx (shdr_info[cnt].data, + shndxdata, inner, + &sym_mem, &xshndx); + if (sym == NULL) + INTERNAL_ERROR (fname); + + if (sym->st_shndx == SHN_UNDEF + || (sym->st_shndx >= shnum + && sym->st_shndx != SHN_XINDEX)) + { + /* This is no section index, leave it alone + unless it is moved. */ + if (destidx != inner + && gelf_update_symshndx (shdr_info[cnt].data, + shndxdata, + destidx, sym, + xshndx) == 0) + INTERNAL_ERROR (fname); + + shdr_info[cnt].newsymidx[inner] = destidx++; + + if (last_was_local + && GELF_ST_BIND (sym->st_info) != STB_LOCAL) + { + last_was_local = false; + shdr_info[cnt].shdr.sh_info = destidx - 1; + } + + continue; + } + + /* Get the full section index, if necessary from the + XINDEX table. */ + if (sym->st_shndx != SHN_XINDEX) + sec = shdr_info[sym->st_shndx].idx; + else + { + assert (shndxdata != NULL); + + sec = shdr_info[xshndx].idx; + } + + if (sec != 0) + { + GElf_Section nshndx; + Elf32_Word nxshndx; + + if (sec < SHN_LORESERVE) + { + nshndx = sec; + nxshndx = 0; + } + else + { + nshndx = SHN_XINDEX; + nxshndx = sec; + } + + assert (sec < SHN_LORESERVE || shndxdata != NULL); + + if ((inner != destidx || nshndx != sym->st_shndx + || (shndxdata != NULL && nxshndx != xshndx)) + && (sym->st_shndx = nshndx, + gelf_update_symshndx (shdr_info[cnt].data, + shndxdata, + destidx, sym, + nxshndx) == 0)) + INTERNAL_ERROR (fname); + + shdr_info[cnt].newsymidx[inner] = destidx++; + + if (last_was_local + && GELF_ST_BIND (sym->st_info) != STB_LOCAL) + { + last_was_local = false; + shdr_info[cnt].shdr.sh_info = destidx - 1; + } + } + else if (debug_fname == NULL + || shdr_info[cnt].debug_data == NULL) + /* This is a section or group signature symbol + for a section which has been removed. */ + { + size_t sidx = (sym->st_shndx != SHN_XINDEX + ? sym->st_shndx : xshndx); + assert (GELF_ST_TYPE (sym->st_info) == STT_SECTION + || (shdr_info[sidx].shdr.sh_type == SHT_GROUP + && shdr_info[sidx].shdr.sh_info == inner)); + } + } + + if (destidx != inner) + { + /* The size of the symbol table changed. */ + shdr_info[cnt].shdr.sh_size = newdata->d_size + = destidx * elsize; + any_symtab_changes = true; + } + else + { + /* The symbol table didn't really change. */ + free (shdr_info[cnt].newsymidx); + shdr_info[cnt].newsymidx = NULL; + } + } + } + + /* If we have to, compute the offset of the section. */ + if (shdr_info[cnt].shdr.sh_offset == 0) + shdr_info[cnt].shdr.sh_offset + = ((lastoffset + shdr_info[cnt].shdr.sh_addralign - 1) + & ~((GElf_Off) (shdr_info[cnt].shdr.sh_addralign - 1))); + + /* Set the section header in the new file. */ + if (unlikely (gelf_update_shdr (scn, &shdr_info[cnt].shdr) == 0)) + /* There cannot be any overflows. */ + INTERNAL_ERROR (fname); + + /* Remember the last section written so far. */ + GElf_Off filesz = (shdr_info[cnt].shdr.sh_type != SHT_NOBITS + ? shdr_info[cnt].shdr.sh_size : 0); + if (lastoffset < shdr_info[cnt].shdr.sh_offset + filesz) + lastoffset = shdr_info[cnt].shdr.sh_offset + filesz; + } + + /* 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) + { + /* 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) + { + 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].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[symtabidx].newsymidx == NULL) + return true; + + /* 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); + + 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)); + + 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)); + + if (gelf_update_rela (d, relidx, &rel_mem) == 0) + INTERNAL_ERROR (fname); + } + } + break; + + case SHT_HASH: + if (no_symtab_updates ()) + break; + + /* We have to recompute the hash table. */ + + assert (shdr_info[cnt].idx > 0); + + /* The hash section in the new file. */ + scn = elf_getscn (newelf, shdr_info[cnt].idx); + + /* The symbol table data. */ + Elf_Data *symd = elf_getdata (elf_getscn (newelf, + shdr_info[symtabidx].idx), + NULL); + assert (symd != NULL); + + /* The hash table data. */ + Elf_Data *hashd = elf_getdata (scn, NULL); + assert (hashd != NULL); + + if (shdr_info[cnt].shdr.sh_entsize == sizeof (Elf32_Word)) + { + /* Sane arches first. */ + Elf32_Word *bucket = (Elf32_Word *) hashd->d_buf; + + 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; + 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); + + 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]; + + while (chain[hidx] != 0) + hidx = chain[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)); + + Elf64_Xword *bucket = (Elf64_Xword *) hashd->d_buf; + + 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. */ + 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; + + if (bucket[hidx] == 0) + bucket[hidx] = inner; + else + { + hidx = bucket[hidx]; + + while (chain[hidx] != 0) + hidx = chain[hidx]; + + chain[hidx] = inner; + } + } + } + break; + + case SHT_GNU_versym: + /* If the symbol table changed we have to adjust the entries. */ + if (no_symtab_updates ()) + break; + + 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; + + /* 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); + + shdr->sh_info = newsymidx[shdr->sh_info]; + + (void) gelf_update_shdr (scn, shdr); + break; + } + } + + /* Remove any relocations between debug sections in ET_REL + for the debug file when requested. These relocations are always + zero based between the unallocated sections. */ + if (debug_fname != NULL && reloc_debug && ehdr->e_type == ET_REL) + { + scn = NULL; + cnt = 0; + while ((scn = elf_nextscn (debugelf, scn)) != NULL) + { + cnt++; + /* We need the actual section and header from the debugelf + not just the cached original in shdr_info because we + might want to change the size. */ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA) + { + /* Make sure that this relocation section points to a + section to relocate with contents, that isn't + allocated and that is a debug section. */ + Elf_Scn *tscn = elf_getscn (debugelf, shdr->sh_info); + GElf_Shdr tshdr_mem; + GElf_Shdr *tshdr = gelf_getshdr (tscn, &tshdr_mem); + if (tshdr->sh_type == SHT_NOBITS + || tshdr->sh_size == 0 + || (tshdr->sh_flags & SHF_ALLOC) != 0) + continue; + + const char *tname = elf_strptr (debugelf, shstrndx, + tshdr->sh_name); + if (! tname || ! ebl_debugscn_p (ebl, tname)) + continue; + + /* OK, lets relocate all trivial cross debug section + relocations. */ + Elf_Data *reldata = elf_getdata (scn, NULL); + /* We actually wanted the rawdata, but since we already + accessed it earlier as elf_getdata () that won't + work. But debug sections are all ELF_T_BYTE, so it + doesn't really matter. */ + Elf_Data *tdata = elf_getdata (tscn, NULL); + if (tdata->d_type != ELF_T_BYTE) + INTERNAL_ERROR (fname); + + /* Pick up the symbol table and shndx table to + resolve relocation symbol indexes. */ + Elf64_Word symt = shdr->sh_link; + Elf_Data *symdata, *xndxdata; + symdata = (shdr_info[symt].debug_data + ?: shdr_info[symt].data); + xndxdata = (shdr_info[shdr_info[symt].symtab_idx].debug_data + ?: shdr_info[shdr_info[symt].symtab_idx].data); + + /* Apply one relocation. Returns true when trivial + relocation actually done. */ + bool relocate (GElf_Addr offset, const GElf_Sxword addend, + bool is_rela, int rtype, int symndx) + { + /* R_*_NONE relocs can always just be removed. */ + if (rtype == 0) + return true; + + /* We only do simple absolute relocations. */ + Elf_Type type = ebl_reloc_simple_type (ebl, rtype); + if (type == ELF_T_NUM) + return false; + + /* These are the types we can relocate. */ +#define TYPES DO_TYPE (BYTE, Byte); DO_TYPE (HALF, Half); \ + DO_TYPE (WORD, Word); DO_TYPE (SWORD, Sword); \ + DO_TYPE (XWORD, Xword); DO_TYPE (SXWORD, Sxword) + + /* And only for relocations against other debug sections. */ + GElf_Sym sym_mem; + Elf32_Word xndx; + GElf_Sym *sym = gelf_getsymshndx (symdata, xndxdata, + symndx, &sym_mem, + &xndx); + Elf32_Word sec = (sym->st_shndx == SHN_XINDEX + ? xndx : sym->st_shndx); + if (ebl_debugscn_p (ebl, shdr_info[sec].name)) + { + size_t size; + +#define DO_TYPE(NAME, Name) GElf_##Name Name; + union { TYPES; } tmpbuf; +#undef DO_TYPE + + switch (type) + { +#define DO_TYPE(NAME, Name) \ + case ELF_T_##NAME: \ + size = sizeof (GElf_##Name); \ + tmpbuf.Name = 0; \ + break; + TYPES; +#undef DO_TYPE + default: + return false; + } + + if (offset > tdata->d_size + || tdata->d_size - offset < size) + error (0, 0, gettext ("bad relocation")); + + /* When the symbol value is zero then for SHT_REL + sections this is all that needs to be checked. + The addend is contained in the original data at + the offset already. So if the (section) symbol + address is zero and the given addend is zero + just remove the relocation, it isn't needed + anymore. */ + if (addend == 0 && sym->st_value == 0) + return true; + + Elf_Data tmpdata = + { + .d_type = type, + .d_buf = &tmpbuf, + .d_size = size, + .d_version = EV_CURRENT, + }; + Elf_Data rdata = + { + .d_type = type, + .d_buf = tdata->d_buf + offset, + .d_size = size, + .d_version = EV_CURRENT, + }; + + GElf_Addr value = sym->st_value; + if (is_rela) + { + /* For SHT_RELA sections we just take the + given addend and add it to the value. */ + value += addend; + } + else + { + /* For SHT_REL sections we have to peek at + what is already in the section at the given + offset to get the addend. */ + Elf_Data *d = gelf_xlatetom (debugelf, &tmpdata, + &rdata, + ehdr->e_ident[EI_DATA]); + if (d == NULL) + INTERNAL_ERROR (fname); + assert (d == &tmpdata); + } + + switch (type) + { +#define DO_TYPE(NAME, Name) \ + case ELF_T_##NAME: \ + tmpbuf.Name += (GElf_##Name) value; \ + break; + TYPES; +#undef DO_TYPE + default: + abort (); + } + + /* Now finally put in the new value. */ + Elf_Data *s = gelf_xlatetof (debugelf, &rdata, + &tmpdata, + ehdr->e_ident[EI_DATA]); + if (s == NULL) + INTERNAL_ERROR (fname); + assert (s == &rdata); + + return true; + } + return false; + } + + size_t nrels = shdr->sh_size / shdr->sh_entsize; + size_t next = 0; + if (shdr->sh_type == SHT_REL) + for (size_t relidx = 0; relidx < nrels; ++relidx) + { + GElf_Rel rel_mem; + GElf_Rel *r = gelf_getrel (reldata, relidx, &rel_mem); + if (! relocate (r->r_offset, 0, false, + GELF_R_TYPE (r->r_info), + GELF_R_SYM (r->r_info))) + { + if (relidx != next) + gelf_update_rel (reldata, next, r); + ++next; + } + } + else + for (size_t relidx = 0; relidx < nrels; ++relidx) + { + GElf_Rela rela_mem; + GElf_Rela *r = gelf_getrela (reldata, relidx, &rela_mem); + if (! relocate (r->r_offset, r->r_addend, true, + GELF_R_TYPE (r->r_info), + GELF_R_SYM (r->r_info))) + { + if (relidx != next) + gelf_update_rela (reldata, next, r); + ++next; + } + } + + nrels = next; + shdr->sh_size = reldata->d_size = nrels * shdr->sh_entsize; + gelf_update_shdr (scn, shdr); + } + } + } + + /* Now that we have done all adjustments to the data, + we can actually write out the debug file. */ + if (debug_fname != 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; + } + + /* 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; + + if (!remove_shdrs) + { + 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 + }; + + /* 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 + libelf from the old file. */ + newehdr = gelf_getehdr (newelf, &newehdr_mem); + if (newehdr == NULL) + INTERNAL_ERROR (fname); + + memcpy (newehdr->e_ident, ehdr->e_ident, EI_NIDENT); + newehdr->e_type = ehdr->e_type; + newehdr->e_machine = ehdr->e_machine; + newehdr->e_version = ehdr->e_version; + newehdr->e_entry = ehdr->e_entry; + newehdr->e_flags = ehdr->e_flags; + newehdr->e_phoff = ehdr->e_phoff; + + /* We need to position the section header table. */ + const size_t offsize = gelf_fsize (elf, ELF_T_OFF, 1, EV_CURRENT); + newehdr->e_shoff = ((shdr_info[shdridx].shdr.sh_offset + + shdr_info[shdridx].shdr.sh_size + offsize - 1) + & ~((GElf_Off) (offsize - 1))); + newehdr->e_shentsize = gelf_fsize (elf, ELF_T_SHDR, 1, EV_CURRENT); + + /* The new section header string table index. */ + if (likely (idx < SHN_HIRESERVE) && likely (idx != SHN_XINDEX)) + newehdr->e_shstrndx = idx; + else + { + /* The index does not fit in the ELF header field. */ + shdr_info[0].scn = elf_getscn (elf, 0); + + if (gelf_getshdr (shdr_info[0].scn, &shdr_info[0].shdr) == NULL) + INTERNAL_ERROR (fname); + + shdr_info[0].shdr.sh_link = idx; + (void) gelf_update_shdr (shdr_info[0].scn, &shdr_info[0].shdr); + + newehdr->e_shstrndx = SHN_XINDEX; + } + + if (gelf_update_ehdr (newelf, newehdr) == 0) + { + error (0, 0, gettext ("%s: error while creating ELF header: %s"), + fname, elf_errmsg (-1)); + return 1; + } + + /* We have everything from the old file. */ + if (elf_cntl (elf, ELF_C_FDDONE) != 0) + { + error (0, 0, gettext ("%s: error while reading the file: %s"), + fname, elf_errmsg (-1)); + return 1; + } + + /* The ELF library better follows our layout when this is not a + relocatable object file. */ + elf_flagelf (newelf, ELF_C_SET, + (ehdr->e_type != ET_REL ? ELF_F_LAYOUT : 0) + | (permissive ? ELF_F_PERMISSIVE : 0)); + + /* Finally write the file. */ + if (elf_update (newelf, ELF_C_WRITE) == -1) + { + error (0, 0, gettext ("while writing '%s': %s"), + fname, elf_errmsg (-1)); + result = 1; + } + + if (remove_shdrs) + { + /* libelf can't cope without the section headers being properly intact. + So we just let it write them normally, and then we nuke them later. */ + + if (newehdr->e_ident[EI_CLASS] == ELFCLASS32) + { + assert (offsetof (Elf32_Ehdr, e_shentsize) + sizeof (Elf32_Half) + == offsetof (Elf32_Ehdr, e_shnum)); + assert (offsetof (Elf32_Ehdr, e_shnum) + sizeof (Elf32_Half) + == offsetof (Elf32_Ehdr, e_shstrndx)); + const Elf32_Off zero_off = 0; + const Elf32_Half zero[3] = { 0, 0, SHN_UNDEF }; + if (pwrite_retry (fd, &zero_off, sizeof zero_off, + offsetof (Elf32_Ehdr, e_shoff)) != sizeof zero_off + || (pwrite_retry (fd, zero, sizeof zero, + offsetof (Elf32_Ehdr, e_shentsize)) + != sizeof zero) + || ftruncate64 (fd, shdr_info[shdridx].shdr.sh_offset) < 0) + { + error (0, errno, gettext ("while writing '%s'"), + fname); + result = 1; + } + } + else + { + assert (offsetof (Elf64_Ehdr, e_shentsize) + sizeof (Elf64_Half) + == offsetof (Elf64_Ehdr, e_shnum)); + assert (offsetof (Elf64_Ehdr, e_shnum) + sizeof (Elf64_Half) + == offsetof (Elf64_Ehdr, e_shstrndx)); + const Elf64_Off zero_off = 0; + const Elf64_Half zero[3] = { 0, 0, SHN_UNDEF }; + if (pwrite_retry (fd, &zero_off, sizeof zero_off, + offsetof (Elf64_Ehdr, e_shoff)) != sizeof zero_off + || (pwrite_retry (fd, zero, sizeof zero, + offsetof (Elf64_Ehdr, e_shentsize)) + != sizeof zero) + || ftruncate64 (fd, shdr_info[shdridx].shdr.sh_offset) < 0) + { + error (0, errno, gettext ("while writing '%s'"), + fname); + result = 1; + } + } + } + + fail_close: + if (shdr_info != NULL) + { + /* For some sections we might have created an table to map symbol + table indices. */ + if (any_symtab_changes) + for (cnt = 1; cnt <= shdridx; ++cnt) + { + 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) + free (shdr_info); + } + + /* Free other resources. */ + if (shstrtab_data != NULL) + free (shstrtab_data->d_buf); + if (shst != NULL) + ebl_strtabfree (shst); + + /* That was it. Close the descriptors. */ + if (elf_end (newelf) != 0) + { + 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, + elf_errmsg (-1)); + result = 1; + } + + fail: + /* Close the EBL backend. */ + if (ebl != NULL) + ebl_closebackend (ebl); + + /* Close debug file descriptor, if opened */ + if (debug_fd >= 0) + { + if (tmp_debug_fname != NULL) + unlink (tmp_debug_fname); + close (debug_fd); + } + + /* If requested, preserve the timestamp. */ + if (tvp != NULL) + { + if (futimes (fd, tvp) != 0) + { + error (0, errno, gettext ("\ +cannot set access and modification date of '%s'"), + output_fname ?: fname); + result = 1; + } + } + + /* Close the file descriptor if we created a new file. */ + if (output_fname != NULL) + close (fd); + + return result; +} + + +static int +handle_ar (int fd, Elf *elf, const char *prefix, const char *fname, + struct timeval tvp[2]) +{ + 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); + + + /* Process all the files contained in the archive. */ + Elf *subelf; + Elf_Cmd cmd = ELF_C_RDWR; + int result = 0; + while ((subelf = elf_begin (fd, cmd, elf)) != NULL) + { + /* The the header for this element. */ + Elf_Arhdr *arhdr = elf_getarhdr (subelf); + + if (elf_kind (subelf) == ELF_K_ELF) + result |= handle_elf (fd, subelf, new_prefix, arhdr->ar_name, 0, NULL); + else if (elf_kind (subelf) == ELF_K_AR) + result |= handle_ar (fd, subelf, new_prefix, arhdr->ar_name, NULL); + + /* Get next archive element. */ + cmd = elf_next (subelf); + if (unlikely (elf_end (subelf) != 0)) + INTERNAL_ERROR (fname); + } + + if (tvp != NULL) + { + if (unlikely (futimes (fd, tvp) != 0)) + { + error (0, errno, gettext ("\ +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); + + return result; +} + + +#include "debugpred.h" diff --git a/0.153/src/symbolhash.c b/0.153/src/symbolhash.c new file mode 100644 index 00000000..670cf056 --- /dev/null +++ b/0.153/src/symbolhash.c @@ -0,0 +1,41 @@ +/* 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. + + 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 <string.h> + +#include <ld.h> + +/* Definitions for the symbol hash table. */ +#define TYPE struct symbol * +#define NAME ld_symbol_tab +#define ITERATE 1 +#define COMPARE(a, b) strcmp ((a)->name, (b)->name) + +#include "../lib/dynamicsizehash.c" diff --git a/0.153/src/symbolhash.h b/0.153/src/symbolhash.h new file mode 100644 index 00000000..54b95397 --- /dev/null +++ b/0.153/src/symbolhash.h @@ -0,0 +1,36 @@ +/* Copyright (C) 2001, 2002 Red Hat, Inc. + This file is part of Red Hat elfutils. + Written by Ulrich Drepper <drepper@redhat.com>, 2001. + + 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 SYMBOLHASH_H +#define SYMBOLHASH_H 1 + +/* Definitions for the symbol hash table. */ +#define TYPE struct symbol * +#define NAME ld_symbol_tab +#define ITERATE 1 +#define COMPARE(a, b) strcmp ((a)->name, (b)->name) +#include <dynamicsizehash.h> + +#endif /* symbolhash.h */ diff --git a/0.153/src/unaligned.h b/0.153/src/unaligned.h new file mode 100644 index 00000000..ad7c55a5 --- /dev/null +++ b/0.153/src/unaligned.h @@ -0,0 +1,110 @@ +/* Unaligned memory access functionality. + 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. + + 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 _UNALIGNED_H +#define _UNALIGNED_H 1 + +#include <byteswap.h> +#include <endian.h> + + +#ifndef UNALIGNED_ACCESS_CLASS +# error "UNALIGNED_ACCESS_CLASS must be defined" +#endif + + +/* Macros to convert from the host byte order to that of the object file. */ +#if UNALIGNED_ACCESS_CLASS == BYTE_ORDER +# define target_bswap_16(n) (n) +# define target_bswap_32(n) (n) +# define target_bswap_64(n) (n) +#else +# define target_bswap_16(n) bswap_16 (n) +# define target_bswap_32(n) bswap_32 (n) +# define target_bswap_64(n) bswap_64 (n) +#endif + + +union u_2ubyte_unaligned +{ + uint16_t u; + char c[2]; +} __attribute__((packed)); + +union u_4ubyte_unaligned +{ + uint32_t u; + char c[4]; +} __attribute__((packed)); + +union u_8ubyte_unaligned +{ + uint64_t u; + char c[8]; +} __attribute__((packed)); + + +/* Macros to store value at unaligned address. */ +#define store_2ubyte_unaligned(ptr, value) \ + (void) (((union u_2ubyte_unaligned *) (ptr))->u = target_bswap_16 (value)) +#define store_4ubyte_unaligned(ptr, value) \ + (void) (((union u_4ubyte_unaligned *) (ptr))->u = target_bswap_32 (value)) +#define store_8ubyte_unaligned(ptr, value) \ + (void) (((union u_8ubyte_unaligned *) (ptr))->u = target_bswap_64 (value)) + + +/* Macros to add value to unaligned address. This is a bit more + complicated since the value must be read from memory and eventually + converted twice. */ +#if UNALIGNED_ACCESS_CLASS == BYTE_ORDER +# define add_2ubyte_unaligned(ptr, value) \ + (void) (((union u_2ubyte_unaligned *) (ptr))->u += value) +# define add_4ubyte_unaligned(ptr, value) \ + (void) (((union u_4ubyte_unaligned *) (ptr))->u += value) +# define add_8ubyte_unaligned(ptr, value) \ + (void) (((union u_8ubyte_unaligned *) (ptr))->u += value) +#else +# define add_2ubyte_unaligned(ptr, value) \ + do { \ + 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 = (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 = (void *) (ptr); \ + uint64_t _val = bswap_64 (_ptr->u) + (value); \ + _ptr->u = bswap_64 (_val); \ + } while (0) +#endif + +#endif /* unaligned.h */ diff --git a/0.153/src/unstrip.c b/0.153/src/unstrip.c new file mode 100644 index 00000000..f62010a3 --- /dev/null +++ b/0.153/src/unstrip.c @@ -0,0 +1,2328 @@ +/* Combine stripped files with separate symbols and debug information. + Copyright (C) 2007-2012 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 <sys/stat.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); +ARGP_PROGRAM_VERSION_HOOK_DEF = print_version; + +/* Bug report address. */ +ARGP_PROGRAM_BUG_ADDRESS_DEF = 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\ +"), "2012"); + 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; +} + +static void +update_shdr (Elf_Scn *outscn, GElf_Shdr *newshdr) +{ + ELF_CHECK (gelf_update_shdr (outscn, newshdr), + _("cannot update section header: %s")); +} + +/* 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; + + update_shdr (outscn, newshdr); +} + +/* 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]; + update_shdr (outscn, newshdr); + } + 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; + update_shdr (symscn, shdr); + + 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); +} + +static 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; + update_shdr (scn, shdr); + } + + 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_getshdrstrndx (unstripped, &unstripped_shstrndx) == 0, + _("cannot get section header string table section index: %s")); + + size_t stripped_shstrndx; + ELF_CHECK (elf_getshdrstrndx (stripped, &stripped_shstrndx) == 0, + _("cannot get section header string table section index: %s")); + + size_t unstripped_shnum; + ELF_CHECK (elf_getshdrnum (unstripped, &unstripped_shnum) == 0, + _("cannot get section count: %s")); + + size_t stripped_shnum; + ELF_CHECK (elf_getshdrnum (stripped, &stripped_shnum) == 0, + _("cannot get section count: %s")); + + if (unlikely (stripped_shnum > unstripped_shnum)) + error (EXIT_FAILURE, 0, _("\ +more sections in stripped file than debug file -- arguments reversed?")); + + /* 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 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; + 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_getshdrnum (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; + } + + update_shdr (sec->outscn, &shdr_mem); + + 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); + update_shdr (unstripped_symtab, shdr); + + 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")); + + /* We must make sure we have read in the data of all sections + beforehand and marked them to be written out. When we're + modifying the existing file in place, we might overwrite + this part of the file before we get to handling the section. */ + + ELF_CHECK (elf_flagdata (elf_getdata (scn, NULL), + ELF_C_SET, ELF_F_DIRTY), + _("cannot read section data: %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; + + update_shdr (scn, shdr); + + 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/0.153/src/versionhash.c b/0.153/src/versionhash.c new file mode 100644 index 00000000..6126eb95 --- /dev/null +++ b/0.153/src/versionhash.c @@ -0,0 +1,40 @@ +/* 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. + + 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 <string.h> + +#include <ld.h> + +/* Definitions for the symbol hash table. */ +#define TYPE struct id_list * +#define NAME ld_version_str_tab +#define COMPARE(a, b) strcmp ((a)->id, (b)->id) + +#include "../lib/dynamicsizehash.c" diff --git a/0.153/src/versionhash.h b/0.153/src/versionhash.h new file mode 100644 index 00000000..63ca1145 --- /dev/null +++ b/0.153/src/versionhash.h @@ -0,0 +1,34 @@ +/* Copyright (C) 2001, 2002 Red Hat, Inc. + This file is part of Red Hat elfutils. + Written by Ulrich Drepper <drepper@redhat.com>, 2001. + + 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 VERSIONHASH_H +#define VERSIONHASH_H 1 + +/* Definitions for the symbol hash table. */ +#define TYPE struct id_list * +#define NAME ld_version_str_tab +#include <dynamicsizehash.h> + +#endif /* versionhash.h */ diff --git a/0.153/src/xelf.h b/0.153/src/xelf.h new file mode 100644 index 00000000..21c7193a --- /dev/null +++ b/0.153/src/xelf.h @@ -0,0 +1,399 @@ +/* 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. + + 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 <libebl.h> + + +/* By default the linker is handling all architectures. But it can + be configured to be a native-only linker. */ +#if NATIVE_ELF == 32 +/* 32-bit only. */ +# define XElf_Ehdr Elf32_Ehdr +# define XElf_Shdr Elf32_Shdr +# define XElf_Off Elf32_Off +# define XElf_Addr Elf32_Addr +# define XElf_Half Elf32_Half +# define XElf_Word Elf32_Word +# define XElf_Xword Elf32_Word +# define XElf_Sxword Elf32_Sword +# define XElf_Versym Elf32_Versym +# define XElf_Sym Elf32_Sym +# define XElf_Rel Elf32_Rel +# define XElf_Rela Elf32_Rela + +# define XElf_Ehdr_vardef(name) Elf32_Ehdr *name +# define xelf_getehdr(elf, name) name = elf32_getehdr (elf) +# define xelf_getehdr_copy(elf, name, copy) \ + (copy) = *(name = elf32_getehdr (elf)) +# define xelf_newehdr(elf, klass) elf32_newehdr (elf) +# define xelf_update_ehdr(elf, ehdr) \ + /* nothing */ ((void) (elf), (void) (ehdr), 1) + +# define xelf_getclass(elf) ELFCLASS32 + +# define XElf_Phdr_vardef(name) Elf32_Phdr *name +# define xelf_newphdr(elf, n) elf32_newphdr (elf, n) +# define xelf_getphdr(elf, idx, name) name = elf32_getphdr (elf) + idx +# define xelf_getphdr_ptr(elf, idx, name) name = elf32_getphdr (elf) + idx +# define xelf_update_phdr(elf, idx, phdr) \ + /* nothing */ ((void) (elf), (void) (idx), (void) (phdr), 1) + +# define XElf_Shdr_vardef(name) Elf32_Shdr *name +# define xelf_getshdr(scn, name) name = elf32_getshdr (scn) +# define xelf_getshdr_copy(scn, name, copy) \ + (copy) = *(name = elf32_getshdr (scn)) +# define xelf_update_shdr(scn, shdr) \ + /* nothing */ ((void) (scn), (void) (shdr), 1) + +# define XElf_Sym_vardef(name) Elf32_Sym *name +# define xelf_getsym(data, idx, name) \ + name = &((Elf32_Sym *) (data)->d_buf)[idx] +# define xelf_getsym_ptr(data, idx, name) \ + name = &((Elf32_Sym *) (data)->d_buf)[idx] +# define xelf_getsymshndx(data, ndxdata, idx, name1, name2) \ + (name1 = &((Elf32_Sym *) ((data)->d_buf))[idx]); \ + name2 = (unlikely ((ndxdata) != NULL) \ + ? ((Elf32_Word *) ((ndxdata)->d_buf))[idx] : 0) +# define xelf_update_sym(data, idx, sym) \ + /* nothing */ ((void) (data), (void) (idx), (void) (sym), 1) +# define xelf_update_symshndx(data, ndxdata, idx, name1, name2, datachanged) \ + if (datachanged) \ + ((Elf32_Sym *) ((data)->d_buf))[idx] = *name1; \ + if (unlikely (ndxdata != NULL)) \ + ((Elf32_Word *) ((ndxdata)->d_buf))[idx] = name2 + +# define XElf_Versym_vardef(name) Elf32_Versym name +# define xelf_getversym_copy(data, idx, name) \ + (name = ((Elf32_Versym *) ((data)->d_buf))[idx], &name) + +# define XElf_Dyn_vardef(name) Elf32_Dyn *name +# define xelf_getdyn(data, idx, name) \ + name = &((Elf32_Dyn *) ((data)->d_buf))[idx] +# define xelf_getdyn_ptr(data, idx, name) \ + name = &((Elf32_Dyn *) ((data)->d_buf))[idx] +# define xelf_update_dyn(data, idx, name) \ + /* nothing */ ((void) (data), (void) (idx), (void) (name), 1) + +# define XElf_Rel_vardef(name) Elf32_Rel *name +# define xelf_getrel(data, idx, name) \ + name = &((Elf32_Rel *) ((data)->d_buf))[idx] +# define xelf_getrel_ptr(data, idx, name) \ + name = &((Elf32_Rel *) ((data)->d_buf))[idx] +# define xelf_update_rel(data, idx, name) \ + /* nothing */ ((void) (data), (void) (idx), (void) (name), 1) + +# define XElf_Rela_vardef(name) Elf32_Rela *name +# define xelf_getrela(data, idx, name) \ + name = &((Elf32_Rela *) ((data)->d_buf))[idx] +# define xelf_getrela_ptr(data, idx, name) \ + name = &((Elf32_Rela *) ((data)->d_buf))[idx] +# define xelf_update_rela(data, idx, name) \ + /* nothing */ ((void) (data), (void) (idx), (void) (name), 1) + +# define XElf_Verdef_vardef(name) Elf32_Verdef *name +# define xelf_getverdef(data, offset, name) \ + name = ((Elf32_Verdef *) ((char *) ((data)->d_buf) + (offset))) + +# define XElf_Verdaux_vardef(name) Elf32_Verdaux *name +# define xelf_getverdaux(data, offset, name) \ + name = ((Elf32_Verdaux *) ((char *) ((data)->d_buf) + (offset))) + +# define XELF_ST_TYPE(info) ELF32_ST_TYPE (info) +# define XELF_ST_BIND(info) ELF32_ST_BIND (info) +# define XELF_ST_INFO(bind, type) ELF32_ST_INFO (bind, type) +# define XELF_ST_VISIBILITY(info) ELF32_ST_VISIBILITY (info) + +# define XELF_R_SYM(info) ELF32_R_SYM (info) +# define XELF_R_TYPE(info) ELF32_R_TYPE (info) +# define XELF_R_INFO(sym, type) ELF32_R_INFO (sym, type) + +# define xelf_fsize(elf, type, cnt) \ + (__builtin_constant_p (type) \ + ? ({ size_t fsize; \ + switch (type) \ + { \ + case ELF_T_BYTE: fsize = 1; break; \ + case ELF_T_ADDR: fsize = sizeof (Elf32_Addr); break; \ + case ELF_T_DYN: fsize = sizeof (Elf32_Dyn); break; \ + case ELF_T_EHDR: fsize = sizeof (Elf32_Ehdr); break; \ + case ELF_T_HALF: fsize = sizeof (Elf32_Half); break; \ + case ELF_T_OFF: fsize = sizeof (Elf32_Off); break; \ + case ELF_T_PHDR: fsize = sizeof (Elf32_Phdr); break; \ + case ELF_T_RELA: fsize = sizeof (Elf32_Rela); break; \ + case ELF_T_REL: fsize = sizeof (Elf32_Rel); break; \ + case ELF_T_SHDR: fsize = sizeof (Elf32_Shdr); break; \ + case ELF_T_SWORD: fsize = sizeof (Elf32_Sword); break; \ + case ELF_T_SYM: fsize = sizeof (Elf32_Sym); break; \ + case ELF_T_WORD: fsize = sizeof (Elf32_Word); break; \ + case ELF_T_XWORD: fsize = sizeof (Elf32_Xword); break; \ + case ELF_T_SXWORD: fsize = sizeof (Elf32_Sxword); break; \ + case ELF_T_VDEF: fsize = sizeof (Elf32_Verdef); break; \ + case ELF_T_VDAUX: fsize = sizeof (Elf32_Verdaux); break; \ + case ELF_T_VNEED: fsize = sizeof (Elf32_Verneed); break; \ + case ELF_T_VNAUX: fsize = sizeof (Elf32_Vernaux); break; \ + case ELF_T_NHDR: fsize = sizeof (Elf32_Nhdr); break; \ + case ELF_T_SYMINFO: fsize = sizeof (Elf32_Syminfo); break; \ + case ELF_T_MOVE: fsize = sizeof (Elf32_Move); break; \ + default: fsize = 0; break; \ + } \ + fsize * (cnt); }) \ + : gelf_fsize (elf, type, cnt, EV_CURRENT)) +#elif NATIVE_ELF == 64 +/* 64-bit only. */ +# define XElf_Ehdr Elf64_Ehdr +# define XElf_Shdr Elf64_Shdr +# define XElf_Addr Elf64_Addr +# define XElf_Half Elf64_Half +# define XElf_Off Elf64_Off +# define XElf_Word Elf64_Word +# define XElf_Xword Elf64_Xword +# define XElf_Sxword Elf64_Sxword +# define XElf_Versym Elf64_Versym +# define XElf_Sym Elf64_Sym +# define XElf_Rel Elf64_Rel +# define XElf_Rela Elf64_Rela + +# define XElf_Ehdr_vardef(name) Elf64_Ehdr *name +# define xelf_getehdr(elf, name) name = elf64_getehdr (elf) +# define xelf_getehdr_copy(elf, name, copy) \ + (copy) = *(name = elf64_getehdr (elf)) +# define xelf_newehdr(elf, klass) elf64_newehdr (elf) +# define xelf_update_ehdr(elf, ehdr) \ + /* nothing */ ((void) (elf), (void) (ehdr), 1) + +# define xelf_getclass(elf) ELFCLASS32 + +# define XElf_Phdr_vardef(name) Elf64_Phdr *name +# define xelf_newphdr(elf, n) elf64_newphdr (elf, n) +# define xelf_getphdr(elf, idx, name) name = elf64_getphdr (elf) + idx +# define xelf_getphdr_ptr(elf, idx, name) name = elf64_getphdr (elf) + idx +# define xelf_update_phdr(elf, idx, phdr) \ + /* nothing */ ((void) (elf), (void) (idx), (void) (phdr), 1) + +# define XElf_Shdr_vardef(name) Elf64_Shdr *name +# define xelf_getshdr(scn, name) name = elf64_getshdr (scn) +# define xelf_getshdr_copy(scn, name, copy) \ + (copy) = *(name = elf64_getshdr (scn)) +# define xelf_update_shdr(scn, shdr) \ + /* nothing */ ((void) (scn), (void) (shdr), 1) + +# define XElf_Sym_vardef(name) Elf64_Sym *name +# define xelf_getsym(data, idx, name) \ + name = &((Elf64_Sym *) (data)->d_buf)[idx] +# define xelf_getsym_ptr(data, idx, name) \ + name = &((Elf64_Sym *) (data)->d_buf)[idx] +# define xelf_getsymshndx(data, ndxdata, idx, name1, name2) \ + (name1 = &((Elf64_Sym *) ((data)->d_buf))[idx]); \ + name2 = (unlikely ((ndxdata) != NULL) \ + ? ((Elf32_Word *) ((ndxdata)->d_buf))[idx] : 0) +# define xelf_update_sym(data, idx, sym) \ + /* nothing */ ((void) (data), (void) (idx), (void) (sym), 1) +# define xelf_update_symshndx(data, ndxdata, idx, name1, name2, datachanged) \ + if (datachanged) \ + ((Elf64_Sym *) ((data)->d_buf))[idx] = *name1; \ + if (ndxdata != NULL) \ + (((Elf32_Word *) ((ndxdata)->d_buf))[idx] = name2) + +# define XElf_Versym_vardef(name) Elf64_Versym name +# define xelf_getversym_copy(data, idx, name) \ + (name = ((Elf64_Versym *) ((data)->d_buf))[idx], (&name)) + +# define XElf_Dyn_vardef(name) Elf64_Dyn *name +# define xelf_getdyn(data, idx, name) \ + name = &((Elf64_Dyn *) ((data)->d_buf))[idx] +# define xelf_getdyn_ptr(data, idx, name) \ + name = &((Elf64_Dyn *) ((data)->d_buf))[idx] +# define xelf_update_dyn(data, idx, name) \ + /* nothing */ ((void) (data), (void) (idx), (void) (name), 1) + +# define XElf_Rel_vardef(name) Elf64_Rel *name +# define xelf_getrel(data, idx, name) \ + name = &((Elf64_Rel *) ((data)->d_buf))[idx] +# define xelf_getrel_ptr(data, idx, name) \ + name = &((Elf64_Rel *) ((data)->d_buf))[idx] +# define xelf_update_rel(data, idx, name) \ + /* nothing */ ((void) (data), (void) (idx), (void) (name), 1) + +# define XElf_Rela_vardef(name) Elf64_Rela *name +# define xelf_getrela(data, idx, name) \ + name = &((Elf64_Rela *) ((data)->d_buf))[idx] +# define xelf_getrela_ptr(data, idx, name) \ + name = &((Elf64_Rela *) ((data)->d_buf))[idx] +# define xelf_update_rela(data, idx, name) \ + /* nothing */ ((void) (data), (void) (idx), (void) (name), 1) + +# define XElf_Verdef_vardef(name) Elf64_Verdef *name +# define xelf_getverdef(data, offset, name) \ + name = ((Elf64_Verdef *) ((char *) ((data)->d_buf) + (offset))) + +# define XElf_Verdaux_vardef(name) Elf64_Verdaux *name +# define xelf_getverdaux(data, offset, name) \ + name = ((Elf64_Verdaux *) ((char *) ((data)->d_buf) + (offset))) + +# define XELF_ST_TYPE(info) ELF64_ST_TYPE (info) +# define XELF_ST_BIND(info) ELF64_ST_BIND (info) +# define XELF_ST_INFO(bind, type) ELF64_ST_INFO (bind, type) +# define XELF_ST_VISIBILITY(info) ELF64_ST_VISIBILITY (info) + +# define XELF_R_SYM(info) ELF64_R_SYM (info) +# define XELF_R_TYPE(info) ELF64_R_TYPE (info) +# define XELF_R_INFO(sym, type) ELF64_R_INFO (sym, type) + +# define xelf_fsize(elf, type, cnt) \ + (__builtin_constant_p (type) \ + ? ({ size_t fsize; \ + switch (type) \ + { \ + case ELF_T_BYTE: fsize = 1; break; \ + case ELF_T_ADDR: fsize = sizeof (Elf64_Addr); break; \ + case ELF_T_DYN: fsize = sizeof (Elf64_Dyn); break; \ + case ELF_T_EHDR: fsize = sizeof (Elf64_Ehdr); break; \ + case ELF_T_HALF: fsize = sizeof (Elf64_Half); break; \ + case ELF_T_OFF: fsize = sizeof (Elf64_Off); break; \ + case ELF_T_PHDR: fsize = sizeof (Elf64_Phdr); break; \ + case ELF_T_RELA: fsize = sizeof (Elf64_Rela); break; \ + case ELF_T_REL: fsize = sizeof (Elf64_Rel); break; \ + case ELF_T_SHDR: fsize = sizeof (Elf64_Shdr); break; \ + case ELF_T_SWORD: fsize = sizeof (Elf64_Sword); break; \ + case ELF_T_SYM: fsize = sizeof (Elf64_Sym); break; \ + case ELF_T_WORD: fsize = sizeof (Elf64_Word); break; \ + case ELF_T_XWORD: fsize = sizeof (Elf64_Xword); break; \ + case ELF_T_SXWORD: fsize = sizeof (Elf64_Sxword); break; \ + case ELF_T_VDEF: fsize = sizeof (Elf64_Verdef); break; \ + case ELF_T_VDAUX: fsize = sizeof (Elf64_Verdaux); break; \ + case ELF_T_VNEED: fsize = sizeof (Elf64_Verneed); break; \ + case ELF_T_VNAUX: fsize = sizeof (Elf64_Vernaux); break; \ + case ELF_T_NHDR: fsize = sizeof (Elf64_Nhdr); break; \ + case ELF_T_SYMINFO: fsize = sizeof (Elf64_Syminfo); break; \ + case ELF_T_MOVE: fsize = sizeof (Elf64_Move); break; \ + default: fsize = 0; break; \ + } \ + fsize * (cnt); }) \ + : gelf_fsize (elf, type, cnt, EV_CURRENT)) +#else +# include <gelf.h> + +/* Generic linker. */ +# define XElf_Ehdr GElf_Ehdr +# define XElf_Shdr GElf_Shdr +# define XElf_Addr GElf_Addr +# define XElf_Half GElf_Half +# define XElf_Off GElf_Off +# define XElf_Word GElf_Word +# define XElf_Xword GElf_Xword +# define XElf_Sxword GElf_Sxword +# define XElf_Versym GElf_Versym +# define XElf_Sym GElf_Sym +# define XElf_Rel GElf_Rel +# define XElf_Rela GElf_Rela + +# define XElf_Ehdr_vardef(name) GElf_Ehdr name##_mem; GElf_Ehdr *name +# define xelf_getehdr(elf, name) name = gelf_getehdr (elf, &name##_mem) +# define xelf_getehdr_copy(elf, name, copy) \ + name = gelf_getehdr (elf, &(copy)) +# define xelf_newehdr(elf, klass) gelf_newehdr (elf, klass) +# define xelf_update_ehdr(elf, ehdr) gelf_update_ehdr (elf, ehdr) + +# define xelf_getclass(elf) gelf_getclass (elf) + +# define XElf_Phdr_vardef(name) GElf_Phdr name##_mem; GElf_Phdr *name +# define xelf_newphdr(elf, n) gelf_newphdr (elf, n) +# define xelf_getphdr(elf, idx, name) \ + name = gelf_getphdr (elf, idx, &name##_mem) +# define xelf_getphdr_ptr(elf, idx, name) \ + name = &name##_mem +# define xelf_update_phdr(elf, idx, phdr) \ + gelf_update_phdr (elf, idx, phdr) + +# define XElf_Shdr_vardef(name) GElf_Shdr name##_mem; GElf_Shdr *name +# define xelf_getshdr(scn, name) name = gelf_getshdr (scn, &name##_mem) +# define xelf_getshdr_copy(scn, name, copy) \ + name = gelf_getshdr (scn, &(copy)) +# define xelf_update_shdr(scn, shdr) gelf_update_shdr (scn, shdr) + +# define XElf_Sym_vardef(name) GElf_Sym name##_mem; GElf_Sym *name +# define xelf_getsym(data, idx, name) \ + name = gelf_getsym (data, idx, &name##_mem) +# define xelf_getsym_ptr(data, idx, name) \ + name = &name##_mem +# define xelf_getsymshndx(data, ndxdata, idx, name1, name2) \ + name1 = gelf_getsymshndx (data, ndxdata, idx, &name1##_mem, &(name2)) +# define xelf_update_sym(data, idx, sym) gelf_update_sym (data, idx, sym) +# define xelf_update_symshndx(data, ndxdata, idx, name1, name2, datachanged) \ + gelf_update_symshndx (data, ndxdata, idx, name1, name2) + +# define XElf_Versym_vardef(name) GElf_Versym name +# define xelf_getversym_copy(data, idx, name) \ + gelf_getversym (data, idx, &name) + +# define XElf_Dyn_vardef(name) GElf_Dyn name##_mem; GElf_Dyn *name +# define xelf_getdyn(data, idx, name) \ + name = gelf_getdyn (data, idx, &name##_mem) +# define xelf_getdyn_ptr(data, idx, name) \ + name = &name##_mem +# define xelf_update_dyn(data, idx, name) \ + gelf_update_dyn (data, idx, name) + +# define XElf_Rel_vardef(name) GElf_Rel name##_mem; GElf_Rel *name +# define xelf_getrel(data, idx, name) \ + name = gelf_getrel (data, idx, &name##_mem) +# define xelf_getrel_ptr(data, idx, name) \ + name = &name##_mem +# define xelf_update_rel(data, idx, name) \ + gelf_update_rel (data, idx, name) + +# define XElf_Rela_vardef(name) GElf_Rela name##_mem; GElf_Rela *name +# define xelf_getrela(data, idx, name) \ + name = gelf_getrela (data, idx, &name##_mem) +# define xelf_getrela_ptr(data, idx, name) \ + name = &name##_mem +# define xelf_update_rela(data, idx, name) \ + gelf_update_rela (data, idx, name) + +# define XElf_Verdef_vardef(name) GElf_Verdef name##_mem; GElf_Verdef *name +# define xelf_getverdef(data, offset, name) \ + name = gelf_getverdef (data, offset, &name##_mem) + +# define XElf_Verdaux_vardef(name) GElf_Verdaux name##_mem; GElf_Verdaux *name +# define xelf_getverdaux(data, offset, name) \ + name = gelf_getverdaux (data, offset, &name##_mem) + +# define XELF_ST_TYPE(info) GELF_ST_TYPE (info) +# define XELF_ST_BIND(info) GELF_ST_BIND (info) +# define XELF_ST_INFO(bind, type) GELF_ST_INFO (bind, type) +# define XELF_ST_VISIBILITY(info) GELF_ST_VISIBILITY (info) + +# define XELF_R_SYM(info) GELF_R_SYM (info) +# define XELF_R_TYPE(info) GELF_R_TYPE (info) +# define XELF_R_INFO(sym, type) GELF_R_INFO (sym, type) + +# define xelf_fsize(elf, type, cnt) \ + gelf_fsize (elf, type, cnt, EV_CURRENT) +#endif diff --git a/0.153/src/ylwrap b/0.153/src/ylwrap new file mode 100644 index 00000000..b77a9ebb --- /dev/null +++ b/0.153/src/ylwrap @@ -0,0 +1,154 @@ +#! /bin/sh +# ylwrap - wrapper for lex/yacc invocations. +# Copyright 1996, 1997, 1998, 1999, 2001 Free Software Foundation, Inc. +# Written by Tom Tromey <tromey@cygnus.com>. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 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 +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Usage: +# ylwrap INPUT [OUTPUT DESIRED]... -- PROGRAM [ARGS]... +# * INPUT is the input file +# * OUTPUT is file PROG generates +# * DESIRED is file we actually want +# * PROGRAM is program to run +# * ARGS are passed to PROG +# Any number of OUTPUT,DESIRED pairs may be used. + +# The input. +input="$1" +shift +case "$input" in + [\\/]* | ?:[\\/]*) + # Absolute path; do nothing. + ;; + *) + # Relative path. Make it absolute. + input="`pwd`/$input" + ;; +esac + +pairlist= +while test "$#" -ne 0; do + if test "$1" = "--"; then + shift + break + fi + pairlist="$pairlist $1" + shift +done + +# The program to run. +prog="$1" +shift +# Make any relative path in $prog absolute. +case "$prog" in + [\\/]* | ?:[\\/]*) ;; + *[\\/]*) prog="`pwd`/$prog" ;; +esac + +# FIXME: add hostname here for parallel makes that run commands on +# other machines. But that might take us over the 14-char limit. +dirname=ylwrap$$ +trap "cd `pwd`; rm -rf $dirname > /dev/null 2>&1" 1 2 3 15 +mkdir $dirname || exit 1 + +cd $dirname + +$prog ${1+"$@"} "$input" +status=$? + +if test $status -eq 0; then + set X $pairlist + shift + first=yes + # Since DOS filename conventions don't allow two dots, + # the DOS version of Bison writes out y_tab.c instead of y.tab.c + # and y_tab.h instead of y.tab.h. Test to see if this is the case. + y_tab_nodot="no" + if test -f y_tab.c || test -f y_tab.h; then + y_tab_nodot="yes" + fi + + # The directory holding the input. + input_dir=`echo "$input" | sed -e 's,\([\\/]\)[^\\/]*$,\1,'` + # Quote $INPUT_DIR so we can use it in a regexp. + # FIXME: really we should care about more than `.' and `\'. + input_rx=`echo "$input_dir" | sed 's,\\\\,\\\\\\\\,g;s,\\.,\\\\.,g'` + + while test "$#" -ne 0; do + from="$1" + # Handle y_tab.c and y_tab.h output by DOS + if test $y_tab_nodot = "yes"; then + if test $from = "y.tab.c"; then + from="y_tab.c" + else + if test $from = "y.tab.h"; then + from="y_tab.h" + fi + fi + fi + if test -f "$from"; then + # If $2 is an absolute path name, then just use that, + # otherwise prepend `../'. + case "$2" in + [\\/]* | ?:[\\/]*) target="$2";; + *) target="../$2";; + esac + + # Edit out `#line' or `#' directives. + # + # We don't want the resulting debug information to point at + # an absolute srcdir; it is better for it to just mention the + # .y file with no path. + # + # We want to use the real output file name, not yy.lex.c for + # instance. + # + # We want the include guards to be adjusted too. + FROM=`echo "$from" | sed \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'\ + -e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g'` + TARGET=`echo "$2" | sed \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'\ + -e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g'` + sed "/^#/{s,$input_rx,,;s,$from,$2,;s,$FORM,$TO,;}" "$from" >"$target" || + status=$? + else + # A missing file is only an error for the first file. This + # is a blatant hack to let us support using "yacc -d". If -d + # is not specified, we don't want an error when the header + # file is "missing". + if test $first = yes; then + status=1 + fi + fi + shift + shift + first=no + done +else + status=$? +fi + +# Remove the directory. +cd .. +rm -rf $dirname + +exit $status |