diff options
Diffstat (limited to 'gold/arm.cc')
-rw-r--r-- | gold/arm.cc | 395 |
1 files changed, 387 insertions, 8 deletions
diff --git a/gold/arm.cc b/gold/arm.cc index 8bcd5d2..88102dc 100644 --- a/gold/arm.cc +++ b/gold/arm.cc @@ -1956,6 +1956,79 @@ class Arm_output_data_got : public Output_data_got<32, big_endian> std::vector<Static_reloc> static_relocs_; }; +// The ARM target has many relocation types with odd-sizes or incontigious +// bits. The default handling of relocatable relocation cannot process these +// relocations. So we have to extend the default code. + +template<bool big_endian, int sh_type, typename Classify_reloc> +class Arm_scan_relocatable_relocs : + public Default_scan_relocatable_relocs<sh_type, Classify_reloc> +{ + public: + // Return the strategy to use for a local symbol which is a section + // symbol, given the relocation type. + inline Relocatable_relocs::Reloc_strategy + local_section_strategy(unsigned int r_type, Relobj*) + { + if (sh_type == elfcpp::SHT_RELA) + return Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_RELA; + else + { + if (r_type == elfcpp::R_ARM_TARGET1 + || r_type == elfcpp::R_ARM_TARGET2) + { + const Target_arm<big_endian>* arm_target = + Target_arm<big_endian>::default_target(); + r_type = arm_target->get_real_reloc_type(r_type); + } + + switch(r_type) + { + // Relocations that write nothing. These exclude R_ARM_TARGET1 + // and R_ARM_TARGET2. + case elfcpp::R_ARM_NONE: + case elfcpp::R_ARM_V4BX: + case elfcpp::R_ARM_TLS_GOTDESC: + case elfcpp::R_ARM_TLS_CALL: + case elfcpp::R_ARM_TLS_DESCSEQ: + case elfcpp::R_ARM_THM_TLS_CALL: + case elfcpp::R_ARM_GOTRELAX: + case elfcpp::R_ARM_GNU_VTENTRY: + case elfcpp::R_ARM_GNU_VTINHERIT: + case elfcpp::R_ARM_THM_TLS_DESCSEQ16: + case elfcpp::R_ARM_THM_TLS_DESCSEQ32: + return Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_0; + // These should have been converted to something else above. + case elfcpp::R_ARM_TARGET1: + case elfcpp::R_ARM_TARGET2: + gold_unreachable(); + // Relocations that write full 32 bits. + case elfcpp::R_ARM_ABS32: + case elfcpp::R_ARM_REL32: + case elfcpp::R_ARM_SBREL32: + case elfcpp::R_ARM_GOTOFF32: + case elfcpp::R_ARM_BASE_PREL: + case elfcpp::R_ARM_GOT_BREL: + case elfcpp::R_ARM_BASE_ABS: + case elfcpp::R_ARM_ABS32_NOI: + case elfcpp::R_ARM_REL32_NOI: + case elfcpp::R_ARM_PLT32_ABS: + case elfcpp::R_ARM_GOT_ABS: + case elfcpp::R_ARM_GOT_PREL: + case elfcpp::R_ARM_TLS_GD32: + case elfcpp::R_ARM_TLS_LDM32: + case elfcpp::R_ARM_TLS_LDO32: + case elfcpp::R_ARM_TLS_IE32: + case elfcpp::R_ARM_TLS_LE32: + return Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_4; + default: + // For all other static relocations, return RELOC_SPECIAL. + return Relocatable_relocs::RELOC_SPECIAL; + } + } + } +}; + // Utilities for manipulating integers of up to 32-bits namespace utils @@ -2189,6 +2262,21 @@ class Target_arm : public Sized_target<32, big_endian> unsigned char* reloc_view, section_size_type reloc_view_size); + // Perform target-specific processing in a relocatable link. This is + // only used if we use the relocation strategy RELOC_SPECIAL. + void + relocate_special_relocatable(const Relocate_info<32, big_endian>* relinfo, + unsigned int sh_type, + const unsigned char* preloc_in, + size_t relnum, + Output_section* output_section, + off_t offset_in_output_section, + unsigned char* view, + typename elfcpp::Elf_types<32>::Elf_Addr + view_address, + section_size_type view_size, + unsigned char* preloc_out); + // Return whether SYM is defined by the ABI. bool do_is_defined_by_abi(Symbol* sym) const @@ -3725,6 +3813,7 @@ Arm_relocate_functions<big_endian>::arm_branch_common( Target_arm<big_endian>::default_target(); if (is_weakly_undefined_without_plt) { + gold_assert(!parameters->options().relocatable()); Valtype cond = val & 0xf0000000U; if (arm_target->may_use_arm_nop()) val = cond | 0x0320f000; @@ -3742,8 +3831,11 @@ Arm_relocate_functions<big_endian>::arm_branch_common( // to switch mode. bool may_use_blx = arm_target->may_use_blx(); Reloc_stub* stub = NULL; - if (utils::has_overflow<26>(branch_offset) - || ((thumb_bit != 0) && !(may_use_blx && r_type == elfcpp::R_ARM_CALL))) + + if (!parameters->options().relocatable() + && (utils::has_overflow<26>(branch_offset) + || ((thumb_bit != 0) + && !(may_use_blx && r_type == elfcpp::R_ARM_CALL)))) { Valtype unadjusted_branch_target = psymval->value(object, 0); @@ -3850,6 +3942,7 @@ Arm_relocate_functions<big_endian>::thumb_branch_common( Target_arm<big_endian>::default_target(); if (is_weakly_undefined_without_plt) { + gold_assert(!parameters->options().relocatable()); if (arm_target->may_use_thumb2_nop()) { elfcpp::Swap<16, big_endian>::writeval(wv, 0xf3af); @@ -3876,11 +3969,12 @@ Arm_relocate_functions<big_endian>::thumb_branch_common( // We need a stub if the branch offset is too large or if we need // to switch mode. bool thumb2 = arm_target->using_thumb2(); - if ((!thumb2 && utils::has_overflow<23>(branch_offset)) - || (thumb2 && utils::has_overflow<25>(branch_offset)) - || ((thumb_bit == 0) - && (((r_type == elfcpp::R_ARM_THM_CALL) && !may_use_blx) - || r_type == elfcpp::R_ARM_THM_JUMP24))) + if (!parameters->options().relocatable() + && ((!thumb2 && utils::has_overflow<23>(branch_offset)) + || (thumb2 && utils::has_overflow<25>(branch_offset)) + || ((thumb_bit == 0) + && (((r_type == elfcpp::R_ARM_THM_CALL) && !may_use_blx) + || r_type == elfcpp::R_ARM_THM_JUMP24)))) { Arm_address unadjusted_branch_target = psymval->value(object, 0); @@ -8919,7 +9013,7 @@ Target_arm<big_endian>::scan_relocatable_relocs( { gold_assert(sh_type == elfcpp::SHT_REL); - typedef gold::Default_scan_relocatable_relocs<elfcpp::SHT_REL, + typedef Arm_scan_relocatable_relocs<big_endian, elfcpp::SHT_REL, Relocatable_size_for_reloc> Scan_relocatable_relocs; gold::scan_relocatable_relocs<32, big_endian, elfcpp::SHT_REL, @@ -8971,6 +9065,291 @@ Target_arm<big_endian>::relocate_for_relocatable( reloc_view_size); } +// Perform target-specific processing in a relocatable link. This is +// only used if we use the relocation strategy RELOC_SPECIAL. + +template<bool big_endian> +void +Target_arm<big_endian>::relocate_special_relocatable( + const Relocate_info<32, big_endian>* relinfo, + unsigned int sh_type, + const unsigned char* preloc_in, + size_t relnum, + Output_section* output_section, + off_t offset_in_output_section, + unsigned char* view, + elfcpp::Elf_types<32>::Elf_Addr view_address, + section_size_type, + unsigned char* preloc_out) +{ + // We can only handle REL type relocation sections. + gold_assert(sh_type == elfcpp::SHT_REL); + + typedef typename Reloc_types<elfcpp::SHT_REL, 32, big_endian>::Reloc Reltype; + typedef typename Reloc_types<elfcpp::SHT_REL, 32, big_endian>::Reloc_write + Reltype_write; + const Arm_address invalid_address = static_cast<Arm_address>(0) - 1; + + const Arm_relobj<big_endian>* object = + Arm_relobj<big_endian>::as_arm_relobj(relinfo->object); + const unsigned int local_count = object->local_symbol_count(); + + Reltype reloc(preloc_in); + Reltype_write reloc_write(preloc_out); + + elfcpp::Elf_types<32>::Elf_WXword r_info = reloc.get_r_info(); + const unsigned int r_sym = elfcpp::elf_r_sym<32>(r_info); + const unsigned int r_type = elfcpp::elf_r_type<32>(r_info); + + const Arm_reloc_property* arp = + arm_reloc_property_table->get_implemented_static_reloc_property(r_type); + gold_assert(arp != NULL); + + // Get the new symbol index. + // We only use RELOC_SPECIAL strategy in local relocations. + gold_assert(r_sym < local_count); + + // We are adjusting a section symbol. We need to find + // the symbol table index of the section symbol for + // the output section corresponding to input section + // in which this symbol is defined. + bool is_ordinary; + unsigned int shndx = object->local_symbol_input_shndx(r_sym, &is_ordinary); + gold_assert(is_ordinary); + Output_section* os = object->output_section(shndx); + gold_assert(os != NULL); + gold_assert(os->needs_symtab_index()); + unsigned int new_symndx = os->symtab_index(); + + // Get the new offset--the location in the output section where + // this relocation should be applied. + + Arm_address offset = reloc.get_r_offset(); + Arm_address new_offset; + if (offset_in_output_section != invalid_address) + new_offset = offset + offset_in_output_section; + else + { + section_offset_type sot_offset = + convert_types<section_offset_type, Arm_address>(offset); + section_offset_type new_sot_offset = + output_section->output_offset(object, relinfo->data_shndx, + sot_offset); + gold_assert(new_sot_offset != -1); + new_offset = new_sot_offset; + } + + // In an object file, r_offset is an offset within the section. + // In an executable or dynamic object, generated by + // --emit-relocs, r_offset is an absolute address. + if (!parameters->options().relocatable()) + { + new_offset += view_address; + if (offset_in_output_section != invalid_address) + new_offset -= offset_in_output_section; + } + + reloc_write.put_r_offset(new_offset); + reloc_write.put_r_info(elfcpp::elf_r_info<32>(new_symndx, r_type)); + + // Handle the reloc addend. + // The relocation uses a section symbol in the input file. + // We are adjusting it to use a section symbol in the output + // file. The input section symbol refers to some address in + // the input section. We need the relocation in the output + // file to refer to that same address. This adjustment to + // the addend is the same calculation we use for a simple + // absolute relocation for the input section symbol. + + const Symbol_value<32>* psymval = object->local_symbol(r_sym); + + // Handle THUMB bit. + Symbol_value<32> symval; + Arm_address thumb_bit = + object->local_symbol_is_thumb_function(r_sym) ? 1 : 0; + if (thumb_bit != 0 + && arp->uses_thumb_bit() + && ((psymval->value(object, 0) & 1) != 0)) + { + Arm_address stripped_value = + psymval->value(object, 0) & ~static_cast<Arm_address>(1); + symval.set_output_value(stripped_value); + psymval = &symval; + } + + unsigned char* paddend = view + offset; + typename Arm_relocate_functions<big_endian>::Status reloc_status = + Arm_relocate_functions<big_endian>::STATUS_OKAY; + switch (r_type) + { + case elfcpp::R_ARM_ABS8: + reloc_status = Arm_relocate_functions<big_endian>::abs8(paddend, object, + psymval); + break; + + case elfcpp::R_ARM_ABS12: + reloc_status = Arm_relocate_functions<big_endian>::abs12(paddend, object, + psymval); + break; + + case elfcpp::R_ARM_ABS16: + reloc_status = Arm_relocate_functions<big_endian>::abs16(paddend, object, + psymval); + break; + + case elfcpp::R_ARM_THM_ABS5: + reloc_status = Arm_relocate_functions<big_endian>::thm_abs5(paddend, + object, + psymval); + break; + + case elfcpp::R_ARM_MOVW_ABS_NC: + case elfcpp::R_ARM_MOVW_PREL_NC: + case elfcpp::R_ARM_MOVW_BREL_NC: + case elfcpp::R_ARM_MOVW_BREL: + reloc_status = Arm_relocate_functions<big_endian>::movw( + paddend, object, psymval, 0, thumb_bit, arp->checks_overflow()); + break; + + case elfcpp::R_ARM_THM_MOVW_ABS_NC: + case elfcpp::R_ARM_THM_MOVW_PREL_NC: + case elfcpp::R_ARM_THM_MOVW_BREL_NC: + case elfcpp::R_ARM_THM_MOVW_BREL: + reloc_status = Arm_relocate_functions<big_endian>::thm_movw( + paddend, object, psymval, 0, thumb_bit, arp->checks_overflow()); + break; + + case elfcpp::R_ARM_THM_CALL: + case elfcpp::R_ARM_THM_XPC22: + case elfcpp::R_ARM_THM_JUMP24: + reloc_status = + Arm_relocate_functions<big_endian>::thumb_branch_common( + r_type, relinfo, paddend, NULL, object, 0, psymval, 0, thumb_bit, + false); + break; + + case elfcpp::R_ARM_PLT32: + case elfcpp::R_ARM_CALL: + case elfcpp::R_ARM_JUMP24: + case elfcpp::R_ARM_XPC25: + reloc_status = + Arm_relocate_functions<big_endian>::arm_branch_common( + r_type, relinfo, paddend, NULL, object, 0, psymval, 0, thumb_bit, + false); + break; + + case elfcpp::R_ARM_THM_JUMP19: + reloc_status = + Arm_relocate_functions<big_endian>::thm_jump19(paddend, object, + psymval, 0, thumb_bit); + break; + + case elfcpp::R_ARM_THM_JUMP6: + reloc_status = + Arm_relocate_functions<big_endian>::thm_jump6(paddend, object, psymval, + 0); + break; + + case elfcpp::R_ARM_THM_JUMP8: + reloc_status = + Arm_relocate_functions<big_endian>::thm_jump8(paddend, object, psymval, + 0); + break; + + case elfcpp::R_ARM_THM_JUMP11: + reloc_status = + Arm_relocate_functions<big_endian>::thm_jump11(paddend, object, psymval, + 0); + break; + + case elfcpp::R_ARM_PREL31: + reloc_status = + Arm_relocate_functions<big_endian>::prel31(paddend, object, psymval, 0, + thumb_bit); + break; + + case elfcpp::R_ARM_THM_PC8: + reloc_status = + Arm_relocate_functions<big_endian>::thm_pc8(paddend, object, psymval, + 0); + break; + + case elfcpp::R_ARM_THM_PC12: + reloc_status = + Arm_relocate_functions<big_endian>::thm_pc12(paddend, object, psymval, + 0); + break; + + case elfcpp::R_ARM_THM_ALU_PREL_11_0: + reloc_status = + Arm_relocate_functions<big_endian>::thm_alu11(paddend, object, psymval, + 0, thumb_bit); + break; + + // These relocation truncate relocation results so we cannot handle them + // in a relocatable link. + case elfcpp::R_ARM_MOVT_ABS: + case elfcpp::R_ARM_THM_MOVT_ABS: + case elfcpp::R_ARM_MOVT_PREL: + case elfcpp::R_ARM_MOVT_BREL: + case elfcpp::R_ARM_THM_MOVT_PREL: + case elfcpp::R_ARM_THM_MOVT_BREL: + case elfcpp::R_ARM_ALU_PC_G0_NC: + case elfcpp::R_ARM_ALU_PC_G0: + case elfcpp::R_ARM_ALU_PC_G1_NC: + case elfcpp::R_ARM_ALU_PC_G1: + case elfcpp::R_ARM_ALU_PC_G2: + case elfcpp::R_ARM_ALU_SB_G0_NC: + case elfcpp::R_ARM_ALU_SB_G0: + case elfcpp::R_ARM_ALU_SB_G1_NC: + case elfcpp::R_ARM_ALU_SB_G1: + case elfcpp::R_ARM_ALU_SB_G2: + case elfcpp::R_ARM_LDR_PC_G0: + case elfcpp::R_ARM_LDR_PC_G1: + case elfcpp::R_ARM_LDR_PC_G2: + case elfcpp::R_ARM_LDR_SB_G0: + case elfcpp::R_ARM_LDR_SB_G1: + case elfcpp::R_ARM_LDR_SB_G2: + case elfcpp::R_ARM_LDRS_PC_G0: + case elfcpp::R_ARM_LDRS_PC_G1: + case elfcpp::R_ARM_LDRS_PC_G2: + case elfcpp::R_ARM_LDRS_SB_G0: + case elfcpp::R_ARM_LDRS_SB_G1: + case elfcpp::R_ARM_LDRS_SB_G2: + case elfcpp::R_ARM_LDC_PC_G0: + case elfcpp::R_ARM_LDC_PC_G1: + case elfcpp::R_ARM_LDC_PC_G2: + case elfcpp::R_ARM_LDC_SB_G0: + case elfcpp::R_ARM_LDC_SB_G1: + case elfcpp::R_ARM_LDC_SB_G2: + gold_error(_("cannot handle %s in a relocatable link"), + arp->name().c_str()); + break; + + default: + gold_unreachable(); + } + + // Report any errors. + switch (reloc_status) + { + case Arm_relocate_functions<big_endian>::STATUS_OKAY: + break; + case Arm_relocate_functions<big_endian>::STATUS_OVERFLOW: + gold_error_at_location(relinfo, relnum, reloc.get_r_offset(), + _("relocation overflow in %s"), + arp->name().c_str()); + break; + case Arm_relocate_functions<big_endian>::STATUS_BAD_RELOC: + gold_error_at_location(relinfo, relnum, reloc.get_r_offset(), + _("unexpected opcode while processing relocation %s"), + arp->name().c_str()); + break; + default: + gold_unreachable(); + } +} + // Return the value to use for a dynamic symbol which requires special // treatment. This is how we support equality comparisons of function // pointers across shared library boundaries, as described in the |