From 43cd72b9aa3a95b0345587974c14b6d237f79a5f Mon Sep 17 00:00:00 2001 From: Bob Wilson Date: Fri, 8 Oct 2004 00:22:15 +0000 Subject: bfd ChangeLog * elf32-xtensa.c (elf32xtensa_size_opt): New global variable. (xtensa_default_isa): Global variable moved here from xtensa-isa.c. (elf32xtensa_no_literal_movement): New global variable. (elf_howto_table): Add entries for new relocations. (elf_xtensa_reloc_type_lookup): Handle new relocations. (property_table_compare): When addresses are equal, compare sizes and various property flags. (property_table_matches): New. (xtensa_read_table_entries): Extend to read new property tables. Add output_addr parameter to indicate that output addresses should be used. Use bfd_get_section_limit. (elf_xtensa_find_property_entry): New. (elf_xtensa_in_literal_pool): Use elf_xtensa_find_property_entry. (elf_xtensa_check_relocs): Handle new relocations. (elf_xtensa_do_reloc): Use bfd_get_section_limit. Handle new relocations. Use new xtensa-isa.h functions. (build_encoding_error_message): Remove encode_result parameter. Add new target_address parameter used to detect alignment errors. (elf_xtensa_relocate_section): Use bfd_get_section_limit. Clean up error handling. Use new is_operand_relocation function. (elf_xtensa_combine_prop_entries, elf_xtensa_merge_private_bfd_data): Use underbar macro for error messages. Formatting. (get_const16_opcode): New. (get_l32r_opcode): Add a separate flag for initialization. (get_relocation_opnd): Operand number is no longer explicit in the relocation. Change to decode the opcode and analyze its operands. (get_relocation_slot): New. (get_relocation_opcode): Add bfd parameter. Use bfd_get_section_limit. Use new xtensa-isa.h functions to handle multislot instructions. (is_l32r_relocation): Add bfd parameter. Use is_operand_relocation. (get_asm_simplify_size, is_alt_relocation, is_operand_relocation, insn_decode_len, insn_decode_opcode, check_branch_target_aligned, check_loop_aligned, check_branch_target_aligned_address, narrowable, widenable, narrow_instruction, widen_instruction, op_single_fmt_table, get_single_format, init_op_single_format_table): New. (elf_xtensa_do_asm_simplify): Add error_message parameter and use it instead of calling _bfd_error_handler. Use new xtensa-isa.h functions. (contract_asm_expansion): Add error_message parameter and pass it to elf_xtensa_do_asm_simplify. Replace use of R_XTENSA_OP0 relocation with R_XTENSA_SLOT0_OP. (get_expanded_call_opcode): Extend to handle either L32R or CONST16 instructions. Use new xtensa-isa.h functions. (r_reloc struct): Add new virtual_offset field. (r_reloc_init): Add contents and content_length parameters. Set virtual_offset field to zero. Add contents to target_offset field for partial_inplace relocations. (r_reloc_is_defined): Check for null. (print_r_reloc): New debug function. (source_reloc struct): Replace xtensa_operand field with pair of the opcode and the operand position. Add is_abs_literal field. (init_source_reloc): Specify operand by opcode/position pair. Set is_abs_literal field. (source_reloc_compare): When target_offsets are equal, compare other fields to make sorting predictable. (literal_value struct): Add is_abs_literal field. (value_map_hash_table struct): Add has_last_loc and last_loc fields. (init_literal_value): New. (is_same_value): Replace with ... (literal_value_equal): ... this function. Add comparisons of virtual_offset and is_abs_literal fields. (value_map_hash_table_init): Use bfd_zmalloc. Check for allocation failure. Initialize has_last_loc field. (value_map_hash_table_delete): New. (hash_literal_value): Rename to ... (literal_value_hash): ... this. Include is_abs_literal flag and virtual_offset field in the hash value. (get_cached_value): Rename to ... (value_map_get_cached_value): ... this. Update calls to literal_value_hash and literal_value_equal. (add_value_map): Check for allocation failure. Update calls to value_map_get_cached_value and literal_value_hash. (text_action, text_action_list, text_action_t): New types. (find_fill_action, compute_removed_action_diff, adjust_fill_action, text_action_add, text_action_add_literal, offset_with_removed_text, offset_with_removed_text_before_fill, find_insn_action, print_action_list, print_removed_literals): New. (offset_with_removed_literals): Delete. (xtensa_relax_info struct): Add is_relaxable_asm_section, action_list, fix_array, fix_array_count, allocated_relocs, relocs_count, and allocated_relocs_count fields. (init_xtensa_relax_info): Initialize new fields. (reloc_bfd_fix struct): Add new translated field. (reloc_bfd_fix_init): Add translated parameter and use it to set the translated field. (fix_compare, cache_fix_array): New. (get_bfd_fix): Remove fix_list parameter and get all relax_info for the section via get_xtensa_relax_info. Use cache_fix_array to set up sorted fix_array and use bsearch instead of linear search. (section_cache_t): New struct. (init_section_cache, section_cache_section, clear_section_cache): New. (ebb_t, ebb_target_enum, proposed_action, ebb_constraint): New types. (init_ebb_constraint, free_ebb_constraint, init_ebb, extend_ebb_bounds, extend_ebb_bounds_forward, extend_ebb_bounds_backward, insn_block_decodable_len, ebb_propose_action, ebb_add_proposed_action): New. (retrieve_contents): Use bfd_get_section_limit. (elf_xtensa_relax_section): Add relocations_analyzed flag. Update call to compute_removed_literals. Free value_map_hash_table when no longer needed. (analyze_relocations): Check is_relaxable_asm_section flag. Call compute_text_actions for all sections. (find_relaxable_sections): Mark sections as relaxable if they contain ASM_EXPAND relocations that can be optimized. Adjust r_reloc_init call. Increment relax_info src_count field only for appropriate relocation types. Remove is_literal_section check. (collect_source_relocs): Use bfd_get_section_limit. Adjust calls to r_reloc_init and find_associated_l32r_irel. Check is_relaxable_asm_section flag. Handle L32R instructions with absolute literals. Pass is_abs_literal flag to init_source_reloc. (is_resolvable_asm_expansion): Use bfd_get_section_limit. Check for CONST16 instructions. Adjust calls to r_reloc_init and pcrel_reloc_fits. Handle weak symbols conservatively. (find_associated_l32r_irel): Add bfd parameter and pass it to is_l32r_relocation. (compute_text_actions, compute_ebb_proposed_actions, compute_ebb_actions, check_section_ebb_pcrels_fit, check_section_ebb_reduces, text_action_add_proposed, compute_fill_extra_space): New. (remove_literals): Replace with ... (compute_removed_literals): ... this function. Call init_section_cache. Use bfd_get_section_limit. Sort internal_relocs. Call xtensa_read_table_entries to get the property table. Skip relocations other than R_XTENSA_32 and R_XTENSA_PLT. Use new is_removable_literal, remove_dead_literal, and identify_literal_placement functions. (get_irel_at_offset): Rewrite to use bsearch on sorted relocations instead of linear search. (is_removable_literal, remove_dead_literal, identify_literal_placement): New. (relocations_reach): Update check for literal not referenced by any PC-relative relocations. Adjust call to pcrel_reloc_fits. (coalesce_shared_literal, move_shared_literal): New. (relax_section): Use bfd_get_section_limit. Call translate_section_fixes. Update calls to r_reloc_init and offset_with_removed_text. Check new is_relaxable_asm_section flag. Add call to pin_internal_relocs. Add special handling for R_XTENSA_ASM_SIMPLIFY and R_XTENSA_DIFF* relocs. Use virtual_offset info to calculate new addend_displacement variable. Replace code for deleting literals with more general code to perform the actions determined by the action_list for the section. (translate_section_fixes, translate_reloc_bfd_fix): New. (translate_reloc): Check new is_relaxable_asm_section flag. Call find_removed_literal only if is_operand_relocation. Update call to offset_with_removed_text. Use new target_offset and removed_bytes variables. (move_literal): New. (relax_property_section): Use bfd_get_section_limit. Set new is_full_prop_section flag and handle new property tables. Update calls to r_reloc_init and offset_with_removed_text. Check is_relaxable_asm_section flag. Handle expansion of zero-sized unreachable entries, with use of offset_with_removed_text_before_fill. For relocatable links, combine entries only for literal tables. (relax_section_symbols): Check is_relaxable_asm_section flag. Update calls to offset_with_removed_text. Translate st_size field for function symbols. (do_fix_for_relocatable_link): Change to return bfd_boolean to indicate failure. Add contents parameter. Update call to get_bfd_fix. Update call to r_reloc_init. Call _bfd_error_handler and return FALSE for R_XTENSA_ASM_EXPAND relocs. (do_fix_for_final_link): Add input_bfd and contents parameters. Update call to get_bfd_fix. Include offset from contents for partial_inplace relocations. (is_reloc_sym_weak): New. (pcrel_reloc_fits): Use new xtensa-isa.h functions. (prop_sec_len): New. (xtensa_is_property_section): Handle new property sections. (is_literal_section): Delete. (internal_reloc_compare): When r_offset matches, compare r_info and r_addend to make sorting predictable. (internal_reloc_matches): New. (xtensa_get_property_section_name): Handle new property sections. (xtensa_get_property_predef_flags): New. (xtensa_callback_required_dependence): Use bfd_get_section_limit. Update calls to xtensa_isa_init, is_l32r_relocation, and r_reloc_init. * xtensa-isa.c (xtensa_default_isa): Moved to elf32-xtensa.c. (xtisa_errno, xtisa_error_msg): New variables. (xtensa_isa_errno, xtensa_isa_error_msg): New. (xtensa_insnbuf_alloc): Add error handling. (xtensa_insnbuf_to_chars): Add num_chars parameter. Update to use xtensa_format_decode. Add error handling. (xtensa_insnbuf_from_chars): Add num_chars parameter. Decode the instruction length to find the number of bytes to copy. (xtensa_isa_init): Add error handling. Replace calls to xtensa_load_isa and xtensa_extend_isa with code to initialize lookup tables in the xtensa_modules structure. (xtensa_check_isa_config, xtensa_add_isa, xtensa_load_isa, xtensa_extend_isa): Delete. (xtensa_isa_free): Change to only free lookup tables. (opname_lookup_compare): Replace with ... (xtensa_isa_name_compare): ... this function. Use strcasecmp. (xtensa_insn_maxlength): Rename to ... (xtensa_isa_maxlength): ... this. (xtensa_insn_length): Delete. (xtensa_insn_length_from_first_byte): Replace with ... (xtensa_isa_length_from_chars): ... this function. (xtensa_num_opcodes): Rename to ... (xtensa_isa_num_opcodes): ... this. (xtensa_isa_num_pipe_stages, xtensa_isa_num_formats, xtensa_isa_num_regfiles, xtensa_isa_num_stages, xtensa_isa_num_sysregs, xtensa_isa_num_interfaces, xtensa_isa_num_funcUnits, xtensa_format_name, xtensa_format_lookup, xtensa_format_decode, xtensa_format_encode, xtensa_format_length, xtensa_format_num_slots, xtensa_format_slot_nop_opcode, xtensa_format_get_slot, xtensa_format_set_slot): New functions. (xtensa_opcode_lookup): Add error handling. (xtensa_decode_insn): Replace with ... (xtensa_opcode_decode): ... this function, with new format and slot parameters. Add error handling. (xtensa_encode_insn): Replace with ... (xtensa_opcode_encode): ... this function, which does the encoding via one of the entries in the "encode_fns" array. Add error handling. (xtensa_opcode_name): Add error handling. (xtensa_opcode_is_branch, xtensa_opcode_is_jump, xtensa_opcode_is_loop, xtensa_opcode_is_call): New. (xtensa_num_operands): Replace with ... (xtensa_opcode_num_operands): ... this function. Add error handling. (xtensa_opcode_num_stateOperands, xtensa_opcode_num_interfaceOperands, xtensa_opcode_num_funcUnit_uses, xtensa_opcode_funcUnit_use, xtensa_operand_name, xtensa_operand_is_visible): New. (xtensa_get_operand, xtensa_operand_kind): Delete. (xtensa_operand_inout): Add error handling and special-case for "sout" operands. (xtensa_operand_get_field, xtensa_operand_set_field): Rewritten to operate on one slot of an instruction. Added error handling. (xtensa_operand_encode): Handle default operands with no encoding functions. Check for success by comparing against decoded value. Add error handling. (xtensa_operand_decode): Handle default operands. Return decoded value through argument pointer. Add error handling. (xtensa_operand_is_register, xtensa_operand_regfile, xtensa_operand_num_regs, xtensa_operand_is_known_reg): New. (xtensa_operand_isPCRelative): Rename to ... (xtensa_operand_is_PCrelative): ... this. Add error handling. (xtensa_operand_do_reloc, xtensa_operand_undo_reloc): Return value through argument pointer. Add error handling. (xtensa_stateOperand_state, xtensa_stateOperand_inout, xtensa_interfaceOperand_interface, xtensa_regfile_lookup, xtensa_regfile_lookup_shortname, xtensa_regfile_name, xtensa_regfile_shortname, xtensa_regfile_view_parent, xtensa_regfile_num_bits, xtensa_regfile_num_entries, xtensa_state_lookup, xtensa_state_name, xtensa_state_num_bits, xtensa_state_is_exported, xtensa_sysreg_lookup, xtensa_sysreg_lookup_name, xtensa_sysreg_name, xtensa_sysreg_number, xtensa_sysreg_is_user, xtensa_interface_lookup, xtensa_interface_name, xtensa_interface_num_bits, xtensa_interface_inout, xtensa_interface_has_side_effect, xtensa_funcUnit_lookup, xtensa_funcUnit_name, xtensa_funcUnit_num_copies): New. * xtensa-modules.c: Rewrite to use new data structures. * reloc.c (BFD_RELOC_XTENSA_DIFF8, BFD_RELOC_XTENSA_DIFF16, BFD_RELOC_XTENSA_DIFF32, BFD_RELOC_XTENSA_SLOT0_OP, BFD_RELOC_XTENSA_SLOT1_OP, BFD_RELOC_XTENSA_SLOT2_OP, BFD_RELOC_XTENSA_SLOT3_OP, BFD_RELOC_XTENSA_SLOT4_OP, BFD_RELOC_XTENSA_SLOT5_OP, BFD_RELOC_XTENSA_SLOT6_OP, BFD_RELOC_XTENSA_SLOT7_OP, BFD_RELOC_XTENSA_SLOT8_OP, BFD_RELOC_XTENSA_SLOT9_OP, BFD_RELOC_XTENSA_SLOT10_OP, BFD_RELOC_XTENSA_SLOT11_OP, BFD_RELOC_XTENSA_SLOT12_OP, BFD_RELOC_XTENSA_SLOT13_OP, BFD_RELOC_XTENSA_SLOT14_OP, BFD_RELOC_XTENSA_SLOT0_ALT, BFD_RELOC_XTENSA_SLOT1_ALT, BFD_RELOC_XTENSA_SLOT2_ALT, BFD_RELOC_XTENSA_SLOT3_ALT, BFD_RELOC_XTENSA_SLOT4_ALT, BFD_RELOC_XTENSA_SLOT5_ALT, BFD_RELOC_XTENSA_SLOT6_ALT, BFD_RELOC_XTENSA_SLOT7_ALT, BFD_RELOC_XTENSA_SLOT8_ALT, BFD_RELOC_XTENSA_SLOT9_ALT, BFD_RELOC_XTENSA_SLOT10_ALT, BFD_RELOC_XTENSA_SLOT11_ALT, BFD_RELOC_XTENSA_SLOT12_ALT, BFD_RELOC_XTENSA_SLOT13_ALT, BFD_RELOC_XTENSA_SLOT14_ALT): Add new relocations. * Makefile.am (xtensa-isa.lo, xtensa-modules.lo): Update dependencies. * Makefile.in: Regenerate. * bfd-in2.h: Likewise. * libbfd.h: Likewise. gas ChangeLog * config/tc-xtensa.c (absolute_literals_supported): New global flag. (UNREACHABLE_MAX_WIDTH): Define. (XTENSA_FETCH_WIDTH): Delete. (cur_vinsn, xtensa_fetch_width, xt_saved_debug_type, past_xtensa_end, prefer_const16, prefer_l32r): New global variables. (LIT4_SECTION_NAME): Define. (lit4_state struct): Add lit4_seg_name and lit4_seg fields. (XTENSA_PROP_*, GET_XTENSA_PROP_*, SET_XTENSA_PROP_*): Define. (frag_flags struct): New. (xtensa_block_info struct): Move from tc-xtensa.h. Add flags field. (subseg_map struct): Add cur_total_freq and cur_target_freq fields. (bitfield, bit_is_set, set_bit, clear_bit): Define. (MAX_FORMATS): Define. (op_placement_info struct, op_placement_table): New. (O_pltrel, O_hi16, O_lo16): Define. (directiveE enum): Rename directive_generics to directive_transform. Delete directive_relax. Add directive_schedule, directive_absolute_literals, and directive_last_directive. (directive_info): Rename "generics" to "transform". Delete "relax". Add "schedule" and "absolute-literals". (directive_state): Adjust entries to match changes in directive_info. (xtensa_relax_statesE, RELAX_IMMED_MAXSTEPS): Move to tc-xtensa.h. (xtensa_const16_opcode, xtensa_movi_opcode, xtensa_movi_n_opcode, xtensa_l32r_opcode, xtensa_nop_opcode, xtensa_rsr_lcount_opcode): New. (xtensa_j_opcode, xtensa_rsr_opcode): Delete. (align_only_targets, software_a0_b_retw_interlock, software_avoid_b_j_loop_end, maybe_has_b_j_loop_end, software_avoid_short_loop, software_avoid_close_loop_end, software_avoid_all_short_loops, specific_opcode): Delete. (warn_unaligned_branch_targets): New. (workaround_a0_b_retw, workaround_b_j_loop_end, workaround_short_loop, workaround_close_loop_end, workaround_all_short_loops): Default FALSE. (option_[no_]link_relax, option_[no_]transform, option_[no_]absolute_literals, option_warn_unaligned_targets, option_prefer_l32r, option_prefer_const16, option_target_hardware): New enum values. (option_[no_]align_only_targets, option_literal_section_name, option_text_section_name, option_data_section_name, option_bss_section_name, option_eb, option_el): Delete. (md_longopts): Add entries for: [no-]transform, [no-]absolute-literals, warn-unaligned-targets, prefer-l32r, prefer-const16, [no-]link-relax, and target-hardware. Delete entries for [no-]target-align-only, literal-section-name, text-section-name, data-section-name, and bss-section-name. (md_parse_option): Handle new options and remove old ones. Accept but ignore [no-]density options. Warn for [no-]generics and [no-]relax and treat them as [no-]transform. (md_show_usage): Add new options and remove old ones. (xtensa_setup_hw_workarounds): New. (md_pseudo_table): Change "word" entry to use xtensa_elf_cons. Add "long", "short", "loc" and "frequency" entries. (use_generics): Rename to ... (use_transform): ... this function. Add past_xtensa_end check. (use_longcalls): Add past_xtensa_end check. (code_density_available, can_relax): Delete. (do_align_targets): New. (get_directive): Accept dashes in directive names. Warn about [no-]generics and [no-]relax directives and treat them as [no-]transform. (xtensa_begin_directive): Call md_flush_pending_output only for some directives. Check for directives inside instruction bundles. Warn about deprecated ".begin literal" usage. Warn and ignore [no-]density directives. Handle new directives. Check generating_literals flag for literal_prefix. (xtensa_end_directive): Check for directives inside instruction bundles. Warn and ignore [no-]density directives. Handle new directives. Call xtensa_set_frag_assembly_state. (xtensa_loc_directive_seen, xtensa_dwarf2_directive_loc, xtensa_dwarf2_emit_insn): New. (xtensa_literal_position): Call md_flush_pending_output. Do not check use_literal_section flag. (xtensa_literal_pseudo): Call md_flush_pending_output. Handle absolute literals. Use xtensa_elf_cons to parse the expression. (xtensa_literal_prefix): Do not check use_literal_section. Support ".lit4" sections for absolute literals. Change prefix convention to replace ".text" (or ".t" in a linkonce section). No need to call subseg_set. (xtensa_frequency_pseudo, xtensa_elf_cons, xtensa_elf_suffix): New. (expression_end): Handle closing braces and colons. (PLT_SUFFIX, plt_suffix): Delete. (expression_maybe_register): Use new xtensa-isa.h functions. Use xtensa_elf_suffix instead of checking for plt suffix, and handle O_lo16 and O_hi16 expressions as well. (tokenize_arguments): Handle closing braces and colons. (parse_arguments): Use new xtensa-isa.h functions. Handle "invisible" operands and paired register syntax. (get_invisible_operands): New. (xg_translate_sysreg_op): Handle new Xtensa LX RSR/WSR/XSR syntax. Use new xtensa-isa.h functions. (xtensa_translate_old_userreg_ops, xtensa_translate_zero_immed): New. (xg_translate_idioms): Check if inside bundle. Use use_transform. Handle new Xtensa LX RSR/WSR/XSR syntax. Remove code to widen density instructions. Use xtensa_translate_zero_immed. (operand_is_immed, operand_is_pcrel_label): Delete. (get_relaxable_immed): Use new xtensa-isa.h functions. (get_opcode_from_buf): Add slot parameter. Use new xtensa-isa.h functions. (xtensa_print_insn_table, print_vliw_insn): New. (is_direct_call_opcode): Use new xtensa-isa.h functions. (is_call_opcode, is_loop_opcode, is_conditional_branch_opcode, is_branch_or_jump_opcode): Delete. (is_movi_opcode, decode_reloc, encode_reloc, encode_alt_reloc): New. (opnum_to_reloc, reloc_to_opnum): Delete. (xtensa_insnbuf_set_operand, xtensa_insnbuf_get_operand): Use new xtensa-isa.h functions. Operate on one slot of an instruction. (xtensa_insnbuf_set_immediate_field, is_negatable_branch, xg_get_insn_size): Delete. (xg_get_build_instr_size): Use xg_get_single_size. (xg_is_narrow_insn, xg_is_single_relaxable_insn): Update calls to xg_build_widen_table. Use xg_get_single_size. (xg_get_max_narrow_insn_size): Delete. (xg_get_max_insn_widen_size, xg_get_max_insn_widen_literal_size, xg_is_relaxable_insn): Update calls to xg_build_widen_table. Use xg_get_single_size. (xg_build_to_insn): Record the loc field. Handle OP_OPERAND_HI16U and OP_OPERAND_LOW16U. Check xg_valid_literal_expression. (xg_expand_to_stack, xg_expand_narrow): Update calls to xg_build_widen_table. Use xg_get_single_size. (xg_immeds_fit): Use new xtensa-isa.h functions. Update call to xg_check_operand. (xg_symbolic_immeds_fit): Likewise. Also handle O_lo16 and O_hi16, and treat weak symbols conservatively. (xg_check_operand): Use new xtensa-isa.h functions. (is_dnrange): Delete. (xg_assembly_relax): Inline previous calls to tinsn_copy. (xg_finish_frag): Specify separate relax states for the frag and slot0. (is_branch_jmp_to_next, xg_add_branch_and_loop_targets): Use new xtensa-isa.h functions. (xg_instruction_matches_option_term, xg_instruction_matches_or_options, xg_instruction_matches_options): New. (xg_instruction_matches_rule): Handle O_register expressions. Call xg_instruction_matches_options. (transition_rule_cmp): New. (xg_instruction_match): Update call to xg_build_simplify_table. (xg_build_token_insn): Record loc fields. (xg_simplify_insn): Check is_specific_opcode field and density_supported flag. (xg_expand_assembly_insn): Skip checking code_density_available. Use new xtensa-isa.h functions. Call use_transform instead of can_relax. (xg_assemble_literal): Add error handling for O_big. Call record_alignment. Handle O_pltrel. (xg_valid_literal_expression): New. (xg_assemble_literal_space): Add slot parameter. Remove call to set_expr_symbol_offset. Add call to record_alignment. Update call to xg_finish_frag. (xg_emit_insn): Delete. (xg_emit_insn_to_buf): Add format parameter. Update calls to xg_add_opcode_fix and xtensa_insnbuf_to_chars. (xg_add_opcode_fix): Change opcode parameter to tinsn and add format and slot parameters. Handle new "alternate" relocations for absolute literals and CONST16 instructions. Check for bad uses of O_lo16 and O_hi16. Use new xtensa-isa.h functions. (xg_assemble_tokens): Delete. (is_register_writer): Use new xtensa-isa.h functions. (is_bad_loopend_opcode): Check for xtensa_rsr_lcount_opcode instead of old-style RSR from LCOUNT. (next_frag_opcode): Delete. (next_frag_opcode_is_loop, next_frag_format_size, frag_format_size, update_next_frag_state): New. (update_next_frag_nop_state): Delete. (next_frag_pre_opcode_bytes): Use next_frag_opcode_is_loop. (xtensa_mark_literal_pool_location): Check use_literal_section flag and the state of the absolute-literals directive. Add calls to record_alignment and xtensa_set_frag_assembly_state. Call xtensa_switch_to_non_abs_literal_fragment instead of xtensa_switch_to_literal_fragment. (build_nop): New. (assemble_nop): Use build_nop. Update call to xtensa_insnbuf_to_chars. (get_expanded_loop_offset): Change check for undefined opcode to an assertion. (xtensa_set_frag_assembly_state, relaxable_section, xtensa_find_unmarked_state_frags, xtensa_find_unaligned_branch_targets, xtensa_find_unaligned_loops, xg_apply_tentative_value): New. (md_begin): Update call to xtensa_isa_init. Initialize linkrelax to 1. Set lit4_seg_name. Call xg_init_vinsn. Initialize new global opcodes. Call init_op_placement_info_table and xtensa_set_frag_assembly_state. (xtensa_init_fix_data): New. (xtensa_frob_label): Reset label symbol to the current frag. Check do_align_targets and generating_literals flag. Propagate frequency info to new alignment frag. Call xtensa_set_frag_assembly_state. (xtensa_unrecognized_line): New. (xtensa_flush_pending_output): Check if inside a bundle. Add a call to xtensa_set_frag_assembly_state. (error_reset_cur_vinsn): New. (md_assemble): Remove check for literal frag. Remove call to istack_init. Call use_transform instead of use_generics. Parse explicit instruction format specifiers. Move code for a0_b_retw_interlock workaround to xg_assemble_vliw_tokens. Call error_reset_cur_vinsn on errors. Add call to get_invisible_operands. Add dwarf2_where call. Remote automatic alignment for ENTRY instructions. Move call to xtensa_clear_insn_labels to the end. Rearrange to handle bundles. (xtensa_cons_fix_new): Delete. (xtensa_handle_align): New. (xtensa_frag_init): Call xtensa_set_frag_assembly_state. Remove assignment to is_no_density field. (md_pcrel_from): Use new xtensa-isa.h functions. Use decode_reloc instead of reloc_to_opnum. Handle "alternate" relocations. (xtensa_force_relocation, xtensa_check_inside_bundle, xtensa_elf_section_change_hook): New. (xtensa_symbol_new_hook): Delete. (xtensa_fix_adjustable): Check for difference of symbols with an offset. Check for external and weak symbols. (md_apply_fix3): Remove cases for XTENSA_OP{0,1,2} relocs. (md_estimate_size_before_relax): Return expansion for the first slot. (tc_gen_reloc): Handle difference of symbols by producing XTENSA_DIFF{8,16,32} relocs and by writing the value of the difference into the output. Handle new XTENSA_SLOT*_OP relocs by storing the tentative values into the output when linkrelax is set. (XTENSA_PROP_SEC_NAME): Define. (xtensa_post_relax_hook): Call xtensa_find_unmarked_state_frags. Create literal tables only if using literal sections. Create new property tables instead of old instruction tables. Check for unaligned branch targets and loops. (finish_vinsn, find_vinsn_conflicts, check_t1_t2_reads_and_writes, new_resource_table, clear_resource_table, resize_resource_table, resources_available, reserve_resources, release_resources, opcode_funcUnit_use_unit, opcode_funcUnit_use_stage, resources_conflict, xg_find_narrowest_format, relaxation_requirements, bundle_single_op, emit_single_op, xg_assemble_vliw_tokens): New. (xtensa_end): Call xtensa_flush_pending_output. Set past_xtensa_end flag. Update checks for workaround options. Call xtensa_mark_narrow_branches and xtensa_mark_zcl_first_insns. (xtensa_cleanup_align_frags): Add special case for branch targets. Check for and mark unreachable frags. (xtensa_fix_target_frags): Remove use of align_only_targets flag. Use RELAX_LOOP_END_BYTES in special case for negatable branch at the end of a zero-overhead loop body. (frag_can_negate_branch): Handle instructions with multiple slots. Use new xtensa-isa.h functions (xtensa_mark_narrow_branches, is_narrow_branch_guaranteed_in_range, xtensa_mark_zcl_first_insns): New. (xtensa_fix_a0_b_retw_frags, xtensa_fix_b_j_loop_end_frags): Error if transformations are disabled. (next_instrs_are_b_retw): Use new xtensa-isa.h functions. Handle multislot instructions. (xtensa_fix_close_loop_end_frags, xtensa_fix_short_loop_frags): Likewise. Also error if transformations are disabled. (unrelaxed_frag_max_size): New. (unrelaxed_frag_min_insn_count, unrelax_frag_has_b_j): Use new xtensa-isa.h functions. (xtensa_sanity_check, is_empty_loop, is_local_forward_loop): Use xtensa_opcode_is_loop instead of is_loop_opcode. (get_text_align_power): Replace as_fatal with assertion. (get_text_align_fill_size): Iterate instead of using modulus when use_nops is false. (get_noop_aligned_address): Assert that this is for a machine-dependent RELAX_ALIGN_NEXT_OPCODE frag. Use next_frag_opcode_is_loop, xg_get_single_size, and frag_format_size. (get_widen_aligned_address): Rename to ... (get_aligned_diff): ... this function. Add max_diff parameter. Remove handling of rs_align/rs_align_code frags. Use next_frag_format_size, get_text_align_power, get_text_align_fill_size, next_frag_opcode_is_loop, and xg_get_single_size. Compute max_diff and pass it back to caller. (xtensa_relax_frag): Use relax_frag_loop_align. Add code for new RELAX_SLOTS, RELAX_MAYBE_UNREACHABLE, RELAX_MAYBE_DESIRE_ALIGN, RELAX_FILL_NOP, and RELAX_UNREACHABLE frag types. Check relax_seen. (relax_frag_text_align): Rename to ... (relax_frag_loop_align): ... this function. Assume loops can only be in the first slot of an instruction. (relax_frag_add_nop): Use assemble_nop instead of constructing an OR instruction. Remove call to frag_wane. (relax_frag_narrow): Rename to ... (relax_frag_for_align): ... this function. Extend to handle RELAX_FILL_NOP and RELAX_UNREACHABLE, as well as RELAX_SLOTS with RELAX_NARROW for the first slot. (find_address_of_next_align_frag, bytes_to_stretch): New. (future_alignment_required): Use find_address_of_next_align_frag and bytes_to_stretch. Look ahead to subsequent frags to make smarter alignment decisions. (relax_frag_immed): Add format, slot, and estimate_only parameters. Check if transformations are enabled for b_j_loop_end workaround. Use new xtensa-isa.h functions and handle multislot instructions. Update call to xg_assembly_relax. (md_convert_frag): Handle new RELAX_SLOTS, RELAX_UNREACHABLE, RELAX_MAYBE_UNREACHABLE, RELAX_MAYBE_DESIRE_ALIGN, and RELAX_FILL_NOP frag types. (convert_frag_narrow): Add segP, format and slot parameters. Call convert_frag_immed for branch instructions. Adjust calls to tinsn_from_chars, tinsn_immed_from_frag, and xg_emit_insn_to_buf. Use xg_get_single_size and xg_get_single_format. (convert_frag_fill_nop): New. (convert_frag_immed): Add format and slot parameters. Handle multislot instructions and use new xtensa-isa.h functions. Update calls to tinsn_immed_from_frag and xg_assembly_relax. Check if transformations enabled for b_j_loop_end workaround. Use build_nop instead of assemble_nop. Check is_specific_opcode flag. Check for unreachable frags. Use xg_get_single_size. Handle O_pltrel. (fix_new_exp_in_seg): Remove check for old plt flag. (convert_frag_immed_finish_loop): Update calls to tinsn_from_chars and xtensa_insnbuf_to_chars. Call tinsn_immed_from_frag. Change check for loop opcode to an assertion. Mark all frags up to the end of the loop as not transformable. (get_last_insn_flags, set_last_insn_flags): Use get_subseg_info. (get_subseg_info): New. (xtensa_move_literals): Call xtensa_set_frag_assembly_state. Add null check for dest_seg. (xtensa_switch_to_literal_fragment): Rewrite to handle absolute literals and use xtensa_switch_to_non_abs_literal_fragment otherwise. (xtensa_switch_to_non_abs_literal_fragment): New. (cache_literal_section): Add is_code parameter and pass it through to retrieve_literal_seg. (retrieve_literal_seg): Add is_code parameter and use it to set the flags on the literal section. Handle case where head parameter is 0. (get_frag_is_no_transform, set_frag_is_specific_opcode, set_frag_is_no_transform): New. (xtensa_create_property_segments): Add end_property_function parameter and pass it through to add_xt_block_frags. Call bfd_get_section_flags and skip SEC_DEBUGGING and !SEC_ALLOC sections. (xtensa_create_xproperty_segments, section_has_xproperty): New. (add_xt_block_frags): Add end_property_function parameter and call it if it is non-zero. Call xtensa_frag_flags_init. (xtensa_frag_flags_is_empty, xtensa_frag_flags_init, get_frag_property_flags, frag_flags_to_number, xtensa_frag_flags_combinable, xt_block_aligned_size, xtensa_xt_block_combine, add_xt_prop_frags, init_op_placement_info_table, opcode_fits_format_slot, xg_get_single_size, xg_get_single_format): New. (istack_push): Inline call to tinsn_copy. (tinsn_copy): Delete. (tinsn_has_invalid_symbolic_operands): Handle O_hi16 and O_lo16 and CONST16 opcodes. Handle O_big, O_illegal, and O_absent. (tinsn_has_complex_operands): Handle O_hi16 and O_lo16. (tinsn_to_insnbuf): Use xg_get_single_format and new xtensa-isa.h functions. Handle invisible operands. (tinsn_to_slotbuf): New. (tinsn_check_arguments): Use new xtensa-isa.h functions. (tinsn_from_chars): Add slot parameter. Rewrite using xg_init_vinsn, vinsn_from_chars, and xg_free_vinsn. (tinsn_from_insnbuf): New. (tinsn_immed_from_frag): Add slot parameter and handle multislot instructions. Handle symbol differences. (get_num_stack_text_bytes): Use xg_get_single_size. (xg_init_vinsn, xg_clear_vinsn, vinsn_has_specific_opcodes, xg_free_vinsn, vinsn_to_insnbuf, vinsn_from_chars, expr_is_register, get_expr_register, set_expr_symbol_offset_diff): New. * config/tc-xtensa.h (MAX_SLOTS): Define. (xtensa_relax_statesE): Move from tc-xtensa.c. Add RELAX_CHECK_ALIGN_NEXT_OPCODE, RELAX_MAYBE_DESIRE_ALIGN, RELAX_SLOTS, RELAX_FILL_NOP, RELAX_UNREACHABLE, RELAX_MAYBE_UNREACHABLE, and RELAX_NONE types. (RELAX_IMMED_MAXSTEPS): Move from tc-xtensa.c. (xtensa_frag_type struct): Add is_assembly_state_set, use_absolute_literals, relax_seen, is_unreachable, is_specific_opcode, is_align, is_text_align, alignment, and is_first_loop_insn fields. Replace is_generics and is_relax fields by is_no_transform field. Delete is_text and is_longcalls fields. Change text_expansion and literal_expansion to arrays of MAX_SLOTS entries. Add arrays of per-slot information: literal_frags, slot_subtypes, slot_symbols, slot_sub_symbols, and slot_offsets. Add fr_prev field. (xtensa_fix_data struct): New. (xtensa_symfield_type struct): Delete plt field. (xtensa_block_info struct): Move definition to tc-xtensa.h. Add forward declaration here. (xt_section_type enum): Delete xt_insn_sec. Add xt_prop_sec. (XTENSA_SECTION_RENAME): Undefine. (TC_FIX_TYPE, TC_INIT_FIX_DATA, TC_FORCE_RELOCATION, NO_PSEUDO_DOT, tc_unrecognized_line, md_do_align, md_elf_section_change_hook, HANDLE_ALIGN, TC_LINKRELAX_FIXUP, SUB_SEGMENT_ALIGN): Define. (TC_CONS_FIX_NEW, tc_symbol_new_hook): Delete. (unit_num_copies_func, opcode_num_units_func, opcode_funcUnit_use_unit_func, opcode_funcUnit_use_stage_func): New. (resource_table struct): New. * config/xtensa-istack.h (MAX_INSN_ARGS): Increase from 6 to 10. (TInsn struct): Add keep_wide, loc, fixup, record_fix, subtype, literal_space, symbol, sub_symbol, offset, and literal_frag fields. (tinsn_copy): Delete prototype. (vliw_insn struct): New. * config/xtensa-relax.c (insn_pattern_struct): Add options field. (widen_spec_list): Add option conditions for density and boolean instructions. Add expansions using CONST16 and conditions for using CONST16 vs. L32R. Use new Xtensa LX RSR/WSR syntax. Add entries for predicted branches. (simplify_spec_list): Add option conditions for density instructions. Add entry for NOP instruction. (append_transition): Add cmp function pointer parameter and use it to insert the new entry in order. (operand_function_LOW16U, operand_function_HI16U): New. (xg_has_userdef_op_fn, xg_apply_userdef_op_fn): Handle OP_OPERAND_LOW16U and OP_OPERAND_HI16U. (enter_opname, split_string): Use xstrdup instead of strdup. (init_insn_pattern): Initialize new options field. (clear_req_or_option_list, clear_req_option_list, clone_req_or_option_list, clone_req_option_list, parse_option_cond): New. (parse_insn_pattern): Parse option conditions. (transition_applies): New. (build_transition): Use new xtensa-isa.h functions. Fix incorrectly swapped last arguments in calls to append_constant_value_condition. Call clone_req_option_list. Add warning about invalid opcode. Handle LOW16U and HI16U function names. (build_transition_table): Add cmp parameter and use it in calls to append_transition. Use new xtensa-isa.h functions. Check transition_applies before adding entries. (xg_build_widen_table, xg_build_simplify_table): Add cmp parameter and pass it through to build_transition_table. * config/xtensa-relax.h (ReqOrOptionList, ReqOrOption, ReqOptionList, ReqOption, transition_cmp_fn): New types. (OpType enum): Add OP_OPERAND_LOW16U and OP_OPERAND_HI16U. (transition_rule struct): Add options field. * doc/as.texinfo (Overview): Update Xtensa options. * doc/c-xtensa.texi (Xtensa Options): Delete --[no-]density, --[no-]relax, and --[no-]generics options. Update descriptions of --text-section-literals and --[no-]longcalls. Add --[no-]absolute-literals and --[no-]transform. (Xtensa Syntax): Add description of syntax for FLIX instructions. Remove use of "generic" and "specific" terminology for opcodes. (Xtensa Registers): Generalize the syntax description to include user-defined register files. (Xtensa Automatic Alignment): Update. (Xtensa Branch Relaxation): Mention limitation of unconditional jumps. (Xtensa Call Relaxation): Linker can now remove most of the overhead. (Xtensa Directives): Remove confusing rules about precedence. (Density Directive, Relax Directive): Delete. (Schedule Directive): New. (Generics Directive): Rename to ... (Transform Directive): ... this node. (Literal Directive): Update for absolute literals. Missing literal_position directive is now an error. (Literal Position Directive): Update for absolute literals. (Freeregs Directive): Delete. (Absolute Literals Directive): New. (Frame Directive): Minor editing. * Makefile.am (DEPTC_xtensa_elf, DEPOBJ_xtensa_elf, DEP_xtensa_elf): Update dependencies. * Makefile.in: Regenerate. gas/testsuite ChangeLog * gas/xtensa/all.exp: Adjust expected error message for j_too_far. Change entry_align test to expect an error. * gas/xtensa/entry_misalign2.s: Use no-transform instead of no-generics directives. include ChangeLog * xtensa-config.h (XSHAL_USE_ABSOLUTE_LITERALS, XCHAL_HAVE_PREDICTED_BRANCHES, XCHAL_INST_FETCH_WIDTH): New. (XCHAL_EXTRA_SA_SIZE, XCHAL_EXTRA_SA_ALIGN): Delete. * xtensa-isa-internal.h (ISA_INTERFACE_VERSION): Delete. (config_sturct struct): Delete. (XTENSA_OPERAND_IS_REGISTER, XTENSA_OPERAND_IS_PCRELATIVE, XTENSA_OPERAND_IS_INVISIBLE, XTENSA_OPERAND_IS_UNKNOWN, XTENSA_OPCODE_IS_BRANCH, XTENSA_OPCODE_IS_JUMP, XTENSA_OPCODE_IS_LOOP, XTENSA_OPCODE_IS_CALL, XTENSA_STATE_IS_EXPORTED, XTENSA_INTERFACE_HAS_SIDE_EFFECT): Define. (xtensa_format_encode_fn, xtensa_get_slot_fn, xtensa_set_slot_fn): New. (xtensa_insn_decode_fn): Rename to ... (xtensa_opcode_decode_fn): ... this. (xtensa_immed_decode_fn, xtensa_immed_encode_fn, xtensa_do_reloc_fn, xtensa_undo_reloc_fn): Update. (xtensa_encoding_template_fn): Delete. (xtensa_opcode_encode_fn, xtensa_format_decode_fn, xtensa_length_decode_fn): New. (xtensa_format_internal, xtensa_slot_internal): New types. (xtensa_operand_internal): Delete operand_kind, inout, isPCRelative, get_field, and set_field fields. Add name, field_id, regfile, num_regs, and flags fields. (xtensa_arg_internal): New type. (xtensa_iclass_internal): Change operands field to array of xtensa_arg_internal. Add num_stateOperands, stateOperands, num_interfaceOperands, and interfaceOperands fields. (xtensa_opcode_internal): Delete length, template, and iclass fields. Add iclass_id, flags, encode_fns, num_funcUnit_uses, and funcUnit_uses. (opname_lookup_entry): Delete. (xtensa_regfile_internal, xtensa_interface_internal, xtensa_funcUnit_internal, xtensa_state_internal, xtensa_sysreg_internal, xtensa_lookup_entry): New. (xtensa_isa_internal): Replace opcode_table field with opcodes field. Change type of opname_lookup_table. Delete num_modules, module_opcode_base, module_decode_fn, config, and has_density fields. Add num_formats, formats, format_decode_fn, length_decode_fn, num_slots, slots, num_fields, num_operands, operands, num_iclasses, iclasses, num_regfiles, regfiles, num_states, states, state_lookup_table, num_sysregs, sysregs, sysreg_lookup_table, max_sysreg_num, sysreg_table, num_interfaces, interfaces, interface_lookup_table, num_funcUnits, funcUnits and funcUnit_lookup_table fields. (xtensa_isa_module, xtensa_isa_modules): Delete. (xtensa_isa_name_compare): New prototype. (xtisa_errno, xtisa_error_msg): New. * xtensa-isa.h (XTENSA_ISA_VERSION): Define. (xtensa_isa): Change type. (xtensa_operand): Delete. (xtensa_format, xtensa_regfile, xtensa_state, xtensa_sysreg, xtensa_interface, xtensa_funcUnit, xtensa_isa_status, xtensa_funcUnit_use): New types. (libisa_module_specifier): Delete. (xtensa_isa_errno, xtensa_isa_error_msg): New prototypes. (xtensa_insnbuf_free, xtensa_insnbuf_to_chars, xtensa_insnbuf_from_chars): Update prototypes. (xtensa_load_isa, xtensa_extend_isa, xtensa_default_isa, xtensa_insn_maxlength, xtensa_num_opcodes, xtensa_decode_insn, xtensa_encode_insn, xtensa_insn_length, xtensa_insn_length_from_first_byte, xtensa_num_operands, xtensa_operand_kind, xtensa_encode_result, xtensa_operand_isPCRelative): Delete. (xtensa_isa_init, xtensa_operand_inout, xtensa_operand_get_field, xtensa_operand_set_field, xtensa_operand_encode, xtensa_operand_decode, xtensa_operand_do_reloc, xtensa_operand_undo_reloc): Update prototypes. (xtensa_isa_maxlength, xtensa_isa_length_from_chars, xtensa_isa_num_pipe_stages, xtensa_isa_num_formats, xtensa_isa_num_opcodes, xtensa_isa_num_regfiles, xtensa_isa_num_states, xtensa_isa_num_sysregs, xtensa_isa_num_interfaces, xtensa_isa_num_funcUnits, xtensa_format_name, xtensa_format_lookup, xtensa_format_decode, xtensa_format_encode, xtensa_format_length, xtensa_format_num_slots, xtensa_format_slot_nop_opcode, xtensa_format_get_slot, xtensa_format_set_slot, xtensa_opcode_decode, xtensa_opcode_encode, xtensa_opcode_is_branch, xtensa_opcode_is_jump, xtensa_opcode_is_loop, xtensa_opcode_is_call, xtensa_opcode_num_operands, xtensa_opcode_num_stateOperands, xtensa_opcode_num_interfaceOperands, xtensa_opcode_num_funcUnit_uses, xtensa_opcode_funcUnit_use, xtensa_operand_name, xtensa_operand_is_visible, xtensa_operand_is_register, xtensa_operand_regfile, xtensa_operand_num_regs, xtensa_operand_is_known_reg, xtensa_operand_is_PCrelative, xtensa_stateOperand_state, xtensa_stateOperand_inout, xtensa_interfaceOperand_interface, xtensa_regfile_lookup, xtensa_regfile_lookup_shortname, xtensa_regfile_name, xtensa_regfile_shortname, xtensa_regfile_view_parent, xtensa_regfile_num_bits, xtensa_regfile_num_entries, xtensa_state_lookup, xtensa_state_name, xtensa_state_num_bits, xtensa_state_is_exported, xtensa_sysreg_lookup, xtensa_sysreg_lookup_name, xtensa_sysreg_name, xtensa_sysreg_number, xtensa_sysreg_is_user, xtensa_interface_lookup, xtensa_interface_name, xtensa_interface_num_bits, xtensa_interface_inout, xtensa_interface_has_side_effect, xtensa_funcUnit_lookup, xtensa_funcUnit_name, xtensa_funcUnit_num_copies): New prototypes. * elf/xtensa.h (R_XTENSA_DIFF8, R_XTENSA_DIFF16, R_XTENSA_DIFF32, R_XTENSA_SLOT*_OP, R_XTENSA_SLOT*_ALT): New relocations. (XTENSA_PROP_SEC_NAME): Define. (property_table_entry): Add flags field. (XTENSA_PROP_*, GET_XTENSA_PROP_*, SET_XTENSA_PROP_*): Define. ld ChangeLog * ld.texinfo (Xtensa): Describe new linker relaxation to optimize assembler-generated longcall sequences. Describe new --size-opt option. * emulparams/elf32xtensa.sh (OTHER_SECTIONS): Add .xt.prop section. * emultempl/xtensaelf.em (remove_section, replace_insn_sec_with_prop_sec, replace_instruction_table_sections, elf_xtensa_after_open): New. (OPTION_OPT_SIZEOPT, OPTION_LITERAL_MOVEMENT, OPTION_NO_LITERAL_MOVEMENT): Define. (elf32xtensa_size_opt, elf32xtensa_no_literal_movement): New globals. (PARSE_AND_LIST_LONGOPTS): Add size-opt and [no-]literal-movement. (PARSE_AND_LIST_OPTIONS): Add --size-opt. (PARSE_AND_LIST_ARGS_CASES): Handle OPTION_OPT_SIZEOPT, OPTION_LITERAL_MOVEMENT, and OPTION_NO_LITERAL_MOVEMENT. (LDEMUL_AFTER_OPEN): Set to elf_xtensa_after_open. * scripttempl/elfxtensa.sc: Update with changes from elf.sc. * Makefile.am (eelf32xtensa.c): Update dependencies. * Makefile.in: Regenerate. ld/testsuite ChangeLog * ld-xtensa/lcall1.s: Use .literal directive. * ld-xtensa/lcall2.s: Align function entry. * ld-xtensa/coalesce2.s: Likewise. opcodes ChangeLog * xtensa-dis.c (state_names): Delete. (fetch_data): Use xtensa_isa_maxlength. (print_xtensa_operand): Replace operand parameter with opcode/operand pair. Remove print_sr_name parameter. Use new xtensa-isa.h functions. (print_insn_xtensa): Use new xtensa-isa.h functions. Handle multislot instruction bundles. Use xmalloc instead of malloc. --- bfd/elf32-xtensa.c | 7347 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 5800 insertions(+), 1547 deletions(-) (limited to 'bfd/elf32-xtensa.c') diff --git a/bfd/elf32-xtensa.c b/bfd/elf32-xtensa.c index 0755e09..09ef0e1 100644 --- a/bfd/elf32-xtensa.c +++ b/bfd/elf32-xtensa.c @@ -35,13 +35,16 @@ #include "xtensa-isa.h" #include "xtensa-config.h" +#define XTENSA_NO_NOP_REMOVAL 0 + /* Main interface functions. */ static void elf_xtensa_info_to_howto_rela PARAMS ((bfd *, arelent *, Elf_Internal_Rela *)); static reloc_howto_type *elf_xtensa_reloc_type_lookup PARAMS ((bfd *abfd, bfd_reloc_code_real_type code)); extern int xtensa_read_table_entries - PARAMS ((bfd *, asection *, property_table_entry **, const char *)); + PARAMS ((bfd *, asection *, property_table_entry **, const char *, + bfd_boolean)); static bfd_boolean elf_xtensa_check_relocs PARAMS ((bfd *, struct bfd_link_info *, asection *, const Elf_Internal_Rela *)); @@ -103,6 +106,10 @@ static bfd_boolean xtensa_elf_dynamic_symbol_p PARAMS ((struct elf_link_hash_entry *, struct bfd_link_info *)); static int property_table_compare PARAMS ((const PTR, const PTR)); +static int property_table_matches + PARAMS ((const PTR, const PTR)); +static property_table_entry *elf_xtensa_find_property_entry + PARAMS ((property_table_entry *, int, bfd_vma)); static bfd_boolean elf_xtensa_in_literal_pool PARAMS ((property_table_entry *, int, bfd_vma)); static void elf_xtensa_make_sym_local @@ -123,13 +130,13 @@ static bfd_reloc_status_type elf_xtensa_do_reloc static char * vsprint_msg VPARAMS ((const char *, const char *, int, ...)); static char *build_encoding_error_message - PARAMS ((xtensa_opcode, xtensa_encode_result)); + PARAMS ((xtensa_opcode, bfd_vma)); static bfd_reloc_status_type bfd_elf_xtensa_reloc PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **)); -static void do_fix_for_relocatable_link - PARAMS ((Elf_Internal_Rela *, bfd *, asection *)); +static bfd_boolean do_fix_for_relocatable_link + PARAMS ((Elf_Internal_Rela *, bfd *, asection *, bfd_byte *)); static void do_fix_for_final_link - PARAMS ((Elf_Internal_Rela *, asection *, bfd_vma *)); + PARAMS ((Elf_Internal_Rela *, bfd *, asection *, bfd_byte *, bfd_vma *)); static bfd_vma elf_xtensa_create_plt_entry PARAMS ((bfd *, bfd *, unsigned)); static int elf_xtensa_combine_prop_entries @@ -148,27 +155,47 @@ static bfd_boolean is_direct_call_opcode PARAMS ((xtensa_opcode)); static bfd_boolean is_windowed_call_opcode PARAMS ((xtensa_opcode)); +static xtensa_opcode get_const16_opcode + PARAMS ((void)); static xtensa_opcode get_l32r_opcode PARAMS ((void)); static bfd_vma l32r_offset PARAMS ((bfd_vma, bfd_vma)); static int get_relocation_opnd - PARAMS ((Elf_Internal_Rela *)); + PARAMS ((xtensa_opcode, int)); +static int get_relocation_slot + PARAMS ((int)); static xtensa_opcode get_relocation_opcode - PARAMS ((asection *, bfd_byte *, Elf_Internal_Rela *)); + PARAMS ((bfd *, asection *, bfd_byte *, Elf_Internal_Rela *)); static bfd_boolean is_l32r_relocation - PARAMS ((asection *, bfd_byte *, Elf_Internal_Rela *)); + PARAMS ((bfd *, asection *, bfd_byte *, Elf_Internal_Rela *)); +static bfd_boolean is_alt_relocation + PARAMS ((int)); +static bfd_boolean is_operand_relocation + PARAMS ((int)); +static bfd_size_type insn_decode_len + PARAMS ((bfd_byte *, bfd_size_type, bfd_size_type)); +static xtensa_opcode insn_decode_opcode + PARAMS ((bfd_byte *, bfd_size_type, bfd_size_type, int)); +static bfd_boolean check_branch_target_aligned + PARAMS ((bfd_byte *, bfd_size_type, bfd_vma, bfd_vma)); +static bfd_boolean check_loop_aligned + PARAMS ((bfd_byte *, bfd_size_type, bfd_vma, bfd_vma)); +static bfd_boolean check_branch_target_aligned_address + PARAMS ((bfd_vma, int)); +static bfd_size_type get_asm_simplify_size + PARAMS ((bfd_byte *, bfd_size_type, bfd_size_type)); /* Functions for link-time code simplifications. */ -static bfd_reloc_status_type elf_xtensa_do_asm_simplify - PARAMS ((bfd_byte *, bfd_vma, bfd_vma)); +static bfd_reloc_status_type elf_xtensa_do_asm_simplify + PARAMS ((bfd_byte *, bfd_vma, bfd_vma, char **)); static bfd_reloc_status_type contract_asm_expansion - PARAMS ((bfd_byte *, bfd_vma, Elf_Internal_Rela *)); + PARAMS ((bfd_byte *, bfd_vma, Elf_Internal_Rela *, char **)); static xtensa_opcode swap_callx_for_call_opcode PARAMS ((xtensa_opcode)); static xtensa_opcode get_expanded_call_opcode - PARAMS ((bfd_byte *, int)); + PARAMS ((bfd_byte *, int, bfd_boolean *)); /* Access to internal relocations, section contents and symbols. */ @@ -199,18 +226,22 @@ static struct elf_link_hash_entry *get_elf_r_symndx_hash_entry PARAMS ((bfd *, unsigned long)); static bfd_vma get_elf_r_symndx_offset PARAMS ((bfd *, unsigned long)); +static bfd_boolean is_reloc_sym_weak + PARAMS ((bfd *, Elf_Internal_Rela *)); static bfd_boolean pcrel_reloc_fits - PARAMS ((xtensa_operand, bfd_vma, bfd_vma)); + PARAMS ((xtensa_opcode, int, bfd_vma, bfd_vma)); static bfd_boolean xtensa_is_property_section PARAMS ((asection *)); static bfd_boolean xtensa_is_littable_section PARAMS ((asection *)); -static bfd_boolean is_literal_section - PARAMS ((asection *)); static int internal_reloc_compare PARAMS ((const PTR, const PTR)); +static int internal_reloc_matches + PARAMS ((const PTR, const PTR)); extern char *xtensa_get_property_section_name PARAMS ((asection *, const char *)); +static flagword xtensa_get_property_predef_flags + PARAMS ((asection *)); /* Other functions called directly by the linker. */ @@ -221,9 +252,20 @@ extern bfd_boolean xtensa_callback_required_dependence deps_callback_t, PTR)); +/* Globally visible flag for choosing size optimization of NOP removal + instead of branch-target-aware minimization for NOP removal. + When nonzero, narrow all instructions and remove all NOPs possible + around longcall expansions. */ +int elf32xtensa_size_opt; + + +/* The "new_section_hook" is used to set up a per-section + "xtensa_relax_info" data structure with additional information used + during relaxation. */ typedef struct xtensa_relax_info_struct xtensa_relax_info; + /* Total count of PLT relocations seen during check_relocs. The actual PLT code must be split into multiple sections and all the sections have to be created before size_dynamic_sections, @@ -234,12 +276,25 @@ typedef struct xtensa_relax_info_struct xtensa_relax_info; static int plt_reloc_count = 0; +/* The GNU tools do not easily allow extending interfaces to pass around + the pointer to the Xtensa ISA information, so instead we add a global + variable here (in BFD) that can be used by any of the tools that need + this information. */ + +xtensa_isa xtensa_default_isa; + + /* When this is true, relocations may have been modified to refer to symbols from other input files. The per-section list of "fix" records needs to be checked when resolving relocations. */ static bfd_boolean relaxing_section = FALSE; +/* When this is true, during final links, literals that cannot be + coalesced and their relocations may be moved to other sections. */ + +int elf32xtensa_no_literal_movement = 1; + static reloc_howto_type elf_howto_table[] = { @@ -296,10 +351,115 @@ static reloc_howto_type elf_howto_table[] = /* GNU extension to record C++ vtable member usage. */ HOWTO (R_XTENSA_GNU_VTENTRY, 0, 2, 0, FALSE, 0, complain_overflow_dont, _bfd_elf_rel_vtable_reloc_fn, "R_XTENSA_GNU_VTENTRY", - FALSE, 0x00000000, 0x00000000, FALSE) + FALSE, 0x00000000, 0x00000000, FALSE), + + /* Relocations for supporting difference of symbols. */ + HOWTO (R_XTENSA_DIFF8, 0, 0, 8, FALSE, 0, complain_overflow_bitfield, + bfd_elf_xtensa_reloc, "R_XTENSA_DIFF8", + FALSE, 0xffffffff, 0xffffffff, FALSE), + HOWTO (R_XTENSA_DIFF16, 0, 1, 16, FALSE, 0, complain_overflow_bitfield, + bfd_elf_xtensa_reloc, "R_XTENSA_DIFF16", + FALSE, 0xffffffff, 0xffffffff, FALSE), + HOWTO (R_XTENSA_DIFF32, 0, 2, 32, FALSE, 0, complain_overflow_bitfield, + bfd_elf_xtensa_reloc, "R_XTENSA_DIFF32", + FALSE, 0xffffffff, 0xffffffff, FALSE), + + /* General immediate operand relocations. */ + HOWTO (R_XTENSA_SLOT0_OP, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_SLOT0_OP", + FALSE, 0x00000000, 0x00000000, TRUE), + HOWTO (R_XTENSA_SLOT1_OP, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_SLOT1_OP", + FALSE, 0x00000000, 0x00000000, TRUE), + HOWTO (R_XTENSA_SLOT2_OP, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_SLOT2_OP", + FALSE, 0x00000000, 0x00000000, TRUE), + HOWTO (R_XTENSA_SLOT3_OP, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_SLOT3_OP", + FALSE, 0x00000000, 0x00000000, TRUE), + HOWTO (R_XTENSA_SLOT4_OP, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_SLOT4_OP", + FALSE, 0x00000000, 0x00000000, TRUE), + HOWTO (R_XTENSA_SLOT5_OP, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_SLOT5_OP", + FALSE, 0x00000000, 0x00000000, TRUE), + HOWTO (R_XTENSA_SLOT6_OP, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_SLOT6_OP", + FALSE, 0x00000000, 0x00000000, TRUE), + HOWTO (R_XTENSA_SLOT7_OP, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_SLOT7_OP", + FALSE, 0x00000000, 0x00000000, TRUE), + HOWTO (R_XTENSA_SLOT8_OP, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_SLOT8_OP", + FALSE, 0x00000000, 0x00000000, TRUE), + HOWTO (R_XTENSA_SLOT9_OP, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_SLOT9_OP", + FALSE, 0x00000000, 0x00000000, TRUE), + HOWTO (R_XTENSA_SLOT10_OP, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_SLOT10_OP", + FALSE, 0x00000000, 0x00000000, TRUE), + HOWTO (R_XTENSA_SLOT11_OP, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_SLOT11_OP", + FALSE, 0x00000000, 0x00000000, TRUE), + HOWTO (R_XTENSA_SLOT12_OP, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_SLOT12_OP", + FALSE, 0x00000000, 0x00000000, TRUE), + HOWTO (R_XTENSA_SLOT13_OP, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_SLOT13_OP", + FALSE, 0x00000000, 0x00000000, TRUE), + HOWTO (R_XTENSA_SLOT14_OP, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_SLOT14_OP", + FALSE, 0x00000000, 0x00000000, TRUE), + + /* "Alternate" relocations. The meaning of these is opcode-specific. */ + HOWTO (R_XTENSA_SLOT0_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_SLOT0_ALT", + FALSE, 0x00000000, 0x00000000, TRUE), + HOWTO (R_XTENSA_SLOT1_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_SLOT1_ALT", + FALSE, 0x00000000, 0x00000000, TRUE), + HOWTO (R_XTENSA_SLOT2_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_SLOT2_ALT", + FALSE, 0x00000000, 0x00000000, TRUE), + HOWTO (R_XTENSA_SLOT3_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_SLOT3_ALT", + FALSE, 0x00000000, 0x00000000, TRUE), + HOWTO (R_XTENSA_SLOT4_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_SLOT4_ALT", + FALSE, 0x00000000, 0x00000000, TRUE), + HOWTO (R_XTENSA_SLOT5_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_SLOT5_ALT", + FALSE, 0x00000000, 0x00000000, TRUE), + HOWTO (R_XTENSA_SLOT6_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_SLOT6_ALT", + FALSE, 0x00000000, 0x00000000, TRUE), + HOWTO (R_XTENSA_SLOT7_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_SLOT7_ALT", + FALSE, 0x00000000, 0x00000000, TRUE), + HOWTO (R_XTENSA_SLOT8_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_SLOT8_ALT", + FALSE, 0x00000000, 0x00000000, TRUE), + HOWTO (R_XTENSA_SLOT9_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_SLOT9_ALT", + FALSE, 0x00000000, 0x00000000, TRUE), + HOWTO (R_XTENSA_SLOT10_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_SLOT10_ALT", + FALSE, 0x00000000, 0x00000000, TRUE), + HOWTO (R_XTENSA_SLOT11_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_SLOT11_ALT", + FALSE, 0x00000000, 0x00000000, TRUE), + HOWTO (R_XTENSA_SLOT12_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_SLOT12_ALT", + FALSE, 0x00000000, 0x00000000, TRUE), + HOWTO (R_XTENSA_SLOT13_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_SLOT13_ALT", + FALSE, 0x00000000, 0x00000000, TRUE), + HOWTO (R_XTENSA_SLOT14_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_SLOT14_ALT", + FALSE, 0x00000000, 0x00000000, TRUE) }; -#ifdef DEBUG_GEN_RELOC +#if DEBUG_GEN_RELOC #define TRACE(str) \ fprintf (stderr, "Xtensa bfd reloc lookup %d (%s)\n", code, str) #else @@ -321,6 +481,18 @@ elf_xtensa_reloc_type_lookup (abfd, code) TRACE ("BFD_RELOC_32"); return &elf_howto_table[(unsigned) R_XTENSA_32 ]; + case BFD_RELOC_XTENSA_DIFF8: + TRACE ("BFD_RELOC_XTENSA_DIFF8"); + return &elf_howto_table[(unsigned) R_XTENSA_DIFF8 ]; + + case BFD_RELOC_XTENSA_DIFF16: + TRACE ("BFD_RELOC_XTENSA_DIFF16"); + return &elf_howto_table[(unsigned) R_XTENSA_DIFF16 ]; + + case BFD_RELOC_XTENSA_DIFF32: + TRACE ("BFD_RELOC_XTENSA_DIFF32"); + return &elf_howto_table[(unsigned) R_XTENSA_DIFF32 ]; + case BFD_RELOC_XTENSA_RTLD: TRACE ("BFD_RELOC_XTENSA_RTLD"); return &elf_howto_table[(unsigned) R_XTENSA_RTLD ]; @@ -370,6 +542,22 @@ elf_xtensa_reloc_type_lookup (abfd, code) return &elf_howto_table[(unsigned) R_XTENSA_GNU_VTENTRY ]; default: + if (code >= BFD_RELOC_XTENSA_SLOT0_OP + && code <= BFD_RELOC_XTENSA_SLOT14_OP) + { + unsigned n = (R_XTENSA_SLOT0_OP + + (code - BFD_RELOC_XTENSA_SLOT0_OP)); + return &elf_howto_table[n]; + } + + if (code >= BFD_RELOC_XTENSA_SLOT0_ALT + && code <= BFD_RELOC_XTENSA_SLOT14_ALT) + { + unsigned n = (R_XTENSA_SLOT0_ALT + + (code - BFD_RELOC_XTENSA_SLOT0_ALT)); + return &elf_howto_table[n]; + } + break; } @@ -468,8 +656,48 @@ property_table_compare (ap, bp) const property_table_entry *a = (const property_table_entry *) ap; const property_table_entry *b = (const property_table_entry *) bp; - /* Check if one entry overlaps with the other; this shouldn't happen - except when searching for a match. */ + if (a->address == b->address) + { + /* The only circumstance where two entries may legitimately have the + same address is when one of them is a zero-size placeholder to + mark a place where fill can be inserted. The zero-size entry should + come first. */ + BFD_ASSERT ((a->size == 0 || b->size == 0)); + + if (a->size != b->size) + return (a->size - b->size); + + if ((a->flags & XTENSA_PROP_ALIGN) != (b->flags & XTENSA_PROP_ALIGN)) + return ((b->flags & XTENSA_PROP_ALIGN) + - (a->flags & XTENSA_PROP_ALIGN)); + + if ((a->flags & XTENSA_PROP_ALIGN) + && (GET_XTENSA_PROP_ALIGNMENT (a->flags) + != GET_XTENSA_PROP_ALIGNMENT (b->flags))) + return (GET_XTENSA_PROP_ALIGNMENT (a->flags) + - GET_XTENSA_PROP_ALIGNMENT (b->flags)); + + if ((a->flags & XTENSA_PROP_UNREACHABLE) + != (b->flags & XTENSA_PROP_UNREACHABLE)) + return ((b->flags & XTENSA_PROP_UNREACHABLE) + - (a->flags & XTENSA_PROP_UNREACHABLE)); + + return (a->flags - b->flags); + } + + return (a->address - b->address); +} + + +static int +property_table_matches (ap, bp) + const PTR ap; + const PTR bp; +{ + const property_table_entry *a = (const property_table_entry *) ap; + const property_table_entry *b = (const property_table_entry *) bp; + + /* Check if one entry overlaps with the other. */ if ((b->address >= a->address && b->address < (a->address + a->size)) || (a->address >= b->address && a->address < (b->address + b->size))) return 0; @@ -478,16 +706,17 @@ property_table_compare (ap, bp) } -/* Get the literal table or instruction table entries for the given - section. Sets TABLE_P and returns the number of entries. On error, - returns a negative value. */ +/* Get the literal table or property table entries for the given + section. Sets TABLE_P and returns the number of entries. On + error, returns a negative value. */ int -xtensa_read_table_entries (abfd, section, table_p, sec_name) +xtensa_read_table_entries (abfd, section, table_p, sec_name, output_addr) bfd *abfd; asection *section; property_table_entry **table_p; const char *sec_name; + bfd_boolean output_addr; { asection *table_section; char *table_section_name; @@ -498,27 +727,44 @@ xtensa_read_table_entries (abfd, section, table_p, sec_name) bfd_size_type num_records; Elf_Internal_Rela *internal_relocs; bfd_vma section_addr; + flagword predef_flags; + bfd_size_type table_entry_size; + + if (!section + || !(section->flags & SEC_ALLOC) + || (section->flags & SEC_DEBUGGING)) + { + *table_p = NULL; + return 0; + } - table_section_name = - xtensa_get_property_section_name (section, sec_name); + table_section_name = xtensa_get_property_section_name (section, sec_name); table_section = bfd_get_section_by_name (abfd, table_section_name); free (table_section_name); - if (table_section != NULL) + if (table_section) table_size = table_section->size; - + if (table_size == 0) { *table_p = NULL; return 0; } - num_records = table_size / 8; + predef_flags = xtensa_get_property_predef_flags (table_section); + table_entry_size = 12; + if (predef_flags) + table_entry_size -= 4; + + num_records = table_size / table_entry_size; table_data = retrieve_contents (abfd, table_section, TRUE); blocks = (property_table_entry *) bfd_malloc (num_records * sizeof (property_table_entry)); block_count = 0; - - section_addr = section->output_section->vma + section->output_offset; + + if (output_addr) + section_addr = section->output_section->vma + section->output_offset; + else + section_addr = section->vma; /* If the file has not yet been relocated, process the relocations and sort out the table entries that apply to the specified section. */ @@ -541,11 +787,18 @@ xtensa_read_table_entries (abfd, section, table_p, sec_name) if (get_elf_r_symndx_section (abfd, r_symndx) == section) { bfd_vma sym_off = get_elf_r_symndx_offset (abfd, r_symndx); + BFD_ASSERT (sym_off == 0); + BFD_ASSERT (rel->r_addend == 0); blocks[block_count].address = (section_addr + sym_off + rel->r_addend + bfd_get_32 (abfd, table_data + rel->r_offset)); blocks[block_count].size = bfd_get_32 (abfd, table_data + rel->r_offset + 4); + if (predef_flags) + blocks[block_count].flags = predef_flags; + else + blocks[block_count].flags = + bfd_get_32 (abfd, table_data + rel->r_offset + 8); block_count++; } } @@ -555,17 +808,23 @@ xtensa_read_table_entries (abfd, section, table_p, sec_name) /* The file has already been relocated and the addresses are already in the table. */ bfd_vma off; + bfd_size_type section_limit = bfd_get_section_limit (abfd, section); - for (off = 0; off < table_size; off += 8) + for (off = 0; off < table_size; off += table_entry_size) { bfd_vma address = bfd_get_32 (abfd, table_data + off); if (address >= section_addr - && address < section_addr + section->size) + && address < section_addr + section_limit) { blocks[block_count].address = address; blocks[block_count].size = bfd_get_32 (abfd, table_data + off + 4); + if (predef_flags) + blocks[block_count].flags = predef_flags; + else + blocks[block_count].flags = + bfd_get_32 (abfd, table_data + off + 8); block_count++; } } @@ -574,34 +833,47 @@ xtensa_read_table_entries (abfd, section, table_p, sec_name) release_contents (table_section, table_data); release_internal_relocs (table_section, internal_relocs); - if (block_count > 0) + if (block_count > 0) { /* Now sort them into address order for easy reference. */ qsort (blocks, block_count, sizeof (property_table_entry), property_table_compare); } - + *table_p = blocks; return block_count; } -static bfd_boolean -elf_xtensa_in_literal_pool (lit_table, lit_table_size, addr) - property_table_entry *lit_table; - int lit_table_size; +property_table_entry * +elf_xtensa_find_property_entry (property_table, property_table_size, addr) + property_table_entry *property_table; + int property_table_size; bfd_vma addr; { property_table_entry entry; + property_table_entry *rv; - if (lit_table_size == 0) - return FALSE; + if (property_table_size == 0) + return NULL; entry.address = addr; entry.size = 1; + entry.flags = 0; - if (bsearch (&entry, lit_table, lit_table_size, - sizeof (property_table_entry), property_table_compare)) + rv = bsearch (&entry, property_table, property_table_size, + sizeof (property_table_entry), property_table_matches); + return rv; +} + + +static bfd_boolean +elf_xtensa_in_literal_pool (lit_table, lit_table_size, addr) + property_table_entry *lit_table; + int lit_table_size; + bfd_vma addr; +{ + if (elf_xtensa_find_property_entry (lit_table, lit_table_size, addr)) return TRUE; return FALSE; @@ -714,8 +986,8 @@ elf_xtensa_check_relocs (abfd, info, sec, relocs) size = symtab_hdr->sh_info; size *= sizeof (bfd_signed_vma); - local_got_refcounts = ((bfd_signed_vma *) - bfd_zalloc (abfd, size)); + local_got_refcounts = + (bfd_signed_vma *) bfd_zalloc (abfd, size); if (local_got_refcounts == NULL) return FALSE; elf_local_got_refcounts (abfd) = local_got_refcounts; @@ -727,8 +999,41 @@ elf_xtensa_check_relocs (abfd, info, sec, relocs) case R_XTENSA_OP0: case R_XTENSA_OP1: case R_XTENSA_OP2: + case R_XTENSA_SLOT0_OP: + case R_XTENSA_SLOT1_OP: + case R_XTENSA_SLOT2_OP: + case R_XTENSA_SLOT3_OP: + case R_XTENSA_SLOT4_OP: + case R_XTENSA_SLOT5_OP: + case R_XTENSA_SLOT6_OP: + case R_XTENSA_SLOT7_OP: + case R_XTENSA_SLOT8_OP: + case R_XTENSA_SLOT9_OP: + case R_XTENSA_SLOT10_OP: + case R_XTENSA_SLOT11_OP: + case R_XTENSA_SLOT12_OP: + case R_XTENSA_SLOT13_OP: + case R_XTENSA_SLOT14_OP: + case R_XTENSA_SLOT0_ALT: + case R_XTENSA_SLOT1_ALT: + case R_XTENSA_SLOT2_ALT: + case R_XTENSA_SLOT3_ALT: + case R_XTENSA_SLOT4_ALT: + case R_XTENSA_SLOT5_ALT: + case R_XTENSA_SLOT6_ALT: + case R_XTENSA_SLOT7_ALT: + case R_XTENSA_SLOT8_ALT: + case R_XTENSA_SLOT9_ALT: + case R_XTENSA_SLOT10_ALT: + case R_XTENSA_SLOT11_ALT: + case R_XTENSA_SLOT12_ALT: + case R_XTENSA_SLOT13_ALT: + case R_XTENSA_SLOT14_ALT: case R_XTENSA_ASM_EXPAND: case R_XTENSA_ASM_SIMPLIFY: + case R_XTENSA_DIFF8: + case R_XTENSA_DIFF16: + case R_XTENSA_DIFF32: /* Nothing to do for these. */ break; @@ -1417,18 +1722,30 @@ elf_xtensa_do_reloc (howto, abfd, input_section, relocation, bfd_boolean is_weak_undef; char **error_message; { + xtensa_format fmt; xtensa_opcode opcode; - xtensa_operand operand; - xtensa_encode_result encode_result; xtensa_isa isa = xtensa_default_isa; - xtensa_insnbuf ibuff; - bfd_vma self_address; - int opnd; + static xtensa_insnbuf ibuff = NULL; + static xtensa_insnbuf sbuff = NULL; + bfd_vma self_address = 0; + bfd_size_type input_size; + int opnd, slot; uint32 newval; + if (!ibuff) + { + ibuff = xtensa_insnbuf_alloc (isa); + sbuff = xtensa_insnbuf_alloc (isa); + } + + input_size = bfd_get_section_limit (abfd, input_section); + switch (howto->type) { case R_XTENSA_NONE: + case R_XTENSA_DIFF8: + case R_XTENSA_DIFF16: + case R_XTENSA_DIFF32: return bfd_reloc_ok; case R_XTENSA_ASM_EXPAND: @@ -1437,14 +1754,14 @@ elf_xtensa_do_reloc (howto, abfd, input_section, relocation, /* Check for windowed CALL across a 1GB boundary. */ xtensa_opcode opcode = get_expanded_call_opcode (contents + address, - input_section->size - address); + input_size - address, 0); if (is_windowed_call_opcode (opcode)) { self_address = (input_section->output_section->vma + input_section->output_offset + address); - if ((self_address >> CALL_SEGMENT_BITS) != - (relocation >> CALL_SEGMENT_BITS)) + if ((self_address >> CALL_SEGMENT_BITS) + != (relocation >> CALL_SEGMENT_BITS)) { *error_message = "windowed longcall crosses 1GB boundary; " "return may fail"; @@ -1455,16 +1772,17 @@ elf_xtensa_do_reloc (howto, abfd, input_section, relocation, return bfd_reloc_ok; case R_XTENSA_ASM_SIMPLIFY: - { + { /* Convert the L32R/CALLX to CALL. */ - bfd_reloc_status_type retval = - elf_xtensa_do_asm_simplify (contents, address, input_section->size); + bfd_reloc_status_type retval = + elf_xtensa_do_asm_simplify (contents, address, input_size, + error_message); if (retval != bfd_reloc_ok) - return retval; + return bfd_reloc_dangerous; /* The CALL needs to be relocated. Continue below for that part. */ address += 3; - howto = &elf_howto_table[(unsigned) R_XTENSA_OP0 ]; + howto = &elf_howto_table[(unsigned) R_XTENSA_SLOT0_OP ]; } break; @@ -1479,67 +1797,125 @@ elf_xtensa_do_reloc (howto, abfd, input_section, relocation, return bfd_reloc_ok; } - /* Read the instruction into a buffer and decode the opcode. */ - ibuff = xtensa_insnbuf_alloc (isa); - xtensa_insnbuf_from_chars (isa, ibuff, contents + address); - opcode = xtensa_decode_insn (isa, ibuff); - - /* Determine which operand is being relocated. */ - if (opcode == XTENSA_UNDEFINED) + /* Only instruction slot-specific relocations handled below.... */ + slot = get_relocation_slot (howto->type); + if (slot == XTENSA_UNDEFINED) { - *error_message = "cannot decode instruction"; + *error_message = "unexpected relocation"; return bfd_reloc_dangerous; } - if (howto->type < R_XTENSA_OP0 || howto->type > R_XTENSA_OP2) + /* Read the instruction into a buffer and decode the opcode. */ + xtensa_insnbuf_from_chars (isa, ibuff, contents + address, + input_size - address); + fmt = xtensa_format_decode (isa, ibuff); + if (fmt == XTENSA_UNDEFINED) { - *error_message = "unexpected relocation"; + *error_message = "cannot decode instruction format"; return bfd_reloc_dangerous; } - opnd = howto->type - R_XTENSA_OP0; + xtensa_format_get_slot (isa, fmt, slot, ibuff, sbuff); - /* Calculate the PC address for this instruction. */ - if (!howto->pc_relative) + opcode = xtensa_opcode_decode (isa, fmt, slot, sbuff); + if (opcode == XTENSA_UNDEFINED) { - *error_message = "expected PC-relative relocation"; + *error_message = "cannot decode instruction opcode"; return bfd_reloc_dangerous; } - self_address = (input_section->output_section->vma - + input_section->output_offset - + address); + /* Check for opcode-specific "alternate" relocations. */ + if (is_alt_relocation (howto->type)) + { + if (opcode == get_l32r_opcode ()) + { + /* Handle the special-case of non-PC-relative L32R instructions. */ + bfd *output_bfd = input_section->output_section->owner; + asection *lit4_sec = bfd_get_section_by_name (output_bfd, ".lit4"); + if (!lit4_sec) + { + *error_message = "relocation references missing .lit4 section"; + return bfd_reloc_dangerous; + } + self_address = ((lit4_sec->vma & ~0xfff) + + 0x40000 - 3); /* -3 to compensate for do_reloc */ + newval = relocation; + opnd = 1; + } + else if (opcode == get_const16_opcode ()) + { + /* ALT used for high 16 bits. */ + newval = relocation >> 16; + opnd = 1; + } + else + { + /* No other "alternate" relocations currently defined. */ + *error_message = "unexpected relocation"; + return bfd_reloc_dangerous; + } + } + else /* Not an "alternate" relocation.... */ + { + if (opcode == get_const16_opcode ()) + { + newval = relocation & 0xffff; + opnd = 1; + } + else + { + /* ...normal PC-relative relocation.... */ + + /* Determine which operand is being relocated. */ + opnd = get_relocation_opnd (opcode, howto->type); + if (opnd == XTENSA_UNDEFINED) + { + *error_message = "unexpected relocation"; + return bfd_reloc_dangerous; + } + + if (!howto->pc_relative) + { + *error_message = "expected PC-relative relocation"; + return bfd_reloc_dangerous; + } - /* Apply the relocation. */ - operand = xtensa_get_operand (isa, opcode, opnd); - newval = xtensa_operand_do_reloc (operand, relocation, self_address); - encode_result = xtensa_operand_encode (operand, &newval); - xtensa_operand_set_field (operand, ibuff, newval); + /* Calculate the PC address for this instruction. */ + self_address = (input_section->output_section->vma + + input_section->output_offset + + address); - /* Write the modified instruction back out of the buffer. */ - xtensa_insnbuf_to_chars (isa, ibuff, contents + address); - free (ibuff); + newval = relocation; + } + } - if (encode_result != xtensa_encode_result_ok) + /* Apply the relocation. */ + if (xtensa_operand_do_reloc (isa, opcode, opnd, &newval, self_address) + || xtensa_operand_encode (isa, opcode, opnd, &newval) + || xtensa_operand_set_field (isa, opcode, opnd, fmt, slot, + sbuff, newval)) { - char *message = build_encoding_error_message (opcode, encode_result); - *error_message = message; + *error_message = build_encoding_error_message (opcode, relocation); return bfd_reloc_dangerous; } - /* Final check for call. */ + /* Check for calls across 1GB boundaries. */ if (is_direct_call_opcode (opcode) && is_windowed_call_opcode (opcode)) { - if ((self_address >> CALL_SEGMENT_BITS) != - (relocation >> CALL_SEGMENT_BITS)) + if ((self_address >> CALL_SEGMENT_BITS) + != (relocation >> CALL_SEGMENT_BITS)) { - *error_message = "windowed call crosses 1GB boundary; " - "return may fail"; + *error_message = + "windowed call crosses 1GB boundary; return may fail"; return bfd_reloc_dangerous; } } + /* Write the modified instruction back out of the buffer. */ + xtensa_format_set_slot (isa, fmt, slot, ibuff, sbuff); + xtensa_insnbuf_to_chars (isa, ibuff, contents + address, + input_size - address); return bfd_reloc_ok; } @@ -1575,55 +1951,29 @@ vsprint_msg VPARAMS ((const char *origmsg, const char *fmt, int arglen, ...)) static char * -build_encoding_error_message (opcode, encode_result) +build_encoding_error_message (opcode, target_address) xtensa_opcode opcode; - xtensa_encode_result encode_result; + bfd_vma target_address; { const char *opname = xtensa_opcode_name (xtensa_default_isa, opcode); - const char *msg = NULL; + const char *msg; - switch (encode_result) + msg = "cannot encode"; + if (is_direct_call_opcode (opcode)) { - case xtensa_encode_result_ok: - msg = "unexpected valid encoding"; - break; - case xtensa_encode_result_align: - msg = "misaligned encoding"; - break; - case xtensa_encode_result_not_in_table: - msg = "encoding not in lookup table"; - break; - case xtensa_encode_result_too_low: - msg = "encoding out of range: too low"; - break; - case xtensa_encode_result_too_high: - msg = "encoding out of range: too high"; - break; - case xtensa_encode_result_not_ok: - default: - msg = "could not encode"; - break; + if ((target_address & 0x3) != 0) + msg = "misaligned call target"; + else + msg = "call target out of range"; } - - if (is_direct_call_opcode (opcode) - && (encode_result == xtensa_encode_result_too_low - || encode_result == xtensa_encode_result_too_high)) - - msg = "direct call out of range"; - - else if (opcode == get_l32r_opcode ()) + else if (opcode == get_l32r_opcode ()) { - /* L32Rs have the strange interaction with encoding in that they - have an unsigned immediate field, so libisa returns "too high" - when the absolute value is out of range and never returns "too - low", but I leave the "too low" message in case anything - changes. */ - if (encode_result == xtensa_encode_result_too_low) - msg = "literal out of range"; - else if (encode_result == xtensa_encode_result_too_high) - msg = "literal placed after use"; + if ((target_address & 0x3) != 0) + msg = "misaligned literal target"; + else + msg = "literal target out of range"; } - + return vsprint_msg (opname, ": %s", strlen (msg) + 2, msg); } @@ -1709,7 +2059,7 @@ bfd_elf_xtensa_reloc (abfd, reloc_entry, symbol, data, input_section, to the reloc entry rather than the raw data. Everything except relocations against section symbols has already been handled above. */ - + BFD_ASSERT (symbol->flags & BSF_SECTION_SYM); reloc_entry->addend = relocation; reloc_entry->address += input_section->output_offset; @@ -1816,9 +2166,10 @@ elf_xtensa_relocate_section (output_bfd, info, input_bfd, property_table_entry *lit_table = 0; int ltblsize = 0; char *error_message = NULL; + bfd_size_type input_size; - if (xtensa_default_isa == NULL) - xtensa_isa_init (); + if (!xtensa_default_isa) + xtensa_default_isa = xtensa_isa_init (0, 0); dynobj = elf_hash_table (info)->dynobj; symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; @@ -1835,11 +2186,14 @@ elf_xtensa_relocate_section (output_bfd, info, input_bfd, if (elf_hash_table (info)->dynamic_sections_created) { ltblsize = xtensa_read_table_entries (input_bfd, input_section, - &lit_table, XTENSA_LIT_SEC_NAME); + &lit_table, XTENSA_LIT_SEC_NAME, + TRUE); if (ltblsize < 0) return FALSE; } + input_size = bfd_get_section_limit (input_bfd, input_section); + rel = relocs; relend = relocs + input_section->reloc_count; for (; rel < relend; rel++) @@ -1872,7 +2226,7 @@ elf_xtensa_relocate_section (output_bfd, info, input_bfd, if (info->relocatable) { - /* This is a relocatable link. + /* This is a relocatable link. 1) If the reloc is against a section symbol, adjust according to the output section. 2) If there is a new target for this relocation, @@ -1883,15 +2237,26 @@ elf_xtensa_relocate_section (output_bfd, info, input_bfd, if (relaxing_section) { /* Check if this references a section in another input file. */ - do_fix_for_relocatable_link (rel, input_bfd, input_section); + if (!do_fix_for_relocatable_link (rel, input_bfd, input_section, + contents)) + return FALSE; r_type = ELF32_R_TYPE (rel->r_info); } - if (r_type == R_XTENSA_ASM_SIMPLIFY) + if (r_type == R_XTENSA_ASM_SIMPLIFY) { + char *error_message = NULL; /* Convert ASM_SIMPLIFY into the simpler relocation so that they never escape a relaxing link. */ - contract_asm_expansion (contents, input_section->size, rel); + r = contract_asm_expansion (contents, input_size, rel, + &error_message); + if (r != bfd_reloc_ok) + { + if (!((*info->callbacks->reloc_dangerous) + (info, error_message, input_bfd, input_section, + rel->r_offset))) + return FALSE; + } r_type = ELF32_R_TYPE (rel->r_info); } @@ -1979,7 +2344,8 @@ elf_xtensa_relocate_section (output_bfd, info, input_bfd, if (relaxing_section) { /* Check if this references a section in another input file. */ - do_fix_for_final_link (rel, input_section, &relocation); + do_fix_for_final_link (rel, input_bfd, input_section, contents, + &relocation); /* Update some already cached values. */ r_type = ELF32_R_TYPE (rel->r_info); @@ -1987,9 +2353,12 @@ elf_xtensa_relocate_section (output_bfd, info, input_bfd, } /* Sanity check the address. */ - if (rel->r_offset >= bfd_get_section_limit (input_bfd, input_section) + if (rel->r_offset >= input_size && ELF32_R_TYPE (rel->r_info) != R_XTENSA_NONE) { + (*_bfd_error_handler) + (_("%B(%A+0x%lx): relocation offset out of range (size=0x%x)"), + input_bfd, input_section, rel->r_offset, input_size); bfd_set_error (bfd_error_bad_value); return FALSE; } @@ -1999,9 +2368,7 @@ elf_xtensa_relocate_section (output_bfd, info, input_bfd, { bfd_boolean dynamic_symbol = xtensa_elf_dynamic_symbol_p (h, info); - if (dynamic_symbol && (r_type == R_XTENSA_OP0 - || r_type == R_XTENSA_OP1 - || r_type == R_XTENSA_OP2)) + if (dynamic_symbol && is_operand_relocation (r_type)) { /* This is an error. The symbol's real value won't be known until runtime and it's likely to be out of range anyway. */ @@ -2073,7 +2440,7 @@ elf_xtensa_relocate_section (output_bfd, info, input_bfd, /* Create the PLT entry and set the initial contents of the literal entry to the address of the PLT entry. */ - relocation = + relocation = elf_xtensa_create_plt_entry (dynobj, output_bfd, srel->reloc_count); } @@ -2114,12 +2481,12 @@ elf_xtensa_relocate_section (output_bfd, info, input_bfd, relocation + rel->r_addend, contents, rel->r_offset, is_weak_undef, &error_message); - + if (r != bfd_reloc_ok && !warned) { const char *name; - BFD_ASSERT (r == bfd_reloc_dangerous); + BFD_ASSERT (r == bfd_reloc_dangerous || r == bfd_reloc_other); BFD_ASSERT (error_message != (char *) NULL); if (h != NULL) @@ -2132,8 +2499,16 @@ elf_xtensa_relocate_section (output_bfd, info, input_bfd, name = bfd_section_name (input_bfd, sec); } if (name) - error_message = vsprint_msg (error_message, ": %s", - strlen (name), name); + { + if (rel->r_addend == 0) + error_message = vsprint_msg (error_message, ": %s", + strlen (name) + 2, name); + else + error_message = vsprint_msg (error_message, ": (%s+0x%x)", + strlen (name) + 22, + name, rel->r_addend); + } + if (!((*info->callbacks->reloc_dangerous) (info, error_message, input_bfd, input_section, rel->r_offset))) @@ -2205,7 +2580,7 @@ elf_xtensa_combine_prop_entries (output_bfd, sxtlit, sgotloc) if (sgotloc_size != section_size) { (*_bfd_error_handler) - ("internal inconsistency in size of .got.loc section"); + (_("internal inconsistency in size of .got.loc section")); return -1; } @@ -2519,10 +2894,10 @@ elf_xtensa_merge_private_bfd_data (ibfd, obfd) out_mach = out_flag & EF_XTENSA_MACH; in_mach = in_flag & EF_XTENSA_MACH; - if (out_mach != in_mach) + if (out_mach != in_mach) { (*_bfd_error_handler) - ("%B: incompatible machine type. Output is 0x%x. Input is 0x%x", + (_("%B: incompatible machine type. Output is 0x%x. Input is 0x%x"), ibfd, out_mach, in_mach); bfd_set_error (bfd_error_wrong_format); return FALSE; @@ -2532,22 +2907,20 @@ elf_xtensa_merge_private_bfd_data (ibfd, obfd) { elf_flags_init (obfd) = TRUE; elf_elfheader (obfd)->e_flags = in_flag; - + if (bfd_get_arch (obfd) == bfd_get_arch (ibfd) && bfd_get_arch_info (obfd)->the_default) return bfd_set_arch_mach (obfd, bfd_get_arch (ibfd), bfd_get_mach (ibfd)); - + return TRUE; } - if ((out_flag & EF_XTENSA_XT_INSN) != - (in_flag & EF_XTENSA_XT_INSN)) - elf_elfheader(obfd)->e_flags &= (~ EF_XTENSA_XT_INSN); + if ((out_flag & EF_XTENSA_XT_INSN) != (in_flag & EF_XTENSA_XT_INSN)) + elf_elfheader (obfd)->e_flags &= (~ EF_XTENSA_XT_INSN); - if ((out_flag & EF_XTENSA_XT_LIT) != - (in_flag & EF_XTENSA_XT_LIT)) - elf_elfheader(obfd)->e_flags &= (~ EF_XTENSA_XT_LIT); + if ((out_flag & EF_XTENSA_XT_LIT) != (in_flag & EF_XTENSA_XT_LIT)) + elf_elfheader (obfd)->e_flags &= (~ EF_XTENSA_XT_LIT); return TRUE; } @@ -2585,7 +2958,7 @@ elf_xtensa_print_private_bfd_data (abfd, farg) flagword e_flags = elf_elfheader (abfd)->e_flags; fprintf (f, "\nXtensa header:\n"); - if ((e_flags & EF_XTENSA_MACH) == E_XTENSA_MACH) + if ((e_flags & EF_XTENSA_MACH) == E_XTENSA_MACH) fprintf (f, "\nMachine = Base\n"); else fprintf (f, "\nMachine Id = 0x%x\n", e_flags & EF_XTENSA_MACH); @@ -2949,13 +3322,29 @@ is_windowed_call_opcode (opcode) static xtensa_opcode +get_const16_opcode (void) +{ + static bfd_boolean done_lookup = FALSE; + static xtensa_opcode const16_opcode = XTENSA_UNDEFINED; + if (!done_lookup) + { + const16_opcode = xtensa_opcode_lookup (xtensa_default_isa, "const16"); + done_lookup = TRUE; + } + return const16_opcode; +} + + +static xtensa_opcode get_l32r_opcode (void) { static xtensa_opcode l32r_opcode = XTENSA_UNDEFINED; - if (l32r_opcode == XTENSA_UNDEFINED) + static bfd_boolean done_lookup = FALSE; + + if (!done_lookup) { l32r_opcode = xtensa_opcode_lookup (xtensa_default_isa, "l32r"); - BFD_ASSERT (l32r_opcode != XTENSA_UNDEFINED); + done_lookup = TRUE; } return l32r_opcode; } @@ -2976,1494 +3365,4157 @@ l32r_offset (addr, pc) } -/* Get the operand number for a PC-relative relocation. - If the relocation is not a PC-relative one, return (-1). */ - static int -get_relocation_opnd (irel) - Elf_Internal_Rela *irel; +get_relocation_opnd (opcode, r_type) + xtensa_opcode opcode; + int r_type; { - if (ELF32_R_TYPE (irel->r_info) < R_XTENSA_OP0 - || ELF32_R_TYPE (irel->r_info) >= R_XTENSA_max) - return -1; - return ELF32_R_TYPE (irel->r_info) - R_XTENSA_OP0; + xtensa_isa isa = xtensa_default_isa; + int last_immed, last_opnd, opi; + + if (opcode == XTENSA_UNDEFINED) + return XTENSA_UNDEFINED; + + /* Find the last visible PC-relative immediate operand for the opcode. + If there are no PC-relative immediates, then choose the last visible + immediate; otherwise, fail and return XTENSA_UNDEFINED. */ + last_immed = XTENSA_UNDEFINED; + last_opnd = xtensa_opcode_num_operands (isa, opcode); + for (opi = last_opnd - 1; opi >= 0; opi--) + { + if (xtensa_operand_is_visible (isa, opcode, opi) == 0) + continue; + if (xtensa_operand_is_PCrelative (isa, opcode, opi) == 1) + { + last_immed = opi; + break; + } + if (last_immed == XTENSA_UNDEFINED + && xtensa_operand_is_register (isa, opcode, opi) == 0) + last_immed = opi; + } + if (last_immed < 0) + return XTENSA_UNDEFINED; + + /* If the operand number was specified in an old-style relocation, + check for consistency with the operand computed above. */ + if (r_type >= R_XTENSA_OP0 && r_type <= R_XTENSA_OP2) + { + int reloc_opnd = r_type - R_XTENSA_OP0; + if (reloc_opnd != last_immed) + return XTENSA_UNDEFINED; + } + + return last_immed; +} + + +int +get_relocation_slot (r_type) + int r_type; +{ + switch (r_type) + { + case R_XTENSA_OP0: + case R_XTENSA_OP1: + case R_XTENSA_OP2: + return 0; + + default: + if (r_type >= R_XTENSA_SLOT0_OP && r_type <= R_XTENSA_SLOT14_OP) + return r_type - R_XTENSA_SLOT0_OP; + if (r_type >= R_XTENSA_SLOT0_ALT && r_type <= R_XTENSA_SLOT14_ALT) + return r_type - R_XTENSA_SLOT0_ALT; + break; + } + + return XTENSA_UNDEFINED; } /* Get the opcode for a relocation. */ static xtensa_opcode -get_relocation_opcode (sec, contents, irel) +get_relocation_opcode (abfd, sec, contents, irel) + bfd *abfd; asection *sec; bfd_byte *contents; Elf_Internal_Rela *irel; { static xtensa_insnbuf ibuff = NULL; + static xtensa_insnbuf sbuff = NULL; xtensa_isa isa = xtensa_default_isa; - - if (get_relocation_opnd (irel) == -1) - return XTENSA_UNDEFINED; + xtensa_format fmt; + int slot; if (contents == NULL) return XTENSA_UNDEFINED; - if (sec->size <= irel->r_offset) + if (bfd_get_section_limit (abfd, sec) <= irel->r_offset) return XTENSA_UNDEFINED; if (ibuff == NULL) - ibuff = xtensa_insnbuf_alloc (isa); - + { + ibuff = xtensa_insnbuf_alloc (isa); + sbuff = xtensa_insnbuf_alloc (isa); + } + /* Decode the instruction. */ - xtensa_insnbuf_from_chars (isa, ibuff, &contents[irel->r_offset]); - return xtensa_decode_insn (isa, ibuff); + xtensa_insnbuf_from_chars (isa, ibuff, &contents[irel->r_offset], + sec->size - irel->r_offset); + fmt = xtensa_format_decode (isa, ibuff); + slot = get_relocation_slot (ELF32_R_TYPE (irel->r_info)); + if (slot == XTENSA_UNDEFINED) + return XTENSA_UNDEFINED; + xtensa_format_get_slot (isa, fmt, slot, ibuff, sbuff); + return xtensa_opcode_decode (isa, fmt, slot, sbuff); } bfd_boolean -is_l32r_relocation (sec, contents, irel) +is_l32r_relocation (abfd, sec, contents, irel) + bfd *abfd; asection *sec; bfd_byte *contents; Elf_Internal_Rela *irel; { xtensa_opcode opcode; - - if (ELF32_R_TYPE (irel->r_info) != R_XTENSA_OP1) + if (!is_operand_relocation (ELF32_R_TYPE (irel->r_info))) return FALSE; - - opcode = get_relocation_opcode (sec, contents, irel); + opcode = get_relocation_opcode (abfd, sec, contents, irel); return (opcode == get_l32r_opcode ()); } - -/* Code for transforming CALLs at link-time. */ -static bfd_reloc_status_type -elf_xtensa_do_asm_simplify (contents, address, content_length) +static bfd_size_type +get_asm_simplify_size (contents, content_len, offset) bfd_byte *contents; - bfd_vma address; - bfd_vma content_length; + bfd_size_type content_len; + bfd_size_type offset; { - static xtensa_insnbuf insnbuf = NULL; - xtensa_opcode opcode; - xtensa_operand operand; - xtensa_opcode direct_call_opcode; - xtensa_isa isa = xtensa_default_isa; - bfd_byte *chbuf = contents + address; - int opn; + bfd_size_type insnlen, size = 0; - if (insnbuf == NULL) - insnbuf = xtensa_insnbuf_alloc (isa); + /* Decode the size of the next two instructions. */ + insnlen = insn_decode_len (contents, content_len, offset); + if (insnlen == 0) + return 0; - if (content_length < address) - { - (*_bfd_error_handler) - ("Attempt to convert L32R/CALLX to CALL failed"); - return bfd_reloc_other; - } - - opcode = get_expanded_call_opcode (chbuf, content_length - address); - direct_call_opcode = swap_callx_for_call_opcode (opcode); - if (direct_call_opcode == XTENSA_UNDEFINED) - { - (*_bfd_error_handler) - ("Attempt to convert L32R/CALLX to CALL failed"); - return bfd_reloc_other; - } + size += insnlen; - /* Assemble a NOP ("or a1, a1, a1") into the 0 byte offset. */ - opcode = xtensa_opcode_lookup (isa, "or"); - xtensa_encode_insn (isa, opcode, insnbuf); - for (opn = 0; opn < 3; opn++) - { - operand = xtensa_get_operand (isa, opcode, opn); - xtensa_operand_set_field (operand, insnbuf, 1); - } - xtensa_insnbuf_to_chars (isa, insnbuf, chbuf); + insnlen = insn_decode_len (contents, content_len, offset + size); + if (insnlen == 0) + return 0; - /* Assemble a CALL ("callN 0") into the 3 byte offset. */ - xtensa_encode_insn (isa, direct_call_opcode, insnbuf); - operand = xtensa_get_operand (isa, opcode, 0); - xtensa_operand_set_field (operand, insnbuf, 0); - xtensa_insnbuf_to_chars (isa, insnbuf, chbuf + 3); + size += insnlen; + return size; +} - return bfd_reloc_ok; + +bfd_boolean +is_alt_relocation (r_type) + int r_type; +{ + return (r_type >= R_XTENSA_SLOT0_ALT + && r_type <= R_XTENSA_SLOT14_ALT); } -static bfd_reloc_status_type -contract_asm_expansion (contents, content_length, irel) - bfd_byte *contents; - bfd_vma content_length; - Elf_Internal_Rela *irel; +bfd_boolean +is_operand_relocation (r_type) + int r_type; { - bfd_reloc_status_type retval = - elf_xtensa_do_asm_simplify (contents, irel->r_offset, content_length); + switch (r_type) + { + case R_XTENSA_OP0: + case R_XTENSA_OP1: + case R_XTENSA_OP2: + return TRUE; - if (retval != bfd_reloc_ok) - return retval; + default: + if (r_type >= R_XTENSA_SLOT0_OP && r_type <= R_XTENSA_SLOT14_OP) + return TRUE; + if (r_type >= R_XTENSA_SLOT0_ALT && r_type <= R_XTENSA_SLOT14_ALT) + return TRUE; + break; + } - /* Update the irel->r_offset field so that the right immediate and - the right instruction are modified during the relocation. */ - irel->r_offset += 3; - irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_XTENSA_OP0); - return bfd_reloc_ok; + return FALSE; } + +#define MIN_INSN_LENGTH 2 -static xtensa_opcode -swap_callx_for_call_opcode (opcode) - xtensa_opcode opcode; +/* Return 0 if it fails to decode. */ + +bfd_size_type +insn_decode_len (contents, content_len, offset) + bfd_byte *contents; + bfd_size_type content_len; + bfd_size_type offset; { - init_call_opcodes (); + int insn_len; + xtensa_isa isa = xtensa_default_isa; + xtensa_format fmt; + static xtensa_insnbuf ibuff = NULL; - if (opcode == callx0_op) return call0_op; - if (opcode == callx4_op) return call4_op; - if (opcode == callx8_op) return call8_op; - if (opcode == callx12_op) return call12_op; + if (offset + MIN_INSN_LENGTH > content_len) + return 0; - /* Return XTENSA_UNDEFINED if the opcode is not an indirect call. */ - return XTENSA_UNDEFINED; + if (ibuff == NULL) + ibuff = xtensa_insnbuf_alloc (isa); + xtensa_insnbuf_from_chars (isa, ibuff, &contents[offset], + content_len - offset); + fmt = xtensa_format_decode (isa, ibuff); + if (fmt == XTENSA_UNDEFINED) + return 0; + insn_len = xtensa_format_length (isa, fmt); + if (insn_len == XTENSA_UNDEFINED) + return 0; + return insn_len; } -/* Check if "buf" is pointing to a "L32R aN; CALLX aN" sequence, and - if so, return the CALLX opcode. If not, return XTENSA_UNDEFINED. */ +/* Decode the opcode for a single slot instruction. + Return 0 if it fails to decode or the instruction is multi-slot. */ -#define L32R_TARGET_REG_OPERAND 0 -#define CALLN_SOURCE_OPERAND 0 - -static xtensa_opcode -get_expanded_call_opcode (buf, bufsize) - bfd_byte *buf; - int bufsize; +xtensa_opcode +insn_decode_opcode (contents, content_len, offset, slot) + bfd_byte *contents; + bfd_size_type content_len; + bfd_size_type offset; + int slot; { - static xtensa_insnbuf insnbuf = NULL; - xtensa_opcode opcode; - xtensa_operand operand; xtensa_isa isa = xtensa_default_isa; - uint32 regno, call_regno; - - /* Buffer must be at least 6 bytes. */ - if (bufsize < 6) + xtensa_format fmt; + static xtensa_insnbuf insnbuf = NULL; + static xtensa_insnbuf slotbuf = NULL; + + if (offset + MIN_INSN_LENGTH > content_len) return XTENSA_UNDEFINED; if (insnbuf == NULL) - insnbuf = xtensa_insnbuf_alloc (isa); - - xtensa_insnbuf_from_chars (isa, insnbuf, buf); - opcode = xtensa_decode_insn (isa, insnbuf); - - if (opcode != get_l32r_opcode ()) - return XTENSA_UNDEFINED; - - operand = xtensa_get_operand (isa, opcode, L32R_TARGET_REG_OPERAND); - regno = xtensa_operand_decode - (operand, xtensa_operand_get_field (operand, insnbuf)); - - /* Next instruction should be an CALLXn with operand 0 == regno. */ - xtensa_insnbuf_from_chars (isa, insnbuf, - buf + xtensa_insn_length (isa, opcode)); - opcode = xtensa_decode_insn (isa, insnbuf); - - if (!is_indirect_call_opcode (opcode)) + { + insnbuf = xtensa_insnbuf_alloc (isa); + slotbuf = xtensa_insnbuf_alloc (isa); + } + + xtensa_insnbuf_from_chars (isa, insnbuf, &contents[offset], + content_len - offset); + fmt = xtensa_format_decode (isa, insnbuf); + if (fmt == XTENSA_UNDEFINED) return XTENSA_UNDEFINED; - - operand = xtensa_get_operand (isa, opcode, CALLN_SOURCE_OPERAND); - call_regno = xtensa_operand_decode - (operand, xtensa_operand_get_field (operand, insnbuf)); - if (call_regno != regno) + + if (slot >= xtensa_format_num_slots (isa, fmt)) return XTENSA_UNDEFINED; - - return opcode; -} - -/* Data structures used during relaxation. */ + xtensa_format_get_slot (isa, fmt, slot, insnbuf, slotbuf); + return xtensa_opcode_decode (isa, fmt, slot, slotbuf); +} -/* r_reloc: relocation values. */ -/* Through the relaxation process, we need to keep track of the values - that will result from evaluating relocations. The standard ELF - relocation structure is not sufficient for this purpose because we're - operating on multiple input files at once, so we need to know which - input file a relocation refers to. The r_reloc structure thus - records both the input file (bfd) and ELF relocation. +/* The offset is the offset in the contents. + The address is the address of that offset. */ - For efficiency, an r_reloc also contains a "target_offset" field to - cache the target-section-relative offset value that is represented by - the relocation. */ +static bfd_boolean +check_branch_target_aligned (contents, content_length, offset, address) + bfd_byte *contents; + bfd_size_type content_length; + bfd_vma offset; + bfd_vma address; +{ + bfd_size_type insn_len = insn_decode_len (contents, content_length, offset); + if (insn_len == 0) + return FALSE; + return check_branch_target_aligned_address (address, insn_len); +} -typedef struct r_reloc_struct r_reloc; -struct r_reloc_struct +static bfd_boolean +check_loop_aligned (contents, content_length, offset, address) + bfd_byte *contents; + bfd_size_type content_length; + bfd_vma offset; + bfd_vma address; { - bfd *abfd; - Elf_Internal_Rela rela; - bfd_vma target_offset; -}; + bfd_size_type loop_len, insn_len; + xtensa_opcode opcode = + insn_decode_opcode (contents, content_length, offset, 0); + BFD_ASSERT (opcode != XTENSA_UNDEFINED); + if (opcode != XTENSA_UNDEFINED) + return FALSE; + BFD_ASSERT (xtensa_opcode_is_loop (xtensa_default_isa, opcode)); + if (!xtensa_opcode_is_loop (xtensa_default_isa, opcode)) + return FALSE; -static bfd_boolean r_reloc_is_const - PARAMS ((const r_reloc *)); -static void r_reloc_init - PARAMS ((r_reloc *, bfd *, Elf_Internal_Rela *)); -static bfd_vma r_reloc_get_target_offset - PARAMS ((const r_reloc *)); -static asection *r_reloc_get_section - PARAMS ((const r_reloc *)); -static bfd_boolean r_reloc_is_defined - PARAMS ((const r_reloc *)); -static struct elf_link_hash_entry *r_reloc_get_hash_entry - PARAMS ((const r_reloc *)); + loop_len = insn_decode_len (contents, content_length, offset); + BFD_ASSERT (loop_len != 0); + if (loop_len == 0) + return FALSE; + + insn_len = insn_decode_len (contents, content_length, offset + loop_len); + BFD_ASSERT (insn_len != 0); + if (insn_len == 0) + return FALSE; + return check_branch_target_aligned_address (address + loop_len, insn_len); +} -/* The r_reloc structure is included by value in literal_value, but not - every literal_value has an associated relocation -- some are simple - constants. In such cases, we set all the fields in the r_reloc - struct to zero. The r_reloc_is_const function should be used to - detect this case. */ static bfd_boolean -r_reloc_is_const (r_rel) - const r_reloc *r_rel; +check_branch_target_aligned_address (addr, len) + bfd_vma addr; + int len; { - return (r_rel->abfd == NULL); + if (len == 8) + return (addr % 8 == 0); + return ((addr >> 2) == ((addr + len - 1) >> 2)); } + +/* Instruction widening and narrowing. */ -static void -r_reloc_init (r_rel, abfd, irel) - r_reloc *r_rel; - bfd *abfd; - Elf_Internal_Rela *irel; -{ - if (irel != NULL) - { - r_rel->rela = *irel; - r_rel->abfd = abfd; - r_rel->target_offset = r_reloc_get_target_offset (r_rel); - } - else - memset (r_rel, 0, sizeof (r_reloc)); -} +static bfd_boolean narrow_instruction + PARAMS ((bfd_byte *, bfd_size_type, bfd_size_type, bfd_boolean)); +static bfd_boolean widen_instruction + PARAMS ((bfd_byte *, bfd_size_type, bfd_size_type, bfd_boolean)); +static xtensa_format get_single_format + PARAMS ((xtensa_opcode)); +static void init_op_single_format_table + PARAMS ((void)); -static bfd_vma -r_reloc_get_target_offset (r_rel) - const r_reloc *r_rel; +struct string_pair { - bfd_vma target_offset; - unsigned long r_symndx; + const char *wide; + const char *narrow; +}; - BFD_ASSERT (!r_reloc_is_const (r_rel)); - r_symndx = ELF32_R_SYM (r_rel->rela.r_info); - target_offset = get_elf_r_symndx_offset (r_rel->abfd, r_symndx); - return (target_offset + r_rel->rela.r_addend); -} +/* For the set of narrowable instructions we do NOT include the + narrowings beqz -> beqz.n or bnez -> bnez.n because of complexities + involved during linker relaxation that may require these to + re-expand in some conditions. Also, the narrowing "or" -> mov.n + requires special case code to ensure it only works when op1 == op2. */ -static struct elf_link_hash_entry * -r_reloc_get_hash_entry (r_rel) - const r_reloc *r_rel; +struct string_pair narrowable[] = { - unsigned long r_symndx = ELF32_R_SYM (r_rel->rela.r_info); - return get_elf_r_symndx_hash_entry (r_rel->abfd, r_symndx); -} - + { "add", "add.n" }, + { "addi", "addi.n" }, + { "addmi", "addi.n" }, + { "l32i", "l32i.n" }, + { "movi", "movi.n" }, + { "ret", "ret.n" }, + { "retw", "retw.n" }, + { "s32i", "s32i.n" }, + { "or", "mov.n" } /* special case only when op1 == op2 */ +}; -static asection * -r_reloc_get_section (r_rel) - const r_reloc *r_rel; +struct string_pair widenable[] = { - unsigned long r_symndx = ELF32_R_SYM (r_rel->rela.r_info); - return get_elf_r_symndx_section (r_rel->abfd, r_symndx); -} + { "add", "add.n" }, + { "addi", "addi.n" }, + { "addmi", "addi.n" }, + { "beqz", "beqz.n" }, + { "bnez", "bnez.n" }, + { "l32i", "l32i.n" }, + { "movi", "movi.n" }, + { "ret", "ret.n" }, + { "retw", "retw.n" }, + { "s32i", "s32i.n" }, + { "or", "mov.n" } /* special case only when op1 == op2 */ +}; +/* Attempt to narrow an instruction. Return true if the narrowing is + valid. If the do_it parameter is non-zero, then perform the action + in-place directly into the contents. Otherwise, do not modify the + contents. The set of valid narrowing are specified by a string table + but require some special case operand checks in some cases. */ + static bfd_boolean -r_reloc_is_defined (r_rel) - const r_reloc *r_rel; +narrow_instruction (contents, content_length, offset, do_it) + bfd_byte *contents; + bfd_size_type content_length; + bfd_size_type offset; + bfd_boolean do_it; { - asection *sec = r_reloc_get_section (r_rel); - if (sec == bfd_abs_section_ptr - || sec == bfd_com_section_ptr - || sec == bfd_und_section_ptr) - return FALSE; - return TRUE; -} + xtensa_opcode opcode; + bfd_size_type insn_len, opi; + xtensa_isa isa = xtensa_default_isa; + xtensa_format fmt, o_fmt; - -/* source_reloc: relocations that reference literal sections. */ + static xtensa_insnbuf insnbuf = NULL; + static xtensa_insnbuf slotbuf = NULL; + static xtensa_insnbuf o_insnbuf = NULL; + static xtensa_insnbuf o_slotbuf = NULL; -/* To determine whether literals can be coalesced, we need to first - record all the relocations that reference the literals. The - source_reloc structure below is used for this purpose. The - source_reloc entries are kept in a per-literal-section array, sorted - by offset within the literal section (i.e., target offset). + if (insnbuf == NULL) + { + insnbuf = xtensa_insnbuf_alloc (isa); + slotbuf = xtensa_insnbuf_alloc (isa); + o_insnbuf = xtensa_insnbuf_alloc (isa); + o_slotbuf = xtensa_insnbuf_alloc (isa); + } - The source_sec and r_rel.rela.r_offset fields identify the source of - the relocation. The r_rel field records the relocation value, i.e., - the offset of the literal being referenced. The opnd field is needed - to determine the range of the immediate field to which the relocation - applies, so we can determine whether another literal with the same - value is within range. The is_null field is true when the relocation - is being removed (e.g., when an L32R is being removed due to a CALLX - that is converted to a direct CALL). */ + BFD_ASSERT (offset < content_length); -typedef struct source_reloc_struct source_reloc; + if (content_length < 2) + return FALSE; -struct source_reloc_struct -{ - asection *source_sec; - r_reloc r_rel; - xtensa_operand opnd; - bfd_boolean is_null; -}; + /* We will hand-code a few of these for a little while. + These have all been specified in the assembler aleady. */ + xtensa_insnbuf_from_chars (isa, insnbuf, &contents[offset], + content_length - offset); + fmt = xtensa_format_decode (isa, insnbuf); + if (xtensa_format_num_slots (isa, fmt) != 1) + return FALSE; + if (xtensa_format_get_slot (isa, fmt, 0, insnbuf, slotbuf) != 0) + return FALSE; -static void init_source_reloc - PARAMS ((source_reloc *, asection *, const r_reloc *, xtensa_operand)); -static source_reloc *find_source_reloc - PARAMS ((source_reloc *, int, asection *, Elf_Internal_Rela *)); -static int source_reloc_compare - PARAMS ((const PTR, const PTR)); + opcode = xtensa_opcode_decode (isa, fmt, 0, slotbuf); + if (opcode == XTENSA_UNDEFINED) + return FALSE; + insn_len = xtensa_format_length (isa, fmt); + if (insn_len > content_length) + return FALSE; + for (opi = 0; opi < (sizeof (narrowable)/sizeof (struct string_pair)); ++opi) + { + bfd_boolean is_or = (strcmp ("or", narrowable[opi].wide) == 0); -static void -init_source_reloc (reloc, source_sec, r_rel, opnd) - source_reloc *reloc; - asection *source_sec; - const r_reloc *r_rel; - xtensa_operand opnd; -{ - reloc->source_sec = source_sec; - reloc->r_rel = *r_rel; - reloc->opnd = opnd; - reloc->is_null = FALSE; -} + if (opcode == xtensa_opcode_lookup (isa, narrowable[opi].wide)) + { + uint32 value, newval; + int i, operand_count, o_operand_count; + xtensa_opcode o_opcode; + /* Address does not matter in this case. We might need to + fix it to handle branches/jumps. */ + bfd_vma self_address = 0; -/* Find the source_reloc for a particular source offset and relocation - type. Note that the array is sorted by _target_ offset, so this is - just a linear search. */ + o_opcode = xtensa_opcode_lookup (isa, narrowable[opi].narrow); + if (o_opcode == XTENSA_UNDEFINED) + return FALSE; + o_fmt = get_single_format (o_opcode); + if (o_fmt == XTENSA_UNDEFINED) + return FALSE; -static source_reloc * -find_source_reloc (src_relocs, src_count, sec, irel) - source_reloc *src_relocs; - int src_count; - asection *sec; - Elf_Internal_Rela *irel; -{ - int i; + if (xtensa_format_length (isa, fmt) != 3 + || xtensa_format_length (isa, o_fmt) != 2) + return FALSE; - for (i = 0; i < src_count; i++) - { - if (src_relocs[i].source_sec == sec - && src_relocs[i].r_rel.rela.r_offset == irel->r_offset - && (ELF32_R_TYPE (src_relocs[i].r_rel.rela.r_info) - == ELF32_R_TYPE (irel->r_info))) - return &src_relocs[i]; - } + xtensa_format_encode (isa, o_fmt, o_slotbuf); + xtensa_format_encode (isa, o_fmt, o_insnbuf); + operand_count = xtensa_opcode_num_operands (isa, opcode); + o_operand_count = xtensa_opcode_num_operands (isa, o_opcode); - return NULL; -} + if (xtensa_opcode_encode (isa, o_fmt, 0, o_slotbuf, o_opcode) != 0) + return FALSE; + if (!is_or) + { + if (xtensa_opcode_num_operands (isa, o_opcode) != operand_count) + return FALSE; + } + else + { + uint32 rawval0, rawval1, rawval2; -static int -source_reloc_compare (ap, bp) - const PTR ap; - const PTR bp; -{ - const source_reloc *a = (const source_reloc *) ap; - const source_reloc *b = (const source_reloc *) bp; + if (o_operand_count + 1 != operand_count) + return FALSE; + if (xtensa_operand_get_field (isa, opcode, 0, + fmt, 0, slotbuf, &rawval0) != 0) + return FALSE; + if (xtensa_operand_get_field (isa, opcode, 1, + fmt, 0, slotbuf, &rawval1) != 0) + return FALSE; + if (xtensa_operand_get_field (isa, opcode, 2, + fmt, 0, slotbuf, &rawval2) != 0) + return FALSE; - return (a->r_rel.target_offset - b->r_rel.target_offset); -} + if (rawval1 != rawval2) + return FALSE; + if (rawval0 == rawval1) /* it is a nop */ + return FALSE; + } - -/* Literal values and value hash tables. */ + for (i = 0; i < o_operand_count; ++i) + { + if (xtensa_operand_get_field (isa, opcode, i, fmt, 0, + slotbuf, &value) + || xtensa_operand_decode (isa, opcode, i, &value)) + return FALSE; -/* Literals with the same value can be coalesced. The literal_value - structure records the value of a literal: the "r_rel" field holds the - information from the relocation on the literal (if there is one) and - the "value" field holds the contents of the literal word itself. + /* PC-relative branches need adjustment, but + the PC-rel operand will always have a relocation. */ + newval = value; + if (xtensa_operand_do_reloc (isa, o_opcode, i, &newval, + self_address) + || xtensa_operand_encode (isa, o_opcode, i, &newval) + || xtensa_operand_set_field (isa, o_opcode, i, o_fmt, 0, + o_slotbuf, newval)) + return FALSE; + } - The value_map structure records a literal value along with the - location of a literal holding that value. The value_map hash table - is indexed by the literal value, so that we can quickly check if a - particular literal value has been seen before and is thus a candidate - for coalescing. */ + if (xtensa_format_set_slot (isa, o_fmt, 0, + o_insnbuf, o_slotbuf) != 0) + return FALSE; -typedef struct literal_value_struct literal_value; -typedef struct value_map_struct value_map; -typedef struct value_map_hash_table_struct value_map_hash_table; + if (do_it) + xtensa_insnbuf_to_chars (isa, o_insnbuf, contents + offset, + content_length - offset); + return TRUE; + } + } + return FALSE; +} -struct literal_value_struct -{ - r_reloc r_rel; - unsigned long value; -}; -struct value_map_struct -{ - literal_value val; /* The literal value. */ - r_reloc loc; /* Location of the literal. */ - value_map *next; -}; +/* Attempt to widen an instruction. Return true if the widening is + valid. If the do_it parameter is non-zero, then the action should + be performed inplace into the contents. Otherwise, do not modify + the contents. The set of valid widenings are specified by a string + table but require some special case operand checks in some + cases. */ -struct value_map_hash_table_struct +static bfd_boolean +widen_instruction (contents, content_length, offset, do_it) + bfd_byte *contents; + bfd_size_type content_length; + bfd_size_type offset; + bfd_boolean do_it; { - unsigned bucket_count; - value_map **buckets; - unsigned count; -}; - + xtensa_opcode opcode; + bfd_size_type insn_len, opi; + xtensa_isa isa = xtensa_default_isa; + xtensa_format fmt, o_fmt; -static bfd_boolean is_same_value - PARAMS ((const literal_value *, const literal_value *, bfd_boolean)); -static value_map_hash_table *value_map_hash_table_init - PARAMS ((void)); -static unsigned hash_literal_value - PARAMS ((const literal_value *)); -static unsigned hash_bfd_vma - PARAMS ((bfd_vma)); -static value_map *get_cached_value - PARAMS ((value_map_hash_table *, const literal_value *, bfd_boolean)); -static value_map *add_value_map - PARAMS ((value_map_hash_table *, const literal_value *, const r_reloc *, - bfd_boolean)); + static xtensa_insnbuf insnbuf = NULL; + static xtensa_insnbuf slotbuf = NULL; + static xtensa_insnbuf o_insnbuf = NULL; + static xtensa_insnbuf o_slotbuf = NULL; + if (insnbuf == NULL) + { + insnbuf = xtensa_insnbuf_alloc (isa); + slotbuf = xtensa_insnbuf_alloc (isa); + o_insnbuf = xtensa_insnbuf_alloc (isa); + o_slotbuf = xtensa_insnbuf_alloc (isa); + } -static bfd_boolean -is_same_value (src1, src2, final_static_link) - const literal_value *src1; - const literal_value *src2; - bfd_boolean final_static_link; -{ - struct elf_link_hash_entry *h1, *h2; + BFD_ASSERT (offset < content_length); - if (r_reloc_is_const (&src1->r_rel) != r_reloc_is_const (&src2->r_rel)) + if (content_length < 2) return FALSE; - if (r_reloc_is_const (&src1->r_rel)) - return (src1->value == src2->value); - - if (ELF32_R_TYPE (src1->r_rel.rela.r_info) - != ELF32_R_TYPE (src2->r_rel.rela.r_info)) + /* We will hand code a few of these for a little while. + These have all been specified in the assembler aleady. */ + xtensa_insnbuf_from_chars (isa, insnbuf, &contents[offset], + content_length - offset); + fmt = xtensa_format_decode (isa, insnbuf); + if (xtensa_format_num_slots (isa, fmt) != 1) return FALSE; - if (r_reloc_get_target_offset (&src1->r_rel) - != r_reloc_get_target_offset (&src2->r_rel)) + if (xtensa_format_get_slot (isa, fmt, 0, insnbuf, slotbuf) != 0) return FALSE; - if (src1->value != src2->value) + opcode = xtensa_opcode_decode (isa, fmt, 0, slotbuf); + if (opcode == XTENSA_UNDEFINED) return FALSE; - - /* Now check for the same section (if defined) or the same elf_hash - (if undefined or weak). */ - h1 = r_reloc_get_hash_entry (&src1->r_rel); - h2 = r_reloc_get_hash_entry (&src2->r_rel); - if (r_reloc_is_defined (&src1->r_rel) - && (final_static_link - || ((!h1 || h1->root.type != bfd_link_hash_defweak) - && (!h2 || h2->root.type != bfd_link_hash_defweak)))) - { - if (r_reloc_get_section (&src1->r_rel) - != r_reloc_get_section (&src2->r_rel)) - return FALSE; - } - else + insn_len = xtensa_format_length (isa, fmt); + if (insn_len > content_length) + return FALSE; + + for (opi = 0; opi < (sizeof (widenable)/sizeof (struct string_pair)); ++opi) { - /* Require that the hash entries (i.e., symbols) be identical. */ - if (h1 != h2 || h1 == 0) - return FALSE; - } + bfd_boolean is_or = (strcmp ("or", widenable[opi].wide) == 0); + bfd_boolean is_branch = (strcmp ("beqz", widenable[opi].wide) == 0 + || strcmp ("bnez", widenable[opi].wide) == 0); - return TRUE; -} + if (opcode == xtensa_opcode_lookup (isa, widenable[opi].narrow)) + { + uint32 value, newval; + int i, operand_count, o_operand_count, check_operand_count; + xtensa_opcode o_opcode; + /* Address does not matter in this case. We might need to fix it + to handle branches/jumps. */ + bfd_vma self_address = 0; -/* Must be power of 2. */ -#define INITIAL_HASH_RELOC_BUCKET_COUNT 1024 + o_opcode = xtensa_opcode_lookup (isa, widenable[opi].wide); + if (o_opcode == XTENSA_UNDEFINED) + return FALSE; + o_fmt = get_single_format (o_opcode); + if (o_fmt == XTENSA_UNDEFINED) + return FALSE; -static value_map_hash_table * -value_map_hash_table_init () -{ - value_map_hash_table *values; + if (xtensa_format_length (isa, fmt) != 2 + || xtensa_format_length (isa, o_fmt) != 3) + return FALSE; - values = (value_map_hash_table *) - bfd_malloc (sizeof (value_map_hash_table)); + xtensa_format_encode (isa, o_fmt, o_slotbuf); + xtensa_format_encode (isa, o_fmt, o_insnbuf); + operand_count = xtensa_opcode_num_operands (isa, opcode); + o_operand_count = xtensa_opcode_num_operands (isa, o_opcode); + check_operand_count = o_operand_count; - values->bucket_count = INITIAL_HASH_RELOC_BUCKET_COUNT; - values->count = 0; - values->buckets = (value_map **) - bfd_zmalloc (sizeof (value_map *) * values->bucket_count); + if (xtensa_opcode_encode (isa, o_fmt, 0, o_slotbuf, o_opcode) != 0) + return FALSE; - return values; + if (!is_or) + { + if (xtensa_opcode_num_operands (isa, o_opcode) != operand_count) + return FALSE; + } + else + { + uint32 rawval0, rawval1; + + if (o_operand_count != operand_count + 1) + return FALSE; + if (xtensa_operand_get_field (isa, opcode, 0, + fmt, 0, slotbuf, &rawval0) != 0) + return FALSE; + if (xtensa_operand_get_field (isa, opcode, 1, + fmt, 0, slotbuf, &rawval1) != 0) + return FALSE; + if (rawval0 == rawval1) /* it is a nop */ + return FALSE; + } + if (is_branch) + check_operand_count--; + + for (i = 0; i < check_operand_count; ++i) + { + int new_i = i; + if (is_or && i == o_operand_count - 1) + new_i = i - 1; + if (xtensa_operand_get_field (isa, opcode, new_i, fmt, 0, + slotbuf, &value) + || xtensa_operand_decode (isa, opcode, new_i, &value)) + return FALSE; + + /* PC-relative branches need adjustment, but + the PC-rel operand will always have a relocation. */ + newval = value; + if (xtensa_operand_do_reloc (isa, o_opcode, i, &newval, + self_address) + || xtensa_operand_encode (isa, o_opcode, i, &newval) + || xtensa_operand_set_field (isa, o_opcode, i, o_fmt, 0, + o_slotbuf, newval)) + return FALSE; + } + + if (xtensa_format_set_slot (isa, o_fmt, 0, o_insnbuf, o_slotbuf)) + return FALSE; + + if (do_it) + xtensa_insnbuf_to_chars (isa, o_insnbuf, contents + offset, + content_length - offset); + return TRUE; + } + } + return FALSE; } -static unsigned -hash_bfd_vma (val) - bfd_vma val; +/* When FLIX is available we need to access certain instructions only + when they are 16-bit or 24-bit instructions. This table caches + information about such instructions by walking through all the + opcodes and finding the smallest single-slot format into which each + can be encoded. */ + +static xtensa_format *op_single_fmt_table = NULL; + + +static xtensa_format +get_single_format (opcode) + xtensa_opcode opcode; { - return (val >> 2) + (val >> 10); + init_op_single_format_table (); + return op_single_fmt_table[opcode]; } -static unsigned -hash_literal_value (src) - const literal_value *src; +static void +init_op_single_format_table () { - unsigned hash_val; + xtensa_isa isa = xtensa_default_isa; + xtensa_insnbuf ibuf; + xtensa_opcode opcode; + xtensa_format fmt; + int num_opcodes; - if (r_reloc_is_const (&src->r_rel)) - return hash_bfd_vma (src->value); + if (op_single_fmt_table != NULL) + return; - hash_val = (hash_bfd_vma (r_reloc_get_target_offset (&src->r_rel)) - + hash_bfd_vma (src->value)); - - /* Now check for the same section and the same elf_hash. */ - if (r_reloc_is_defined (&src->r_rel)) - hash_val += hash_bfd_vma ((bfd_vma) (unsigned) r_reloc_get_section (&src->r_rel)); - else - hash_val += hash_bfd_vma ((bfd_vma) (unsigned) r_reloc_get_hash_entry (&src->r_rel)); + ibuf = xtensa_insnbuf_alloc (isa); + num_opcodes = xtensa_isa_num_opcodes (isa); - return hash_val; + op_single_fmt_table = (xtensa_format *) + bfd_malloc (sizeof (xtensa_format) * num_opcodes); + for (opcode = 0; opcode < num_opcodes; opcode++) + { + op_single_fmt_table[opcode] = XTENSA_UNDEFINED; + for (fmt = 0; fmt < xtensa_isa_num_formats (isa); fmt++) + { + if (xtensa_format_num_slots (isa, fmt) == 1 + && xtensa_opcode_encode (isa, fmt, 0, ibuf, opcode) == 0) + { + xtensa_opcode old_fmt = op_single_fmt_table[opcode]; + int fmt_length = xtensa_format_length (isa, fmt); + if (old_fmt == XTENSA_UNDEFINED + || fmt_length < xtensa_format_length (isa, old_fmt)) + op_single_fmt_table[opcode] = fmt; + } + } + } + xtensa_insnbuf_free (isa, ibuf); } + +/* Code for transforming CALLs at link-time. */ -/* Check if the specified literal_value has been seen before. */ - -static value_map * -get_cached_value (map, val, final_static_link) - value_map_hash_table *map; - const literal_value *val; - bfd_boolean final_static_link; +static bfd_reloc_status_type +elf_xtensa_do_asm_simplify (contents, address, content_length, error_message) + bfd_byte *contents; + bfd_vma address; + bfd_vma content_length; + char **error_message; { - value_map *map_e; - value_map *bucket; - unsigned idx; + static xtensa_insnbuf insnbuf = NULL; + static xtensa_insnbuf slotbuf = NULL; + xtensa_format core_format = XTENSA_UNDEFINED; + xtensa_opcode opcode; + xtensa_opcode direct_call_opcode; + xtensa_isa isa = xtensa_default_isa; + bfd_byte *chbuf = contents + address; + int opn; - idx = hash_literal_value (val); - idx = idx & (map->bucket_count - 1); - bucket = map->buckets[idx]; - for (map_e = bucket; map_e; map_e = map_e->next) + if (insnbuf == NULL) { - if (is_same_value (&map_e->val, val, final_static_link)) - return map_e; + insnbuf = xtensa_insnbuf_alloc (isa); + slotbuf = xtensa_insnbuf_alloc (isa); } - return NULL; -} + if (content_length < address) + { + *error_message = _("Attempt to convert L32R/CALLX to CALL failed"); + return bfd_reloc_other; + } -/* Record a new literal value. It is illegal to call this if VALUE - already has an entry here. */ + opcode = get_expanded_call_opcode (chbuf, content_length - address, 0); + direct_call_opcode = swap_callx_for_call_opcode (opcode); + if (direct_call_opcode == XTENSA_UNDEFINED) + { + *error_message = _("Attempt to convert L32R/CALLX to CALL failed"); + return bfd_reloc_other; + } + + /* Assemble a NOP ("or a1, a1, a1") into the 0 byte offset. */ + core_format = xtensa_format_lookup (isa, "x24"); + opcode = xtensa_opcode_lookup (isa, "or"); + xtensa_opcode_encode (isa, core_format, 0, slotbuf, opcode); + for (opn = 0; opn < 3; opn++) + { + uint32 regno = 1; + xtensa_operand_encode (isa, opcode, opn, ®no); + xtensa_operand_set_field (isa, opcode, opn, core_format, 0, + slotbuf, regno); + } + xtensa_format_encode (isa, core_format, insnbuf); + xtensa_format_set_slot (isa, core_format, 0, insnbuf, slotbuf); + xtensa_insnbuf_to_chars (isa, insnbuf, chbuf, content_length - address); -static value_map * -add_value_map (map, val, loc, final_static_link) - value_map_hash_table *map; - const literal_value *val; - const r_reloc *loc; - bfd_boolean final_static_link; -{ - value_map **bucket_p; - unsigned idx; + /* Assemble a CALL ("callN 0") into the 3 byte offset. */ + xtensa_opcode_encode (isa, core_format, 0, slotbuf, direct_call_opcode); + xtensa_operand_set_field (isa, opcode, 0, core_format, 0, slotbuf, 0); - value_map *val_e = (value_map *) bfd_zmalloc (sizeof (value_map)); + xtensa_format_encode (isa, core_format, insnbuf); + xtensa_format_set_slot (isa, core_format, 0, insnbuf, slotbuf); + xtensa_insnbuf_to_chars (isa, insnbuf, chbuf + 3, + content_length - address - 3); - BFD_ASSERT (get_cached_value (map, val, final_static_link) == NULL); - val_e->val = *val; - val_e->loc = *loc; + return bfd_reloc_ok; +} - idx = hash_literal_value (val); - idx = idx & (map->bucket_count - 1); - bucket_p = &map->buckets[idx]; - val_e->next = *bucket_p; - *bucket_p = val_e; - map->count++; - /* FIXME: consider resizing the hash table if we get too many entries */ - - return val_e; -} +static bfd_reloc_status_type +contract_asm_expansion (contents, content_length, irel, error_message) + bfd_byte *contents; + bfd_vma content_length; + Elf_Internal_Rela *irel; + char **error_message; +{ + bfd_reloc_status_type retval = + elf_xtensa_do_asm_simplify (contents, irel->r_offset, content_length, + error_message); - -/* Lists of literals being coalesced or removed. */ + if (retval != bfd_reloc_ok) + return bfd_reloc_dangerous; -/* In the usual case, the literal identified by "from" is being - coalesced with another literal identified by "to". If the literal is - unused and is being removed altogether, "to.abfd" will be NULL. - The removed_literal entries are kept on a per-section list, sorted - by the "from" offset field. */ + /* Update the irel->r_offset field so that the right immediate and + the right instruction are modified during the relocation. */ + irel->r_offset += 3; + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_XTENSA_SLOT0_OP); + return bfd_reloc_ok; +} -typedef struct removed_literal_struct removed_literal; -typedef struct removed_literal_list_struct removed_literal_list; -struct removed_literal_struct +static xtensa_opcode +swap_callx_for_call_opcode (opcode) + xtensa_opcode opcode; { - r_reloc from; - r_reloc to; - removed_literal *next; -}; + init_call_opcodes (); -struct removed_literal_list_struct -{ - removed_literal *head; - removed_literal *tail; -}; + if (opcode == callx0_op) return call0_op; + if (opcode == callx4_op) return call4_op; + if (opcode == callx8_op) return call8_op; + if (opcode == callx12_op) return call12_op; + /* Return XTENSA_UNDEFINED if the opcode is not an indirect call. */ + return XTENSA_UNDEFINED; +} -static void add_removed_literal - PARAMS ((removed_literal_list *, const r_reloc *, const r_reloc *)); -static removed_literal *find_removed_literal - PARAMS ((removed_literal_list *, bfd_vma)); -static bfd_vma offset_with_removed_literals - PARAMS ((removed_literal_list *, bfd_vma)); +/* Check if "buf" is pointing to a "L32R aN; CALLX aN" or "CONST16 aN; + CONST16 aN; CALLX aN" sequence, and if so, return the CALLX opcode. + If not, return XTENSA_UNDEFINED. */ -/* Record that the literal at "from" is being removed. If "to" is not - NULL, the "from" literal is being coalesced with the "to" literal. */ +#define L32R_TARGET_REG_OPERAND 0 +#define CONST16_TARGET_REG_OPERAND 0 +#define CALLN_SOURCE_OPERAND 0 -static void -add_removed_literal (removed_list, from, to) - removed_literal_list *removed_list; - const r_reloc *from; - const r_reloc *to; +static xtensa_opcode +get_expanded_call_opcode (buf, bufsize, p_uses_l32r) + bfd_byte *buf; + int bufsize; + bfd_boolean *p_uses_l32r; { - removed_literal *r, *new_r, *next_r; - - new_r = (removed_literal *) bfd_zmalloc (sizeof (removed_literal)); + static xtensa_insnbuf insnbuf = NULL; + static xtensa_insnbuf slotbuf = NULL; + xtensa_format fmt; + xtensa_opcode opcode; + xtensa_isa isa = xtensa_default_isa; + uint32 regno, const16_regno, call_regno; + int offset = 0; - new_r->from = *from; - if (to) - new_r->to = *to; - else - new_r->to.abfd = NULL; - new_r->next = NULL; - - r = removed_list->head; - if (r == NULL) + if (insnbuf == NULL) { - removed_list->head = new_r; - removed_list->tail = new_r; + insnbuf = xtensa_insnbuf_alloc (isa); + slotbuf = xtensa_insnbuf_alloc (isa); } - /* Special check for common case of append. */ - else if (removed_list->tail->from.target_offset < from->target_offset) + + xtensa_insnbuf_from_chars (isa, insnbuf, buf, bufsize); + fmt = xtensa_format_decode (isa, insnbuf); + if (fmt == XTENSA_UNDEFINED + || xtensa_format_get_slot (isa, fmt, 0, insnbuf, slotbuf)) + return XTENSA_UNDEFINED; + + opcode = xtensa_opcode_decode (isa, fmt, 0, slotbuf); + if (opcode == XTENSA_UNDEFINED) + return XTENSA_UNDEFINED; + + if (opcode == get_l32r_opcode ()) { - removed_list->tail->next = new_r; - removed_list->tail = new_r; + if (p_uses_l32r) + *p_uses_l32r = TRUE; + if (xtensa_operand_get_field (isa, opcode, L32R_TARGET_REG_OPERAND, + fmt, 0, slotbuf, ®no) + || xtensa_operand_decode (isa, opcode, L32R_TARGET_REG_OPERAND, + ®no)) + return XTENSA_UNDEFINED; } - else + else if (opcode == get_const16_opcode ()) { - while (r->from.target_offset < from->target_offset - && r->next != NULL) - { - r = r->next; - } - next_r = r->next; - r->next = new_r; - new_r->next = next_r; - if (next_r == NULL) - removed_list->tail = new_r; + if (p_uses_l32r) + *p_uses_l32r = FALSE; + if (xtensa_operand_get_field (isa, opcode, CONST16_TARGET_REG_OPERAND, + fmt, 0, slotbuf, ®no) + || xtensa_operand_decode (isa, opcode, CONST16_TARGET_REG_OPERAND, + ®no)) + return XTENSA_UNDEFINED; + + /* Check that the next instruction is also CONST16. */ + offset += xtensa_format_length (isa, fmt); + xtensa_insnbuf_from_chars (isa, insnbuf, buf + offset, bufsize - offset); + fmt = xtensa_format_decode (isa, insnbuf); + if (fmt == XTENSA_UNDEFINED + || xtensa_format_get_slot (isa, fmt, 0, insnbuf, slotbuf)) + return XTENSA_UNDEFINED; + opcode = xtensa_opcode_decode (isa, fmt, 0, slotbuf); + if (opcode != get_const16_opcode ()) + return XTENSA_UNDEFINED; + + if (xtensa_operand_get_field (isa, opcode, CONST16_TARGET_REG_OPERAND, + fmt, 0, slotbuf, &const16_regno) + || xtensa_operand_decode (isa, opcode, CONST16_TARGET_REG_OPERAND, + &const16_regno) + || const16_regno != regno) + return XTENSA_UNDEFINED; } -} - + else + return XTENSA_UNDEFINED; -/* Check if the list of removed literals contains an entry for the - given address. Return the entry if found. */ + /* Next instruction should be an CALLXn with operand 0 == regno. */ + offset += xtensa_format_length (isa, fmt); + xtensa_insnbuf_from_chars (isa, insnbuf, buf + offset, bufsize - offset); + fmt = xtensa_format_decode (isa, insnbuf); + if (fmt == XTENSA_UNDEFINED + || xtensa_format_get_slot (isa, fmt, 0, insnbuf, slotbuf)) + return XTENSA_UNDEFINED; + opcode = xtensa_opcode_decode (isa, fmt, 0, slotbuf); + if (opcode == XTENSA_UNDEFINED + || !is_indirect_call_opcode (opcode)) + return XTENSA_UNDEFINED; -static removed_literal * -find_removed_literal (removed_list, addr) - removed_literal_list *removed_list; - bfd_vma addr; -{ - removed_literal *r = removed_list->head; - while (r && r->from.target_offset < addr) - r = r->next; - if (r && r->from.target_offset == addr) - return r; - return NULL; -} + if (xtensa_operand_get_field (isa, opcode, CALLN_SOURCE_OPERAND, + fmt, 0, slotbuf, &call_regno) + || xtensa_operand_decode (isa, opcode, CALLN_SOURCE_OPERAND, + &call_regno)) + return XTENSA_UNDEFINED; + if (call_regno != regno) + return XTENSA_UNDEFINED; -/* Adjust an offset in a section to compensate for literals that are - being removed. Search the list of removed literals and subtract - 4 bytes for every removed literal prior to the given address. */ + return opcode; +} -static bfd_vma -offset_with_removed_literals (removed_list, addr) - removed_literal_list *removed_list; - bfd_vma addr; -{ - removed_literal *r = removed_list->head; - unsigned num_bytes = 0; + +/* Data structures used during relaxation. */ - if (r == NULL) - return addr; +/* r_reloc: relocation values. */ - while (r && r->from.target_offset <= addr) - { - num_bytes += 4; - r = r->next; - } - if (num_bytes > addr) - return 0; - return (addr - num_bytes); -} +/* Through the relaxation process, we need to keep track of the values + that will result from evaluating relocations. The standard ELF + relocation structure is not sufficient for this purpose because we're + operating on multiple input files at once, so we need to know which + input file a relocation refers to. The r_reloc structure thus + records both the input file (bfd) and ELF relocation. - -/* Coalescing literals may require a relocation to refer to a section in - a different input file, but the standard relocation information - cannot express that. Instead, the reloc_bfd_fix structures are used - to "fix" the relocations that refer to sections in other input files. - These structures are kept on per-section lists. The "src_type" field - records the relocation type in case there are multiple relocations on - the same location. FIXME: This is ugly; an alternative might be to - add new symbols with the "owner" field to some other input file. */ + For efficiency, an r_reloc also contains a "target_offset" field to + cache the target-section-relative offset value that is represented by + the relocation. + + The r_reloc also contains a virtual offset that allows multiple + inserted literals to be placed at the same "address" with + different offsets. */ -typedef struct reloc_bfd_fix_struct reloc_bfd_fix; +typedef struct r_reloc_struct r_reloc; -struct reloc_bfd_fix_struct +struct r_reloc_struct { - asection *src_sec; - bfd_vma src_offset; - unsigned src_type; /* Relocation type. */ - - bfd *target_abfd; - asection *target_sec; + bfd *abfd; + Elf_Internal_Rela rela; bfd_vma target_offset; - - reloc_bfd_fix *next; + bfd_vma virtual_offset; }; +static bfd_boolean r_reloc_is_const + PARAMS ((const r_reloc *)); +static void r_reloc_init + PARAMS ((r_reloc *, bfd *, Elf_Internal_Rela *, bfd_byte *, bfd_size_type)); +static bfd_vma r_reloc_get_target_offset + PARAMS ((const r_reloc *)); +static asection *r_reloc_get_section + PARAMS ((const r_reloc *)); +static bfd_boolean r_reloc_is_defined + PARAMS ((const r_reloc *)); +static struct elf_link_hash_entry *r_reloc_get_hash_entry + PARAMS ((const r_reloc *)); +#if DEBUG +static void print_r_reloc + PARAMS ((FILE *fp, const r_reloc *r)); +#endif /* DEBUG */ -static reloc_bfd_fix *reloc_bfd_fix_init - PARAMS ((asection *, bfd_vma, unsigned, bfd *, asection *, bfd_vma)); -static reloc_bfd_fix *get_bfd_fix - PARAMS ((reloc_bfd_fix *, asection *, bfd_vma, unsigned)); +/* The r_reloc structure is included by value in literal_value, but not + every literal_value has an associated relocation -- some are simple + constants. In such cases, we set all the fields in the r_reloc + struct to zero. The r_reloc_is_const function should be used to + detect this case. */ -static reloc_bfd_fix * -reloc_bfd_fix_init (src_sec, src_offset, src_type, - target_abfd, target_sec, target_offset) - asection *src_sec; - bfd_vma src_offset; - unsigned src_type; - bfd *target_abfd; - asection *target_sec; - bfd_vma target_offset; +static bfd_boolean +r_reloc_is_const (r_rel) + const r_reloc *r_rel; { - reloc_bfd_fix *fix; - - fix = (reloc_bfd_fix *) bfd_malloc (sizeof (reloc_bfd_fix)); - fix->src_sec = src_sec; - fix->src_offset = src_offset; - fix->src_type = src_type; - fix->target_abfd = target_abfd; - fix->target_sec = target_sec; - fix->target_offset = target_offset; - - return fix; + return (r_rel->abfd == NULL); } -static reloc_bfd_fix * -get_bfd_fix (fix_list, sec, offset, type) - reloc_bfd_fix *fix_list; - asection *sec; - bfd_vma offset; - unsigned type; +static void +r_reloc_init (r_rel, abfd, irel, contents, content_length) + r_reloc *r_rel; + bfd *abfd; + Elf_Internal_Rela *irel; + bfd_byte *contents; + bfd_size_type content_length; { - reloc_bfd_fix *r; + int r_type; + reloc_howto_type *howto; - for (r = fix_list; r != NULL; r = r->next) + if (irel != NULL) { - if (r->src_sec == sec - && r->src_offset == offset - && r->src_type == type) - return r; + r_rel->rela = *irel; + r_rel->abfd = abfd; + r_rel->target_offset = r_reloc_get_target_offset (r_rel); + r_rel->virtual_offset = 0; + r_type = ELF32_R_TYPE (r_rel->rela.r_info); + howto = &elf_howto_table[r_type]; + if (howto->partial_inplace) + { + bfd_vma inplace_val; + BFD_ASSERT (r_rel->rela.r_offset < content_length); + + inplace_val = bfd_get_32 (abfd, &contents[r_rel->rela.r_offset]); + r_rel->target_offset += inplace_val; + } } - return NULL; + else + memset (r_rel, 0, sizeof (r_reloc)); } - -/* Per-section data for relaxation. */ -struct xtensa_relax_info_struct +static bfd_vma +r_reloc_get_target_offset (r_rel) + const r_reloc *r_rel; { - bfd_boolean is_relaxable_literal_section; - int visited; /* Number of times visited. */ - - source_reloc *src_relocs; /* Array[src_count]. */ - int src_count; - int src_next; /* Next src_relocs entry to assign. */ + bfd_vma target_offset; + unsigned long r_symndx; - removed_literal_list removed_list; + BFD_ASSERT (!r_reloc_is_const (r_rel)); + r_symndx = ELF32_R_SYM (r_rel->rela.r_info); + target_offset = get_elf_r_symndx_offset (r_rel->abfd, r_symndx); + return (target_offset + r_rel->rela.r_addend); +} - reloc_bfd_fix *fix_list; -}; -struct elf_xtensa_section_data +static struct elf_link_hash_entry * +r_reloc_get_hash_entry (r_rel) + const r_reloc *r_rel; { - struct bfd_elf_section_data elf; - xtensa_relax_info relax_info; -}; + unsigned long r_symndx = ELF32_R_SYM (r_rel->rela.r_info); + return get_elf_r_symndx_hash_entry (r_rel->abfd, r_symndx); +} -static void init_xtensa_relax_info - PARAMS ((asection *)); -static xtensa_relax_info *get_xtensa_relax_info - PARAMS ((asection *)); -static void add_fix - PARAMS ((asection *, reloc_bfd_fix *)); + +static asection * +r_reloc_get_section (r_rel) + const r_reloc *r_rel; +{ + unsigned long r_symndx = ELF32_R_SYM (r_rel->rela.r_info); + return get_elf_r_symndx_section (r_rel->abfd, r_symndx); +} static bfd_boolean -elf_xtensa_new_section_hook (abfd, sec) - bfd *abfd; - asection *sec; +r_reloc_is_defined (r_rel) + const r_reloc *r_rel; { - struct elf_xtensa_section_data *sdata; - bfd_size_type amt = sizeof (*sdata); - - sdata = (struct elf_xtensa_section_data *) bfd_zalloc (abfd, amt); - if (sdata == NULL) + asection *sec; + if (r_rel == NULL) return FALSE; - sec->used_by_bfd = (PTR) sdata; - return _bfd_elf_new_section_hook (abfd, sec); + sec = r_reloc_get_section (r_rel); + if (sec == bfd_abs_section_ptr + || sec == bfd_com_section_ptr + || sec == bfd_und_section_ptr) + return FALSE; + return TRUE; } +#if DEBUG + static void -init_xtensa_relax_info (sec) - asection *sec; +print_r_reloc (fp, r_rel) + FILE *fp; + const r_reloc *r_rel; { - xtensa_relax_info *relax_info = get_xtensa_relax_info (sec); + if (r_reloc_is_defined (r_rel)) + { + asection *sec = r_reloc_get_section (r_rel); + fprintf (fp, " %s(%s + ", sec->owner->filename, sec->name); + } + else if (r_reloc_get_hash_entry (r_rel)) + fprintf (fp, " %s + ", r_reloc_get_hash_entry (r_rel)->root.root.string); + else + fprintf (fp, " ?? + "); - relax_info->is_relaxable_literal_section = FALSE; - relax_info->visited = 0; + fprintf_vma (fp, r_rel->target_offset); + if (r_rel->virtual_offset) + { + fprintf (fp, " + "); + fprintf_vma (fp, r_rel->virtual_offset); + } + + fprintf (fp, ")"); +} - relax_info->src_relocs = NULL; - relax_info->src_count = 0; - relax_info->src_next = 0; +#endif /* DEBUG */ - relax_info->removed_list.head = NULL; - relax_info->removed_list.tail = NULL; + +/* source_reloc: relocations that reference literals. */ - relax_info->fix_list = NULL; -} +/* To determine whether literals can be coalesced, we need to first + record all the relocations that reference the literals. The + source_reloc structure below is used for this purpose. The + source_reloc entries are kept in a per-literal-section array, sorted + by offset within the literal section (i.e., target offset). + The source_sec and r_rel.rela.r_offset fields identify the source of + the relocation. The r_rel field records the relocation value, i.e., + the offset of the literal being referenced. The opnd field is needed + to determine the range of the immediate field to which the relocation + applies, so we can determine whether another literal with the same + value is within range. The is_null field is true when the relocation + is being removed (e.g., when an L32R is being removed due to a CALLX + that is converted to a direct CALL). */ -static xtensa_relax_info * -get_xtensa_relax_info (sec) - asection *sec; +typedef struct source_reloc_struct source_reloc; + +struct source_reloc_struct { - struct elf_xtensa_section_data *section_data; + asection *source_sec; + r_reloc r_rel; + xtensa_opcode opcode; + int opnd; + bfd_boolean is_null; + bfd_boolean is_abs_literal; +}; - /* No info available if no section or if it is an output section. */ - if (!sec || sec == sec->output_section) - return NULL; - section_data = (struct elf_xtensa_section_data *) elf_section_data (sec); - return §ion_data->relax_info; -} +static void init_source_reloc + PARAMS ((source_reloc *, asection *, const r_reloc *, + xtensa_opcode, int, bfd_boolean)); +static source_reloc *find_source_reloc + PARAMS ((source_reloc *, int, asection *, Elf_Internal_Rela *)); +static int source_reloc_compare + PARAMS ((const PTR, const PTR)); static void -add_fix (src_sec, fix) - asection *src_sec; - reloc_bfd_fix *fix; +init_source_reloc (reloc, source_sec, r_rel, opcode, opnd, is_abs_literal) + source_reloc *reloc; + asection *source_sec; + const r_reloc *r_rel; + xtensa_opcode opcode; + int opnd; + bfd_boolean is_abs_literal; { - xtensa_relax_info *relax_info; - - relax_info = get_xtensa_relax_info (src_sec); - fix->next = relax_info->fix_list; - relax_info->fix_list = fix; + reloc->source_sec = source_sec; + reloc->r_rel = *r_rel; + reloc->opcode = opcode; + reloc->opnd = opnd; + reloc->is_null = FALSE; + reloc->is_abs_literal = is_abs_literal; } - -/* Access to internal relocations, section contents and symbols. */ -/* During relaxation, we need to modify relocations, section contents, - and symbol definitions, and we need to keep the original values from - being reloaded from the input files, i.e., we need to "pin" the - modified values in memory. We also want to continue to observe the - setting of the "keep-memory" flag. The following functions wrap the - standard BFD functions to take care of this for us. */ +/* Find the source_reloc for a particular source offset and relocation + type. Note that the array is sorted by _target_ offset, so this is + just a linear search. */ -static Elf_Internal_Rela * -retrieve_internal_relocs (abfd, sec, keep_memory) - bfd *abfd; +static source_reloc * +find_source_reloc (src_relocs, src_count, sec, irel) + source_reloc *src_relocs; + int src_count; asection *sec; - bfd_boolean keep_memory; + Elf_Internal_Rela *irel; { - Elf_Internal_Rela *internal_relocs; + int i; - if ((sec->flags & SEC_LINKER_CREATED) != 0) - return NULL; + for (i = 0; i < src_count; i++) + { + if (src_relocs[i].source_sec == sec + && src_relocs[i].r_rel.rela.r_offset == irel->r_offset + && (ELF32_R_TYPE (src_relocs[i].r_rel.rela.r_info) + == ELF32_R_TYPE (irel->r_info))) + return &src_relocs[i]; + } - internal_relocs = elf_section_data (sec)->relocs; - if (internal_relocs == NULL) - internal_relocs = (_bfd_elf_link_read_relocs - (abfd, sec, (PTR) NULL, (Elf_Internal_Rela *) NULL, - keep_memory)); - return internal_relocs; + return NULL; } -static void -pin_internal_relocs (sec, internal_relocs) - asection *sec; - Elf_Internal_Rela *internal_relocs; +static int +source_reloc_compare (ap, bp) + const PTR ap; + const PTR bp; { - elf_section_data (sec)->relocs = internal_relocs; -} + const source_reloc *a = (const source_reloc *) ap; + const source_reloc *b = (const source_reloc *) bp; + if (a->r_rel.target_offset != b->r_rel.target_offset) + return (a->r_rel.target_offset - b->r_rel.target_offset); -static void -release_internal_relocs (sec, internal_relocs) - asection *sec; - Elf_Internal_Rela *internal_relocs; -{ - if (internal_relocs - && elf_section_data (sec)->relocs != internal_relocs) - free (internal_relocs); + /* We don't need to sort on these criteria for correctness, + but enforcing a more strict ordering prevents unstable qsort + from behaving differently with different implementations. + Without the code below we get correct but different results + on Solaris 2.7 and 2.8. We would like to always produce the + same results no matter the host. */ + + if ((!a->is_null) - (!b->is_null)) + return ((!a->is_null) - (!b->is_null)); + return internal_reloc_compare (&a->r_rel.rela, &b->r_rel.rela); } + +/* Literal values and value hash tables. */ -static bfd_byte * -retrieve_contents (abfd, sec, keep_memory) - bfd *abfd; - asection *sec; - bfd_boolean keep_memory; -{ - bfd_byte *contents; +/* Literals with the same value can be coalesced. The literal_value + structure records the value of a literal: the "r_rel" field holds the + information from the relocation on the literal (if there is one) and + the "value" field holds the contents of the literal word itself. - contents = elf_section_data (sec)->this_hdr.contents; - - if (contents == NULL && sec->size != 0) - { - if (!bfd_malloc_and_get_section (abfd, sec, &contents)) - { - if (contents != NULL) - free (contents); - return NULL; - } - if (keep_memory) - elf_section_data (sec)->this_hdr.contents = contents; - } - return contents; -} + The value_map structure records a literal value along with the + location of a literal holding that value. The value_map hash table + is indexed by the literal value, so that we can quickly check if a + particular literal value has been seen before and is thus a candidate + for coalescing. */ +typedef struct literal_value_struct literal_value; +typedef struct value_map_struct value_map; +typedef struct value_map_hash_table_struct value_map_hash_table; -static void -pin_contents (sec, contents) - asection *sec; - bfd_byte *contents; +struct literal_value_struct { - elf_section_data (sec)->this_hdr.contents = contents; -} + r_reloc r_rel; + unsigned long value; + bfd_boolean is_abs_literal; +}; + +struct value_map_struct +{ + literal_value val; /* The literal value. */ + r_reloc loc; /* Location of the literal. */ + value_map *next; +}; + +struct value_map_hash_table_struct +{ + unsigned bucket_count; + value_map **buckets; + unsigned count; + bfd_boolean has_last_loc; + r_reloc last_loc; +}; + + +static void init_literal_value + PARAMS ((literal_value *, const r_reloc *, unsigned long, bfd_boolean)); +static bfd_boolean literal_value_equal + PARAMS ((const literal_value *, const literal_value *, bfd_boolean)); +static value_map_hash_table *value_map_hash_table_init + PARAMS ((void)); +static void value_map_hash_table_delete + PARAMS ((value_map_hash_table *)); +static unsigned literal_value_hash + PARAMS ((const literal_value *)); +static unsigned hash_bfd_vma + PARAMS ((bfd_vma)); +static value_map *value_map_get_cached_value + PARAMS ((value_map_hash_table *, const literal_value *, bfd_boolean)); +static value_map *add_value_map + PARAMS ((value_map_hash_table *, const literal_value *, const r_reloc *, + bfd_boolean)); static void -release_contents (sec, contents) - asection *sec; - bfd_byte *contents; +init_literal_value (lit, r_rel, value, is_abs_literal) + literal_value *lit; + const r_reloc *r_rel; + unsigned long value; + bfd_boolean is_abs_literal; { - if (contents && - elf_section_data (sec)->this_hdr.contents != contents) - free (contents); + lit->r_rel = *r_rel; + lit->value = value; + lit->is_abs_literal = is_abs_literal; } -static Elf_Internal_Sym * -retrieve_local_syms (input_bfd) - bfd *input_bfd; +static bfd_boolean +literal_value_equal (src1, src2, final_static_link) + const literal_value *src1; + const literal_value *src2; + bfd_boolean final_static_link; { - Elf_Internal_Shdr *symtab_hdr; - Elf_Internal_Sym *isymbuf; - size_t locsymcount; + struct elf_link_hash_entry *h1, *h2; - symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; - locsymcount = symtab_hdr->sh_info; + if (r_reloc_is_const (&src1->r_rel) != r_reloc_is_const (&src2->r_rel)) + return FALSE; - isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents; - if (isymbuf == NULL && locsymcount != 0) - isymbuf = bfd_elf_get_elf_syms (input_bfd, symtab_hdr, locsymcount, 0, - NULL, NULL, NULL); + if (r_reloc_is_const (&src1->r_rel)) + return (src1->value == src2->value); - /* Save the symbols for this input file so they won't be read again. */ - if (isymbuf && isymbuf != (Elf_Internal_Sym *) symtab_hdr->contents) - symtab_hdr->contents = (unsigned char *) isymbuf; + if (ELF32_R_TYPE (src1->r_rel.rela.r_info) + != ELF32_R_TYPE (src2->r_rel.rela.r_info)) + return FALSE; - return isymbuf; + if (src1->r_rel.target_offset != src2->r_rel.target_offset) + return FALSE; + + if (src1->r_rel.virtual_offset != src2->r_rel.virtual_offset) + return FALSE; + + if (src1->value != src2->value) + return FALSE; + + /* Now check for the same section (if defined) or the same elf_hash + (if undefined or weak). */ + h1 = r_reloc_get_hash_entry (&src1->r_rel); + h2 = r_reloc_get_hash_entry (&src2->r_rel); + if (r_reloc_is_defined (&src1->r_rel) + && (final_static_link + || ((!h1 || h1->root.type != bfd_link_hash_defweak) + && (!h2 || h2->root.type != bfd_link_hash_defweak)))) + { + if (r_reloc_get_section (&src1->r_rel) + != r_reloc_get_section (&src2->r_rel)) + return FALSE; + } + else + { + /* Require that the hash entries (i.e., symbols) be identical. */ + if (h1 != h2 || h1 == 0) + return FALSE; + } + + if (src1->is_abs_literal != src2->is_abs_literal) + return FALSE; + + return TRUE; } - -/* Code for link-time relaxation. */ -/* Local helper functions. */ -static bfd_boolean analyze_relocations - PARAMS ((struct bfd_link_info *)); -static bfd_boolean find_relaxable_sections - PARAMS ((bfd *, asection *, struct bfd_link_info *, bfd_boolean *)); -static bfd_boolean collect_source_relocs - PARAMS ((bfd *, asection *, struct bfd_link_info *)); -static bfd_boolean is_resolvable_asm_expansion - PARAMS ((bfd *, asection *, bfd_byte *, Elf_Internal_Rela *, - struct bfd_link_info *, bfd_boolean *)); -static bfd_boolean remove_literals - PARAMS ((bfd *, asection *, struct bfd_link_info *, value_map_hash_table *)); -static bfd_boolean relax_section - PARAMS ((bfd *, asection *, struct bfd_link_info *)); -static bfd_boolean relax_property_section - PARAMS ((bfd *, asection *, struct bfd_link_info *)); -static bfd_boolean relax_section_symbols - PARAMS ((bfd *, asection *)); -static bfd_boolean relocations_reach - PARAMS ((source_reloc *, int, const r_reloc *)); -static void translate_reloc - PARAMS ((const r_reloc *, r_reloc *)); -static Elf_Internal_Rela *get_irel_at_offset - PARAMS ((asection *, Elf_Internal_Rela *, bfd_vma)); -static Elf_Internal_Rela *find_associated_l32r_irel - PARAMS ((asection *, bfd_byte *, Elf_Internal_Rela *, - Elf_Internal_Rela *)); -static void shrink_dynamic_reloc_sections - PARAMS ((struct bfd_link_info *, bfd *, asection *, Elf_Internal_Rela *)); +/* Must be power of 2. */ +#define INITIAL_HASH_RELOC_BUCKET_COUNT 1024 +static value_map_hash_table * +value_map_hash_table_init () +{ + value_map_hash_table *values; -static bfd_boolean -elf_xtensa_relax_section (abfd, sec, link_info, again) - bfd *abfd; - asection *sec; - struct bfd_link_info *link_info; - bfd_boolean *again; + values = (value_map_hash_table *) + bfd_zmalloc (sizeof (value_map_hash_table)); + values->bucket_count = INITIAL_HASH_RELOC_BUCKET_COUNT; + values->count = 0; + values->buckets = (value_map **) + bfd_zmalloc (sizeof (value_map *) * values->bucket_count); + if (values->buckets == NULL) + { + free (values); + return NULL; + } + values->has_last_loc = FALSE; + + return values; +} + + +static void +value_map_hash_table_delete (table) + value_map_hash_table *table; { - static value_map_hash_table *values = NULL; - xtensa_relax_info *relax_info; + free (table->buckets); + free (table); +} + + +static unsigned +hash_bfd_vma (val) + bfd_vma val; +{ + return (val >> 2) + (val >> 10); +} + + +static unsigned +literal_value_hash (src) + const literal_value *src; +{ + unsigned hash_val; - if (!values) + hash_val = hash_bfd_vma (src->value); + if (!r_reloc_is_const (&src->r_rel)) { - /* Do some overall initialization for relaxation. */ - values = value_map_hash_table_init (); - relaxing_section = TRUE; - if (!analyze_relocations (link_info)) - return FALSE; + void *sec_or_hash; + + hash_val += hash_bfd_vma (src->is_abs_literal * 1000); + hash_val += hash_bfd_vma (src->r_rel.target_offset); + hash_val += hash_bfd_vma (src->r_rel.virtual_offset); + + /* Now check for the same section and the same elf_hash. */ + if (r_reloc_is_defined (&src->r_rel)) + sec_or_hash = r_reloc_get_section (&src->r_rel); + else + sec_or_hash = r_reloc_get_hash_entry (&src->r_rel); + hash_val += hash_bfd_vma ((bfd_vma) (unsigned) sec_or_hash); } - *again = FALSE; + return hash_val; +} - /* Don't mess with linker-created sections. */ - if ((sec->flags & SEC_LINKER_CREATED) != 0) - return TRUE; - relax_info = get_xtensa_relax_info (sec); - BFD_ASSERT (relax_info != NULL); +/* Check if the specified literal_value has been seen before. */ - switch (relax_info->visited) +static value_map * +value_map_get_cached_value (map, val, final_static_link) + value_map_hash_table *map; + const literal_value *val; + bfd_boolean final_static_link; +{ + value_map *map_e; + value_map *bucket; + unsigned idx; + + idx = literal_value_hash (val); + idx = idx & (map->bucket_count - 1); + bucket = map->buckets[idx]; + for (map_e = bucket; map_e; map_e = map_e->next) { - case 0: - /* Note: It would be nice to fold this pass into - analyze_relocations, but it is important for this step that the - sections be examined in link order. */ - if (!remove_literals (abfd, sec, link_info, values)) - return FALSE; - *again = TRUE; - break; + if (literal_value_equal (&map_e->val, val, final_static_link)) + return map_e; + } + return NULL; +} - case 1: - if (!relax_section (abfd, sec, link_info)) - return FALSE; - *again = TRUE; - break; - case 2: - if (!relax_section_symbols (abfd, sec)) - return FALSE; - break; +/* Record a new literal value. It is illegal to call this if VALUE + already has an entry here. */ + +static value_map * +add_value_map (map, val, loc, final_static_link) + value_map_hash_table *map; + const literal_value *val; + const r_reloc *loc; + bfd_boolean final_static_link; +{ + value_map **bucket_p; + unsigned idx; + + value_map *val_e = (value_map *) bfd_zmalloc (sizeof (value_map)); + if (val_e == NULL) + { + bfd_set_error (bfd_error_no_memory); + return NULL; } - relax_info->visited++; - return TRUE; + BFD_ASSERT (!value_map_get_cached_value (map, val, final_static_link)); + val_e->val = *val; + val_e->loc = *loc; + + idx = literal_value_hash (val); + idx = idx & (map->bucket_count - 1); + bucket_p = &map->buckets[idx]; + + val_e->next = *bucket_p; + *bucket_p = val_e; + map->count++; + /* FIXME: Consider resizing the hash table if we get too many entries. */ + + return val_e; } -/* Initialization for relaxation. */ + +/* Lists of text actions (ta_) for narrowing, widening, longcall + conversion, space fill, code & literal removal, etc. */ + +/* The following text actions are generated: + + "ta_remove_insn" remove an instruction or instructions + "ta_remove_longcall" convert longcall to call + "ta_convert_longcall" convert longcall to nop/call + "ta_narrow_insn" narrow a wide instruction + "ta_widen" widen a narrow instruction + "ta_fill" add fill or remove fill + removed < 0 is a fill; branches to the fill address will be + changed to address + fill size (e.g., address - removed) + removed >= 0 branches to the fill address will stay unchanged + "ta_remove_literal" remove a literal; this action is + indicated when a literal is removed + or replaced. + "ta_add_literal" insert a new literal; this action is + indicated when a literal has been moved. + It may use a virtual_offset because + multiple literals can be placed at the + same location. + + For each of these text actions, we also record the number of bytes + removed by performing the text action. In the case of a "ta_widen" + or a "ta_fill" that adds space, the removed_bytes will be negative. */ + +typedef struct text_action_struct text_action; +typedef struct text_action_list_struct text_action_list; +typedef enum text_action_enum_t text_action_t; + +enum text_action_enum_t +{ + ta_none, + ta_remove_insn, /* removed = -size */ + ta_remove_longcall, /* removed = -size */ + ta_convert_longcall, /* removed = 0 */ + ta_narrow_insn, /* removed = -1 */ + ta_widen_insn, /* removed = +1 */ + ta_fill, /* removed = +size */ + ta_remove_literal, + ta_add_literal +}; -/* This function is called once at the start of relaxation. It scans - all the input sections and marks the ones that are relaxable (i.e., - literal sections with L32R relocations against them). It then - collect source_reloc information for all the relocations against - those relaxable sections. */ -static bfd_boolean -analyze_relocations (link_info) - struct bfd_link_info *link_info; +/* Structure for a text action record. */ +struct text_action_struct { - bfd *abfd; - asection *sec; - bfd_boolean is_relaxable = FALSE; + text_action_t action; + asection *sec; /* Optional */ + bfd_vma offset; + bfd_vma virtual_offset; /* Zero except for adding literals. */ + int removed_bytes; + literal_value value; /* Only valid when adding literals. */ - /* Initialize the per-section relaxation info. */ - for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link_next) - for (sec = abfd->sections; sec != NULL; sec = sec->next) - { - init_xtensa_relax_info (sec); - } + text_action *next; +}; - /* Mark relaxable sections (and count relocations against each one). */ - for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link_next) - for (sec = abfd->sections; sec != NULL; sec = sec->next) - { - if (!find_relaxable_sections (abfd, sec, link_info, &is_relaxable)) - return FALSE; - } - /* Bail out if there are no relaxable sections. */ - if (!is_relaxable) - return TRUE; +/* List of all of the actions taken on a text section. */ +struct text_action_list_struct +{ + text_action *head; +}; - /* Allocate space for source_relocs. */ - for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link_next) - for (sec = abfd->sections; sec != NULL; sec = sec->next) - { - xtensa_relax_info *relax_info; - relax_info = get_xtensa_relax_info (sec); - if (relax_info->is_relaxable_literal_section) - { - relax_info->src_relocs = (source_reloc *) - bfd_malloc (relax_info->src_count * sizeof (source_reloc)); - } - } +static text_action *find_fill_action + PARAMS ((text_action_list *, asection *, bfd_vma)); +static int compute_removed_action_diff + PARAMS ((const text_action *, asection *, bfd_vma, int, int)); +static void adjust_fill_action + PARAMS ((text_action *, int)); +static void text_action_add + PARAMS ((text_action_list *, text_action_t, asection *, bfd_vma, int)); +static void text_action_add_literal + PARAMS ((text_action_list *, text_action_t, const r_reloc *, + const literal_value *, int)); +static bfd_vma offset_with_removed_text + PARAMS ((text_action_list *, bfd_vma)); +static bfd_vma offset_with_removed_text_before_fill + PARAMS ((text_action_list *, bfd_vma)); +static text_action *find_insn_action + PARAMS ((text_action_list *, bfd_vma)); +#if DEBUG +static void print_action_list + PARAMS ((FILE *, text_action_list *)); +#endif + + +text_action * +find_fill_action (l, sec, offset) + text_action_list *l; + asection *sec; + bfd_vma offset; +{ + text_action **m_p; + + /* It is not necessary to fill at the end of a section. */ + if (sec->size == offset) + return NULL; + + for (m_p = &l->head; + *m_p != NULL && (*m_p)->offset <= offset; + m_p = &(*m_p)->next) + { + text_action *t = *m_p; + /* When the action is another fill at the same address, + just increase the size. */ + if (t->offset == offset && t->action == ta_fill) + return t; + } + return NULL; +} + + +static int +compute_removed_action_diff (ta, sec, offset, removed, removable_space) + const text_action *ta; + asection *sec; + bfd_vma offset; + int removed; + int removable_space; +{ + int new_removed; + int current_removed = 0; + + if (ta != NULL) + current_removed = ta->removed_bytes; + + BFD_ASSERT (ta == NULL || ta->offset == offset); + BFD_ASSERT (ta == NULL || ta->action == ta_fill); + + /* It is not necessary to fill at the end of a section. Clean this up. */ + if (sec->size == offset) + new_removed = removable_space - 0; + else + { + int space; + int added = -removed - current_removed; + /* Ignore multiples of the section alignment. */ + added = ((1 << sec->alignment_power) - 1) & added; + new_removed = (-added); + + /* Modify for removable. */ + space = removable_space - new_removed; + new_removed = (removable_space + - (((1 << sec->alignment_power) - 1) & space)); + } + return (new_removed - current_removed); +} + + +void +adjust_fill_action (ta, fill_diff) + text_action *ta; + int fill_diff; +{ + ta->removed_bytes += fill_diff; +} + + +/* Add a modification action to the text. For the case of adding or + removing space, modify any current fill and assume that + "unreachable_space" bytes can be freely contracted. Note that a + negative removed value is a fill. */ + +static void +text_action_add (l, action, sec, offset, removed) + text_action_list *l; + text_action_t action; + asection *sec; + bfd_vma offset; + int removed; +{ + text_action **m_p; + text_action *ta; + + /* It is not necessary to fill at the end of a section. */ + if (action == ta_fill && sec->size == offset) + return; + + /* It is not necessary to fill 0 bytes. */ + if (action == ta_fill && removed == 0) + return; + + for (m_p = &l->head; + *m_p != NULL && (*m_p)->offset <= offset; + m_p = &(*m_p)->next) + { + text_action *t = *m_p; + /* When the action is another fill at the same address, + just increase the size. */ + if (t->offset == offset && t->action == ta_fill && action == ta_fill) + { + t->removed_bytes += removed; + return; + } + } + + /* Create a new record and fill it up. */ + ta = (text_action *) bfd_zmalloc (sizeof (text_action)); + ta->action = action; + ta->sec = sec; + ta->offset = offset; + ta->removed_bytes = removed; + ta->next = (*m_p); + *m_p = ta; +} + + +static void +text_action_add_literal (l, action, loc, value, removed) + text_action_list *l; + text_action_t action; + const r_reloc *loc; + const literal_value *value; + int removed; +{ + text_action **m_p; + text_action *ta; + asection *sec = r_reloc_get_section (loc); + bfd_vma offset = loc->target_offset; + bfd_vma virtual_offset = loc->virtual_offset; + + BFD_ASSERT (action == ta_add_literal); + + for (m_p = &l->head; *m_p != NULL; m_p = &(*m_p)->next) + { + if ((*m_p)->offset > offset + && ((*m_p)->offset != offset + || (*m_p)->virtual_offset > virtual_offset)) + break; + } + + /* Create a new record and fill it up. */ + ta = (text_action *) bfd_zmalloc (sizeof (text_action)); + ta->action = action; + ta->sec = sec; + ta->offset = offset; + ta->virtual_offset = virtual_offset; + ta->value = *value; + ta->removed_bytes = removed; + ta->next = (*m_p); + *m_p = ta; +} + + +bfd_vma +offset_with_removed_text (action_list, offset) + text_action_list *action_list; + bfd_vma offset; +{ + text_action *r; + int removed = 0; + + for (r = action_list->head; r && r->offset <= offset; r = r->next) + { + if (r->offset < offset + || (r->action == ta_fill && r->removed_bytes < 0)) + removed += r->removed_bytes; + } + + return (offset - removed); +} + + +bfd_vma +offset_with_removed_text_before_fill (action_list, offset) + text_action_list *action_list; + bfd_vma offset; +{ + text_action *r; + int removed = 0; + + for (r = action_list->head; r && r->offset < offset; r = r->next) + removed += r->removed_bytes; + + return (offset - removed); +} + + +/* The find_insn_action routine will only find non-fill actions. */ + +text_action * +find_insn_action (action_list, offset) + text_action_list *action_list; + bfd_vma offset; +{ + text_action *t; + for (t = action_list->head; t; t = t->next) + { + if (t->offset == offset) + { + switch (t->action) + { + case ta_none: + case ta_fill: + break; + case ta_remove_insn: + case ta_remove_longcall: + case ta_convert_longcall: + case ta_narrow_insn: + case ta_widen_insn: + return t; + case ta_remove_literal: + case ta_add_literal: + BFD_ASSERT (0); + break; + } + } + } + return NULL; +} + + +#if DEBUG + +static void +print_action_list (fp, action_list) + FILE *fp; + text_action_list *action_list; +{ + text_action *r; + + fprintf (fp, "Text Action\n"); + for (r = action_list->head; r != NULL; r = r->next) + { + const char *t = "unknown"; + switch (r->action) + { + case ta_remove_insn: + t = "remove_insn"; break; + case ta_remove_longcall: + t = "remove_longcall"; break; + case ta_convert_longcall: + t = "remove_longcall"; break; + case ta_narrow_insn: + t = "narrow_insn"; break; + case ta_widen_insn: + t = "widen_insn"; break; + case ta_fill: + t = "fill"; break; + case ta_none: + t = "none"; break; + case ta_remove_literal: + t = "remove_literal"; break; + case ta_add_literal: + t = "add_literal"; break; + } + + fprintf (fp, "%s: %s[0x%lx] \"%s\" %d\n", + r->sec->owner->filename, + r->sec->name, r->offset, t, r->removed_bytes); + } +} + +#endif /* DEBUG */ + + +/* Lists of literals being coalesced or removed. */ + +/* In the usual case, the literal identified by "from" is being + coalesced with another literal identified by "to". If the literal is + unused and is being removed altogether, "to.abfd" will be NULL. + The removed_literal entries are kept on a per-section list, sorted + by the "from" offset field. */ + +typedef struct removed_literal_struct removed_literal; +typedef struct removed_literal_list_struct removed_literal_list; + +struct removed_literal_struct +{ + r_reloc from; + r_reloc to; + removed_literal *next; +}; + +struct removed_literal_list_struct +{ + removed_literal *head; + removed_literal *tail; +}; + + +static void add_removed_literal + PARAMS ((removed_literal_list *, const r_reloc *, const r_reloc *)); +static removed_literal *find_removed_literal + PARAMS ((removed_literal_list *, bfd_vma)); +#if DEBUG +static void print_removed_literals + PARAMS ((FILE *, removed_literal_list *)); +#endif /* DEBUG */ + + +/* Record that the literal at "from" is being removed. If "to" is not + NULL, the "from" literal is being coalesced with the "to" literal. */ + +static void +add_removed_literal (removed_list, from, to) + removed_literal_list *removed_list; + const r_reloc *from; + const r_reloc *to; +{ + removed_literal *r, *new_r, *next_r; + + new_r = (removed_literal *) bfd_zmalloc (sizeof (removed_literal)); + + new_r->from = *from; + if (to) + new_r->to = *to; + else + new_r->to.abfd = NULL; + new_r->next = NULL; + + r = removed_list->head; + if (r == NULL) + { + removed_list->head = new_r; + removed_list->tail = new_r; + } + /* Special check for common case of append. */ + else if (removed_list->tail->from.target_offset < from->target_offset) + { + removed_list->tail->next = new_r; + removed_list->tail = new_r; + } + else + { + while (r->from.target_offset < from->target_offset + && r->next != NULL) + { + r = r->next; + } + next_r = r->next; + r->next = new_r; + new_r->next = next_r; + if (next_r == NULL) + removed_list->tail = new_r; + } +} + + +/* Check if the list of removed literals contains an entry for the + given address. Return the entry if found. */ + +static removed_literal * +find_removed_literal (removed_list, addr) + removed_literal_list *removed_list; + bfd_vma addr; +{ + removed_literal *r = removed_list->head; + while (r && r->from.target_offset < addr) + r = r->next; + if (r && r->from.target_offset == addr) + return r; + return NULL; +} + + +#if DEBUG + +static void +print_removed_literals (fp, removed_list) + FILE *fp; + removed_literal_list *removed_list; +{ + removed_literal *r; + r = removed_list->head; + if (r) + fprintf (fp, "Removed Literals\n"); + for (; r != NULL; r = r->next) + { + print_r_reloc (fp, &r->from); + fprintf (fp, " => "); + if (r->to.abfd == NULL) + fprintf (fp, "REMOVED"); + else + print_r_reloc (fp, &r->to); + fprintf (fp, "\n"); + } +} + +#endif /* DEBUG */ + + +/* Per-section data for relaxation. */ + +typedef struct reloc_bfd_fix_struct reloc_bfd_fix; + +struct xtensa_relax_info_struct +{ + bfd_boolean is_relaxable_literal_section; + bfd_boolean is_relaxable_asm_section; + int visited; /* Number of times visited. */ + + source_reloc *src_relocs; /* Array[src_count]. */ + int src_count; + int src_next; /* Next src_relocs entry to assign. */ + + removed_literal_list removed_list; + text_action_list action_list; + + reloc_bfd_fix *fix_list; + reloc_bfd_fix *fix_array; + unsigned fix_array_count; + + /* Support for expanding the reloc array that is stored + in the section structure. If the relocations have been + reallocated, the newly allocated relocations will be referenced + here along with the actual size allocated. The relocation + count will always be found in the section structure. */ + Elf_Internal_Rela *allocated_relocs; + unsigned relocs_count; + unsigned allocated_relocs_count; +}; + +struct elf_xtensa_section_data +{ + struct bfd_elf_section_data elf; + xtensa_relax_info relax_info; +}; + +static void init_xtensa_relax_info + PARAMS ((asection *)); +static xtensa_relax_info *get_xtensa_relax_info + PARAMS ((asection *)); + + +static bfd_boolean +elf_xtensa_new_section_hook (abfd, sec) + bfd *abfd; + asection *sec; +{ + struct elf_xtensa_section_data *sdata; + bfd_size_type amt = sizeof (*sdata); + + sdata = (struct elf_xtensa_section_data *) bfd_zalloc (abfd, amt); + if (sdata == NULL) + return FALSE; + sec->used_by_bfd = (PTR) sdata; + + return _bfd_elf_new_section_hook (abfd, sec); +} + + +static void +init_xtensa_relax_info (sec) + asection *sec; +{ + xtensa_relax_info *relax_info = get_xtensa_relax_info (sec); + + relax_info->is_relaxable_literal_section = FALSE; + relax_info->is_relaxable_asm_section = FALSE; + relax_info->visited = 0; + + relax_info->src_relocs = NULL; + relax_info->src_count = 0; + relax_info->src_next = 0; + + relax_info->removed_list.head = NULL; + relax_info->removed_list.tail = NULL; + + relax_info->action_list.head = NULL; + + relax_info->fix_list = NULL; + relax_info->fix_array = NULL; + relax_info->fix_array_count = 0; + + relax_info->allocated_relocs = NULL; + relax_info->relocs_count = 0; + relax_info->allocated_relocs_count = 0; +} + + +static xtensa_relax_info * +get_xtensa_relax_info (sec) + asection *sec; +{ + struct elf_xtensa_section_data *section_data; + + /* No info available if no section or if it is an output section. */ + if (!sec || sec == sec->output_section) + return NULL; + + section_data = (struct elf_xtensa_section_data *) elf_section_data (sec); + return §ion_data->relax_info; +} + + +/* Coalescing literals may require a relocation to refer to a section in + a different input file, but the standard relocation information + cannot express that. Instead, the reloc_bfd_fix structures are used + to "fix" the relocations that refer to sections in other input files. + These structures are kept on per-section lists. The "src_type" field + records the relocation type in case there are multiple relocations on + the same location. FIXME: This is ugly; an alternative might be to + add new symbols with the "owner" field to some other input file. */ + +struct reloc_bfd_fix_struct +{ + asection *src_sec; + bfd_vma src_offset; + unsigned src_type; /* Relocation type. */ + + bfd *target_abfd; + asection *target_sec; + bfd_vma target_offset; + bfd_boolean translated; + + reloc_bfd_fix *next; +}; + + +static reloc_bfd_fix *reloc_bfd_fix_init + PARAMS ((asection *, bfd_vma, unsigned, bfd *, asection *, bfd_vma, + bfd_boolean)); +static void add_fix + PARAMS ((asection *, reloc_bfd_fix *)); +static int fix_compare + PARAMS ((const PTR, const PTR)); +static void cache_fix_array + PARAMS ((asection *)); +static reloc_bfd_fix *get_bfd_fix + PARAMS ((asection *, bfd_vma, unsigned)); + + +static reloc_bfd_fix * +reloc_bfd_fix_init (src_sec, src_offset, src_type, + target_abfd, target_sec, target_offset, translated) + asection *src_sec; + bfd_vma src_offset; + unsigned src_type; + bfd *target_abfd; + asection *target_sec; + bfd_vma target_offset; + bfd_boolean translated; +{ + reloc_bfd_fix *fix; + + fix = (reloc_bfd_fix *) bfd_malloc (sizeof (reloc_bfd_fix)); + fix->src_sec = src_sec; + fix->src_offset = src_offset; + fix->src_type = src_type; + fix->target_abfd = target_abfd; + fix->target_sec = target_sec; + fix->target_offset = target_offset; + fix->translated = translated; + + return fix; +} + + +static void +add_fix (src_sec, fix) + asection *src_sec; + reloc_bfd_fix *fix; +{ + xtensa_relax_info *relax_info; + + relax_info = get_xtensa_relax_info (src_sec); + fix->next = relax_info->fix_list; + relax_info->fix_list = fix; +} + + +static int +fix_compare (ap, bp) + const PTR ap; + const PTR bp; +{ + const reloc_bfd_fix *a = (const reloc_bfd_fix *) ap; + const reloc_bfd_fix *b = (const reloc_bfd_fix *) bp; + + if (a->src_offset != b->src_offset) + return (a->src_offset - b->src_offset); + return (a->src_type - b->src_type); +} + + +static void +cache_fix_array (sec) + asection *sec; +{ + unsigned i, count = 0; + reloc_bfd_fix *r; + xtensa_relax_info *relax_info = get_xtensa_relax_info (sec); + + if (relax_info == NULL) + return; + if (relax_info->fix_list == NULL) + return; + + for (r = relax_info->fix_list; r != NULL; r = r->next) + count++; + + relax_info->fix_array = + (reloc_bfd_fix *) bfd_malloc (sizeof (reloc_bfd_fix) * count); + relax_info->fix_array_count = count; + + r = relax_info->fix_list; + for (i = 0; i < count; i++, r = r->next) + { + relax_info->fix_array[count - 1 - i] = *r; + relax_info->fix_array[count - 1 - i].next = NULL; + } + + qsort (relax_info->fix_array, relax_info->fix_array_count, + sizeof (reloc_bfd_fix), fix_compare); +} + + +static reloc_bfd_fix * +get_bfd_fix (sec, offset, type) + asection *sec; + bfd_vma offset; + unsigned type; +{ + xtensa_relax_info *relax_info = get_xtensa_relax_info (sec); + reloc_bfd_fix *rv; + reloc_bfd_fix key; + + if (relax_info == NULL) + return NULL; + if (relax_info->fix_list == NULL) + return NULL; + + if (relax_info->fix_array == NULL) + cache_fix_array (sec); + + key.src_offset = offset; + key.src_type = type; + rv = bsearch (&key, relax_info->fix_array, relax_info->fix_array_count, + sizeof (reloc_bfd_fix), fix_compare); + return rv; +} + + +/* Section caching. */ + +typedef struct section_cache_struct section_cache_t; + +struct section_cache_struct +{ + asection *sec; + + bfd_byte *contents; /* Cache of the section contents. */ + bfd_size_type content_length; + + property_table_entry *ptbl; /* Cache of the section property table. */ + unsigned pte_count; + + Elf_Internal_Rela *relocs; /* Cache of the section relocations. */ + unsigned reloc_count; +}; + + +static void init_section_cache + PARAMS ((section_cache_t *)); +static bfd_boolean section_cache_section + PARAMS ((section_cache_t *, asection *, struct bfd_link_info *)); +static void clear_section_cache + PARAMS ((section_cache_t *)); + + +static void +init_section_cache (sec_cache) + section_cache_t *sec_cache; +{ + memset (sec_cache, 0, sizeof (*sec_cache)); +} + + +static bfd_boolean +section_cache_section (sec_cache, sec, link_info) + section_cache_t *sec_cache; + asection *sec; + struct bfd_link_info *link_info; +{ + bfd *abfd; + property_table_entry *prop_table = NULL; + int ptblsize = 0; + bfd_byte *contents = NULL; + Elf_Internal_Rela *internal_relocs = NULL; + bfd_size_type sec_size; + + if (sec == NULL) + return FALSE; + if (sec == sec_cache->sec) + return TRUE; + + abfd = sec->owner; + sec_size = bfd_get_section_limit (abfd, sec); + + /* Get the contents. */ + contents = retrieve_contents (abfd, sec, link_info->keep_memory); + if (contents == NULL && sec_size != 0) + goto err; + + /* Get the relocations. */ + internal_relocs = retrieve_internal_relocs (abfd, sec, + link_info->keep_memory); + + /* Get the entry table. */ + ptblsize = xtensa_read_table_entries (abfd, sec, &prop_table, + XTENSA_PROP_SEC_NAME, FALSE); + if (ptblsize < 0) + goto err; + + /* Fill in the new section cache. */ + clear_section_cache (sec_cache); + memset (sec_cache, 0, sizeof (sec_cache)); + + sec_cache->sec = sec; + sec_cache->contents = contents; + sec_cache->content_length = sec_size; + sec_cache->relocs = internal_relocs; + sec_cache->reloc_count = sec->reloc_count; + sec_cache->pte_count = ptblsize; + sec_cache->ptbl = prop_table; + + return TRUE; + + err: + release_contents (sec, contents); + release_internal_relocs (sec, internal_relocs); + if (prop_table) + free (prop_table); + return FALSE; +} + + +static void +clear_section_cache (sec_cache) + section_cache_t *sec_cache; +{ + if (sec_cache->sec) + { + release_contents (sec_cache->sec, sec_cache->contents); + release_internal_relocs (sec_cache->sec, sec_cache->relocs); + if (sec_cache->ptbl) + free (sec_cache->ptbl); + memset (sec_cache, 0, sizeof (sec_cache)); + } +} + + +/* Extended basic blocks. */ + +/* An ebb_struct represents an Extended Basic Block. Within this + range, we guarantee that all instructions are decodable, the + property table entries are contiguous, and no property table + specifies a segment that cannot have instructions moved. This + structure contains caches of the contents, property table and + relocations for the specified section for easy use. The range is + specified by ranges of indices for the byte offset, property table + offsets and relocation offsets. These must be consistent. */ + +typedef struct ebb_struct ebb_t; + +struct ebb_struct +{ + asection *sec; + + bfd_byte *contents; /* Cache of the section contents. */ + bfd_size_type content_length; + + property_table_entry *ptbl; /* Cache of the section property table. */ + unsigned pte_count; + + Elf_Internal_Rela *relocs; /* Cache of the section relocations. */ + unsigned reloc_count; + + bfd_vma start_offset; /* Offset in section. */ + unsigned start_ptbl_idx; /* Offset in the property table. */ + unsigned start_reloc_idx; /* Offset in the relocations. */ + + bfd_vma end_offset; + unsigned end_ptbl_idx; + unsigned end_reloc_idx; + + bfd_boolean ends_section; /* Is this the last ebb in a section? */ + + /* The unreachable property table at the end of this set of blocks; + NULL if the end is not an unreachable block. */ + property_table_entry *ends_unreachable; +}; + + +enum ebb_target_enum +{ + EBB_NO_ALIGN = 0, + EBB_DESIRE_TGT_ALIGN, + EBB_REQUIRE_TGT_ALIGN, + EBB_REQUIRE_LOOP_ALIGN, + EBB_REQUIRE_ALIGN +}; + + +/* proposed_action_struct is similar to the text_action_struct except + that is represents a potential transformation, not one that will + occur. We build a list of these for an extended basic block + and use them to compute the actual actions desired. We must be + careful that the entire set of actual actions we perform do not + break any relocations that would fit if the actions were not + performed. */ + +typedef struct proposed_action_struct proposed_action; + +struct proposed_action_struct +{ + enum ebb_target_enum align_type; /* for the target alignment */ + bfd_vma alignment_pow; + text_action_t action; + bfd_vma offset; + int removed_bytes; + bfd_boolean do_action; /* If false, then we will not perform the action. */ +}; + + +/* The ebb_constraint_struct keeps a set of proposed actions for an + extended basic block. */ + +typedef struct ebb_constraint_struct ebb_constraint; + +struct ebb_constraint_struct +{ + ebb_t ebb; + bfd_boolean start_movable; + + /* Bytes of extra space at the beginning if movable. */ + int start_extra_space; + + enum ebb_target_enum start_align; + + bfd_boolean end_movable; + + /* Bytes of extra space at the end if movable. */ + int end_extra_space; + + unsigned action_count; + unsigned action_allocated; + + /* Array of proposed actions. */ + proposed_action *actions; + + /* Action alignments -- one for each proposed action. */ + enum ebb_target_enum *action_aligns; +}; + + +static void init_ebb_constraint + PARAMS ((ebb_constraint *)); +static void free_ebb_constraint + PARAMS ((ebb_constraint *)); +static void init_ebb + PARAMS ((ebb_t *, asection *, bfd_byte *, bfd_size_type, + property_table_entry *, unsigned, Elf_Internal_Rela *, unsigned)); +static bfd_boolean extend_ebb_bounds + PARAMS ((ebb_t *)); +static bfd_boolean extend_ebb_bounds_forward + PARAMS ((ebb_t *)); +static bfd_boolean extend_ebb_bounds_backward + PARAMS ((ebb_t *)); +static bfd_size_type insn_block_decodable_len + PARAMS ((bfd_byte *, bfd_size_type, bfd_vma, bfd_size_type)); +static void ebb_propose_action + PARAMS ((ebb_constraint *, enum ebb_target_enum, bfd_vma, text_action_t, + bfd_vma, int, bfd_boolean)); +static void ebb_add_proposed_action + PARAMS ((ebb_constraint *, proposed_action *)); + + +static void +init_ebb_constraint (c) + ebb_constraint *c; +{ + memset (c, 0, sizeof (ebb_constraint)); +} + + +static void +free_ebb_constraint (c) + ebb_constraint *c; +{ + if (c->actions != NULL) + free (c->actions); +} + + +static void +init_ebb (ebb, sec, contents, content_length, prop_table, ptblsize, + internal_relocs, reloc_count) + ebb_t *ebb; + asection *sec; + bfd_byte *contents; + bfd_size_type content_length; + property_table_entry *prop_table; + unsigned ptblsize; + Elf_Internal_Rela *internal_relocs; + unsigned reloc_count; +{ + memset (ebb, 0, sizeof (ebb_t)); + ebb->sec = sec; + ebb->contents = contents; + ebb->content_length = content_length; + ebb->ptbl = prop_table; + ebb->pte_count = ptblsize; + ebb->relocs = internal_relocs; + ebb->reloc_count = reloc_count; + ebb->start_offset = 0; + ebb->end_offset = ebb->content_length - 1; + ebb->start_ptbl_idx = 0; + ebb->end_ptbl_idx = ptblsize; + ebb->start_reloc_idx = 0; + ebb->end_reloc_idx = reloc_count; +} + + +/* Extend the ebb to all decodable contiguous sections. The algorithm + for building a basic block around an instruction is to push it + forward until we hit the end of a section, an unreachable block or + a block that cannot be transformed. Then we push it backwards + searching for similar conditions. */ + +static bfd_boolean +extend_ebb_bounds (ebb) + ebb_t *ebb; +{ + if (!extend_ebb_bounds_forward (ebb)) + return FALSE; + if (!extend_ebb_bounds_backward (ebb)) + return FALSE; + return TRUE; +} + + +static bfd_boolean +extend_ebb_bounds_forward (ebb) + ebb_t *ebb; +{ + property_table_entry *the_entry, *new_entry; + + the_entry = &ebb->ptbl[ebb->end_ptbl_idx]; + + /* Stop when (1) we cannot decode an instruction, (2) we are at + the end of the property tables, (3) we hit a non-contiguous property + table entry, (4) we hit a NO_TRANSFORM region. */ + + while (1) + { + bfd_vma entry_end; + bfd_size_type insn_block_len; + + entry_end = the_entry->address - ebb->sec->vma + the_entry->size; + insn_block_len = + insn_block_decodable_len (ebb->contents, ebb->content_length, + ebb->end_offset, + entry_end - ebb->end_offset); + if (insn_block_len != (entry_end - ebb->end_offset)) + { + (*_bfd_error_handler) + (_("%B(%A+0x%lx): could not decode instruction; possible configuration mismatch"), + ebb->sec->owner, ebb->sec, ebb->end_offset + insn_block_len); + return FALSE; + } + ebb->end_offset += insn_block_len; + + if (ebb->end_offset == ebb->sec->size) + ebb->ends_section = TRUE; + + /* Update the reloc counter. */ + while (ebb->end_reloc_idx + 1 < ebb->reloc_count + && (ebb->relocs[ebb->end_reloc_idx + 1].r_offset + < ebb->end_offset)) + { + ebb->end_reloc_idx++; + } + + if (ebb->end_ptbl_idx + 1 == ebb->pte_count) + return TRUE; + + new_entry = &ebb->ptbl[ebb->end_ptbl_idx + 1]; + if (((new_entry->flags & XTENSA_PROP_INSN) == 0) + || ((new_entry->flags & XTENSA_PROP_INSN_NO_TRANSFORM) != 0) + || ((the_entry->flags & XTENSA_PROP_ALIGN) != 0)) + break; + + if (the_entry->address + the_entry->size != new_entry->address) + break; + + the_entry = new_entry; + ebb->end_ptbl_idx++; + } + + /* Quick check for an unreachable or end of file just at the end. */ + if (ebb->end_ptbl_idx + 1 == ebb->pte_count) + { + if (ebb->end_offset == ebb->content_length) + ebb->ends_section = TRUE; + } + else + { + new_entry = &ebb->ptbl[ebb->end_ptbl_idx + 1]; + if ((new_entry->flags & XTENSA_PROP_UNREACHABLE) != 0 + && the_entry->address + the_entry->size == new_entry->address) + ebb->ends_unreachable = new_entry; + } + + /* Any other ending requires exact alignment. */ + return TRUE; +} + + +static bfd_boolean +extend_ebb_bounds_backward (ebb) + ebb_t *ebb; +{ + property_table_entry *the_entry, *new_entry; + + the_entry = &ebb->ptbl[ebb->start_ptbl_idx]; + + /* Stop when (1) we cannot decode the instructions in the current entry. + (2) we are at the beginning of the property tables, (3) we hit a + non-contiguous property table entry, (4) we hit a NO_TRANSFORM region. */ + + while (1) + { + bfd_vma block_begin; + bfd_size_type insn_block_len; + + block_begin = the_entry->address - ebb->sec->vma; + insn_block_len = + insn_block_decodable_len (ebb->contents, ebb->content_length, + block_begin, + ebb->start_offset - block_begin); + if (insn_block_len != ebb->start_offset - block_begin) + { + (*_bfd_error_handler) + (_("%B(%A+0x%lx): could not decode instruction; possible configuration mismatch"), + ebb->sec->owner, ebb->sec, ebb->end_offset + insn_block_len); + return FALSE; + } + ebb->start_offset -= insn_block_len; + + /* Update the reloc counter. */ + while (ebb->start_reloc_idx > 0 + && (ebb->relocs[ebb->start_reloc_idx - 1].r_offset + >= ebb->start_offset)) + { + ebb->start_reloc_idx--; + } + + if (ebb->start_ptbl_idx == 0) + return TRUE; + + new_entry = &ebb->ptbl[ebb->start_ptbl_idx - 1]; + if ((new_entry->flags & XTENSA_PROP_INSN) == 0 + || ((new_entry->flags & XTENSA_PROP_INSN_NO_TRANSFORM) != 0) + || ((new_entry->flags & XTENSA_PROP_ALIGN) != 0)) + return TRUE; + if (new_entry->address + new_entry->size != the_entry->address) + return TRUE; + + the_entry = new_entry; + ebb->start_ptbl_idx--; + } + return TRUE; +} + + +static bfd_size_type +insn_block_decodable_len (contents, content_len, block_offset, block_len) + bfd_byte *contents; + bfd_size_type content_len; + bfd_vma block_offset; + bfd_size_type block_len; +{ + bfd_vma offset = block_offset; + + while (offset < block_offset + block_len) + { + bfd_size_type insn_len = 0; + + insn_len = insn_decode_len (contents, content_len, offset); + if (insn_len == 0) + return (offset - block_offset); + offset += insn_len; + } + return (offset - block_offset); +} + + +static void +ebb_propose_action (c, align_type, alignment_pow, action, offset, + removed_bytes, do_action) + ebb_constraint *c; + bfd_vma alignment_pow; + enum ebb_target_enum align_type; + text_action_t action; + bfd_vma offset; + int removed_bytes; + bfd_boolean do_action; +{ + proposed_action paction; + paction.align_type = align_type; + paction.alignment_pow = alignment_pow; + paction.action = action; + paction.offset = offset; + paction.removed_bytes = removed_bytes; + paction.do_action = do_action; + ebb_add_proposed_action (c, &paction); +} + + +static void +ebb_add_proposed_action (c, action) + ebb_constraint *c; + proposed_action *action; +{ + unsigned i; + if (c->action_allocated <= c->action_count) + { + unsigned new_allocated = (c->action_count + 2) * 2; + proposed_action *new_actions = (proposed_action *) + bfd_zmalloc (sizeof (proposed_action) * new_allocated); + + for (i = 0; i < c->action_count; i++) + new_actions[i] = c->actions[i]; + if (c->actions != NULL) + free (c->actions); + c->actions = new_actions; + c->action_allocated = new_allocated; + } + c->actions[c->action_count] = *action; + c->action_count++; +} + + +/* Access to internal relocations, section contents and symbols. */ + +/* During relaxation, we need to modify relocations, section contents, + and symbol definitions, and we need to keep the original values from + being reloaded from the input files, i.e., we need to "pin" the + modified values in memory. We also want to continue to observe the + setting of the "keep-memory" flag. The following functions wrap the + standard BFD functions to take care of this for us. */ + +static Elf_Internal_Rela * +retrieve_internal_relocs (abfd, sec, keep_memory) + bfd *abfd; + asection *sec; + bfd_boolean keep_memory; +{ + Elf_Internal_Rela *internal_relocs; + + if ((sec->flags & SEC_LINKER_CREATED) != 0) + return NULL; + + internal_relocs = elf_section_data (sec)->relocs; + if (internal_relocs == NULL) + internal_relocs = (_bfd_elf_link_read_relocs + (abfd, sec, (PTR) NULL, (Elf_Internal_Rela *) NULL, + keep_memory)); + return internal_relocs; +} + + +static void +pin_internal_relocs (sec, internal_relocs) + asection *sec; + Elf_Internal_Rela *internal_relocs; +{ + elf_section_data (sec)->relocs = internal_relocs; +} + + +static void +release_internal_relocs (sec, internal_relocs) + asection *sec; + Elf_Internal_Rela *internal_relocs; +{ + if (internal_relocs + && elf_section_data (sec)->relocs != internal_relocs) + free (internal_relocs); +} + + +static bfd_byte * +retrieve_contents (abfd, sec, keep_memory) + bfd *abfd; + asection *sec; + bfd_boolean keep_memory; +{ + bfd_byte *contents; + bfd_size_type sec_size; + + sec_size = bfd_get_section_limit (abfd, sec); + contents = elf_section_data (sec)->this_hdr.contents; + + if (contents == NULL && sec_size != 0) + { + if (!bfd_malloc_and_get_section (abfd, sec, &contents)) + { + if (contents != NULL) + free (contents); + return NULL; + } + if (keep_memory) + elf_section_data (sec)->this_hdr.contents = contents; + } + return contents; +} + + +static void +pin_contents (sec, contents) + asection *sec; + bfd_byte *contents; +{ + elf_section_data (sec)->this_hdr.contents = contents; +} + + +static void +release_contents (sec, contents) + asection *sec; + bfd_byte *contents; +{ + if (contents && elf_section_data (sec)->this_hdr.contents != contents) + free (contents); +} + + +static Elf_Internal_Sym * +retrieve_local_syms (input_bfd) + bfd *input_bfd; +{ + Elf_Internal_Shdr *symtab_hdr; + Elf_Internal_Sym *isymbuf; + size_t locsymcount; + + symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; + locsymcount = symtab_hdr->sh_info; + + isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents; + if (isymbuf == NULL && locsymcount != 0) + isymbuf = bfd_elf_get_elf_syms (input_bfd, symtab_hdr, locsymcount, 0, + NULL, NULL, NULL); + + /* Save the symbols for this input file so they won't be read again. */ + if (isymbuf && isymbuf != (Elf_Internal_Sym *) symtab_hdr->contents) + symtab_hdr->contents = (unsigned char *) isymbuf; + + return isymbuf; +} + + +/* Code for link-time relaxation. */ + +/* Initialization for relaxation: */ +static bfd_boolean analyze_relocations + PARAMS ((struct bfd_link_info *)); +static bfd_boolean find_relaxable_sections + PARAMS ((bfd *, asection *, struct bfd_link_info *, bfd_boolean *)); +static bfd_boolean collect_source_relocs + PARAMS ((bfd *, asection *, struct bfd_link_info *)); +static bfd_boolean is_resolvable_asm_expansion + PARAMS ((bfd *, asection *, bfd_byte *, Elf_Internal_Rela *, + struct bfd_link_info *, bfd_boolean *)); +static Elf_Internal_Rela *find_associated_l32r_irel + PARAMS ((bfd *, asection *, bfd_byte *, Elf_Internal_Rela *, + Elf_Internal_Rela *)); +static bfd_boolean compute_text_actions + PARAMS ((bfd *, asection *, struct bfd_link_info *)); +static bfd_boolean compute_ebb_proposed_actions + PARAMS ((ebb_constraint *)); +static bfd_boolean compute_ebb_actions + PARAMS ((ebb_constraint *)); +static bfd_boolean check_section_ebb_pcrels_fit + PARAMS ((bfd *, asection *, bfd_byte *, Elf_Internal_Rela *, + const ebb_constraint *)); +static bfd_boolean check_section_ebb_reduces + PARAMS ((const ebb_constraint *)); +static void text_action_add_proposed + PARAMS ((text_action_list *, const ebb_constraint *, asection *)); +static int compute_fill_extra_space + PARAMS ((property_table_entry *)); + +/* First pass: */ +static bfd_boolean compute_removed_literals + PARAMS ((bfd *, asection *, struct bfd_link_info *, value_map_hash_table *)); +static Elf_Internal_Rela *get_irel_at_offset + PARAMS ((asection *, Elf_Internal_Rela *, bfd_vma)); +static bfd_boolean is_removable_literal + PARAMS ((const source_reloc *, int, const source_reloc *, int)); +static bfd_boolean remove_dead_literal + PARAMS ((bfd *, asection *, struct bfd_link_info *, Elf_Internal_Rela *, + Elf_Internal_Rela *, source_reloc *, property_table_entry *, int)); +static bfd_boolean identify_literal_placement + PARAMS ((bfd *, asection *, bfd_byte *, struct bfd_link_info *, + value_map_hash_table *, bfd_boolean *, Elf_Internal_Rela *, int, + source_reloc *, property_table_entry *, int, section_cache_t *, + bfd_boolean)); +static bfd_boolean relocations_reach + PARAMS ((source_reloc *, int, const r_reloc *)); +static bfd_boolean coalesce_shared_literal + PARAMS ((asection *, source_reloc *, property_table_entry *, int, + value_map *)); +static bfd_boolean move_shared_literal + PARAMS ((asection *, struct bfd_link_info *, source_reloc *, + property_table_entry *, int, const r_reloc *, + const literal_value *, section_cache_t *)); + +/* Second pass: */ +static bfd_boolean relax_section + PARAMS ((bfd *, asection *, struct bfd_link_info *)); +static bfd_boolean translate_section_fixes + PARAMS ((asection *)); +static bfd_boolean translate_reloc_bfd_fix + PARAMS ((reloc_bfd_fix *)); +static void translate_reloc + PARAMS ((const r_reloc *, r_reloc *)); +static void shrink_dynamic_reloc_sections + PARAMS ((struct bfd_link_info *, bfd *, asection *, Elf_Internal_Rela *)); +static bfd_boolean move_literal + PARAMS ((bfd *, struct bfd_link_info *, asection *, bfd_vma, bfd_byte *, + xtensa_relax_info *, Elf_Internal_Rela **, const literal_value *)); +static bfd_boolean relax_property_section + PARAMS ((bfd *, asection *, struct bfd_link_info *)); + +/* Third pass: */ +static bfd_boolean relax_section_symbols + PARAMS ((bfd *, asection *)); + + +static bfd_boolean +elf_xtensa_relax_section (abfd, sec, link_info, again) + bfd *abfd; + asection *sec; + struct bfd_link_info *link_info; + bfd_boolean *again; +{ + static value_map_hash_table *values = NULL; + static bfd_boolean relocations_analyzed = FALSE; + xtensa_relax_info *relax_info; + + if (!relocations_analyzed) + { + /* Do some overall initialization for relaxation. */ + values = value_map_hash_table_init (); + if (values == NULL) + return FALSE; + relaxing_section = TRUE; + if (!analyze_relocations (link_info)) + return FALSE; + relocations_analyzed = TRUE; + } + *again = FALSE; + + /* Don't mess with linker-created sections. */ + if ((sec->flags & SEC_LINKER_CREATED) != 0) + return TRUE; + + relax_info = get_xtensa_relax_info (sec); + BFD_ASSERT (relax_info != NULL); + + switch (relax_info->visited) + { + case 0: + /* Note: It would be nice to fold this pass into + analyze_relocations, but it is important for this step that the + sections be examined in link order. */ + if (!compute_removed_literals (abfd, sec, link_info, values)) + return FALSE; + *again = TRUE; + break; + + case 1: + if (values) + value_map_hash_table_delete (values); + values = NULL; + if (!relax_section (abfd, sec, link_info)) + return FALSE; + *again = TRUE; + break; + + case 2: + if (!relax_section_symbols (abfd, sec)) + return FALSE; + break; + } + + relax_info->visited++; + return TRUE; +} + + +/* Initialization for relaxation. */ + +/* This function is called once at the start of relaxation. It scans + all the input sections and marks the ones that are relaxable (i.e., + literal sections with L32R relocations against them), and then + collects source_reloc information for all the relocations against + those relaxable sections. During this process, it also detects + longcalls, i.e., calls relaxed by the assembler into indirect + calls, that can be optimized back into direct calls. Within each + extended basic block (ebb) containing an optimized longcall, it + computes a set of "text actions" that can be performed to remove + the L32R associated with the longcall while optionally preserving + branch target alignments. */ + +static bfd_boolean +analyze_relocations (link_info) + struct bfd_link_info *link_info; +{ + bfd *abfd; + asection *sec; + bfd_boolean is_relaxable = FALSE; + + /* Initialize the per-section relaxation info. */ + for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link_next) + for (sec = abfd->sections; sec != NULL; sec = sec->next) + { + init_xtensa_relax_info (sec); + } + + /* Mark relaxable sections (and count relocations against each one). */ + for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link_next) + for (sec = abfd->sections; sec != NULL; sec = sec->next) + { + if (!find_relaxable_sections (abfd, sec, link_info, &is_relaxable)) + return FALSE; + } + + /* Bail out if there are no relaxable sections. */ + if (!is_relaxable) + return TRUE; + + /* Allocate space for source_relocs. */ + for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link_next) + for (sec = abfd->sections; sec != NULL; sec = sec->next) + { + xtensa_relax_info *relax_info; + + relax_info = get_xtensa_relax_info (sec); + if (relax_info->is_relaxable_literal_section + || relax_info->is_relaxable_asm_section) + { + relax_info->src_relocs = (source_reloc *) + bfd_malloc (relax_info->src_count * sizeof (source_reloc)); + } + } + + /* Collect info on relocations against each relaxable section. */ + for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link_next) + for (sec = abfd->sections; sec != NULL; sec = sec->next) + { + if (!collect_source_relocs (abfd, sec, link_info)) + return FALSE; + } + + /* Compute the text actions. */ + for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link_next) + for (sec = abfd->sections; sec != NULL; sec = sec->next) + { + if (!compute_text_actions (abfd, sec, link_info)) + return FALSE; + } + + return TRUE; +} + + +/* Find all the sections that might be relaxed. The motivation for + this pass is that collect_source_relocs() needs to record _all_ the + relocations that target each relaxable section. That is expensive + and unnecessary unless the target section is actually going to be + relaxed. This pass identifies all such sections by checking if + they have L32Rs pointing to them. In the process, the total number + of relocations targeting each section is also counted so that we + know how much space to allocate for source_relocs against each + relaxable literal section. */ + +static bfd_boolean +find_relaxable_sections (abfd, sec, link_info, is_relaxable_p) + bfd *abfd; + asection *sec; + struct bfd_link_info *link_info; + bfd_boolean *is_relaxable_p; +{ + Elf_Internal_Rela *internal_relocs; + bfd_byte *contents; + bfd_boolean ok = TRUE; + unsigned i; + xtensa_relax_info *source_relax_info; + + internal_relocs = retrieve_internal_relocs (abfd, sec, + link_info->keep_memory); + if (internal_relocs == NULL) + return ok; + + contents = retrieve_contents (abfd, sec, link_info->keep_memory); + if (contents == NULL && sec->size != 0) + { + ok = FALSE; + goto error_return; + } + + source_relax_info = get_xtensa_relax_info (sec); + for (i = 0; i < sec->reloc_count; i++) + { + Elf_Internal_Rela *irel = &internal_relocs[i]; + r_reloc r_rel; + asection *target_sec; + xtensa_relax_info *target_relax_info; + + /* If this section has not already been marked as "relaxable", and + if it contains any ASM_EXPAND relocations (marking expanded + longcalls) that can be optimized into direct calls, then mark + the section as "relaxable". */ + if (source_relax_info + && !source_relax_info->is_relaxable_asm_section + && ELF32_R_TYPE (irel->r_info) == R_XTENSA_ASM_EXPAND) + { + bfd_boolean is_reachable = FALSE; + if (is_resolvable_asm_expansion (abfd, sec, contents, irel, + link_info, &is_reachable) + && is_reachable) + { + source_relax_info->is_relaxable_asm_section = TRUE; + *is_relaxable_p = TRUE; + } + } + + r_reloc_init (&r_rel, abfd, irel, contents, + bfd_get_section_limit (abfd, sec)); + + target_sec = r_reloc_get_section (&r_rel); + target_relax_info = get_xtensa_relax_info (target_sec); + if (!target_relax_info) + continue; + + /* Count PC-relative operand relocations against the target section. + Note: The conditions tested here must match the conditions under + which init_source_reloc is called in collect_source_relocs(). */ + if (is_operand_relocation (ELF32_R_TYPE (irel->r_info)) + && (!is_alt_relocation (ELF32_R_TYPE (irel->r_info)) + || is_l32r_relocation (abfd, sec, contents, irel))) + target_relax_info->src_count++; + + if (is_l32r_relocation (abfd, sec, contents, irel) + && r_reloc_is_defined (&r_rel)) + { + /* Mark the target section as relaxable. */ + target_relax_info->is_relaxable_literal_section = TRUE; + *is_relaxable_p = TRUE; + } + } + + error_return: + release_contents (sec, contents); + release_internal_relocs (sec, internal_relocs); + return ok; +} + + +/* Record _all_ the relocations that point to relaxable sections, and + get rid of ASM_EXPAND relocs by either converting them to + ASM_SIMPLIFY or by removing them. */ + +static bfd_boolean +collect_source_relocs (abfd, sec, link_info) + bfd *abfd; + asection *sec; + struct bfd_link_info *link_info; +{ + Elf_Internal_Rela *internal_relocs; + bfd_byte *contents; + bfd_boolean ok = TRUE; + unsigned i; + bfd_size_type sec_size; + + internal_relocs = retrieve_internal_relocs (abfd, sec, + link_info->keep_memory); + if (internal_relocs == NULL) + return ok; + + sec_size = bfd_get_section_limit (abfd, sec); + contents = retrieve_contents (abfd, sec, link_info->keep_memory); + if (contents == NULL && sec_size != 0) + { + ok = FALSE; + goto error_return; + } + + /* Record relocations against relaxable literal sections. */ + for (i = 0; i < sec->reloc_count; i++) + { + Elf_Internal_Rela *irel = &internal_relocs[i]; + r_reloc r_rel; + asection *target_sec; + xtensa_relax_info *target_relax_info; + + r_reloc_init (&r_rel, abfd, irel, contents, sec_size); + + target_sec = r_reloc_get_section (&r_rel); + target_relax_info = get_xtensa_relax_info (target_sec); + + if (target_relax_info + && (target_relax_info->is_relaxable_literal_section + || target_relax_info->is_relaxable_asm_section)) + { + xtensa_opcode opcode = XTENSA_UNDEFINED; + int opnd = -1; + bfd_boolean is_abs_literal = FALSE; + + if (is_alt_relocation (ELF32_R_TYPE (irel->r_info))) + { + /* None of the current alternate relocs are PC-relative, + and only PC-relative relocs matter here. However, we + still need to record the opcode for literal + coalescing. */ + opcode = get_relocation_opcode (abfd, sec, contents, irel); + if (opcode == get_l32r_opcode ()) + { + is_abs_literal = TRUE; + opnd = 1; + } + else + opcode = XTENSA_UNDEFINED; + } + else if (is_operand_relocation (ELF32_R_TYPE (irel->r_info))) + { + opcode = get_relocation_opcode (abfd, sec, contents, irel); + opnd = get_relocation_opnd (opcode, ELF32_R_TYPE (irel->r_info)); + } + + if (opcode != XTENSA_UNDEFINED) + { + int src_next = target_relax_info->src_next++; + source_reloc *s_reloc = &target_relax_info->src_relocs[src_next]; + + init_source_reloc (s_reloc, sec, &r_rel, opcode, opnd, + is_abs_literal); + } + } + } + + /* Now get rid of ASM_EXPAND relocations. At this point, the + src_relocs array for the target literal section may still be + incomplete, but it must at least contain the entries for the L32R + relocations associated with ASM_EXPANDs because they were just + added in the preceding loop over the relocations. */ + + for (i = 0; i < sec->reloc_count; i++) + { + Elf_Internal_Rela *irel = &internal_relocs[i]; + bfd_boolean is_reachable; + + if (!is_resolvable_asm_expansion (abfd, sec, contents, irel, link_info, + &is_reachable)) + continue; + + if (is_reachable) + { + Elf_Internal_Rela *l32r_irel; + r_reloc r_rel; + asection *target_sec; + xtensa_relax_info *target_relax_info; + + /* Mark the source_reloc for the L32R so that it will be + removed in compute_removed_literals(), along with the + associated literal. */ + l32r_irel = find_associated_l32r_irel (abfd, sec, contents, + irel, internal_relocs); + if (l32r_irel == NULL) + continue; + + r_reloc_init (&r_rel, abfd, l32r_irel, contents, sec_size); + + target_sec = r_reloc_get_section (&r_rel); + target_relax_info = get_xtensa_relax_info (target_sec); + + if (target_relax_info + && (target_relax_info->is_relaxable_literal_section + || target_relax_info->is_relaxable_asm_section)) + { + source_reloc *s_reloc; + + /* Search the source_relocs for the entry corresponding to + the l32r_irel. Note: The src_relocs array is not yet + sorted, but it wouldn't matter anyway because we're + searching by source offset instead of target offset. */ + s_reloc = find_source_reloc (target_relax_info->src_relocs, + target_relax_info->src_next, + sec, l32r_irel); + BFD_ASSERT (s_reloc); + s_reloc->is_null = TRUE; + } + + /* Convert this reloc to ASM_SIMPLIFY. */ + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), + R_XTENSA_ASM_SIMPLIFY); + l32r_irel->r_info = ELF32_R_INFO (0, R_XTENSA_NONE); + + pin_internal_relocs (sec, internal_relocs); + } + else + { + /* It is resolvable but doesn't reach. We resolve now + by eliminating the relocation -- the call will remain + expanded into L32R/CALLX. */ + irel->r_info = ELF32_R_INFO (0, R_XTENSA_NONE); + pin_internal_relocs (sec, internal_relocs); + } + } + + error_return: + release_contents (sec, contents); + release_internal_relocs (sec, internal_relocs); + return ok; +} + + +/* Return TRUE if the asm expansion can be resolved. Generally it can + be resolved on a final link or when a partial link locates it in the + same section as the target. Set "is_reachable" flag if the target of + the call is within the range of a direct call, given the current VMA + for this section and the target section. */ + +bfd_boolean +is_resolvable_asm_expansion (abfd, sec, contents, irel, link_info, + is_reachable_p) + bfd *abfd; + asection *sec; + bfd_byte *contents; + Elf_Internal_Rela *irel; + struct bfd_link_info *link_info; + bfd_boolean *is_reachable_p; +{ + asection *target_sec; + bfd_vma target_offset; + r_reloc r_rel; + xtensa_opcode opcode, direct_call_opcode; + bfd_vma self_address; + bfd_vma dest_address; + bfd_boolean uses_l32r; + bfd_size_type sec_size; + + *is_reachable_p = FALSE; + + if (contents == NULL) + return FALSE; + + if (ELF32_R_TYPE (irel->r_info) != R_XTENSA_ASM_EXPAND) + return FALSE; + + sec_size = bfd_get_section_limit (abfd, sec); + opcode = get_expanded_call_opcode (contents + irel->r_offset, + sec_size - irel->r_offset, &uses_l32r); + /* Optimization of longcalls that use CONST16 is not yet implemented. */ + if (!uses_l32r) + return FALSE; + + direct_call_opcode = swap_callx_for_call_opcode (opcode); + if (direct_call_opcode == XTENSA_UNDEFINED) + return FALSE; + + /* Check and see that the target resolves. */ + r_reloc_init (&r_rel, abfd, irel, contents, sec_size); + if (!r_reloc_is_defined (&r_rel)) + return FALSE; + + target_sec = r_reloc_get_section (&r_rel); + target_offset = r_rel.target_offset; + + /* If the target is in a shared library, then it doesn't reach. This + isn't supposed to come up because the compiler should never generate + non-PIC calls on systems that use shared libraries, but the linker + shouldn't crash regardless. */ + if (!target_sec->output_section) + return FALSE; + + /* For relocatable sections, we can only simplify when the output + section of the target is the same as the output section of the + source. */ + if (link_info->relocatable + && (target_sec->output_section != sec->output_section + || is_reloc_sym_weak (abfd, irel))) + return FALSE; + + self_address = (sec->output_section->vma + + sec->output_offset + irel->r_offset + 3); + dest_address = (target_sec->output_section->vma + + target_sec->output_offset + target_offset); + + *is_reachable_p = pcrel_reloc_fits (direct_call_opcode, 0, + self_address, dest_address); + + if ((self_address >> CALL_SEGMENT_BITS) != + (dest_address >> CALL_SEGMENT_BITS)) + return FALSE; + + return TRUE; +} + + +static Elf_Internal_Rela * +find_associated_l32r_irel (abfd, sec, contents, other_irel, internal_relocs) + bfd *abfd; + asection *sec; + bfd_byte *contents; + Elf_Internal_Rela *other_irel; + Elf_Internal_Rela *internal_relocs; +{ + unsigned i; - /* Collect info on relocations against each relaxable section. */ - for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link_next) - for (sec = abfd->sections; sec != NULL; sec = sec->next) - { - if (!collect_source_relocs (abfd, sec, link_info)) - return FALSE; - } + for (i = 0; i < sec->reloc_count; i++) + { + Elf_Internal_Rela *irel = &internal_relocs[i]; - return TRUE; + if (irel == other_irel) + continue; + if (irel->r_offset != other_irel->r_offset) + continue; + if (is_l32r_relocation (abfd, sec, contents, irel)) + return irel; + } + + return NULL; } -/* Find all the literal sections that might be relaxed. The motivation - for this pass is that collect_source_relocs() needs to record _all_ - the relocations that target each relaxable section. That is - expensive and unnecessary unless the target section is actually going - to be relaxed. This pass identifies all such sections by checking if - they have L32Rs pointing to them. In the process, the total number - of relocations targeting each section is also counted so that we - know how much space to allocate for source_relocs against each - relaxable literal section. */ +/* The compute_text_actions function will build a list of potential + transformation actions for code in the extended basic block of each + longcall that is optimized to a direct call. From this list we + generate a set of actions to actually perform that optimizes for + space and, if not using size_opt, maintains branch target + alignments. -static bfd_boolean -find_relaxable_sections (abfd, sec, link_info, is_relaxable_p) + These actions to be performed are placed on a per-section list. + The actual changes are performed by relax_section() in the second + pass. */ + +bfd_boolean +compute_text_actions (abfd, sec, link_info) bfd *abfd; asection *sec; struct bfd_link_info *link_info; - bfd_boolean *is_relaxable_p; { - Elf_Internal_Rela *internal_relocs; + xtensa_relax_info *relax_info; bfd_byte *contents; + Elf_Internal_Rela *internal_relocs; bfd_boolean ok = TRUE; unsigned i; + property_table_entry *prop_table = 0; + int ptblsize = 0; + bfd_size_type sec_size; + static bfd_boolean no_insn_move = FALSE; + + if (no_insn_move) + return ok; + + /* Do nothing if the section contains no optimized longcalls. */ + relax_info = get_xtensa_relax_info (sec); + BFD_ASSERT (relax_info); + if (!relax_info->is_relaxable_asm_section) + return ok; internal_relocs = retrieve_internal_relocs (abfd, sec, link_info->keep_memory); - if (internal_relocs == NULL) - return ok; + if (internal_relocs) + qsort (internal_relocs, sec->reloc_count, sizeof (Elf_Internal_Rela), + internal_reloc_compare); + + sec_size = bfd_get_section_limit (abfd, sec); contents = retrieve_contents (abfd, sec, link_info->keep_memory); - if (contents == NULL && sec->size != 0) + if (contents == NULL && sec_size != 0) { ok = FALSE; goto error_return; } - for (i = 0; i < sec->reloc_count; i++) + ptblsize = xtensa_read_table_entries (abfd, sec, &prop_table, + XTENSA_PROP_SEC_NAME, FALSE); + if (ptblsize < 0) + { + ok = FALSE; + goto error_return; + } + + for (i = 0; i < sec->reloc_count; i++) { Elf_Internal_Rela *irel = &internal_relocs[i]; - r_reloc r_rel; - asection *target_sec; - xtensa_relax_info *target_relax_info; + bfd_vma r_offset; + property_table_entry *the_entry; + int ptbl_idx; + ebb_t *ebb; + ebb_constraint ebb_table; + bfd_size_type simplify_size; + + if (irel && ELF32_R_TYPE (irel->r_info) != R_XTENSA_ASM_SIMPLIFY) + continue; + r_offset = irel->r_offset; - r_reloc_init (&r_rel, abfd, irel); + simplify_size = get_asm_simplify_size (contents, sec_size, r_offset); + if (simplify_size == 0) + { + (*_bfd_error_handler) + (_("%B(%A+0x%lx): could not decode instruction for XTENSA_ASM_SIMPLIFY relocation; possible configuration mismatch"), + sec->owner, sec, r_offset); + continue; + } - target_sec = r_reloc_get_section (&r_rel); - target_relax_info = get_xtensa_relax_info (target_sec); - if (!target_relax_info) - continue; + /* If the instruction table is not around, then don't do this + relaxation. */ + the_entry = elf_xtensa_find_property_entry (prop_table, ptblsize, + sec->vma + irel->r_offset); + if (the_entry == NULL || XTENSA_NO_NOP_REMOVAL) + { + text_action_add (&relax_info->action_list, + ta_convert_longcall, sec, r_offset, + 0); + continue; + } + + /* If the next longcall happens to be at the same address as an + unreachable section of size 0, then skip forward. */ + ptbl_idx = the_entry - prop_table; + while ((the_entry->flags & XTENSA_PROP_UNREACHABLE) + && the_entry->size == 0 + && ptbl_idx + 1 < ptblsize + && (prop_table[ptbl_idx + 1].address + == prop_table[ptbl_idx].address)) + { + ptbl_idx++; + the_entry++; + } - /* Count relocations against the target section. */ - target_relax_info->src_count++; + if (the_entry->flags & XTENSA_PROP_INSN_NO_TRANSFORM) + /* NO_REORDER is OK */ + continue; - if (is_literal_section (target_sec) - && is_l32r_relocation (sec, contents, irel) - && r_reloc_is_defined (&r_rel)) + init_ebb_constraint (&ebb_table); + ebb = &ebb_table.ebb; + init_ebb (ebb, sec, contents, sec_size, prop_table, ptblsize, + internal_relocs, sec->reloc_count); + ebb->start_offset = r_offset + simplify_size; + ebb->end_offset = r_offset + simplify_size; + ebb->start_ptbl_idx = ptbl_idx; + ebb->end_ptbl_idx = ptbl_idx; + ebb->start_reloc_idx = i; + ebb->end_reloc_idx = i; + + if (!extend_ebb_bounds (ebb) + || !compute_ebb_proposed_actions (&ebb_table) + || !compute_ebb_actions (&ebb_table) + || !check_section_ebb_pcrels_fit (abfd, sec, contents, + internal_relocs, &ebb_table) + || !check_section_ebb_reduces (&ebb_table)) { - /* Mark the target section as relaxable. */ - target_relax_info->is_relaxable_literal_section = TRUE; - *is_relaxable_p = TRUE; + /* If anything goes wrong or we get unlucky and something does + not fit, with our plan because of expansion between + critical branches, just convert to a NOP. */ + + text_action_add (&relax_info->action_list, + ta_convert_longcall, sec, r_offset, 0); + i = ebb_table.ebb.end_reloc_idx; + free_ebb_constraint (&ebb_table); + continue; } + + text_action_add_proposed (&relax_info->action_list, &ebb_table, sec); + + /* Update the index so we do not go looking at the relocations + we have already processed. */ + i = ebb_table.ebb.end_reloc_idx; + free_ebb_constraint (&ebb_table); } - error_return: +#if DEBUG + if (relax_info->action_list.head != NULL) + print_action_list (stderr, &relax_info->action_list); +#endif + +error_return: release_contents (sec, contents); release_internal_relocs (sec, internal_relocs); + if (prop_table) + free (prop_table); + return ok; } -/* Record _all_ the relocations that point to relaxable literal - sections, and get rid of ASM_EXPAND relocs by either converting them - to ASM_SIMPLIFY or by removing them. */ +/* Find all of the possible actions for an extended basic block. */ -static bfd_boolean -collect_source_relocs (abfd, sec, link_info) - bfd *abfd; - asection *sec; - struct bfd_link_info *link_info; +bfd_boolean +compute_ebb_proposed_actions (ebb_table) + ebb_constraint *ebb_table; { - Elf_Internal_Rela *internal_relocs; - bfd_byte *contents; - bfd_boolean ok = TRUE; - unsigned i; + const ebb_t *ebb = &ebb_table->ebb; + unsigned rel_idx = ebb->start_reloc_idx; + property_table_entry *entry, *start_entry, *end_entry; - internal_relocs = retrieve_internal_relocs (abfd, sec, - link_info->keep_memory); - if (internal_relocs == NULL) - return ok; + start_entry = &ebb->ptbl[ebb->start_ptbl_idx]; + end_entry = &ebb->ptbl[ebb->end_ptbl_idx]; - contents = retrieve_contents (abfd, sec, link_info->keep_memory); - if (contents == NULL && sec->size != 0) + for (entry = start_entry; entry <= end_entry; entry++) { - ok = FALSE; - goto error_return; - } + bfd_vma offset, start_offset, end_offset; + bfd_size_type insn_len; - /* Record relocations against relaxable literal sections. */ - for (i = 0; i < sec->reloc_count; i++) - { - Elf_Internal_Rela *irel = &internal_relocs[i]; - r_reloc r_rel; - asection *target_sec; - xtensa_relax_info *target_relax_info; + start_offset = entry->address - ebb->sec->vma; + end_offset = entry->address + entry->size - ebb->sec->vma; - r_reloc_init (&r_rel, abfd, irel); + if (entry == start_entry) + start_offset = ebb->start_offset; + if (entry == end_entry) + end_offset = ebb->end_offset; + offset = start_offset; - target_sec = r_reloc_get_section (&r_rel); - target_relax_info = get_xtensa_relax_info (target_sec); + if (offset == entry->address - ebb->sec->vma + && (entry->flags & XTENSA_PROP_INSN_BRANCH_TARGET) != 0) + { + enum ebb_target_enum align_type = EBB_DESIRE_TGT_ALIGN; + BFD_ASSERT (offset != end_offset); + if (offset == end_offset) + return FALSE; - if (target_relax_info - && target_relax_info->is_relaxable_literal_section) + insn_len = insn_decode_len (ebb->contents, ebb->content_length, + offset); + + /* Propose no actions for a section with an undecodable offset. */ + if (insn_len == 0) + { + (*_bfd_error_handler) + (_("%B(%A+0x%lx): could not decode instruction; possible configuration mismatch"), + ebb->sec->owner, ebb->sec, offset); + return FALSE; + } + if (check_branch_target_aligned_address (offset, insn_len)) + align_type = EBB_REQUIRE_TGT_ALIGN; + + ebb_propose_action (ebb_table, align_type, 0, + ta_none, offset, 0, TRUE); + } + + while (offset != end_offset) { + Elf_Internal_Rela *irel; xtensa_opcode opcode; - xtensa_operand opnd; - source_reloc *s_reloc; - int src_next; - src_next = target_relax_info->src_next++; - s_reloc = &target_relax_info->src_relocs[src_next]; + while (rel_idx < ebb->end_reloc_idx + && (ebb->relocs[rel_idx].r_offset < offset + || (ebb->relocs[rel_idx].r_offset == offset + && (ELF32_R_TYPE (ebb->relocs[rel_idx].r_info) + != R_XTENSA_ASM_SIMPLIFY)))) + rel_idx++; + + /* Check for longcall. */ + irel = &ebb->relocs[rel_idx]; + if (irel->r_offset == offset + && ELF32_R_TYPE (irel->r_info) == R_XTENSA_ASM_SIMPLIFY) + { + bfd_size_type simplify_size; - opcode = get_relocation_opcode (sec, contents, irel); - if (opcode == XTENSA_UNDEFINED) - opnd = NULL; - else - opnd = xtensa_get_operand (xtensa_default_isa, opcode, - get_relocation_opnd (irel)); + simplify_size = get_asm_simplify_size (ebb->contents, + ebb->content_length, + irel->r_offset); + if (simplify_size == 0) + { + (*_bfd_error_handler) + (_("%B(%A+0x%lx): could not decode instruction for XTENSA_ASM_SIMPLIFY relocation; possible configuration mismatch"), + ebb->sec->owner, ebb->sec, offset); + return FALSE; + } + + ebb_propose_action (ebb_table, EBB_NO_ALIGN, 0, + ta_convert_longcall, offset, 0, TRUE); + + offset += simplify_size; + continue; + } - init_source_reloc (s_reloc, sec, &r_rel, opnd); + insn_len = insn_decode_len (ebb->contents, ebb->content_length, + offset); + /* If the instruction is undecodable, then report an error. */ + if (insn_len == 0) + { + (*_bfd_error_handler) + (_("%B(%A+0x%lx): could not decode instruction; possible configuration mismatch"), + ebb->sec->owner, ebb->sec, offset); + return FALSE; + } + + if ((entry->flags & XTENSA_PROP_INSN_NO_DENSITY) == 0 + && (entry->flags & XTENSA_PROP_INSN_NO_TRANSFORM) == 0 + && narrow_instruction (ebb->contents, ebb->content_length, + offset, FALSE)) + { + /* Add an instruction narrow action. */ + ebb_propose_action (ebb_table, EBB_NO_ALIGN, 0, + ta_narrow_insn, offset, 0, FALSE); + offset += insn_len; + continue; + } + if ((entry->flags & XTENSA_PROP_INSN_NO_TRANSFORM) == 0 + && widen_instruction (ebb->contents, ebb->content_length, + offset, FALSE)) + { + /* Add an instruction widen action. */ + ebb_propose_action (ebb_table, EBB_NO_ALIGN, 0, + ta_widen_insn, offset, 0, FALSE); + offset += insn_len; + continue; + } + opcode = insn_decode_opcode (ebb->contents, ebb->content_length, + offset, 0); + if (xtensa_opcode_is_loop (xtensa_default_isa, opcode)) + { + /* Check for branch targets. */ + ebb_propose_action (ebb_table, EBB_REQUIRE_LOOP_ALIGN, 0, + ta_none, offset, 0, TRUE); + offset += insn_len; + continue; + } + + offset += insn_len; } } - /* Now get rid of ASM_EXPAND relocations. At this point, the - src_relocs array for the target literal section may still be - incomplete, but it must at least contain the entries for the L32R - relocations associated with ASM_EXPANDs because they were just - added in the preceding loop over the relocations. */ + if (ebb->ends_unreachable) + { + ebb_propose_action (ebb_table, EBB_NO_ALIGN, 0, + ta_fill, ebb->end_offset, 0, TRUE); + } - for (i = 0; i < sec->reloc_count; i++) + return TRUE; +} + + +/* After all of the information has collected about the + transformations possible in an EBB, compute the appropriate actions + here in compute_ebb_actions. We still must check later to make + sure that the actions do not break any relocations. The algorithm + used here is pretty greedy. Basically, it removes as many no-ops + as possible so that the end of the EBB has the same alignment + characteristics as the original. First, it uses narrowing, then + fill space at the end of the EBB, and finally widenings. If that + does not work, it tries again with one fewer no-op removed. The + optimization will only be performed if all of the branch targets + that were aligned before transformation are also aligned after the + transformation. + + When the size_opt flag is set, ignore the branch target alignments, + narrow all wide instructions, and remove all no-ops unless the end + of the EBB prevents it. */ + +bfd_boolean +compute_ebb_actions (ebb_table) + ebb_constraint *ebb_table; +{ + unsigned i = 0; + unsigned j; + int removed_bytes = 0; + ebb_t *ebb = &ebb_table->ebb; + unsigned seg_idx_start = 0; + unsigned seg_idx_end = 0; + + /* We perform this like the assembler relaxation algorithm: Start by + assuming all instructions are narrow and all no-ops removed; then + walk through.... */ + + /* For each segment of this that has a solid constraint, check to + see if there are any combinations that will keep the constraint. + If so, use it. */ + for (seg_idx_end = 0; seg_idx_end < ebb_table->action_count; seg_idx_end++) { - Elf_Internal_Rela *irel = &internal_relocs[i]; - bfd_boolean is_reachable; + bfd_boolean requires_text_end_align = FALSE; + unsigned longcall_count = 0; + unsigned longcall_convert_count = 0; + unsigned narrowable_count = 0; + unsigned narrowable_convert_count = 0; + unsigned widenable_count = 0; + unsigned widenable_convert_count = 0; - if (!is_resolvable_asm_expansion (abfd, sec, contents, irel, link_info, - &is_reachable)) - continue; + proposed_action *action = NULL; + int align = (1 << ebb_table->ebb.sec->alignment_power); - if (is_reachable) - { - Elf_Internal_Rela *l32r_irel; - r_reloc r_rel; - asection *target_sec; - xtensa_relax_info *target_relax_info; + seg_idx_start = seg_idx_end; - /* Mark the source_reloc for the L32R so that it will be - removed in remove_literals(), along with the associated - literal. */ - l32r_irel = find_associated_l32r_irel (sec, contents, - irel, internal_relocs); - if (l32r_irel == NULL) - continue; + for (i = seg_idx_start; i < ebb_table->action_count; i++) + { + action = &ebb_table->actions[i]; + if (action->action == ta_convert_longcall) + longcall_count++; + if (action->action == ta_narrow_insn) + narrowable_count++; + if (action->action == ta_widen_insn) + widenable_count++; + if (action->action == ta_fill) + break; + if (action->align_type == EBB_REQUIRE_LOOP_ALIGN) + break; + if (action->align_type == EBB_REQUIRE_TGT_ALIGN + && !elf32xtensa_size_opt) + break; + } + seg_idx_end = i; - r_reloc_init (&r_rel, abfd, l32r_irel); + if (seg_idx_end == ebb_table->action_count && !ebb->ends_unreachable) + requires_text_end_align = TRUE; - target_sec = r_reloc_get_section (&r_rel); - target_relax_info = get_xtensa_relax_info (target_sec); + if (elf32xtensa_size_opt && !requires_text_end_align + && action->align_type != EBB_REQUIRE_LOOP_ALIGN + && action->align_type != EBB_REQUIRE_TGT_ALIGN) + { + longcall_convert_count = longcall_count; + narrowable_convert_count = narrowable_count; + widenable_convert_count = 0; + } + else + { + /* There is a constraint. Convert the max number of longcalls. */ + narrowable_convert_count = 0; + longcall_convert_count = 0; + widenable_convert_count = 0; - if (target_relax_info - && target_relax_info->is_relaxable_literal_section) + for (j = 0; j < longcall_count; j++) { - source_reloc *s_reloc; + int removed = (longcall_count - j) * 3 & (align - 1); + unsigned desire_narrow = (align - removed) & (align - 1); + unsigned desire_widen = removed; + if (desire_narrow <= narrowable_count) + { + narrowable_convert_count = desire_narrow; + narrowable_convert_count += + (align * ((narrowable_count - narrowable_convert_count) + / align)); + longcall_convert_count = (longcall_count - j); + widenable_convert_count = 0; + break; + } + if (desire_widen <= widenable_count && !elf32xtensa_size_opt) + { + narrowable_convert_count = 0; + longcall_convert_count = longcall_count - j; + widenable_convert_count = desire_widen; + break; + } + } + } - /* Search the source_relocs for the entry corresponding to - the l32r_irel. Note: The src_relocs array is not yet - sorted, but it wouldn't matter anyway because we're - searching by source offset instead of target offset. */ - s_reloc = find_source_reloc (target_relax_info->src_relocs, - target_relax_info->src_next, - sec, l32r_irel); - BFD_ASSERT (s_reloc); - s_reloc->is_null = TRUE; + /* Now the number of conversions are saved. Do them. */ + for (i = seg_idx_start; i < seg_idx_end; i++) + { + action = &ebb_table->actions[i]; + switch (action->action) + { + case ta_convert_longcall: + if (longcall_convert_count != 0) + { + action->action = ta_remove_longcall; + action->do_action = TRUE; + action->removed_bytes += 3; + longcall_convert_count--; + } + break; + case ta_narrow_insn: + if (narrowable_convert_count != 0) + { + action->do_action = TRUE; + action->removed_bytes += 1; + narrowable_convert_count--; + } + break; + case ta_widen_insn: + if (widenable_convert_count != 0) + { + action->do_action = TRUE; + action->removed_bytes -= 1; + widenable_convert_count--; + } + break; + default: + break; } + } + } - /* Convert this reloc to ASM_SIMPLIFY. */ - irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), - R_XTENSA_ASM_SIMPLIFY); - l32r_irel->r_info = ELF32_R_INFO (0, R_XTENSA_NONE); + /* Now we move on to some local opts. Try to remove each of the + remaining longcalls. */ - pin_internal_relocs (sec, internal_relocs); - } - else + if (ebb_table->ebb.ends_section || ebb_table->ebb.ends_unreachable) + { + removed_bytes = 0; + for (i = 0; i < ebb_table->action_count; i++) { - /* It is resolvable but doesn't reach. We resolve now - by eliminating the relocation -- the call will remain - expanded into L32R/CALLX. */ - irel->r_info = ELF32_R_INFO (0, R_XTENSA_NONE); - pin_internal_relocs (sec, internal_relocs); + int old_removed_bytes = removed_bytes; + proposed_action *action = &ebb_table->actions[i]; + + if (action->do_action && action->action == ta_convert_longcall) + { + bfd_boolean bad_alignment = FALSE; + removed_bytes += 3; + for (j = i + 1; j < ebb_table->action_count; j++) + { + proposed_action *new_action = &ebb_table->actions[j]; + bfd_vma offset = new_action->offset; + if (new_action->align_type == EBB_REQUIRE_TGT_ALIGN) + { + if (!check_branch_target_aligned + (ebb_table->ebb.contents, + ebb_table->ebb.content_length, + offset, offset - removed_bytes)) + { + bad_alignment = TRUE; + break; + } + } + if (new_action->align_type == EBB_REQUIRE_LOOP_ALIGN) + { + if (!check_loop_aligned (ebb_table->ebb.contents, + ebb_table->ebb.content_length, + offset, + offset - removed_bytes)) + { + bad_alignment = TRUE; + break; + } + } + if (new_action->action == ta_narrow_insn + && !new_action->do_action + && ebb_table->ebb.sec->alignment_power == 2) + { + /* Narrow an instruction and we are done. */ + new_action->do_action = TRUE; + new_action->removed_bytes += 1; + bad_alignment = FALSE; + break; + } + if (new_action->action == ta_widen_insn + && new_action->do_action + && ebb_table->ebb.sec->alignment_power == 2) + { + /* Narrow an instruction and we are done. */ + new_action->do_action = FALSE; + new_action->removed_bytes += 1; + bad_alignment = FALSE; + break; + } + } + if (!bad_alignment) + { + action->removed_bytes += 3; + action->action = ta_remove_longcall; + action->do_action = TRUE; + } + } + removed_bytes = old_removed_bytes; + if (action->do_action) + removed_bytes += action->removed_bytes; } } - error_return: - release_contents (sec, contents); - release_internal_relocs (sec, internal_relocs); - return ok; + removed_bytes = 0; + for (i = 0; i < ebb_table->action_count; ++i) + { + proposed_action *action = &ebb_table->actions[i]; + if (action->do_action) + removed_bytes += action->removed_bytes; + } + + if ((removed_bytes % (1 << ebb_table->ebb.sec->alignment_power)) != 0 + && ebb->ends_unreachable) + { + proposed_action *action; + int br; + int extra_space; + + BFD_ASSERT (ebb_table->action_count != 0); + action = &ebb_table->actions[ebb_table->action_count - 1]; + BFD_ASSERT (action->action == ta_fill); + BFD_ASSERT (ebb->ends_unreachable->flags & XTENSA_PROP_UNREACHABLE); + + extra_space = compute_fill_extra_space (ebb->ends_unreachable); + br = action->removed_bytes + removed_bytes + extra_space; + br = br & ((1 << ebb->sec->alignment_power ) - 1); + + action->removed_bytes = extra_space - br; + } + return TRUE; } -/* Return TRUE if the asm expansion can be resolved. Generally it can - be resolved on a final link or when a partial link locates it in the - same section as the target. Set "is_reachable" flag if the target of - the call is within the range of a direct call, given the current VMA - for this section and the target section. */ +/* Use check_section_ebb_pcrels_fit to make sure that all of the + relocations in a section will fit if a proposed set of actions + are performed. */ -bfd_boolean -is_resolvable_asm_expansion (abfd, sec, contents, irel, link_info, - is_reachable_p) +static bfd_boolean +check_section_ebb_pcrels_fit (abfd, sec, contents, internal_relocs, constraint) bfd *abfd; asection *sec; bfd_byte *contents; - Elf_Internal_Rela *irel; - struct bfd_link_info *link_info; - bfd_boolean *is_reachable_p; + Elf_Internal_Rela *internal_relocs; + const ebb_constraint *constraint; { - asection *target_sec; - bfd_vma target_offset; - r_reloc r_rel; - xtensa_opcode opcode, direct_call_opcode; - bfd_vma self_address; - bfd_vma dest_address; + unsigned i, j; + Elf_Internal_Rela *irel; + xtensa_relax_info *relax_info; - *is_reachable_p = FALSE; + relax_info = get_xtensa_relax_info (sec); - if (contents == NULL) - return FALSE; + for (i = 0; i < sec->reloc_count; i++) + { + r_reloc r_rel; + bfd_vma orig_self_offset, orig_target_offset; + bfd_vma self_offset, target_offset; + int r_type; + reloc_howto_type *howto; + int self_removed_bytes, target_removed_bytes; - if (ELF32_R_TYPE (irel->r_info) != R_XTENSA_ASM_EXPAND) - return FALSE; - - opcode = get_expanded_call_opcode (contents + irel->r_offset, - sec->size - irel->r_offset); - - direct_call_opcode = swap_callx_for_call_opcode (opcode); - if (direct_call_opcode == XTENSA_UNDEFINED) - return FALSE; + irel = &internal_relocs[i]; + r_type = ELF32_R_TYPE (irel->r_info); - /* Check and see that the target resolves. */ - r_reloc_init (&r_rel, abfd, irel); - if (!r_reloc_is_defined (&r_rel)) - return FALSE; + howto = &elf_howto_table[r_type]; + /* We maintain the required invariant: PC-relative relocations + that fit before linking must fit after linking. Thus we only + need to deal with relocations to the same section that are + PC-relative. */ + if (ELF32_R_TYPE (irel->r_info) == R_XTENSA_ASM_SIMPLIFY + || !howto->pc_relative) + continue; - target_sec = r_reloc_get_section (&r_rel); - target_offset = r_reloc_get_target_offset (&r_rel); + r_reloc_init (&r_rel, abfd, irel, contents, + bfd_get_section_limit (abfd, sec)); - /* If the target is in a shared library, then it doesn't reach. This - isn't supposed to come up because the compiler should never generate - non-PIC calls on systems that use shared libraries, but the linker - shouldn't crash regardless. */ - if (!target_sec->output_section) - return FALSE; - - /* For relocatable sections, we can only simplify when the output - section of the target is the same as the output section of the - source. */ - if (link_info->relocatable - && (target_sec->output_section != sec->output_section)) - return FALSE; + if (r_reloc_get_section (&r_rel) != sec) + continue; - self_address = (sec->output_section->vma - + sec->output_offset + irel->r_offset + 3); - dest_address = (target_sec->output_section->vma - + target_sec->output_offset + target_offset); - - *is_reachable_p = pcrel_reloc_fits - (xtensa_get_operand (xtensa_default_isa, direct_call_opcode, 0), - self_address, dest_address); + orig_self_offset = irel->r_offset; + orig_target_offset = r_rel.target_offset; - if ((self_address >> CALL_SEGMENT_BITS) != - (dest_address >> CALL_SEGMENT_BITS)) + self_offset = orig_self_offset; + target_offset = orig_target_offset; + + if (relax_info) + { + self_offset = offset_with_removed_text (&relax_info->action_list, + orig_self_offset); + target_offset = offset_with_removed_text (&relax_info->action_list, + orig_target_offset); + } + + self_removed_bytes = 0; + target_removed_bytes = 0; + + for (j = 0; j < constraint->action_count; ++j) + { + proposed_action *action = &constraint->actions[j]; + bfd_vma offset = action->offset; + int removed_bytes = action->removed_bytes; + if (offset < orig_self_offset + || (offset == orig_self_offset && action->action == ta_fill + && action->removed_bytes < 0)) + self_removed_bytes += removed_bytes; + if (offset < orig_target_offset + || (offset == orig_target_offset && action->action == ta_fill + && action->removed_bytes < 0)) + target_removed_bytes += removed_bytes; + } + self_offset -= self_removed_bytes; + target_offset -= target_removed_bytes; + + /* Try to encode it. Get the operand and check. */ + if (is_alt_relocation (ELF32_R_TYPE (irel->r_info))) + { + /* None of the current alternate relocs are PC-relative, + and only PC-relative relocs matter here. */ + } + else + { + xtensa_opcode opcode; + int opnum; + + opcode = get_relocation_opcode (abfd, sec, contents, irel); + if (opcode == XTENSA_UNDEFINED) + return FALSE; + + opnum = get_relocation_opnd (opcode, ELF32_R_TYPE (irel->r_info)); + if (opnum == XTENSA_UNDEFINED) + return FALSE; + + if (!pcrel_reloc_fits (opcode, opnum, self_offset, target_offset)) + return FALSE; + } + } + + return TRUE; +} + + +static bfd_boolean +check_section_ebb_reduces (constraint) + const ebb_constraint *constraint; +{ + int removed = 0; + unsigned i; + + for (i = 0; i < constraint->action_count; i++) + { + const proposed_action *action = &constraint->actions[i]; + if (action->do_action) + removed += action->removed_bytes; + } + if (removed < 0) return FALSE; return TRUE; } -static Elf_Internal_Rela * -find_associated_l32r_irel (sec, contents, other_irel, internal_relocs) +void +text_action_add_proposed (l, ebb_table, sec) + text_action_list *l; + const ebb_constraint *ebb_table; asection *sec; - bfd_byte *contents; - Elf_Internal_Rela *other_irel; - Elf_Internal_Rela *internal_relocs; { unsigned i; - for (i = 0; i < sec->reloc_count; i++) + for (i = 0; i < ebb_table->action_count; i++) { - Elf_Internal_Rela *irel = &internal_relocs[i]; + proposed_action *action = &ebb_table->actions[i]; - if (irel == other_irel) - continue; - if (irel->r_offset != other_irel->r_offset) + if (!action->do_action) continue; - if (is_l32r_relocation (sec, contents, irel)) - return irel; + switch (action->action) + { + case ta_remove_insn: + case ta_remove_longcall: + case ta_convert_longcall: + case ta_narrow_insn: + case ta_widen_insn: + case ta_fill: + case ta_remove_literal: + text_action_add (l, action->action, sec, action->offset, + action->removed_bytes); + break; + case ta_none: + break; + default: + BFD_ASSERT (0); + break; + } } +} - return NULL; + +int +compute_fill_extra_space (entry) + property_table_entry *entry; +{ + int fill_extra_space; + + if (!entry) + return 0; + + if ((entry->flags & XTENSA_PROP_UNREACHABLE) == 0) + return 0; + + fill_extra_space = entry->size; + if ((entry->flags & XTENSA_PROP_ALIGN) != 0) + { + /* Fill bytes for alignment: + (2**n)-1 - (addr + (2**n)-1) & (2**n -1) */ + int pow = GET_XTENSA_PROP_ALIGNMENT (entry->flags); + int nsm = (1 << pow) - 1; + bfd_vma addr = entry->address + entry->size; + bfd_vma align_fill = nsm - ((addr + nsm) & nsm); + fill_extra_space += align_fill; + } + return fill_extra_space; } + /* First relaxation pass. */ -/* If the section is relaxable (i.e., a literal section), check each - literal to see if it has the same value as another literal that has - already been seen, either in the current section or a previous one. - If so, add an entry to the per-section list of removed literals. The +/* If the section contains relaxable literals, check each literal to + see if it has the same value as another literal that has already + been seen, either in the current section or a previous one. If so, + add an entry to the per-section list of removed literals. The actual changes are deferred until the next pass. */ static bfd_boolean -remove_literals (abfd, sec, link_info, values) +compute_removed_literals (abfd, sec, link_info, values) bfd *abfd; asection *sec; struct bfd_link_info *link_info; @@ -4472,115 +7524,378 @@ remove_literals (abfd, sec, link_info, values) xtensa_relax_info *relax_info; bfd_byte *contents; Elf_Internal_Rela *internal_relocs; - source_reloc *src_relocs; - bfd_boolean final_static_link; + source_reloc *src_relocs, *rel; bfd_boolean ok = TRUE; - int i; + property_table_entry *prop_table = NULL; + int ptblsize; + int i, prev_i; + bfd_boolean last_loc_is_prev = FALSE; + bfd_vma last_target_offset = 0; + section_cache_t target_sec_cache; + bfd_size_type sec_size; + + init_section_cache (&target_sec_cache); /* Do nothing if it is not a relaxable literal section. */ relax_info = get_xtensa_relax_info (sec); BFD_ASSERT (relax_info); - if (!relax_info->is_relaxable_literal_section) return ok; internal_relocs = retrieve_internal_relocs (abfd, sec, link_info->keep_memory); + sec_size = bfd_get_section_limit (abfd, sec); contents = retrieve_contents (abfd, sec, link_info->keep_memory); - if (contents == NULL && sec->size != 0) + if (contents == NULL && sec_size != 0) { ok = FALSE; goto error_return; } - final_static_link = - (!link_info->relocatable - && !elf_hash_table (link_info)->dynamic_sections_created); - /* Sort the source_relocs by target offset. */ src_relocs = relax_info->src_relocs; qsort (src_relocs, relax_info->src_count, sizeof (source_reloc), source_reloc_compare); + qsort (internal_relocs, sec->reloc_count, sizeof (Elf_Internal_Rela), + internal_reloc_compare); + ptblsize = xtensa_read_table_entries (abfd, sec, &prop_table, + XTENSA_PROP_SEC_NAME, FALSE); + if (ptblsize < 0) + { + ok = FALSE; + goto error_return; + } + + prev_i = -1; for (i = 0; i < relax_info->src_count; i++) { - source_reloc *rel; Elf_Internal_Rela *irel = NULL; - literal_value val; - value_map *val_map; rel = &src_relocs[i]; + if (get_l32r_opcode () != rel->opcode) + continue; irel = get_irel_at_offset (sec, internal_relocs, rel->r_rel.target_offset); + /* If the relocation on this is not a simple R_XTENSA_32 or + R_XTENSA_PLT then do not consider it. This may happen when + the difference of two symbols is used in a literal. */ + if (irel && (ELF32_R_TYPE (irel->r_info) != R_XTENSA_32 + && ELF32_R_TYPE (irel->r_info) != R_XTENSA_PLT)) + continue; + /* If the target_offset for this relocation is the same as the previous relocation, then we've already considered whether the literal can be coalesced. Skip to the next one.... */ - if (i != 0 && (src_relocs[i-1].r_rel.target_offset - == rel->r_rel.target_offset)) + if (i != 0 && prev_i != -1 + && src_relocs[i-1].r_rel.target_offset == rel->r_rel.target_offset) continue; + prev_i = i; + + if (last_loc_is_prev && + last_target_offset + 4 != rel->r_rel.target_offset) + last_loc_is_prev = FALSE; /* Check if the relocation was from an L32R that is being removed because a CALLX was converted to a direct CALL, and check if there are no other relocations to the literal. */ - if (rel->is_null - && (i == relax_info->src_count - 1 - || (src_relocs[i+1].r_rel.target_offset - != rel->r_rel.target_offset))) + if (is_removable_literal (rel, i, src_relocs, relax_info->src_count)) { - /* Mark the unused literal so that it will be removed. */ - add_removed_literal (&relax_info->removed_list, &rel->r_rel, NULL); - - /* Zero out the relocation on this literal location. */ - if (irel) + if (!remove_dead_literal (abfd, sec, link_info, internal_relocs, + irel, rel, prop_table, ptblsize)) { - if (elf_hash_table (link_info)->dynamic_sections_created) - shrink_dynamic_reloc_sections (link_info, abfd, sec, irel); - - irel->r_info = ELF32_R_INFO (0, R_XTENSA_NONE); + ok = FALSE; + goto error_return; } - + last_target_offset = rel->r_rel.target_offset; continue; } - /* Find the literal value. */ - r_reloc_init (&val.r_rel, abfd, irel); - BFD_ASSERT (rel->r_rel.target_offset < sec->size); - val.value = bfd_get_32 (abfd, contents + rel->r_rel.target_offset); - - /* Check if we've seen another literal with the same value. */ - val_map = get_cached_value (values, &val, final_static_link); - if (val_map != NULL) + if (!identify_literal_placement (abfd, sec, contents, link_info, + values, + &last_loc_is_prev, irel, + relax_info->src_count - i, rel, + prop_table, ptblsize, + &target_sec_cache, rel->is_abs_literal)) { - /* First check that THIS and all the other relocs to this - literal will FIT if we move them to the new address. */ + ok = FALSE; + goto error_return; + } + last_target_offset = rel->r_rel.target_offset; + } - if (relocations_reach (rel, relax_info->src_count - i, - &val_map->loc)) - { - /* Mark that the literal will be coalesced. */ - add_removed_literal (&relax_info->removed_list, - &rel->r_rel, &val_map->loc); - } - else +#if DEBUG + print_removed_literals (stderr, &relax_info->removed_list); + print_action_list (stderr, &relax_info->action_list); +#endif /* DEBUG */ + +error_return: + if (prop_table) free (prop_table); + clear_section_cache (&target_sec_cache); + + release_contents (sec, contents); + release_internal_relocs (sec, internal_relocs); + return ok; +} + + +static Elf_Internal_Rela * +get_irel_at_offset (sec, internal_relocs, offset) + asection *sec; + Elf_Internal_Rela *internal_relocs; + bfd_vma offset; +{ + unsigned i; + Elf_Internal_Rela *irel; + unsigned r_type; + Elf_Internal_Rela key; + + if (!internal_relocs) + return NULL; + + key.r_offset = offset; + irel = bsearch (&key, internal_relocs, sec->reloc_count, + sizeof (Elf_Internal_Rela), internal_reloc_matches); + if (!irel) + return NULL; + + /* bsearch does not guarantee which will be returned if there are + multiple matches. We need the first that is not an alignment. */ + i = irel - internal_relocs; + while (i > 0) + { + if (internal_relocs[i-1].r_offset != offset) + break; + i--; + } + for ( ; i < sec->reloc_count; i++) + { + irel = &internal_relocs[i]; + r_type = ELF32_R_TYPE (irel->r_info); + if (irel->r_offset == offset && r_type != R_XTENSA_NONE) + return irel; + } + + return NULL; +} + + +bfd_boolean +is_removable_literal (rel, i, src_relocs, src_count) + const source_reloc *rel; + int i; + const source_reloc *src_relocs; + int src_count; +{ + const source_reloc *curr_rel; + if (!rel->is_null) + return FALSE; + + for (++i; i < src_count; ++i) + { + curr_rel = &src_relocs[i]; + /* If all others have the same target offset.... */ + if (curr_rel->r_rel.target_offset != rel->r_rel.target_offset) + return TRUE; + + if (!curr_rel->is_null + && !xtensa_is_property_section (curr_rel->source_sec) + && !(curr_rel->source_sec->flags & SEC_DEBUGGING)) + return FALSE; + } + return TRUE; +} + + +bfd_boolean +remove_dead_literal (abfd, sec, link_info, internal_relocs, + irel, rel, prop_table, ptblsize) + bfd *abfd; + asection *sec; + struct bfd_link_info *link_info; + Elf_Internal_Rela *internal_relocs; + Elf_Internal_Rela *irel; + source_reloc *rel; + property_table_entry *prop_table; + int ptblsize; +{ + property_table_entry *entry; + xtensa_relax_info *relax_info; + + relax_info = get_xtensa_relax_info (sec); + if (!relax_info) + return FALSE; + + entry = elf_xtensa_find_property_entry (prop_table, ptblsize, + sec->vma + rel->r_rel.target_offset); + + /* Mark the unused literal so that it will be removed. */ + add_removed_literal (&relax_info->removed_list, &rel->r_rel, NULL); + + text_action_add (&relax_info->action_list, + ta_remove_literal, sec, rel->r_rel.target_offset, 4); + + /* If the section is 4-byte aligned, do not add fill. */ + if (sec->alignment_power > 2) + { + int fill_extra_space; + bfd_vma entry_sec_offset; + text_action *fa; + property_table_entry *the_add_entry; + int removed_diff; + + if (entry) + entry_sec_offset = entry->address - sec->vma + entry->size; + else + entry_sec_offset = rel->r_rel.target_offset + 4; + + /* If the literal range is at the end of the section, + do not add fill. */ + the_add_entry = elf_xtensa_find_property_entry (prop_table, ptblsize, + entry_sec_offset); + fill_extra_space = compute_fill_extra_space (the_add_entry); + + fa = find_fill_action (&relax_info->action_list, sec, entry_sec_offset); + removed_diff = compute_removed_action_diff (fa, sec, entry_sec_offset, + -4, fill_extra_space); + if (fa) + adjust_fill_action (fa, removed_diff); + else + text_action_add (&relax_info->action_list, + ta_fill, sec, entry_sec_offset, removed_diff); + } + + /* Zero out the relocation on this literal location. */ + if (irel) + { + if (elf_hash_table (link_info)->dynamic_sections_created) + shrink_dynamic_reloc_sections (link_info, abfd, sec, irel); + + irel->r_info = ELF32_R_INFO (0, R_XTENSA_NONE); + pin_internal_relocs (sec, internal_relocs); + } + + /* Do not modify "last_loc_is_prev". */ + return TRUE; +} + + +bfd_boolean +identify_literal_placement (abfd, sec, contents, link_info, values, + last_loc_is_prev_p, irel, remaining_src_rels, + rel, prop_table, ptblsize, target_sec_cache, + is_abs_literal) + bfd *abfd; + asection *sec; + bfd_byte *contents; + struct bfd_link_info *link_info; + value_map_hash_table *values; + bfd_boolean *last_loc_is_prev_p; + Elf_Internal_Rela *irel; + int remaining_src_rels; + source_reloc *rel; + property_table_entry *prop_table; + int ptblsize; + section_cache_t *target_sec_cache; + bfd_boolean is_abs_literal; +{ + literal_value val; + value_map *val_map; + xtensa_relax_info *relax_info; + bfd_boolean literal_placed = FALSE; + r_reloc r_rel; + unsigned long value; + bfd_boolean final_static_link; + bfd_size_type sec_size; + + relax_info = get_xtensa_relax_info (sec); + if (!relax_info) + return FALSE; + + sec_size = bfd_get_section_limit (abfd, sec); + + final_static_link = + (!link_info->relocatable + && !elf_hash_table (link_info)->dynamic_sections_created); + + /* The placement algorithm first checks to see if the literal is + already in the value map. If so and the value map is reachable + from all uses, then the literal is moved to that location. If + not, then we identify the last location where a fresh literal was + placed. If the literal can be safely moved there, then we do so. + If not, then we assume that the literal is not to move and leave + the literal where it is, marking it as the last literal + location. */ + + /* Find the literal value. */ + value = 0; + r_reloc_init (&r_rel, abfd, irel, contents, sec_size); + if (!irel) + { + BFD_ASSERT (rel->r_rel.target_offset < sec_size); + value = bfd_get_32 (abfd, contents + rel->r_rel.target_offset); + } + init_literal_value (&val, &r_rel, value, is_abs_literal); + + /* Check if we've seen another literal with the same value that + is in the same output section. */ + val_map = value_map_get_cached_value (values, &val, final_static_link); + + if (val_map + && (r_reloc_get_section (&val_map->loc)->output_section + == sec->output_section) + && relocations_reach (rel, remaining_src_rels, &val_map->loc) + && coalesce_shared_literal (sec, rel, prop_table, ptblsize, val_map)) + { + /* No change to last_loc_is_prev. */ + literal_placed = TRUE; + } + + /* For relocatable links, do not try to move literals. To do it + correctly might increase the number of relocations in an input + section making the default relocatable linking fail. */ + if (!link_info->relocatable && !literal_placed + && values->has_last_loc && !(*last_loc_is_prev_p)) + { + asection *target_sec = r_reloc_get_section (&values->last_loc); + if (target_sec && target_sec->output_section == sec->output_section) + { + /* Increment the virtual offset. */ + r_reloc try_loc = values->last_loc; + try_loc.virtual_offset += 4; + + /* There is a last loc that was in the same output section. */ + if (relocations_reach (rel, remaining_src_rels, &try_loc) + && move_shared_literal (sec, link_info, rel, + prop_table, ptblsize, + &try_loc, &val, target_sec_cache)) { - /* Relocations do not reach -- do not remove this literal. */ - val_map->loc = rel->r_rel; + values->last_loc.virtual_offset += 4; + literal_placed = TRUE; + if (!val_map) + val_map = add_value_map (values, &val, &try_loc, + final_static_link); + else + val_map->loc = try_loc; } } + } + + if (!literal_placed) + { + /* Nothing worked, leave the literal alone but update the last loc. */ + values->has_last_loc = TRUE; + values->last_loc = rel->r_rel; + if (!val_map) + val_map = add_value_map (values, &val, &rel->r_rel, final_static_link); else - { - /* This is the first time we've seen this literal value. */ - BFD_ASSERT (sec == r_reloc_get_section (&rel->r_rel)); - add_value_map (values, &val, &rel->r_rel, final_static_link); - } + val_map->loc = rel->r_rel; + *last_loc_is_prev_p = TRUE; } -error_return: - release_contents (sec, contents); - release_internal_relocs (sec, internal_relocs); - return ok; + return TRUE; } @@ -4626,9 +7941,8 @@ relocations_reach (reloc, remaining_relocs, r_rel) != sec->output_section) return FALSE; - /* A NULL operand means it is not a PC-relative relocation, so - the literal can be moved anywhere. */ - if (reloc[i].opnd) + /* A literal with no PC-relative relocations can be moved anywhere. */ + if (reloc[i].opnd != -1) { /* Otherwise, check to see that it fits. */ source_address = (reloc[i].source_sec->output_section->vma @@ -4638,7 +7952,8 @@ relocations_reach (reloc, remaining_relocs, r_rel) + sec->output_offset + r_rel->target_offset); - if (!pcrel_reloc_fits (reloc[i].opnd, source_address, dest_address)) + if (!pcrel_reloc_fits (reloc[i].opcode, reloc[i].opnd, + source_address, dest_address)) return FALSE; } } @@ -4647,27 +7962,222 @@ relocations_reach (reloc, remaining_relocs, r_rel) } -/* WARNING: linear search here. If the relocation are in order by - address, we can use a faster binary search. ALSO, we assume that - there is only 1 non-NONE relocation per address. */ +/* Move a literal to another literal location because it is + the same as the other literal value. */ -static Elf_Internal_Rela * -get_irel_at_offset (sec, internal_relocs, offset) +static bfd_boolean +coalesce_shared_literal (sec, rel, prop_table, ptblsize, val_map) asection *sec; - Elf_Internal_Rela *internal_relocs; - bfd_vma offset; + source_reloc *rel; + property_table_entry *prop_table; + int ptblsize; + value_map *val_map; { - unsigned i; - if (!internal_relocs) - return NULL; - for (i = 0; i < sec->reloc_count; i++) + property_table_entry *entry; + text_action *fa; + property_table_entry *the_add_entry; + int removed_diff; + xtensa_relax_info *relax_info; + + relax_info = get_xtensa_relax_info (sec); + if (!relax_info) + return FALSE; + + entry = elf_xtensa_find_property_entry + (prop_table, ptblsize, sec->vma + rel->r_rel.target_offset); + if (entry && (entry->flags & XTENSA_PROP_INSN_NO_TRANSFORM)) + return TRUE; + + /* Mark that the literal will be coalesced. */ + add_removed_literal (&relax_info->removed_list, &rel->r_rel, &val_map->loc); + + text_action_add (&relax_info->action_list, + ta_remove_literal, sec, rel->r_rel.target_offset, 4); + + /* If the section is 4-byte aligned, do not add fill. */ + if (sec->alignment_power > 2) { - Elf_Internal_Rela *irel = &internal_relocs[i]; - if (irel->r_offset == offset - && ELF32_R_TYPE (irel->r_info) != R_XTENSA_NONE) - return irel; + int fill_extra_space; + bfd_vma entry_sec_offset; + + if (entry) + entry_sec_offset = entry->address - sec->vma + entry->size; + else + entry_sec_offset = rel->r_rel.target_offset + 4; + + /* If the literal range is at the end of the section, + do not add fill. */ + fill_extra_space = 0; + the_add_entry = elf_xtensa_find_property_entry (prop_table, ptblsize, + entry_sec_offset); + if (the_add_entry && (the_add_entry->flags & XTENSA_PROP_UNREACHABLE)) + fill_extra_space = the_add_entry->size; + + fa = find_fill_action (&relax_info->action_list, sec, entry_sec_offset); + removed_diff = compute_removed_action_diff (fa, sec, entry_sec_offset, + -4, fill_extra_space); + if (fa) + adjust_fill_action (fa, removed_diff); + else + text_action_add (&relax_info->action_list, + ta_fill, sec, entry_sec_offset, removed_diff); } - return NULL; + + return TRUE; +} + + +/* Move a literal to another location. This may actually increase the + total amount of space used because of alignments so we need to do + this carefully. Also, it may make a branch go out of range. */ + +static bfd_boolean +move_shared_literal (sec, link_info, rel, prop_table, ptblsize, + target_loc, lit_value, target_sec_cache) + asection *sec; + struct bfd_link_info *link_info; + source_reloc *rel; + property_table_entry *prop_table; + int ptblsize; + const r_reloc *target_loc; + const literal_value *lit_value; + section_cache_t *target_sec_cache; +{ + property_table_entry *the_add_entry, *src_entry, *target_entry = NULL; + text_action *fa, *target_fa; + int removed_diff; + xtensa_relax_info *relax_info, *target_relax_info; + asection *target_sec; + ebb_t *ebb; + ebb_constraint ebb_table; + bfd_boolean relocs_fit; + + /* If this routine always returns FALSE, the literals that cannot be + coalesced will not be moved. */ + if (elf32xtensa_no_literal_movement) + return FALSE; + + relax_info = get_xtensa_relax_info (sec); + if (!relax_info) + return FALSE; + + target_sec = r_reloc_get_section (target_loc); + target_relax_info = get_xtensa_relax_info (target_sec); + + /* Literals to undefined sections may not be moved because they + must report an error. */ + if (bfd_is_und_section (target_sec)) + return FALSE; + + src_entry = elf_xtensa_find_property_entry + (prop_table, ptblsize, sec->vma + rel->r_rel.target_offset); + + if (!section_cache_section (target_sec_cache, target_sec, link_info)) + return FALSE; + + target_entry = elf_xtensa_find_property_entry + (target_sec_cache->ptbl, target_sec_cache->pte_count, + target_sec->vma + target_loc->target_offset); + + if (!target_entry) + return FALSE; + + /* Make sure that we have not broken any branches. */ + relocs_fit = FALSE; + + init_ebb_constraint (&ebb_table); + ebb = &ebb_table.ebb; + init_ebb (ebb, target_sec_cache->sec, target_sec_cache->contents, + target_sec_cache->content_length, + target_sec_cache->ptbl, target_sec_cache->pte_count, + target_sec_cache->relocs, target_sec_cache->reloc_count); + + /* Propose to add 4 bytes + worst-case alignment size increase to + destination. */ + ebb_propose_action (&ebb_table, EBB_NO_ALIGN, 0, + ta_fill, target_loc->target_offset, + -4 - (1 << target_sec->alignment_power), TRUE); + + /* Check all of the PC-relative relocations to make sure they still fit. */ + relocs_fit = check_section_ebb_pcrels_fit (target_sec->owner, target_sec, + target_sec_cache->contents, + target_sec_cache->relocs, + &ebb_table); + + if (!relocs_fit) + return FALSE; + + text_action_add_literal (&target_relax_info->action_list, + ta_add_literal, target_loc, lit_value, -4); + + if (target_sec->alignment_power > 2 && target_entry != src_entry) + { + /* May need to add or remove some fill to maintain alignment. */ + int fill_extra_space; + bfd_vma entry_sec_offset; + + entry_sec_offset = + target_entry->address - target_sec->vma + target_entry->size; + + /* If the literal range is at the end of the section, + do not add fill. */ + fill_extra_space = 0; + the_add_entry = + elf_xtensa_find_property_entry (target_sec_cache->ptbl, + target_sec_cache->pte_count, + entry_sec_offset); + if (the_add_entry && (the_add_entry->flags & XTENSA_PROP_UNREACHABLE)) + fill_extra_space = the_add_entry->size; + + target_fa = find_fill_action (&target_relax_info->action_list, + target_sec, entry_sec_offset); + removed_diff = compute_removed_action_diff (target_fa, target_sec, + entry_sec_offset, 4, + fill_extra_space); + if (target_fa) + adjust_fill_action (target_fa, removed_diff); + else + text_action_add (&target_relax_info->action_list, + ta_fill, target_sec, entry_sec_offset, removed_diff); + } + + /* Mark that the literal will be moved to the new location. */ + add_removed_literal (&relax_info->removed_list, &rel->r_rel, target_loc); + + /* Remove the literal. */ + text_action_add (&relax_info->action_list, + ta_remove_literal, sec, rel->r_rel.target_offset, 4); + + /* If the section is 4-byte aligned, do not add fill. */ + if (sec->alignment_power > 2 && target_entry != src_entry) + { + int fill_extra_space; + bfd_vma entry_sec_offset; + + if (src_entry) + entry_sec_offset = src_entry->address - sec->vma + src_entry->size; + else + entry_sec_offset = rel->r_rel.target_offset+4; + + /* If the literal range is at the end of the section, + do not add fill. */ + fill_extra_space = 0; + the_add_entry = elf_xtensa_find_property_entry (prop_table, ptblsize, + entry_sec_offset); + if (the_add_entry && (the_add_entry->flags & XTENSA_PROP_UNREACHABLE)) + fill_extra_space = the_add_entry->size; + + fa = find_fill_action (&relax_info->action_list, sec, entry_sec_offset); + removed_diff = compute_removed_action_diff (fa, sec, entry_sec_offset, + -4, fill_extra_space); + if (fa) + adjust_fill_action (fa, removed_diff); + else + text_action_add (&relax_info->action_list, + ta_fill, sec, entry_sec_offset, removed_diff); + } + + return TRUE; } @@ -4675,9 +8185,9 @@ get_irel_at_offset (sec, internal_relocs, offset) /* Modify all of the relocations to point to the right spot, and if this is a relaxable section, delete the unwanted literals and fix the - cooked_size. */ + section size. */ -bfd_boolean +bfd_boolean relax_section (abfd, sec, link_info) bfd *abfd; asection *sec; @@ -4688,10 +8198,17 @@ relax_section (abfd, sec, link_info) bfd_byte *contents; bfd_boolean ok = TRUE; unsigned i; + bfd_boolean rv = FALSE; + bfd_boolean virtual_action; + bfd_size_type sec_size; + sec_size = bfd_get_section_limit (abfd, sec); relax_info = get_xtensa_relax_info (sec); BFD_ASSERT (relax_info); + /* First translate any of the fixes that have been added already. */ + translate_section_fixes (sec); + /* Handle property sections (e.g., literal tables) specially. */ if (xtensa_is_property_section (sec)) { @@ -4699,118 +8216,522 @@ relax_section (abfd, sec, link_info) return relax_property_section (abfd, sec, link_info); } - internal_relocs = retrieve_internal_relocs (abfd, sec, - link_info->keep_memory); - contents = retrieve_contents (abfd, sec, link_info->keep_memory); - if (contents == NULL && sec->size != 0) - { - ok = FALSE; - goto error_return; - } + internal_relocs = retrieve_internal_relocs (abfd, sec, + link_info->keep_memory); + contents = retrieve_contents (abfd, sec, link_info->keep_memory); + if (contents == NULL && sec_size != 0) + { + ok = FALSE; + goto error_return; + } + + if (internal_relocs) + { + for (i = 0; i < sec->reloc_count; i++) + { + Elf_Internal_Rela *irel; + xtensa_relax_info *target_relax_info; + bfd_vma source_offset, old_source_offset; + r_reloc r_rel; + unsigned r_type; + asection *target_sec; + + /* Locally change the source address. + Translate the target to the new target address. + If it points to this section and has been removed, + NULLify it. + Write it back. */ + + irel = &internal_relocs[i]; + source_offset = irel->r_offset; + old_source_offset = source_offset; + + r_type = ELF32_R_TYPE (irel->r_info); + r_reloc_init (&r_rel, abfd, irel, contents, + bfd_get_section_limit (abfd, sec)); + + /* If this section could have changed then we may need to + change the relocation's offset. */ + + if (relax_info->is_relaxable_literal_section + || relax_info->is_relaxable_asm_section) + { + if (r_type != R_XTENSA_NONE + && find_removed_literal (&relax_info->removed_list, + irel->r_offset)) + { + /* Remove this relocation. */ + if (elf_hash_table (link_info)->dynamic_sections_created) + shrink_dynamic_reloc_sections (link_info, abfd, sec, irel); + irel->r_info = ELF32_R_INFO (0, R_XTENSA_NONE); + irel->r_offset = offset_with_removed_text + (&relax_info->action_list, irel->r_offset); + pin_internal_relocs (sec, internal_relocs); + continue; + } + + if (r_type == R_XTENSA_ASM_SIMPLIFY) + { + text_action *action = + find_insn_action (&relax_info->action_list, + irel->r_offset); + if (action && (action->action == ta_convert_longcall + || action->action == ta_remove_longcall)) + { + bfd_reloc_status_type retval; + char *error_message = NULL; + + retval = contract_asm_expansion (contents, sec_size, + irel, &error_message); + if (retval != bfd_reloc_ok) + { + (*link_info->callbacks->reloc_dangerous) + (link_info, error_message, abfd, sec, + irel->r_offset); + goto error_return; + } + /* Update the action so that the code that moves + the contents will do the right thing. */ + if (action->action == ta_remove_longcall) + action->action = ta_remove_insn; + else + action->action = ta_none; + /* Refresh the info in the r_rel. */ + r_reloc_init (&r_rel, abfd, irel, contents, sec_size); + r_type = ELF32_R_TYPE (irel->r_info); + } + } + + source_offset = offset_with_removed_text + (&relax_info->action_list, irel->r_offset); + irel->r_offset = source_offset; + } + + /* If the target section could have changed then + we may need to change the relocation's target offset. */ + + target_sec = r_reloc_get_section (&r_rel); + target_relax_info = get_xtensa_relax_info (target_sec); + + if (target_relax_info + && (target_relax_info->is_relaxable_literal_section + || target_relax_info->is_relaxable_asm_section)) + { + r_reloc new_reloc; + reloc_bfd_fix *fix; + bfd_vma addend_displacement; + + translate_reloc (&r_rel, &new_reloc); + + if (r_type == R_XTENSA_DIFF8 + || r_type == R_XTENSA_DIFF16 + || r_type == R_XTENSA_DIFF32) + { + bfd_vma diff_value = 0, new_end_offset, diff_mask = 0; + + if (bfd_get_section_limit (abfd, sec) < old_source_offset) + { + (*link_info->callbacks->reloc_dangerous) + (link_info, _("invalid relocation address"), + abfd, sec, old_source_offset); + goto error_return; + } + + switch (r_type) + { + case R_XTENSA_DIFF8: + diff_value = + bfd_get_8 (abfd, &contents[old_source_offset]); + break; + case R_XTENSA_DIFF16: + diff_value = + bfd_get_16 (abfd, &contents[old_source_offset]); + break; + case R_XTENSA_DIFF32: + diff_value = + bfd_get_32 (abfd, &contents[old_source_offset]); + break; + } + + new_end_offset = offset_with_removed_text + (&target_relax_info->action_list, + r_rel.target_offset + diff_value); + diff_value = new_end_offset - new_reloc.target_offset; + + switch (r_type) + { + case R_XTENSA_DIFF8: + diff_mask = 0xff; + bfd_put_8 (abfd, diff_value, + &contents[old_source_offset]); + break; + case R_XTENSA_DIFF16: + diff_mask = 0xffff; + bfd_put_16 (abfd, diff_value, + &contents[old_source_offset]); + break; + case R_XTENSA_DIFF32: + diff_mask = 0xffffffff; + bfd_put_32 (abfd, diff_value, + &contents[old_source_offset]); + break; + } + + /* Check for overflow. */ + if ((diff_value & ~diff_mask) != 0) + { + (*link_info->callbacks->reloc_dangerous) + (link_info, _("overflow after relaxation"), + abfd, sec, old_source_offset); + goto error_return; + } + + pin_contents (sec, contents); + } + + /* FIXME: If the relocation still references a section in + the same input file, the relocation should be modified + directly instead of adding a "fix" record. */ + + addend_displacement = + new_reloc.target_offset + new_reloc.virtual_offset; + + fix = reloc_bfd_fix_init (sec, source_offset, r_type, 0, + r_reloc_get_section (&new_reloc), + addend_displacement, TRUE); + add_fix (sec, fix); + } + + pin_internal_relocs (sec, internal_relocs); + } + } + + if ((relax_info->is_relaxable_literal_section + || relax_info->is_relaxable_asm_section) + && relax_info->action_list.head) + { + /* Walk through the planned actions and build up a table + of move, copy and fill records. Use the move, copy and + fill records to perform the actions once. */ + + bfd_size_type size = sec->size; + int removed = 0; + bfd_size_type final_size, copy_size, orig_insn_size; + bfd_byte *scratch = NULL; + bfd_byte *dup_contents = NULL; + bfd_size_type orig_size = size; + bfd_vma orig_dot = 0; + bfd_vma orig_dot_copied = 0; /* Byte copied already from + orig dot in physical memory. */ + bfd_vma orig_dot_vo = 0; /* Virtual offset from orig_dot. */ + bfd_vma dup_dot = 0; + + text_action *action = relax_info->action_list.head; + + final_size = sec->size; + for (action = relax_info->action_list.head; action; + action = action->next) + { + final_size -= action->removed_bytes; + } + + scratch = (bfd_byte *) bfd_zmalloc (final_size); + dup_contents = (bfd_byte *) bfd_zmalloc (final_size); + + /* The dot is the current fill location. */ +#if DEBUG + print_action_list (stderr, &relax_info->action_list); +#endif + + for (action = relax_info->action_list.head; action; + action = action->next) + { + virtual_action = FALSE; + if (action->offset > orig_dot) + { + orig_dot += orig_dot_copied; + orig_dot_copied = 0; + orig_dot_vo = 0; + /* Out of the virtual world. */ + } + + if (action->offset > orig_dot) + { + copy_size = action->offset - orig_dot; + memmove (&dup_contents[dup_dot], &contents[orig_dot], copy_size); + orig_dot += copy_size; + dup_dot += copy_size; + BFD_ASSERT (action->offset == orig_dot); + } + else if (action->offset < orig_dot) + { + if (action->action == ta_fill + && action->offset - action->removed_bytes == orig_dot) + { + /* This is OK because the fill only effects the dup_dot. */ + } + else if (action->action == ta_add_literal) + { + /* TBD. Might need to handle this. */ + } + } + if (action->offset == orig_dot) + { + if (action->virtual_offset > orig_dot_vo) + { + if (orig_dot_vo == 0) + { + /* Need to copy virtual_offset bytes. Probably four. */ + copy_size = action->virtual_offset - orig_dot_vo; + memmove (&dup_contents[dup_dot], + &contents[orig_dot], copy_size); + orig_dot_copied = copy_size; + dup_dot += copy_size; + } + virtual_action = TRUE; + } + else + BFD_ASSERT (action->virtual_offset <= orig_dot_vo); + } + switch (action->action) + { + case ta_remove_literal: + case ta_remove_insn: + BFD_ASSERT (action->removed_bytes >= 0); + orig_dot += action->removed_bytes; + break; + + case ta_narrow_insn: + orig_insn_size = 3; + copy_size = 2; + memmove (scratch, &contents[orig_dot], orig_insn_size); + BFD_ASSERT (action->removed_bytes == 1); + rv = narrow_instruction (scratch, final_size, 0, TRUE); + BFD_ASSERT (rv); + memmove (&dup_contents[dup_dot], scratch, copy_size); + orig_dot += orig_insn_size; + dup_dot += copy_size; + break; + + case ta_fill: + if (action->removed_bytes >= 0) + orig_dot += action->removed_bytes; + else + { + /* Already zeroed in dup_contents. Just bump the + counters. */ + dup_dot += (-action->removed_bytes); + } + break; + + case ta_none: + BFD_ASSERT (action->removed_bytes == 0); + break; + + case ta_convert_longcall: + case ta_remove_longcall: + /* These will be removed or converted before we get here. */ + BFD_ASSERT (0); + break; + + case ta_widen_insn: + orig_insn_size = 2; + copy_size = 3; + memmove (scratch, &contents[orig_dot], orig_insn_size); + BFD_ASSERT (action->removed_bytes == -1); + rv = widen_instruction (scratch, final_size, 0, TRUE); + BFD_ASSERT (rv); + memmove (&dup_contents[dup_dot], scratch, copy_size); + orig_dot += orig_insn_size; + dup_dot += copy_size; + break; + + case ta_add_literal: + orig_insn_size = 0; + copy_size = 4; + BFD_ASSERT (action->removed_bytes == -4); + /* TBD -- place the literal value here and insert + into the table. */ + memset (&dup_contents[dup_dot], 0, 4); + pin_internal_relocs (sec, internal_relocs); + pin_contents (sec, contents); + + if (!move_literal (abfd, link_info, sec, dup_dot, dup_contents, + relax_info, &internal_relocs, &action->value)) + goto error_return; + + if (virtual_action) + orig_dot_vo += copy_size; + + orig_dot += orig_insn_size; + dup_dot += copy_size; + break; + + default: + /* Not implemented yet. */ + BFD_ASSERT (0); + break; + } + + size -= action->removed_bytes; + removed += action->removed_bytes; + BFD_ASSERT (dup_dot <= final_size); + BFD_ASSERT (orig_dot <= orig_size); + } + + orig_dot += orig_dot_copied; + orig_dot_copied = 0; + + if (orig_dot != orig_size) + { + copy_size = orig_size - orig_dot; + BFD_ASSERT (orig_size > orig_dot); + BFD_ASSERT (dup_dot + copy_size == final_size); + memmove (&dup_contents[dup_dot], &contents[orig_dot], copy_size); + orig_dot += copy_size; + dup_dot += copy_size; + } + BFD_ASSERT (orig_size == orig_dot); + BFD_ASSERT (final_size == dup_dot); + + /* Move the dup_contents back. */ + if (final_size > orig_size) + { + /* Contents need to be reallocated. Swap the dup_contents into + contents. */ + sec->contents = dup_contents; + free (contents); + contents = dup_contents; + pin_contents (sec, contents); + } + else + { + BFD_ASSERT (final_size <= orig_size); + memset (contents, 0, orig_size); + memcpy (contents, dup_contents, final_size); + free (dup_contents); + } + free (scratch); + pin_contents (sec, contents); + + sec->size = final_size; + } + + error_return: + release_internal_relocs (sec, internal_relocs); + release_contents (sec, contents); + return ok; +} + + +static bfd_boolean +translate_section_fixes (sec) + asection *sec; +{ + xtensa_relax_info *relax_info; + reloc_bfd_fix *r; + + relax_info = get_xtensa_relax_info (sec); + if (!relax_info) + return TRUE; + + for (r = relax_info->fix_list; r != NULL; r = r->next) + if (!translate_reloc_bfd_fix (r)) + return FALSE; - if (internal_relocs) - { - for (i = 0; i < sec->reloc_count; i++) - { - Elf_Internal_Rela *irel; - xtensa_relax_info *target_relax_info; - bfd_vma source_offset; - r_reloc r_rel; - unsigned r_type; - asection *target_sec; + return TRUE; +} - /* Locally change the source address. - Translate the target to the new target address. - If it points to this section and has been removed, - NULLify it. - Write it back. */ - irel = &internal_relocs[i]; - source_offset = irel->r_offset; +/* Translate a fix given the mapping in the relax info for the target + section. If it has already been translated, no work is required. */ - r_type = ELF32_R_TYPE (irel->r_info); - r_reloc_init (&r_rel, abfd, irel); - - if (relax_info->is_relaxable_literal_section) - { - if (r_type != R_XTENSA_NONE - && find_removed_literal (&relax_info->removed_list, - irel->r_offset)) - { - /* Remove this relocation. */ - if (elf_hash_table (link_info)->dynamic_sections_created) - shrink_dynamic_reloc_sections (link_info, abfd, sec, irel); - irel->r_info = ELF32_R_INFO (0, R_XTENSA_NONE); - irel->r_offset = offset_with_removed_literals - (&relax_info->removed_list, irel->r_offset); - continue; - } - source_offset = - offset_with_removed_literals (&relax_info->removed_list, - irel->r_offset); - irel->r_offset = source_offset; - } +static bfd_boolean +translate_reloc_bfd_fix (fix) + reloc_bfd_fix *fix; +{ + reloc_bfd_fix new_fix; + asection *sec; + xtensa_relax_info *relax_info; + removed_literal *removed; + bfd_vma new_offset, target_offset; - target_sec = r_reloc_get_section (&r_rel); - target_relax_info = get_xtensa_relax_info (target_sec); + if (fix->translated) + return TRUE; - if (target_relax_info - && target_relax_info->is_relaxable_literal_section) - { - r_reloc new_rel; - reloc_bfd_fix *fix; + sec = fix->target_sec; + target_offset = fix->target_offset; - translate_reloc (&r_rel, &new_rel); + relax_info = get_xtensa_relax_info (sec); + if (!relax_info) + { + fix->translated = TRUE; + return TRUE; + } - /* FIXME: If the relocation still references a section in - the same input file, the relocation should be modified - directly instead of adding a "fix" record. */ + new_fix = *fix; - fix = reloc_bfd_fix_init (sec, source_offset, r_type, 0, - r_reloc_get_section (&new_rel), - new_rel.target_offset); - add_fix (sec, fix); - } + /* The fix does not need to be translated if the section cannot change. */ + if (!relax_info->is_relaxable_literal_section + && !relax_info->is_relaxable_asm_section) + { + fix->translated = TRUE; + return TRUE; + } - pin_internal_relocs (sec, internal_relocs); - } + /* If the literal has been moved and this relocation was on an + opcode, then the relocation should move to the new literal + location. Otherwise, the relocation should move within the + section. */ + + removed = FALSE; + if (is_operand_relocation (fix->src_type)) + { + /* Check if the original relocation is against a literal being + removed. */ + removed = find_removed_literal (&relax_info->removed_list, + target_offset); } - if (relax_info->is_relaxable_literal_section) + if (removed) { - /* Walk through the contents and delete literals that are not needed - anymore. */ + asection *new_sec; - unsigned long size = sec->size; - unsigned long removed = 0; + /* The fact that there is still a relocation to this literal indicates + that the literal is being coalesced, not simply removed. */ + BFD_ASSERT (removed->to.abfd != NULL); - removed_literal *reloc = relax_info->removed_list.head; - for (; reloc; reloc = reloc->next) + /* This was moved to some other address (possibly another section). */ + new_sec = r_reloc_get_section (&removed->to); + if (new_sec != sec) { - unsigned long upper = sec->size; - bfd_vma start = reloc->from.target_offset + 4; - if (reloc->next) - upper = reloc->next->from.target_offset; - if (upper - start != 0) + sec = new_sec; + relax_info = get_xtensa_relax_info (sec); + if (!relax_info || + (!relax_info->is_relaxable_literal_section + && !relax_info->is_relaxable_asm_section)) { - BFD_ASSERT (start <= upper); - memmove (contents + start - removed - 4, - contents + start, - upper - start ); - pin_contents (sec, contents); + target_offset = removed->to.target_offset; + new_fix.target_sec = new_sec; + new_fix.target_offset = target_offset; + new_fix.translated = TRUE; + *fix = new_fix; + return TRUE; } - removed += 4; - size -= 4; } - - /* Change the section size. */ - sec->size = size; + target_offset = removed->to.target_offset; + new_fix.target_sec = new_sec; } - - error_return: - release_internal_relocs (sec, internal_relocs); - release_contents (sec, contents); - return ok; + + /* The target address may have been moved within its section. */ + new_offset = offset_with_removed_text (&relax_info->action_list, + target_offset); + + new_fix.target_offset = new_offset; + new_fix.target_offset = new_offset; + new_fix.translated = TRUE; + *fix = new_fix; + return TRUE; } @@ -4824,7 +8745,7 @@ translate_reloc (orig_rel, new_rel) asection *sec; xtensa_relax_info *relax_info; removed_literal *removed; - unsigned long new_offset; + bfd_vma new_offset, target_offset, removed_bytes; *new_rel = *orig_rel; @@ -4835,13 +8756,21 @@ translate_reloc (orig_rel, new_rel) relax_info = get_xtensa_relax_info (sec); BFD_ASSERT (relax_info); - if (!relax_info->is_relaxable_literal_section) + if (!relax_info->is_relaxable_literal_section + && !relax_info->is_relaxable_asm_section) return; - /* Check if the original relocation is against a literal being removed. */ - removed = find_removed_literal (&relax_info->removed_list, - orig_rel->target_offset); - if (removed) + target_offset = orig_rel->target_offset; + + removed = FALSE; + if (is_operand_relocation (ELF32_R_TYPE (orig_rel->rela.r_info))) + { + /* Check if the original relocation is against a literal being + removed. */ + removed = find_removed_literal (&relax_info->removed_list, + target_offset); + } + if (removed && removed->to.abfd) { asection *new_sec; @@ -4849,25 +8778,30 @@ translate_reloc (orig_rel, new_rel) that the literal is being coalesced, not simply removed. */ BFD_ASSERT (removed->to.abfd != NULL); - /* This was moved to some other address (possibly in another section). */ + /* This was moved to some other address + (possibly in another section). */ *new_rel = removed->to; new_sec = r_reloc_get_section (new_rel); - if (new_sec != sec) + if (new_sec != sec) { sec = new_sec; relax_info = get_xtensa_relax_info (sec); - if (!relax_info || !relax_info->is_relaxable_literal_section) + if (!relax_info + || (!relax_info->is_relaxable_literal_section + && !relax_info->is_relaxable_asm_section)) return; } + target_offset = new_rel->target_offset; } /* ...and the target address may have been moved within its section. */ - new_offset = offset_with_removed_literals (&relax_info->removed_list, - new_rel->target_offset); + new_offset = offset_with_removed_text (&relax_info->action_list, + target_offset); /* Modify the offset and addend. */ + removed_bytes = target_offset - new_offset; new_rel->target_offset = new_offset; - new_rel->rela.r_addend += (new_offset - new_rel->target_offset); + new_rel->rela.r_addend -= removed_bytes; } @@ -4977,12 +8911,149 @@ shrink_dynamic_reloc_sections (info, abfd, input_section, rel) } +/* Take an r_rel and move it to another section. This usually + requires extending the interal_relocation array and pinning it. If + the original r_rel is from the same BFD, we can complete this here. + Otherwise, we add a fix record to let the final link fix the + appropriate address. Contents and internal relocations for the + section must be pinned after calling this routine. */ + +static bfd_boolean +move_literal (abfd, link_info, sec, offset, contents, relax_info, + internal_relocs_p, lit) + bfd *abfd; + struct bfd_link_info *link_info; + asection *sec; + bfd_vma offset; + bfd_byte *contents; + xtensa_relax_info *relax_info; + Elf_Internal_Rela **internal_relocs_p; + const literal_value *lit; +{ + Elf_Internal_Rela *new_relocs = NULL; + size_t new_relocs_count = 0; + Elf_Internal_Rela this_rela; + const r_reloc *r_rel; + + r_rel = &lit->r_rel; + BFD_ASSERT (elf_section_data (sec)->relocs == *internal_relocs_p); + + if (r_reloc_is_const (r_rel)) + bfd_put_32 (abfd, lit->value, contents + offset); + else + { + int r_type; + unsigned i; + asection *target_sec; + reloc_bfd_fix *fix; + unsigned insert_at; + + r_type = ELF32_R_TYPE (r_rel->rela.r_info); + target_sec = r_reloc_get_section (r_rel); + + /* This is the difficult case. We have to create a fix up. */ + this_rela.r_offset = offset; + this_rela.r_info = ELF32_R_INFO (0, r_type); + this_rela.r_addend = + r_rel->target_offset - r_reloc_get_target_offset (r_rel); + bfd_put_32 (abfd, lit->value, contents + offset); + + /* Currently, we cannot move relocations during a relocatable link. */ + BFD_ASSERT (!link_info->relocatable); + fix = reloc_bfd_fix_init (sec, offset, r_type, r_rel->abfd, + r_reloc_get_section (r_rel), + r_rel->target_offset + r_rel->virtual_offset, + FALSE); + /* We also need to mark that relocations are needed here. */ + sec->flags |= SEC_RELOC; + + translate_reloc_bfd_fix (fix); + /* This fix has not yet been translated. */ + add_fix (sec, fix); + + /* Add the relocation. If we have already allocated our own + space for the relocations and we have room for more, then use + it. Otherwise, allocate new space and move the literals. */ + insert_at = sec->reloc_count; + for (i = 0; i < sec->reloc_count; ++i) + { + if (this_rela.r_offset < (*internal_relocs_p)[i].r_offset) + { + insert_at = i; + break; + } + } + + if (*internal_relocs_p != relax_info->allocated_relocs + || sec->reloc_count + 1 > relax_info->allocated_relocs_count) + { + BFD_ASSERT (relax_info->allocated_relocs == NULL + || sec->reloc_count == relax_info->relocs_count); + + if (relax_info->allocated_relocs_count == 0) + new_relocs_count = (sec->reloc_count + 2) * 2; + else + new_relocs_count = (relax_info->allocated_relocs_count + 2) * 2; + + new_relocs = (Elf_Internal_Rela *) + bfd_zmalloc (sizeof (Elf_Internal_Rela) * (new_relocs_count)); + if (!new_relocs) + return FALSE; + + /* We could handle this more quickly by finding the split point. */ + if (insert_at != 0) + memcpy (new_relocs, *internal_relocs_p, + insert_at * sizeof (Elf_Internal_Rela)); + + new_relocs[insert_at] = this_rela; + + if (insert_at != sec->reloc_count) + memcpy (new_relocs + insert_at + 1, + (*internal_relocs_p) + insert_at, + (sec->reloc_count - insert_at) + * sizeof (Elf_Internal_Rela)); + + if (*internal_relocs_p != relax_info->allocated_relocs) + { + /* The first time we re-allocate, we can only free the + old relocs if they were allocated with bfd_malloc. + This is not true when keep_memory is in effect. */ + if (!link_info->keep_memory) + free (*internal_relocs_p); + } + else + free (*internal_relocs_p); + relax_info->allocated_relocs = new_relocs; + relax_info->allocated_relocs_count = new_relocs_count; + elf_section_data (sec)->relocs = new_relocs; + sec->reloc_count++; + relax_info->relocs_count = sec->reloc_count; + *internal_relocs_p = new_relocs; + } + else + { + if (insert_at != sec->reloc_count) + { + unsigned idx; + for (idx = sec->reloc_count; idx > insert_at; idx--) + (*internal_relocs_p)[idx] = (*internal_relocs_p)[idx-1]; + } + (*internal_relocs_p)[insert_at] = this_rela; + sec->reloc_count++; + if (relax_info->allocated_relocs) + relax_info->relocs_count = sec->reloc_count; + } + } + return TRUE; +} + + /* This is similar to relax_section except that when a target is moved, we shift addresses up. We also need to modify the size. This algorithm does NOT allow for relocations into the middle of the property sections. */ -static bfd_boolean +static bfd_boolean relax_property_section (abfd, sec, link_info) bfd *abfd; asection *sec; @@ -4992,25 +9063,36 @@ relax_property_section (abfd, sec, link_info) bfd_byte *contents; unsigned i, nexti; bfd_boolean ok = TRUE; + bfd_boolean is_full_prop_section; + size_t last_zfill_target_offset = 0; + asection *last_zfill_target_sec = NULL; + bfd_size_type sec_size; + sec_size = bfd_get_section_limit (abfd, sec); internal_relocs = retrieve_internal_relocs (abfd, sec, link_info->keep_memory); contents = retrieve_contents (abfd, sec, link_info->keep_memory); - if (contents == NULL && sec->size != 0) + if (contents == NULL && sec_size != 0) { ok = FALSE; goto error_return; } - if (internal_relocs) + is_full_prop_section = + ((strcmp (sec->name, XTENSA_PROP_SEC_NAME) == 0) + || (strncmp (sec->name, ".gnu.linkonce.prop.", + sizeof ".gnu.linkonce.prop." - 1) == 0)); + + if (internal_relocs) { - for (i = 0; i < sec->reloc_count; i++) + for (i = 0; i < sec->reloc_count; i++) { Elf_Internal_Rela *irel; xtensa_relax_info *target_relax_info; - r_reloc r_rel; unsigned r_type; asection *target_sec; + literal_value val; + bfd_byte *size_p, *flags_p; /* Locally change the source address. Translate the target to the new target address. @@ -5023,42 +9105,88 @@ relax_property_section (abfd, sec, link_info) if (r_type == R_XTENSA_NONE) continue; - r_reloc_init (&r_rel, abfd, irel); + /* Find the literal value. */ + r_reloc_init (&val.r_rel, abfd, irel, contents, sec_size); + size_p = &contents[irel->r_offset + 4]; + flags_p = NULL; + if (is_full_prop_section) + { + flags_p = &contents[irel->r_offset + 8]; + BFD_ASSERT (irel->r_offset + 12 <= sec_size); + } + else + BFD_ASSERT (irel->r_offset + 8 <= sec_size); - target_sec = r_reloc_get_section (&r_rel); + target_sec = r_reloc_get_section (&val.r_rel); target_relax_info = get_xtensa_relax_info (target_sec); if (target_relax_info - && target_relax_info->is_relaxable_literal_section) + && (target_relax_info->is_relaxable_literal_section + || target_relax_info->is_relaxable_asm_section )) { /* Translate the relocation's destination. */ - bfd_vma new_offset; - bfd_vma new_end_offset; - bfd_byte *size_p; + bfd_vma new_offset, new_end_offset; long old_size, new_size; - new_offset = - offset_with_removed_literals (&target_relax_info->removed_list, - r_rel.target_offset); + new_offset = offset_with_removed_text + (&target_relax_info->action_list, val.r_rel.target_offset); /* Assert that we are not out of bounds. */ - size_p = &contents[irel->r_offset + 4]; - old_size = bfd_get_32 (abfd, &contents[irel->r_offset + 4]); + old_size = bfd_get_32 (abfd, size_p); + + if (old_size == 0) + { + /* Only the first zero-sized unreachable entry is + allowed to expand. In this case the new offset + should be the offset before the fill and the new + size is the expansion size. For other zero-sized + entries the resulting size should be zero with an + offset before or after the fill address depending + on whether the expanding unreachable entry + preceeds it. */ + if (last_zfill_target_sec + && last_zfill_target_sec == target_sec + && last_zfill_target_offset == val.r_rel.target_offset) + new_end_offset = new_offset; + else + { + new_end_offset = new_offset; + new_offset = offset_with_removed_text_before_fill + (&target_relax_info->action_list, + val.r_rel.target_offset); + + /* If it is not unreachable and we have not yet + seen an unreachable at this address, place it + before the fill address. */ + if (!flags_p + || (bfd_get_32 (abfd, flags_p) + & XTENSA_PROP_UNREACHABLE) == 0) + new_end_offset = new_offset; + else + { + last_zfill_target_sec = target_sec; + last_zfill_target_offset = val.r_rel.target_offset; + } + } + } + else + { + new_end_offset = offset_with_removed_text_before_fill + (&target_relax_info->action_list, + val.r_rel.target_offset + old_size); + } - new_end_offset = - offset_with_removed_literals (&target_relax_info->removed_list, - r_rel.target_offset + old_size); - new_size = new_end_offset - new_offset; + if (new_size != old_size) { bfd_put_32 (abfd, new_size, size_p); pin_contents (sec, contents); } - - if (new_offset != r_rel.target_offset) + + if (new_offset != val.r_rel.target_offset) { - bfd_vma diff = new_offset - r_rel.target_offset; + bfd_vma diff = new_offset - val.r_rel.target_offset; irel->r_addend += diff; pin_internal_relocs (sec, internal_relocs); } @@ -5070,12 +9198,22 @@ relax_property_section (abfd, sec, link_info) finish_dynamic_sections() but at that point it's too late to reclaim the space in the output section, so we do this twice. */ - if (internal_relocs) + if (internal_relocs && (!link_info->relocatable + || strcmp (sec->name, XTENSA_LIT_SEC_NAME) == 0)) { Elf_Internal_Rela *last_irel = NULL; int removed_bytes = 0; bfd_vma offset, last_irel_offset; bfd_vma section_size; + bfd_size_type entry_size; + flagword predef_flags; + + if (is_full_prop_section) + entry_size = 12; + else + entry_size = 8; + + predef_flags = xtensa_get_property_predef_flags (sec); /* Walk over memory and irels at the same time. This REQUIRES that the internal_relocs be sorted by offset. */ @@ -5088,13 +9226,14 @@ relax_property_section (abfd, sec, link_info) last_irel_offset = (bfd_vma) -1; section_size = sec->size; - BFD_ASSERT (section_size % 8 == 0); + BFD_ASSERT (section_size % entry_size == 0); - for (offset = 0; offset < section_size; offset += 8) + for (offset = 0; offset < section_size; offset += entry_size) { Elf_Internal_Rela *irel, *next_irel; bfd_vma bytes_to_remove, size, actual_offset; bfd_boolean remove_this_irel; + flagword flags; irel = NULL; next_irel = NULL; @@ -5132,28 +9271,38 @@ relax_property_section (abfd, sec, link_info) actual_offset = offset - removed_bytes; size = bfd_get_32 (abfd, &contents[actual_offset + 4]); + if (is_full_prop_section) + flags = bfd_get_32 (abfd, &contents[actual_offset + 8]); + else + flags = predef_flags; + /* Check that the irels are sorted by offset, with only one per address. */ BFD_ASSERT (!irel || (int) irel->r_offset > (int) last_irel_offset); BFD_ASSERT (!next_irel || next_irel->r_offset > irel->r_offset); - /* Make sure there isn't a reloc on the size field. */ - if (irel && irel->r_offset == offset + 4) + /* Make sure there aren't relocs on the size or flag fields. */ + if ((irel && irel->r_offset == offset + 4) + || (is_full_prop_section + && irel && irel->r_offset == offset + 8)) { irel->r_offset -= removed_bytes; last_irel_offset = irel->r_offset; } - else if (next_irel && next_irel->r_offset == offset + 4) + else if (next_irel && (next_irel->r_offset == offset + 4 + || (is_full_prop_section + && next_irel->r_offset == offset + 8))) { nexti += 1; irel->r_offset -= removed_bytes; next_irel->r_offset -= removed_bytes; last_irel_offset = next_irel->r_offset; } - else if (size == 0) + else if (size == 0 && (flags & XTENSA_PROP_ALIGN) == 0 + && (flags & XTENSA_PROP_UNREACHABLE) == 0) { - /* Always remove entries with zero size. */ - bytes_to_remove = 8; + /* Always remove entries with zero size and no alignment. */ + bytes_to_remove = entry_size; if (irel && irel->r_offset == offset) { remove_this_irel = TRUE; @@ -5168,23 +9317,32 @@ relax_property_section (abfd, sec, link_info) { if (last_irel) { - bfd_vma old_size = + flagword old_flags; + bfd_vma old_size = bfd_get_32 (abfd, &contents[last_irel->r_offset + 4]); - bfd_vma old_address = - (last_irel->r_addend + bfd_vma old_address = + (last_irel->r_addend + bfd_get_32 (abfd, &contents[last_irel->r_offset])); - bfd_vma new_address = - (irel->r_addend + bfd_vma new_address = + (irel->r_addend + bfd_get_32 (abfd, &contents[actual_offset])); - - if ((ELF32_R_SYM (irel->r_info) == - ELF32_R_SYM (last_irel->r_info)) - && (old_address + old_size == new_address)) + if (is_full_prop_section) + old_flags = bfd_get_32 + (abfd, &contents[last_irel->r_offset + 8]); + else + old_flags = predef_flags; + + if ((ELF32_R_SYM (irel->r_info) + == ELF32_R_SYM (last_irel->r_info)) + && old_address + old_size == new_address + && old_flags == flags + && (old_flags & XTENSA_PROP_INSN_BRANCH_TARGET) == 0 + && (old_flags & XTENSA_PROP_INSN_LOOP_TARGET) == 0) { - /* fix the old size */ + /* Fix the old size. */ bfd_put_32 (abfd, old_size + size, &contents[last_irel->r_offset + 4]); - bytes_to_remove = 8; + bytes_to_remove = entry_size; remove_this_irel = TRUE; } else @@ -5207,14 +9365,14 @@ relax_property_section (abfd, sec, link_info) if (bytes_to_remove != 0) { removed_bytes += bytes_to_remove; - if (offset + 8 < section_size) + if (offset + bytes_to_remove < section_size) memmove (&contents[actual_offset], - &contents[actual_offset+8], - section_size - offset - 8); + &contents[actual_offset + bytes_to_remove], + section_size - offset - bytes_to_remove); } } - if (removed_bytes) + if (removed_bytes) { /* Clear the removed bytes. */ memset (&contents[section_size - removed_bytes], 0, removed_bytes); @@ -5246,7 +9404,7 @@ relax_property_section (abfd, sec, link_info) /* Change symbol values to account for removed literals. */ -bfd_boolean +bfd_boolean relax_section_symbols (abfd, sec) bfd *abfd; asection *sec; @@ -5260,7 +9418,8 @@ relax_section_symbols (abfd, sec) relax_info = get_xtensa_relax_info (sec); BFD_ASSERT (relax_info); - if (!relax_info->is_relaxable_literal_section) + if (!relax_info->is_relaxable_literal_section + && !relax_info->is_relaxable_asm_section) return TRUE; sec_shndx = _bfd_elf_section_from_bfd_section (abfd, sec); @@ -5278,10 +9437,19 @@ relax_section_symbols (abfd, sec) if (isym->st_shndx == sec_shndx) { - bfd_vma new_address = offset_with_removed_literals - (&relax_info->removed_list, isym->st_value); - if (new_address != isym->st_value) - isym->st_value = new_address; + bfd_vma new_address = offset_with_removed_text + (&relax_info->action_list, isym->st_value); + bfd_vma new_size = isym->st_size; + + if (ELF32_ST_TYPE (isym->st_info) == STT_FUNC) + { + bfd_vma new_end = offset_with_removed_text + (&relax_info->action_list, isym->st_value + isym->st_size); + new_size = new_end - new_address; + } + + isym->st_value = new_address; + isym->st_size = new_size; } } @@ -5299,10 +9467,20 @@ relax_section_symbols (abfd, sec) || sym_hash->root.type == bfd_link_hash_defweak) && sym_hash->root.u.def.section == sec) { - bfd_vma new_address = offset_with_removed_literals - (&relax_info->removed_list, sym_hash->root.u.def.value); - if (new_address != sym_hash->root.u.def.value) - sym_hash->root.u.def.value = new_address; + bfd_vma new_address = offset_with_removed_text + (&relax_info->action_list, sym_hash->root.u.def.value); + bfd_vma new_size = sym_hash->size; + + if (sym_hash->type == STT_FUNC) + { + bfd_vma new_end = offset_with_removed_text + (&relax_info->action_list, + sym_hash->root.u.def.value + sym_hash->size); + new_size = new_end - new_address; + } + + sym_hash->root.u.def.value = new_address; + sym_hash->size = new_size; } } @@ -5312,37 +9490,41 @@ relax_section_symbols (abfd, sec) /* "Fix" handling functions, called while performing relocations. */ -static void -do_fix_for_relocatable_link (rel, input_bfd, input_section) +static bfd_boolean +do_fix_for_relocatable_link (rel, input_bfd, input_section, contents) Elf_Internal_Rela *rel; bfd *input_bfd; asection *input_section; + bfd_byte *contents; { r_reloc r_rel; asection *sec, *old_sec; bfd_vma old_offset; int r_type = ELF32_R_TYPE (rel->r_info); - reloc_bfd_fix *fix_list; reloc_bfd_fix *fix; if (r_type == R_XTENSA_NONE) - return; - - fix_list = (get_xtensa_relax_info (input_section))->fix_list; - if (fix_list == NULL) - return; + return TRUE; - fix = get_bfd_fix (fix_list, input_section, rel->r_offset, r_type); - if (fix == NULL) - return; + fix = get_bfd_fix (input_section, rel->r_offset, r_type); + if (!fix) + return TRUE; - r_reloc_init (&r_rel, input_bfd, rel); + r_reloc_init (&r_rel, input_bfd, rel, contents, + bfd_get_section_limit (input_bfd, input_section)); old_sec = r_reloc_get_section (&r_rel); - old_offset = r_reloc_get_target_offset (&r_rel); - - if (old_sec == NULL || !r_reloc_is_defined (&r_rel)) + old_offset = r_rel.target_offset; + + if (!old_sec || !r_reloc_is_defined (&r_rel)) { - BFD_ASSERT (r_type == R_XTENSA_ASM_EXPAND); + if (r_type != R_XTENSA_ASM_EXPAND) + { + (*_bfd_error_handler) + (_("%B(%A+0x%lx): unexpected fix for %s relocation"), + input_bfd, input_section, rel->r_offset, + elf_howto_table[r_type].name); + return FALSE; + } /* Leave it be. Resolution will happen in a later stage. */ } else @@ -5351,35 +9533,45 @@ do_fix_for_relocatable_link (rel, input_bfd, input_section) rel->r_addend += ((sec->output_offset + fix->target_offset) - (old_sec->output_offset + old_offset)); } + return TRUE; } static void -do_fix_for_final_link (rel, input_section, relocationp) +do_fix_for_final_link (rel, input_bfd, input_section, contents, relocationp) Elf_Internal_Rela *rel; + bfd *input_bfd; asection *input_section; + bfd_byte *contents; bfd_vma *relocationp; { asection *sec; int r_type = ELF32_R_TYPE (rel->r_info); - reloc_bfd_fix *fix_list; reloc_bfd_fix *fix; + bfd_vma fixup_diff; if (r_type == R_XTENSA_NONE) return; - fix_list = (get_xtensa_relax_info (input_section))->fix_list; - if (fix_list == NULL) - return; - - fix = get_bfd_fix (fix_list, input_section, rel->r_offset, r_type); - if (fix == NULL) + fix = get_bfd_fix (input_section, rel->r_offset, r_type); + if (!fix) return; sec = fix->target_sec; + + fixup_diff = rel->r_addend; + if (elf_howto_table[fix->src_type].partial_inplace) + { + bfd_vma inplace_val; + BFD_ASSERT (fix->src_offset + < bfd_get_section_limit (input_bfd, input_section)); + inplace_val = bfd_get_32 (input_bfd, &contents[fix->src_offset]); + fixup_diff += inplace_val; + } + *relocationp = (sec->output_section->vma + sec->output_offset - + fix->target_offset - rel->r_addend); + + fix->target_offset - fixup_diff); } @@ -5430,7 +9622,7 @@ get_elf_r_symndx_section (abfd, r_symndx) { Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr; asection *target_sec = NULL; - if (r_symndx < symtab_hdr->sh_info) + if (r_symndx < symtab_hdr->sh_info) { Elf_Internal_Sym *isymbuf; unsigned int section_index; @@ -5446,7 +9638,7 @@ get_elf_r_symndx_section (abfd, r_symndx) target_sec = bfd_abs_section_ptr; else if (section_index == SHN_COMMON) target_sec = bfd_com_section_ptr; - else + else /* Who knows? */ target_sec = NULL; } @@ -5492,7 +9684,7 @@ get_elf_r_symndx_hash_entry (abfd, r_symndx) if (r_symndx < symtab_hdr->sh_info) return NULL; - + indx = r_symndx - symtab_hdr->sh_info; h = elf_sym_hashes (abfd)[indx]; while (h->root.type == bfd_link_hash_indirect @@ -5512,7 +9704,7 @@ get_elf_r_symndx_offset (abfd, r_symndx) Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr; bfd_vma offset = 0; - if (r_symndx < symtab_hdr->sh_info) + if (r_symndx < symtab_hdr->sh_info) { Elf_Internal_Sym *isymbuf; isymbuf = retrieve_local_syms (abfd); @@ -5536,21 +9728,40 @@ get_elf_r_symndx_offset (abfd, r_symndx) static bfd_boolean -pcrel_reloc_fits (opnd, self_address, dest_address) - xtensa_operand opnd; +is_reloc_sym_weak (abfd, rel) + bfd *abfd; + Elf_Internal_Rela *rel; +{ + unsigned long r_symndx = ELF32_R_SYM (rel->r_info); + struct elf_link_hash_entry *h; + + h = get_elf_r_symndx_hash_entry (abfd, r_symndx); + if (h && h->root.type == bfd_link_hash_defweak) + return TRUE; + return FALSE; +} + + +static bfd_boolean +pcrel_reloc_fits (opc, opnd, self_address, dest_address) + xtensa_opcode opc; + int opnd; bfd_vma self_address; bfd_vma dest_address; { - uint32 new_address = - xtensa_operand_do_reloc (opnd, dest_address, self_address); - return (xtensa_operand_encode (opnd, &new_address) - == xtensa_encode_result_ok); + xtensa_isa isa = xtensa_default_isa; + uint32 valp = dest_address; + if (xtensa_operand_do_reloc (isa, opc, opnd, &valp, self_address) + || xtensa_operand_encode (isa, opc, opnd, &valp)) + return FALSE; + return TRUE; } static int linkonce_len = sizeof (".gnu.linkonce.") - 1; static int insn_sec_len = sizeof (XTENSA_INSN_SEC_NAME) - 1; static int lit_sec_len = sizeof (XTENSA_LIT_SEC_NAME) - 1; +static int prop_sec_len = sizeof (XTENSA_PROP_SEC_NAME) - 1; static bfd_boolean @@ -5558,13 +9769,14 @@ xtensa_is_property_section (sec) asection *sec; { if (strncmp (XTENSA_INSN_SEC_NAME, sec->name, insn_sec_len) == 0 - || strncmp (XTENSA_LIT_SEC_NAME, sec->name, lit_sec_len) == 0) + || strncmp (XTENSA_LIT_SEC_NAME, sec->name, lit_sec_len) == 0 + || strncmp (XTENSA_PROP_SEC_NAME, sec->name, prop_sec_len) == 0) return TRUE; if (strncmp (".gnu.linkonce.", sec->name, linkonce_len) == 0 - && (sec->name[linkonce_len] == 'x' - || sec->name[linkonce_len] == 'p') - && sec->name[linkonce_len + 1] == '.') + && (strncmp (&sec->name[linkonce_len], "x.", 2) == 0 + || strncmp (&sec->name[linkonce_len], "p.", 2) == 0 + || strncmp (&sec->name[linkonce_len], "prop.", 5) == 0)) return TRUE; return FALSE; @@ -5587,25 +9799,41 @@ xtensa_is_littable_section (sec) } -static bfd_boolean -is_literal_section (sec) - asection *sec; +static int +internal_reloc_compare (ap, bp) + const PTR ap; + const PTR bp; { - /* FIXME: the current definition of this leaves a lot to be desired.... */ - if (sec == NULL || sec->name == NULL) - return FALSE; - return (strstr (sec->name, "literal") != NULL); + const Elf_Internal_Rela *a = (const Elf_Internal_Rela *) ap; + const Elf_Internal_Rela *b = (const Elf_Internal_Rela *) bp; + + if (a->r_offset != b->r_offset) + return (a->r_offset - b->r_offset); + + /* We don't need to sort on these criteria for correctness, + but enforcing a more strict ordering prevents unstable qsort + from behaving differently with different implementations. + Without the code below we get correct but different results + on Solaris 2.7 and 2.8. We would like to always produce the + same results no matter the host. */ + + if (a->r_info != b->r_info) + return (a->r_info - b->r_info); + + return (a->r_addend - b->r_addend); } static int -internal_reloc_compare (ap, bp) +internal_reloc_matches (ap, bp) const PTR ap; const PTR bp; { const Elf_Internal_Rela *a = (const Elf_Internal_Rela *) ap; const Elf_Internal_Rela *b = (const Elf_Internal_Rela *) bp; + /* Check if one entry overlaps with the other; this shouldn't happen + except when searching for a match. */ return (a->r_offset - b->r_offset); } @@ -5619,26 +9847,28 @@ xtensa_get_property_section_name (sec, base_name) { char *prop_sec_name; const char *suffix; - char linkonce_kind = 0; + char *linkonce_kind = 0; if (strcmp (base_name, XTENSA_INSN_SEC_NAME) == 0) - linkonce_kind = 'x'; + linkonce_kind = "x"; else if (strcmp (base_name, XTENSA_LIT_SEC_NAME) == 0) - linkonce_kind = 'p'; + linkonce_kind = "p"; + else if (strcmp (base_name, XTENSA_PROP_SEC_NAME) == 0) + linkonce_kind = "prop."; else abort (); - prop_sec_name = (char *) bfd_malloc (strlen (sec->name) + 1); + prop_sec_name = (char *) bfd_malloc (strlen (sec->name) + + strlen (linkonce_kind) + 1); memcpy (prop_sec_name, ".gnu.linkonce.", linkonce_len); - prop_sec_name[linkonce_len] = linkonce_kind; - prop_sec_name[linkonce_len + 1] = '.'; + strcpy (prop_sec_name + linkonce_len, linkonce_kind); suffix = sec->name + linkonce_len; /* For backward compatibility, replace "t." instead of inserting - the new linkonce_kind. */ - if (strncmp (suffix, "t.", 2) == 0) - suffix += 2; - strcpy (prop_sec_name + linkonce_len + 2, suffix); + the new linkonce_kind (but not for "prop" sections). */ + if (strncmp (suffix, "t.", 2) == 0 && linkonce_kind[1] == '.') + suffix += 2; + strcat (prop_sec_name + linkonce_len, suffix); return prop_sec_name; } @@ -5646,6 +9876,26 @@ xtensa_get_property_section_name (sec, base_name) return strdup (base_name); } + +flagword +xtensa_get_property_predef_flags (sec) + asection *sec; +{ + if (strcmp (sec->name, XTENSA_INSN_SEC_NAME) == 0 + || strncmp (sec->name, ".gnu.linkonce.x.", + sizeof ".gnu.linkonce.x." - 1) == 0) + return (XTENSA_PROP_INSN + | XTENSA_PROP_INSN_NO_TRANSFORM + | XTENSA_PROP_INSN_NO_REORDER); + + if (xtensa_is_littable_section (sec)) + return (XTENSA_PROP_LITERAL + | XTENSA_PROP_INSN_NO_TRANSFORM + | XTENSA_PROP_INSN_NO_REORDER); + + return 0; +} + /* Other functions called directly by the linker. */ @@ -5661,6 +9911,9 @@ xtensa_callback_required_dependence (abfd, sec, link_info, callback, closure) bfd_byte *contents; unsigned i; bfd_boolean ok = TRUE; + bfd_size_type sec_size; + + sec_size = bfd_get_section_limit (abfd, sec); /* ".plt*" sections have no explicit relocations but they contain L32R instructions that reference the corresponding ".got.plt*" sections. */ @@ -5688,43 +9941,43 @@ xtensa_callback_required_dependence (abfd, sec, link_info, callback, closure) /* Assume worst-case offsets: L32R at the very end of the ".plt" section referencing a literal at the very beginning of ".got.plt". This is very close to the real dependence, anyway. */ - (*callback) (sec, sec->size, sgotplt, 0, closure); + (*callback) (sec, sec_size, sgotplt, 0, closure); } internal_relocs = retrieve_internal_relocs (abfd, sec, link_info->keep_memory); if (internal_relocs == NULL - || sec->reloc_count == 0) + || sec->reloc_count == 0) return ok; /* Cache the contents for the duration of this scan. */ contents = retrieve_contents (abfd, sec, link_info->keep_memory); - if (contents == NULL && sec->size != 0) + if (contents == NULL && sec_size != 0) { ok = FALSE; goto error_return; } - if (xtensa_default_isa == NULL) - xtensa_isa_init (); + if (!xtensa_default_isa) + xtensa_default_isa = xtensa_isa_init (0, 0); - for (i = 0; i < sec->reloc_count; i++) + for (i = 0; i < sec->reloc_count; i++) { Elf_Internal_Rela *irel = &internal_relocs[i]; - if (is_l32r_relocation (sec, contents, irel)) + if (is_l32r_relocation (abfd, sec, contents, irel)) { r_reloc l32r_rel; asection *target_sec; bfd_vma target_offset; - - r_reloc_init (&l32r_rel, abfd, irel); + + r_reloc_init (&l32r_rel, abfd, irel, contents, sec_size); target_sec = NULL; target_offset = 0; /* L32Rs must be local to the input file. */ if (r_reloc_is_defined (&l32r_rel)) { target_sec = r_reloc_get_section (&l32r_rel); - target_offset = r_reloc_get_target_offset (&l32r_rel); + target_offset = l32r_rel.target_offset; } (*callback) (sec, irel->r_offset, target_sec, target_offset, closure); @@ -5761,7 +10014,7 @@ static struct bfd_elf_special_section const elf_xtensa_special_sections[]= value so that pre-T1040 tools can read the files. As soon as we stop caring about pre-T1040 tools, the following two values should be swapped. At the same time, any other code that uses EM_XTENSA_OLD - (e.g., prep_headers() in elf.c) should be changed to use EM_XTENSA. */ + should be changed to use EM_XTENSA. */ #define ELF_MACHINE_CODE EM_XTENSA_OLD #define ELF_MACHINE_ALT1 EM_XTENSA -- cgit v1.1