Arm_output_section(const char* name, elfcpp::Elf_Word type,
elfcpp::Elf_Xword flags)
: Output_section(name, type, flags)
- { }
+ {
+ if (type == elfcpp::SHT_ARM_EXIDX)
+ this->set_always_keeps_input_sections();
+ }
~Arm_output_section()
{ }
Symbol_table* symtab,
bool merge_exidx_entries);
+ // Link an EXIDX section into its corresponding text section.
+ void
+ set_exidx_section_link();
+
private:
// For convenience.
typedef Output_section::Input_section Input_section;
Arm_exidx_input_section(Relobj* relobj, unsigned int shndx,
unsigned int link, uint32_t size, uint32_t addralign)
: relobj_(relobj), shndx_(shndx), link_(link), size_(size),
- addralign_(addralign)
+ addralign_(addralign), has_errors_(false)
{ }
~Arm_exidx_input_section()
addralign() const
{ return this->addralign_; }
+ // Whether there are any errors in the EXIDX input section.
+ bool
+ has_errors() const
+ { return this->has_errors_; }
+
+ // Set has-errors flag.
+ void
+ set_has_errors()
+ { this->has_errors_ = true; }
+
private:
// Object containing this.
Relobj* relobj_;
uint32_t size_;
// Address alignment of this. For ARM 32-bit is sufficient.
uint32_t addralign_;
+ // Whether this has any errors.
+ bool has_errors_;
};
// Arm_relobj class.
merge_flags_and_attributes() const
{ return this->merge_flags_and_attributes_; }
+ // Export list of EXIDX section indices.
+ void
+ get_exidx_shndx_list(std::vector<unsigned int>* list) const
+ {
+ list->clear();
+ for (Exidx_section_map::const_iterator p = this->exidx_section_map_.begin();
+ p != this->exidx_section_map_.end();
+ ++p)
+ {
+ if (p->second->shndx() == p->first)
+ list->push_back(p->first);
+ }
+ // Sort list to make result independent of implementation of map.
+ std::sort(list->begin(), list->end());
+ }
+
protected:
// Post constructor setup.
void
void
make_exidx_input_section(unsigned int shndx,
const elfcpp::Shdr<32, big_endian>& shdr,
- unsigned int text_shndx);
+ unsigned int text_shndx,
+ const elfcpp::Shdr<32, big_endian>& text_shdr);
// Return the output address of either a plain input section or a
// relaxed input section. SHNDX is the section index.
fix_cortex_a8_(false), cortex_a8_relocs_info_()
{ }
+ // Virtual function which is set to return true by a target if
+ // it can use relocation types to determine if a function's
+ // pointer is taken.
+ virtual bool
+ can_check_for_function_pointers() const
+ { return true; }
+
+ // Whether a section called SECTION_NAME may have function pointers to
+ // sections not eligible for safe ICF folding.
+ virtual bool
+ section_may_have_icf_unsafe_pointers(const char* section_name) const
+ {
+ return (!is_prefix_of(".ARM.exidx", section_name)
+ && !is_prefix_of(".ARM.extab", section_name)
+ && Target::section_may_have_icf_unsafe_pointers(section_name));
+ }
+
// Whether we can use BLX.
bool
may_use_blx() const
Output_section* ,
const elfcpp::Rel<32, big_endian>& ,
unsigned int ,
- const elfcpp::Sym<32, big_endian>&)
- { return false; }
+ const elfcpp::Sym<32, big_endian>&);
inline bool
global_reloc_may_be_function_pointer(Symbol_table* , Layout* , Target_arm* ,
unsigned int ,
Output_section* ,
const elfcpp::Rel<32, big_endian>& ,
- unsigned int , Symbol*)
- { return false; }
+ unsigned int , Symbol*);
private:
static void
|| sym->is_preemptible()));
}
+ inline bool
+ possible_function_pointer_reloc(unsigned int r_type);
+
// Whether we have issued an error about a non-PIC compilation.
bool issued_non_pic_error_;
};
// Fix .ARM.exidx section coverage.
void
- fix_exidx_coverage(Layout*, Arm_output_section<big_endian>*, Symbol_table*);
+ fix_exidx_coverage(Layout*, const Input_objects*,
+ Arm_output_section<big_endian>*, Symbol_table*);
// Functors for STL set.
struct output_section_address_less_than
this->got_ = new Arm_output_data_got<big_endian>(symtab, layout);
- Output_section* os;
- os = layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS,
- (elfcpp::SHF_ALLOC
- | elfcpp::SHF_WRITE),
- this->got_, false, false, false,
- true);
+ layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS,
+ (elfcpp::SHF_ALLOC
+ | elfcpp::SHF_WRITE),
+ this->got_, false, false, false, true);
// The old GNU linker creates a .got.plt section. We just
// create another set of data in the .got section. Note that we
// always create a PLT if we create a GOT, although the PLT
// might be empty.
this->got_plt_ = new Output_data_space(4, "** GOT PLT");
- os = layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS,
- (elfcpp::SHF_ALLOC
- | elfcpp::SHF_WRITE),
- this->got_plt_, false, false,
- false, false);
+ layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS,
+ (elfcpp::SHF_ALLOC
+ | elfcpp::SHF_WRITE),
+ this->got_plt_, false, false, false,
+ false);
// The first three entries are reserved.
this->got_plt_->set_current_data_size(3 * 4);
Arm_output_section<big_endian>::append_text_sections_to_list(
Text_section_list* list)
{
- // We only care about text sections.
- if ((this->flags() & elfcpp::SHF_EXECINSTR) == 0)
- return;
-
gold_assert((this->flags() & elfcpp::SHF_ALLOC) != 0);
for (Input_section_list::const_iterator p = this->input_sections().begin();
const Arm_exidx_input_section* exidx_input_section =
arm_relobj->exidx_input_section_by_link(shndx);
- // If this text section has no EXIDX section, force an EXIDX_CANTUNWIND
- // entry pointing to the end of the last seen EXIDX section.
- if (exidx_input_section == NULL)
+ // If this text section has no EXIDX section or if the EXIDX section
+ // has errors, force an EXIDX_CANTUNWIND entry pointing to the end
+ // of the last seen EXIDX section.
+ if (exidx_input_section == NULL || exidx_input_section->has_errors())
{
exidx_fixup.add_exidx_cantunwind_as_needed();
continue;
if (processed_input_sections.find(Section_id(p->relobj(), p->shndx()))
== processed_input_sections.end())
{
- // We only discard a known EXIDX section because its linked
- // text section has been folded by ICF.
+ // We discard a known EXIDX section because its linked
+ // text section has been folded by ICF. We also discard an
+ // EXIDX section with error, the output does not matter in this
+ // case. We do this to avoid triggering asserts.
Arm_relobj<big_endian>* arm_relobj =
Arm_relobj<big_endian>::as_arm_relobj(p->relobj());
const Arm_exidx_input_section* exidx_input_section =
arm_relobj->exidx_input_section_by_shndx(p->shndx());
gold_assert(exidx_input_section != NULL);
- unsigned int text_shndx = exidx_input_section->link();
- gold_assert(symtab->is_section_folded(p->relobj(), text_shndx));
+ if (!exidx_input_section->has_errors())
+ {
+ unsigned int text_shndx = exidx_input_section->link();
+ gold_assert(symtab->is_section_folded(p->relobj(), text_shndx));
+ }
// Remove this from link. We also need to recount the
// local symbols.
this->set_section_offsets_need_adjustment();
}
+// Link EXIDX output sections to text output sections.
+
+template<bool big_endian>
+void
+Arm_output_section<big_endian>::set_exidx_section_link()
+{
+ gold_assert(this->type() == elfcpp::SHT_ARM_EXIDX);
+ if (!this->input_sections().empty())
+ {
+ Input_section_list::const_iterator p = this->input_sections().begin();
+ Arm_relobj<big_endian>* arm_relobj =
+ Arm_relobj<big_endian>::as_arm_relobj(p->relobj());
+ unsigned exidx_shndx = p->shndx();
+ const Arm_exidx_input_section* exidx_input_section =
+ arm_relobj->exidx_input_section_by_shndx(exidx_shndx);
+ gold_assert(exidx_input_section != NULL);
+ unsigned int text_shndx = exidx_input_section->link();
+ Output_section* os = arm_relobj->output_section(text_shndx);
+ this->set_link_section(os);
+ }
+}
+
// Arm_relobj methods.
// Determine if an input section is scannable for stub processing. SHDR is
Arm_relobj<big_endian>::make_exidx_input_section(
unsigned int shndx,
const elfcpp::Shdr<32, big_endian>& shdr,
- unsigned int text_shndx)
+ unsigned int text_shndx,
+ const elfcpp::Shdr<32, big_endian>& text_shdr)
{
- // Issue an error and ignore this EXIDX section if it points to a text
- // section already has an EXIDX section.
- if (this->exidx_section_map_[text_shndx] != NULL)
- {
- gold_error(_("EXIDX sections %u and %u both link to text section %u "
- "in %s"),
- shndx, this->exidx_section_map_[text_shndx]->shndx(),
- text_shndx, this->name().c_str());
- return;
- }
-
// Create an Arm_exidx_input_section object for this EXIDX section.
Arm_exidx_input_section* exidx_input_section =
new Arm_exidx_input_section(this, shndx, text_shndx, shdr.get_sh_size(),
shdr.get_sh_addralign());
- this->exidx_section_map_[text_shndx] = exidx_input_section;
- // Also map the EXIDX section index to this.
gold_assert(this->exidx_section_map_[shndx] == NULL);
this->exidx_section_map_[shndx] = exidx_input_section;
+
+ if (text_shndx == elfcpp::SHN_UNDEF || text_shndx >= this->shnum())
+ {
+ gold_error(_("EXIDX section %s(%u) links to invalid section %u in %s"),
+ this->section_name(shndx).c_str(), shndx, text_shndx,
+ this->name().c_str());
+ exidx_input_section->set_has_errors();
+ }
+ else if (this->exidx_section_map_[text_shndx] != NULL)
+ {
+ unsigned other_exidx_shndx =
+ this->exidx_section_map_[text_shndx]->shndx();
+ gold_error(_("EXIDX sections %s(%u) and %s(%u) both link to text section"
+ "%s(%u) in %s"),
+ this->section_name(shndx).c_str(), shndx,
+ this->section_name(other_exidx_shndx).c_str(),
+ other_exidx_shndx, this->section_name(text_shndx).c_str(),
+ text_shndx, this->name().c_str());
+ exidx_input_section->set_has_errors();
+ }
+ else
+ this->exidx_section_map_[text_shndx] = exidx_input_section;
+
+ // Check section flags of text section.
+ if ((text_shdr.get_sh_flags() & elfcpp::SHF_ALLOC) == 0)
+ {
+ gold_error(_("EXIDX section %s(%u) links to non-allocated section %s(%u) "
+ " in %s"),
+ this->section_name(shndx).c_str(), shndx,
+ this->section_name(text_shndx).c_str(), text_shndx,
+ this->name().c_str());
+ exidx_input_section->set_has_errors();
+ }
+ else if ((text_shdr.get_sh_flags() & elfcpp::SHF_EXECINSTR) == 0)
+ // I would like to make this an error but currenlty ld just ignores
+ // this.
+ gold_warning(_("EXIDX section %s(%u) links to non-executable section "
+ "%s(%u) in %s"),
+ this->section_name(shndx).c_str(), shndx,
+ this->section_name(text_shndx).c_str(), text_shndx,
+ this->name().c_str());
}
// Read the symbol information.
else if (shdr.get_sh_type() == elfcpp::SHT_ARM_EXIDX)
{
unsigned int text_shndx = this->adjust_shndx(shdr.get_sh_link());
- if (text_shndx >= this->shnum())
- gold_error(_("EXIDX section %u linked to invalid section %u"),
- i, text_shndx);
- else if (text_shndx == elfcpp::SHN_UNDEF)
+ if (text_shndx == elfcpp::SHN_UNDEF)
deferred_exidx_sections.push_back(i);
else
- this->make_exidx_input_section(i, shdr, text_shndx);
+ {
+ elfcpp::Shdr<32, big_endian> text_shdr(pshdrs
+ + text_shndx * shdr_size);
+ this->make_exidx_input_section(i, shdr, text_shndx, text_shdr);
+ }
}
}
// This is rare.
if (!must_merge_flags_and_attributes)
{
+ gold_assert(deferred_exidx_sections.empty());
this->merge_flags_and_attributes_ = false;
return;
}
{
unsigned int shndx = deferred_exidx_sections[i];
elfcpp::Shdr<32, big_endian> shdr(pshdrs + shndx * shdr_size);
- unsigned int text_shndx;
+ unsigned int text_shndx = elfcpp::SHN_UNDEF;
Reloc_map::const_iterator it = reloc_map.find(shndx);
- if (it != reloc_map.end()
- && find_linked_text_section(pshdrs + it->second * shdr_size,
- psyms, &text_shndx))
- this->make_exidx_input_section(shndx, shdr, text_shndx);
- else
- gold_error(_("EXIDX section %u has no linked text section."),
- shndx);
+ if (it != reloc_map.end())
+ find_linked_text_section(pshdrs + it->second * shdr_size,
+ psyms, &text_shndx);
+ elfcpp::Shdr<32, big_endian> text_shdr(pshdrs
+ + text_shndx * shdr_size);
+ this->make_exidx_input_section(shndx, shdr, text_shndx, text_shdr);
}
}
}
object->name().c_str(), r_type, gsym->demangled_name().c_str());
}
+template<bool big_endian>
+inline bool
+Target_arm<big_endian>::Scan::possible_function_pointer_reloc(
+ unsigned int r_type)
+{
+ switch (r_type)
+ {
+ case elfcpp::R_ARM_PC24:
+ case elfcpp::R_ARM_THM_CALL:
+ case elfcpp::R_ARM_PLT32:
+ case elfcpp::R_ARM_CALL:
+ case elfcpp::R_ARM_JUMP24:
+ case elfcpp::R_ARM_THM_JUMP24:
+ case elfcpp::R_ARM_SBREL31:
+ case elfcpp::R_ARM_PREL31:
+ case elfcpp::R_ARM_THM_JUMP19:
+ case elfcpp::R_ARM_THM_JUMP6:
+ case elfcpp::R_ARM_THM_JUMP11:
+ case elfcpp::R_ARM_THM_JUMP8:
+ // All the relocations above are branches except SBREL31 and PREL31.
+ return false;
+
+ default:
+ // Be conservative and assume this is a function pointer.
+ return true;
+ }
+}
+
+template<bool big_endian>
+inline bool
+Target_arm<big_endian>::Scan::local_reloc_may_be_function_pointer(
+ Symbol_table*,
+ Layout*,
+ Target_arm<big_endian>* target,
+ Sized_relobj<32, big_endian>*,
+ unsigned int,
+ Output_section*,
+ const elfcpp::Rel<32, big_endian>&,
+ unsigned int r_type,
+ const elfcpp::Sym<32, big_endian>&)
+{
+ r_type = target->get_real_reloc_type(r_type);
+ return possible_function_pointer_reloc(r_type);
+}
+
+template<bool big_endian>
+inline bool
+Target_arm<big_endian>::Scan::global_reloc_may_be_function_pointer(
+ Symbol_table*,
+ Layout*,
+ Target_arm<big_endian>* target,
+ Sized_relobj<32, big_endian>*,
+ unsigned int,
+ Output_section*,
+ const elfcpp::Rel<32, big_endian>&,
+ unsigned int r_type,
+ Symbol* gsym)
+{
+ // GOT is not a function.
+ if (strcmp(gsym->name(), "_GLOBAL_OFFSET_TABLE_") == 0)
+ return false;
+
+ r_type = target->get_real_reloc_type(r_type);
+ return possible_function_pointer_reloc(r_type);
+}
+
// Scan a relocation for a global symbol.
template<bool big_endian>
const Input_objects* input_objects,
Symbol_table* symtab)
{
+ bool merged_any_attributes = false;
// Merge processor-specific flags.
for (Input_objects::Relobj_iterator p = input_objects->relobj_begin();
p != input_objects->relobj_end();
arm_relobj->processor_specific_flags());
this->merge_object_attributes(arm_relobj->name().c_str(),
arm_relobj->attributes_section_data());
+ merged_any_attributes = true;
}
}
arm_dynobj->processor_specific_flags());
this->merge_object_attributes(arm_dynobj->name().c_str(),
arm_dynobj->attributes_section_data());
+ merged_any_attributes = true;
}
// Create an empty uninitialized attribute section if we still don't have it
}
}
- // Create an .ARM.attributes section unless we have no regular input
- // object. In that case the output will be empty.
- if (input_objects->number_of_relobjs() != 0)
+ // Create an .ARM.attributes section if we have merged any attributes
+ // from inputs.
+ if (merged_any_attributes)
{
Output_attributes_section_data* attributes_section =
new Output_attributes_section_data(*this->attributes_section_data_);
attributes_section, false, false, false,
false);
}
+
+ // Fix up links in section EXIDX headers.
+ for (Layout::Section_list::const_iterator p = layout->section_list().begin();
+ p != layout->section_list().end();
+ ++p)
+ if ((*p)->type() == elfcpp::SHT_ARM_EXIDX)
+ {
+ Arm_output_section<big_endian>* os =
+ Arm_output_section<big_endian>::as_arm_output_section(*p);
+ os->set_exidx_section_link();
+ }
}
// Return whether a direct absolute static relocation needs to be applied.
// be converted into an NOP.
is_weakly_undefined_without_plt = true;
}
+ else if (gsym->is_undefined() && reloc_property->uses_symbol())
+ {
+ // This relocation uses the symbol value but the symbol is
+ // undefined. Exit early and have the caller reporting an
+ // error.
+ return true;
+ }
else
{
// Set thumb bit if symbol:
{
// v4 and v5 are the same spec before and after it was released,
// so allow mixing them.
- if ((v1 == elfcpp::EF_ARM_EABI_VER4 && v2 == elfcpp::EF_ARM_EABI_VER5)
+ if ((v1 == elfcpp::EF_ARM_EABI_UNKNOWN || v2 == elfcpp::EF_ARM_EABI_UNKNOWN)
+ || (v1 == elfcpp::EF_ARM_EABI_VER4 && v2 == elfcpp::EF_ARM_EABI_VER5)
|| (v1 == elfcpp::EF_ARM_EABI_VER5 && v2 == elfcpp::EF_ARM_EABI_VER4))
return true;
group_sections(layout, stub_group_size, stubs_always_after_branch);
// Also fix .ARM.exidx section coverage.
- Output_section* os = layout->find_output_section(".ARM.exidx");
- if (os != NULL && os->type() == elfcpp::SHT_ARM_EXIDX)
+ Arm_output_section<big_endian>* exidx_output_section = NULL;
+ for (Layout::Section_list::const_iterator p =
+ layout->section_list().begin();
+ p != layout->section_list().end();
+ ++p)
+ if ((*p)->type() == elfcpp::SHT_ARM_EXIDX)
+ {
+ if (exidx_output_section == NULL)
+ exidx_output_section =
+ Arm_output_section<big_endian>::as_arm_output_section(*p);
+ else
+ // We cannot handle this now.
+ gold_error(_("multiple SHT_ARM_EXIDX sections %s and %s in a "
+ "non-relocatable link"),
+ exidx_output_section->name(),
+ (*p)->name());
+ }
+
+ if (exidx_output_section != NULL)
{
- Arm_output_section<big_endian>* exidx_output_section =
- Arm_output_section<big_endian>::as_arm_output_section(os);
- this->fix_exidx_coverage(layout, exidx_output_section, symtab);
+ this->fix_exidx_coverage(layout, input_objects, exidx_output_section,
+ symtab);
done_exidx_fixup = true;
}
}
void
Target_arm<big_endian>::fix_exidx_coverage(
Layout* layout,
+ const Input_objects* input_objects,
Arm_output_section<big_endian>* exidx_section,
Symbol_table* symtab)
{
typedef std::set<Output_section*, output_section_address_less_than>
Sorted_output_section_list;
Sorted_output_section_list sorted_output_sections;
- Layout::Section_list section_list;
- layout->get_allocated_sections(§ion_list);
- for (Layout::Section_list::const_iterator p = section_list.begin();
- p != section_list.end();
+
+ // Find out all the output sections of input sections pointed by
+ // EXIDX input sections.
+ for (Input_objects::Relobj_iterator p = input_objects->relobj_begin();
+ p != input_objects->relobj_end();
++p)
{
- // We only care about output sections that contain executable code.
- if (((*p)->flags() & elfcpp::SHF_EXECINSTR) != 0)
- sorted_output_sections.insert(*p);
+ Arm_relobj<big_endian>* arm_relobj =
+ Arm_relobj<big_endian>::as_arm_relobj(*p);
+ std::vector<unsigned int> shndx_list;
+ arm_relobj->get_exidx_shndx_list(&shndx_list);
+ for (size_t i = 0; i < shndx_list.size(); ++i)
+ {
+ const Arm_exidx_input_section* exidx_input_section =
+ arm_relobj->exidx_input_section_by_shndx(shndx_list[i]);
+ gold_assert(exidx_input_section != NULL);
+ if (!exidx_input_section->has_errors())
+ {
+ unsigned int text_shndx = exidx_input_section->link();
+ Output_section *os = arm_relobj->output_section(text_shndx);
+ if (os != NULL && (os->flags() & elfcpp::SHF_ALLOC) != 0)
+ sorted_output_sections.insert(os);
+ }
+ }
}
// Go over the output sections in ascending order of output addresses.