2009-11-06 Doug Kwan <dougkwan@google.com>

* arm.cc (class Reloc_stub): Correct a comment.
	(Target_arm::Target_arm): Initialize arm_input_section_map_.
	(Target_arm::scan_section_for_stubs): New method declaration.
	(Target_arm::do_make_elf_object, Target_arm::do_make_output_section):
	Change methods from private to protected.
	(Target_arm::do_may_relax): New method definition.
	(Target_arm::do_relax, Target_arm::group_sections,
	Target_arm::scan_reloc_for_stub,
	Target_arm::scan_reloc_section_for_stubs): New method declarations.
	(Target_arm::arm_input_section_map_): New data member declaration.
	(Target_arm::scan_reloc_for_stub,
	Target_arm::scan_reloc_section_for_stubs,
	Target_arm::scan_section_for_stubs, Target_arm::group_sections,
	Target_arm::do_relax): New method definitions.
This commit is contained in:
Doug Kwan 2009-11-07 01:58:06 +00:00
parent 07d59916b9
commit eb44217c8b
2 changed files with 511 additions and 17 deletions

View File

@ -1,3 +1,20 @@
2009-11-06 Doug Kwan <dougkwan@google.com>
* arm.cc (class Reloc_stub): Correct a comment.
(Target_arm::Target_arm): Initialize arm_input_section_map_.
(Target_arm::scan_section_for_stubs): New method declaration.
(Target_arm::do_make_elf_object, Target_arm::do_make_output_section):
Change methods from private to protected.
(Target_arm::do_may_relax): New method definition.
(Target_arm::do_relax, Target_arm::group_sections,
Target_arm::scan_reloc_for_stub,
Target_arm::scan_reloc_section_for_stubs): New method declarations.
(Target_arm::arm_input_section_map_): New data member declaration.
(Target_arm::scan_reloc_for_stub,
Target_arm::scan_reloc_section_for_stubs,
Target_arm::scan_section_for_stubs, Target_arm::group_sections,
Target_arm::do_relax): New method definitions.
2009-11-06 Mikolaj Zalewski <mikolaj@google.com>
* configure.ac: Check for (struct stat)::st_mtim

View File

