+// 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,
+ typename elfcpp::Elf_types<32>::Elf_Off 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();
+ }
+}
+