2010-05-26 Doug Kwan <dougkwan@google.com>

* arm.cc (Arm_scan_relocatable_relocs): New class.
	(Target_arm::relocate_special_relocatable): New method.
	(Arm_relocate_functions::arm_branch_common): Handle relocatable link.
	(Arm_relocate_functions::thumb_branch_common): Same.
	(Target_arm::scan_relocatable_relocs): Use Arm_scan_relocatable_relocs
	instead of Default_scan_relocatable_relocs.
	* target-reloc.h (relocate_for_relocatable): Let target handle
	relocation strategy Relocatable_relocs::RELOC_SPECIAL.
	* target.h (Sized_target::relocate_special_relocatable): New method.
This commit is contained in:
Doug Kwan 2010-05-26 03:33:59 +00:00
parent bca1c3ae45
commit 5c3885291f
4 changed files with 443 additions and 9 deletions

View File

@ -1,3 +1,15 @@
2010-05-26 Doug Kwan <dougkwan@google.com>
* arm.cc (Arm_scan_relocatable_relocs): New class.
(Target_arm::relocate_special_relocatable): New method.
(Arm_relocate_functions::arm_branch_common): Handle relocatable link.
(Arm_relocate_functions::thumb_branch_common): Same.
(Target_arm::scan_relocatable_relocs): Use Arm_scan_relocatable_relocs
instead of Default_scan_relocatable_relocs.
* target-reloc.h (relocate_for_relocatable): Let target handle
relocation strategy Relocatable_relocs::RELOC_SPECIAL.
* target.h (Sized_target::relocate_special_relocatable): New method.
2010-05-25 Viktor Kutuzov <vkutuzov@accesssoftek.com> 2010-05-25 Viktor Kutuzov <vkutuzov@accesssoftek.com>
* timer.cc: Only #include <sys/times.h> if HAVE_TIMES is defined. * timer.cc: Only #include <sys/times.h> if HAVE_TIMES is defined.

View File