@ -572,7 +572,7 @@ class Reloc_stub : public Stub
unsigned int r_sym_;
// If r_sym_ is invalid index. This points to a global symbol.
// Otherwise, this points a relobj. We used the unsized and target
// independent Symbol and Relobj classes instead of Arm_symbol and
// independent Symbol and Relobj classes instead of Sized_symbol<32> and
// Arm_relobj. This is done to avoid making the stub class a template
// as most of the stub machinery is endianity-neutral. However, it
// may require a bit of casting done by users of this class.
@ -1145,7 +1145,8 @@ class Target_arm : public Sized_target<32, big_endian>
got_(NULL), plt_(NULL), got_plt_(NULL), rel_dyn_(NULL),
copy_relocs_(elfcpp::R_ARM_COPY), dynbss_(NULL), stub_tables_(),
stub_factory_(Stub_factory::get_instance()),
may_use_blx_(true), should_force_pic_veneer_(false)
may_use_blx_(true), should_force_pic_veneer_(false),
arm_input_section_map_()
{ }
// Whether we can use BLX.
@ -1304,6 +1305,13 @@ class Target_arm : public Sized_target<32, big_endian>
Stub_table<big_endian>*
new_stub_table(Arm_input_section<big_endian>*);
// Scan a section for stub generation.
void
scan_section_for_stubs(const Relocate_info<32, big_endian>*, unsigned int,
const unsigned char*, size_t, Output_section*,
bool, const unsigned char*, Arm_address,
section_size_type);
// Get the default ARM target.
static const Target_arm<big_endian>&
default_target()
@ -1318,9 +1326,44 @@ class Target_arm : public Sized_target<32, big_endian>
reloc_uses_thumb_bit(unsigned int r_type);
protected:
// Make an ELF object.
Object*
do_make_elf_object(const std::string&, Input_file*, off_t,
const elfcpp::Ehdr<32, big_endian>& ehdr);
Object*
do_make_elf_object(const std::string&, Input_file*, off_t,
const elfcpp::Ehdr<32, !big_endian>&)
{ gold_unreachable(); }
Object*
do_make_elf_object(const std::string&, Input_file*, off_t,
const elfcpp::Ehdr<64, false>&)
{ gold_unreachable(); }
Object*
do_make_elf_object(const std::string&, Input_file*, off_t,
const elfcpp::Ehdr<64, true>&)
{ gold_unreachable(); }
// Make an output section.
Output_section*
do_make_output_section(const char* name, elfcpp::Elf_Word type,
elfcpp::Elf_Xword flags)
{ return new Arm_output_section<big_endian>(name, type, flags); }
void
do_adjust_elf_header(unsigned char* view, int len) const;
// We only need to generate stubs, and hence perform relaxation if we are
// not doing relocatable linking.
bool
do_may_relax() const
{ return !parameters->options().relocatable(); }
bool
do_relax(int, const Input_objects*, Symbol_table*, Layout*);
private:
// The class which scans relocations.
class Scan
@ -1500,24 +1543,33 @@ class Target_arm : public Sized_target<32, big_endian>
void
merge_processor_specific_flags(const std::string&, elfcpp::Elf_Word);
Object*
do_make_elf_object(const std::string&, Input_file*, off_t,
const elfcpp::Ehdr<32, big_endian>& ehdr);
//
// Methods to support stub-generations.
//
Object*
do_make_elf_object(const std::string&, Input_file*, off_t,
const elfcpp::Ehdr<32, !big_endian>&)
{ gold_unreachable(); }
// Group input sections for stub generation.
void
group_sections(Layout*, section_size_type, bool);
Object*
do_make_elf_object(const std::string&, Input_file*, off_t,
const elfcpp::Ehdr<64, false>&)
{ gold_unreachable(); }
// Scan a relocation for stub generation.
void
scan_reloc_for_stub(const Relocate_info<32, big_endian>*, unsigned int,
const Sized_symbol<32>*, unsigned int,
const Symbol_value<32>*,
elfcpp::Elf_types<32>::Elf_Swxword, Arm_address);
Object*
do_make_elf_object(const std::string&, Input_file*, off_t,
const elfcpp::Ehdr<64, true>&)
{ gold_unreachable(); }
// Scan a relocation section for stub.
template<int sh_type>
void
scan_reloc_section_for_stubs(
const Relocate_info<32, big_endian>* relinfo,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
bool needs_special_offset_handling,
const unsigned char* view,
elfcpp::Elf_types<32>::Elf_Addr view_address,
section_size_type);
// Information about this specific target which we pass to the
// general Target structure.
@ -1558,6 +1610,8 @@ class Target_arm : public Sized_target<32, big_endian>
bool may_use_blx_;
// Whether we force PIC branch veneers.
bool should_force_pic_veneer_;
// Map for locating Arm_input_sections.
Arm_input_section_map arm_input_section_map_;
};
template<bool big_endian>
@ -5119,6 +5173,429 @@ Target_arm<big_endian>::new_stub_table(Arm_input_section<big_endian>* owner)
return stub_table;
}
// Scan a relocation for stub generation.
template<bool big_endian>
void
Target_arm<big_endian>::scan_reloc_for_stub(
const Relocate_info<32, big_endian>* relinfo,
unsigned int r_type,
const Sized_symbol<32>* gsym,
unsigned int r_sym,
const Symbol_value<32>* psymval,
elfcpp::Elf_types<32>::Elf_Swxword addend,
Arm_address address)
{
typedef typename Target_arm<big_endian>::Relocate Relocate;
const Arm_relobj<big_endian>* arm_relobj =
Arm_relobj<big_endian>::as_arm_relobj(relinfo->object);
bool target_is_thumb;
Symbol_value<32> symval;
if (gsym != NULL)
{
// This is a global symbol. Determine if we use PLT and if the
// final target is THUMB.
if (gsym->use_plt_offset(Relocate::reloc_is_non_pic(r_type)))
{
// This uses a PLT, change the symbol value.
symval.set_output_value(this->plt_section()->address()
+ gsym->plt_offset());
psymval = &symval;
target_is_thumb = false;
}
else if (gsym->is_undefined())
// There is no need to generate a stub symbol is undefined.
return;
else
{
target_is_thumb =
((gsym->type() == elfcpp::STT_ARM_TFUNC)
|| (gsym->type() == elfcpp::STT_FUNC
&& !gsym->is_undefined()
&& ((psymval->value(arm_relobj, 0) & 1) != 0)));
}
}
else
{
// This is a local symbol. Determine if the final target is THUMB.
target_is_thumb = arm_relobj->local_symbol_is_thumb_function(r_sym);
}
// Strip LSB if this points to a THUMB target.
if (target_is_thumb
&& Target_arm<big_endian>::reloc_uses_thumb_bit(r_type)
&& ((psymval->value(arm_relobj, 0) & 1) != 0))
{
Arm_address stripped_value =
psymval->value(arm_relobj, 0) & ~static_cast<Arm_address>(1);
symval.set_output_value(stripped_value);
psymval = &symval;
}
// Get the symbol value.
Symbol_value<32>::Value value = psymval->value(arm_relobj, 0);
// Owing to pipelining, the PC relative branches below actually skip
// two instructions when the branch offset is 0.
Arm_address destination;
switch (r_type)
{
case elfcpp::R_ARM_CALL:
case elfcpp::R_ARM_JUMP24:
case elfcpp::R_ARM_PLT32:
// ARM branches.
destination = value + addend + 8;
break;
case elfcpp::R_ARM_THM_CALL:
case elfcpp::R_ARM_THM_XPC22:
case elfcpp::R_ARM_THM_JUMP24:
case elfcpp::R_ARM_THM_JUMP19:
// THUMB branches.
destination = value + addend + 4;
break;
default:
gold_unreachable();
}
Stub_type stub_type =
Reloc_stub::stub_type_for_reloc(r_type, address, destination,
target_is_thumb);
// This reloc does not need a stub.
if (stub_type == arm_stub_none)
return;
// Try looking up an existing stub from a stub table.
Stub_table<big_endian>* stub_table =
arm_relobj->stub_table(relinfo->data_shndx);
gold_assert(stub_table != NULL);
// Locate stub by destination.
Reloc_stub::Key stub_key(stub_type, gsym, arm_relobj, r_sym, addend);
// Create a stub if there is not one already
Reloc_stub* stub = stub_table->find_reloc_stub(stub_key);
if (stub == NULL)
{
// create a new stub and add it to stub table.
stub = this->stub_factory().make_reloc_stub(stub_type);
stub_table->add_reloc_stub(stub, stub_key);
}
// Record the destination address.
stub->set_destination_address(destination
| (target_is_thumb ? 1 : 0));
}
// This function scans a relocation sections for stub generation.
// The template parameter Relocate must be a class type which provides
// a single function, relocate(), which implements the machine
// specific part of a relocation.
// BIG_ENDIAN is the endianness of the data. SH_TYPE is the section type:
// SHT_REL or SHT_RELA.
// PRELOCS points to the relocation data. RELOC_COUNT is the number
// of relocs. OUTPUT_SECTION is the output section.
// NEEDS_SPECIAL_OFFSET_HANDLING is true if input offsets need to be
// mapped to output offsets.
// VIEW is the section data, VIEW_ADDRESS is its memory address, and
// VIEW_SIZE is the size. These refer to the input section, unless
// NEEDS_SPECIAL_OFFSET_HANDLING is true, in which case they refer to
// the output section.
template<bool big_endian>
template<int sh_type>
void inline
Target_arm<big_endian>::scan_reloc_section_for_stubs(
const Relocate_info<32, big_endian>* relinfo,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
bool needs_special_offset_handling,
const unsigned char* view,
elfcpp::Elf_types<32>::Elf_Addr view_address,
section_size_type)
{
typedef typename Reloc_types<sh_type, 32, big_endian>::Reloc Reltype;
const int reloc_size =
Reloc_types<sh_type, 32, big_endian>::reloc_size;
Arm_relobj<big_endian>* arm_object =
Arm_relobj<big_endian>::as_arm_relobj(relinfo->object);
unsigned int local_count = arm_object->local_symbol_count();
Comdat_behavior comdat_behavior = CB_UNDETERMINED;
for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size)
{
Reltype reloc(prelocs);
typename elfcpp::Elf_types<32>::Elf_WXword r_info = reloc.get_r_info();
unsigned int r_sym = elfcpp::elf_r_sym<32>(r_info);
unsigned int r_type = elfcpp::elf_r_type<32>(r_info);
r_type = this->get_real_reloc_type(r_type);
// Only a few relocation types need stubs.
if ((r_type != elfcpp::R_ARM_CALL)
&& (r_type != elfcpp::R_ARM_JUMP24)
&& (r_type != elfcpp::R_ARM_PLT32)
&& (r_type != elfcpp::R_ARM_THM_CALL)
&& (r_type != elfcpp::R_ARM_THM_XPC22)
&& (r_type != elfcpp::R_ARM_THM_JUMP24)
&& (r_type != elfcpp::R_ARM_THM_JUMP19))
continue;
section_offset_type offset =
convert_to_section_size_type(reloc.get_r_offset());
if (needs_special_offset_handling)
{
offset = output_section->output_offset(relinfo->object,
relinfo->data_shndx,
offset);
if (offset == -1)
continue;
}
// Get the addend.
Stub_addend_reader<sh_type, big_endian> stub_addend_reader;
elfcpp::Elf_types<32>::Elf_Swxword addend =
stub_addend_reader(r_type, view + offset, reloc);
const Sized_symbol<32>* sym;
Symbol_value<32> symval;
const Symbol_value<32> *psymval;
if (r_sym < local_count)
{
sym = NULL;
psymval = arm_object->local_symbol(r_sym);
// If the local symbol belongs to a section we are discarding,
// and that section is a debug section, try to find the
// corresponding kept section and map this symbol to its
// counterpart in the kept section. The symbol must not
// correspond to a section we are folding.
bool is_ordinary;
unsigned int shndx = psymval->input_shndx(&is_ordinary);
if (is_ordinary
&& shndx != elfcpp::SHN_UNDEF
&& !arm_object->is_section_included(shndx)
&& !(relinfo->symtab->is_section_folded(arm_object, shndx)))
{
if (comdat_behavior == CB_UNDETERMINED)
{
std::string name =
arm_object->section_name(relinfo->data_shndx);
comdat_behavior = get_comdat_behavior(name.c_str());
}
if (comdat_behavior == CB_PRETEND)
{
bool found;
typename elfcpp::Elf_types<32>::Elf_Addr value =
arm_object->map_to_kept_section(shndx, &found);
if (found)
symval.set_output_value(value + psymval->input_value());
else
symval.set_output_value(0);
}
else
{
symval.set_output_value(0);
}
symval.set_no_output_symtab_entry();
psymval = &symval;
}
}
else
{
const Symbol* gsym = arm_object->global_symbol(r_sym);
gold_assert(gsym != NULL);
if (gsym->is_forwarder())
gsym = relinfo->symtab->resolve_forwards(gsym);
sym = static_cast<const Sized_symbol<32>*>(gsym);
if (sym->has_symtab_index())
symval.set_output_symtab_index(sym->symtab_index());
else
symval.set_no_output_symtab_entry();
// We need to compute the would-be final value of this global
// symbol.
const Symbol_table* symtab = relinfo->symtab;
const Sized_symbol<32>* sized_symbol =
symtab->get_sized_symbol<32>(gsym);
Symbol_table::Compute_final_value_status status;
Arm_address value =
symtab->compute_final_value<32>(sized_symbol, &status);
// Skip this if the symbol has not output section.
if (status == Symbol_table::CFVS_NO_OUTPUT_SECTION)
continue;
symval.set_output_value(value);
psymval = &symval;
}
// If symbol is a section symbol, we don't know the actual type of
// destination. Give up.
if (psymval->is_section_symbol())
continue;
this->scan_reloc_for_stub(relinfo, r_type, sym, r_sym, psymval,
addend, view_address + offset);
}
}
// Scan an input section for stub generation.
template<bool big_endian>
void
Target_arm<big_endian>::scan_section_for_stubs(
const Relocate_info<32, big_endian>* relinfo,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
bool needs_special_offset_handling,
const unsigned char* view,
Arm_address view_address,
section_size_type view_size)
{
if (sh_type == elfcpp::SHT_REL)
this->scan_reloc_section_for_stubs<elfcpp::SHT_REL>(
relinfo,
prelocs,
reloc_count,
output_section,
needs_special_offset_handling,
view,
view_address,
view_size);
else if (sh_type == elfcpp::SHT_RELA)
// We do not support RELA type relocations yet. This is provided for
// completeness.
this->scan_reloc_section_for_stubs<elfcpp::SHT_RELA>(
relinfo,
prelocs,
reloc_count,
output_section,
needs_special_offset_handling,
view,
view_address,
view_size);
else
gold_unreachable();
}
// Group input sections for stub generation.
//
// We goup input sections in an output sections so that the total size,
// including any padding space due to alignment is smaller than GROUP_SIZE
// unless the only input section in group is bigger than GROUP_SIZE already.
// Then an ARM stub table is created to follow the last input section
// in group. For each group an ARM stub table is created an is placed
// after the last group. If STUB_ALWATS_AFTER_BRANCH is false, we further
// extend the group after the stub table.
template<bool big_endian>
void
Target_arm<big_endian>::group_sections(
Layout* layout,
section_size_type group_size,
bool stubs_always_after_branch)
{
// Group input sections and insert stub table
Layout::Section_list section_list;
layout->get_allocated_sections(&section_list);
for (Layout::Section_list::const_iterator p = section_list.begin();
p != section_list.end();
++p)
{
Arm_output_section<big_endian>* output_section =
Arm_output_section<big_endian>::as_arm_output_section(*p);
output_section->group_sections(group_size, stubs_always_after_branch,
this);
}
}
// Relaxation hook. This is where we do stub generation.
template<bool big_endian>
bool
Target_arm<big_endian>::do_relax(
int pass,
const Input_objects* input_objects,
Symbol_table* symtab,
Layout* layout)
{
// No need to generate stubs if this is a relocatable link.
gold_assert(!parameters->options().relocatable());
// If this is the first pass, we need to group input sections into
// stub groups.
if (pass == 1)
{
// Determine the stub group size. The group size is the absolute
// value of the parameter --stub-group-size. If --stub-group-size
// is passed a negative value, we restict stubs to be always after
// the stubbed branches.
int32_t stub_group_size_param =
parameters->options().stub_group_size();
bool stubs_always_after_branch = stub_group_size_param < 0;
section_size_type stub_group_size = abs(stub_group_size_param);
if (stub_group_size == 1)
{
// Default value.
// Thumb branch range is +-4MB has to be used as the default
// maximum size (a given section can contain both ARM and Thumb
// code, so the worst case has to be taken into account).
//
// This value is 24K less than that, which allows for 2025
// 12-byte stubs. If we exceed that, then we will fail to link.
// The user will have to relink with an explicit group size
// option.
stub_group_size = 4170000;
}
group_sections(layout, stub_group_size, stubs_always_after_branch);
}
// clear changed flags for all stub_tables
typedef typename Stub_table_list::iterator Stub_table_iterator;
for (Stub_table_iterator sp = this->stub_tables_.begin();
sp != this->stub_tables_.end();
++sp)
(*sp)->set_has_been_changed(false);
// scan relocs for stubs
for (Input_objects::Relobj_iterator op = input_objects->relobj_begin();
op != input_objects->relobj_end();
++op)
{
Arm_relobj<big_endian>* arm_relobj =
Arm_relobj<big_endian>::as_arm_relobj(*op);
arm_relobj->scan_sections_for_stubs(this, symtab, layout);
}
bool any_stub_table_changed = false;
for (Stub_table_iterator sp = this->stub_tables_.begin();
(sp != this->stub_tables_.end()) && !any_stub_table_changed;
++sp)
{
if ((*sp)->has_been_changed())
any_stub_table_changed = true;
}
return any_stub_table_changed;
}
// The selector for arm object files.
template<bool big_endian>