@ -1956,6 +1956,79 @@ class Arm_output_data_got : public Output_data_got<32, big_endian>
std::vector<Static_reloc> static_relocs_; 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 // Utilities for manipulating integers of up to 32-bits
namespace utils namespace utils
@ -2189,6 +2262,21 @@ class Target_arm : public Sized_target<32, big_endian>
unsigned char* reloc_view, unsigned char* reloc_view,
section_size_type reloc_view_size); 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. // Return whether SYM is defined by the ABI.
bool bool
do_is_defined_by_abi(Symbol* sym) const 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(); Target_arm<big_endian>::default_target();
if (is_weakly_undefined_without_plt) if (is_weakly_undefined_without_plt)
{ {
gold_assert(!parameters->options().relocatable());
Valtype cond = val & 0xf0000000U; Valtype cond = val & 0xf0000000U;
if (arm_target->may_use_arm_nop()) if (arm_target->may_use_arm_nop())
val = cond | 0x0320f000; val = cond | 0x0320f000;
@ -3742,8 +3831,11 @@ Arm_relocate_functions<big_endian>::arm_branch_common(
// to switch mode. // to switch mode.
bool may_use_blx = arm_target->may_use_blx(); bool may_use_blx = arm_target->may_use_blx();
Reloc_stub* stub = NULL; 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); 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(); Target_arm<big_endian>::default_target();
if (is_weakly_undefined_without_plt) if (is_weakly_undefined_without_plt)
{ {
gold_assert(!parameters->options().relocatable());
if (arm_target->may_use_thumb2_nop()) if (arm_target->may_use_thumb2_nop())
{ {
elfcpp::Swap<16, big_endian>::writeval(wv, 0xf3af); 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 // We need a stub if the branch offset is too large or if we need
// to switch mode. // to switch mode.
bool thumb2 = arm_target->using_thumb2(); bool thumb2 = arm_target->using_thumb2();
if ((!thumb2 && utils::has_overflow<23>(branch_offset)) if (!parameters->options().relocatable()
|| (thumb2 && utils::has_overflow<25>(branch_offset)) && ((!thumb2 && utils::has_overflow<23>(branch_offset))
|| ((thumb_bit == 0) || (thumb2 && utils::has_overflow<25>(branch_offset))
&& (((r_type == elfcpp::R_ARM_THM_CALL) && !may_use_blx) || ((thumb_bit == 0)
|| r_type == elfcpp::R_ARM_THM_JUMP24))) && (((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); 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); 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; Relocatable_size_for_reloc> Scan_relocatable_relocs;
gold::scan_relocatable_relocs<32, big_endian, elfcpp::SHT_REL, gold::scan_relocatable_relocs<32, big_endian, elfcpp::SHT_REL,
@ -8971,6 +9065,291 @@ Target_arm<big_endian>::relocate_for_relocatable(
reloc_view_size); 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 // Return the value to use for a dynamic symbol which requires special
// treatment. This is how we support equality comparisons of function // treatment. This is how we support equality comparisons of function
// pointers across shared library boundaries, as described in the // pointers across shared library boundaries, as described in the

View File

@ -515,7 +515,7 @@ relocate_for_relocatable(
const Relocatable_relocs* rr, const Relocatable_relocs* rr,
unsigned char* view, unsigned char* view,
typename elfcpp::Elf_types<size>::Elf_Addr view_address, typename elfcpp::Elf_types<size>::Elf_Addr view_address,
section_size_type, section_size_type view_size,
unsigned char* reloc_view, unsigned char* reloc_view,
section_size_type reloc_view_size) section_size_type reloc_view_size)
{ {
@ -537,6 +537,19 @@ relocate_for_relocatable(
if (strategy == Relocatable_relocs::RELOC_DISCARD) if (strategy == Relocatable_relocs::RELOC_DISCARD)
continue; continue;
if (strategy == Relocatable_relocs::RELOC_SPECIAL)
{
// Target wants to handle this relocation.
Sized_target<size, big_endian>* target =
parameters->sized_target<size, big_endian>();
target->relocate_special_relocatable(relinfo, sh_type, prelocs,
i, output_section,
offset_in_output_section,
view, view_address,
view_size, pwrite);
pwrite += reloc_size;
continue;
}
Reltype reloc(prelocs); Reltype reloc(prelocs);
Reltype_write reloc_write(pwrite); Reltype_write reloc_write(pwrite);

View File

@ -687,7 +687,37 @@ class Sized_target : public Target
section_size_type view_size, section_size_type view_size,
unsigned char* reloc_view, unsigned char* reloc_view,
section_size_type reloc_view_size) = 0; section_size_type reloc_view_size) = 0;
// Perform target-specific processing in a relocatable link. This is
// only used if we use the relocation strategy RELOC_SPECIAL.
// RELINFO points to a Relocation_info structure. SH_TYPE is the relocation
// section type. PRELOC_IN points to the original relocation. RELNUM is
// the index number of the relocation in the relocation section.
// OUTPUT_SECTION is the output section to which the relocation is applied.
// OFFSET_IN_OUTPUT_SECTION is the offset of the relocation input section
// within the output section. VIEW points to the output view of the
// output section. VIEW_ADDRESS is output address of the view. VIEW_SIZE
// is the size of the output view and PRELOC_OUT points to the new
// relocation in the output object.
//
// A target only needs to override this if the generic code in
// target-reloc.h cannot handle some relocation types.
virtual void
relocate_special_relocatable(const Relocate_info<size, 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<size>::Elf_Addr
/* view_address */,
section_size_type /* view_size */,
unsigned char* /* preloc_out*/)
{ gold_unreachable(); }
protected: protected:
Sized_target(const Target::Target_info* pti) Sized_target(const Target::Target_info* pti)
: Target(pti) : Target(pti)