diff options
Diffstat (limited to 'gold')
-rw-r--r-- | gold/arm.cc | 369 |
1 files changed, 354 insertions, 15 deletions
diff --git a/gold/arm.cc b/gold/arm.cc index e73e0ee..fd1aae5 100644 --- a/gold/arm.cc +++ b/gold/arm.cc @@ -75,8 +75,6 @@ class Output_data_plt_arm; // R_ARM_PREL31 // // Coming soon (pending patches): -// - Local scanner -// - Global scanner // - Relocation // - Defining section symbols __exidx_start and __exidx_stop. // - Support interworking. @@ -209,6 +207,7 @@ class Target_arm : public Sized_target<32, big_endian> { public: Scan() + : issued_non_pic_error_(false) { } inline void @@ -237,6 +236,29 @@ class Target_arm : public Sized_target<32, big_endian> static void unsupported_reloc_global(Sized_relobj<32, big_endian>*, unsigned int r_type, Symbol*); + + void + check_non_pic(Relobj*, unsigned int r_type); + + // Almost identical to Symbol::needs_plt_entry except that it also + // handles STT_ARM_TFUNC. + static bool + symbol_needs_plt_entry(const Symbol* sym) + { + // An undefined symbol from an executable does not need a PLT entry. + if (sym->is_undefined() && !parameters->options().shared()) + return false; + + return (!parameters->doing_static_link() + && (sym->type() == elfcpp::STT_FUNC + || sym->type() == elfcpp::STT_ARM_TFUNC) + && (sym->is_from_dynobj() + || sym->is_undefined() + || sym->is_preemptible())); + } + + // Whether we have issued an error about a non-PIC compilation. + bool issued_non_pic_error_; }; // The class which implements relocation. @@ -249,6 +271,13 @@ class Target_arm : public Sized_target<32, big_endian> ~Relocate() { } + // Return whether the static relocation needs to be applied. + inline bool + should_apply_static_reloc(const Sized_symbol<32>* gsym, + int ref_flags, + bool is_32bit, + Output_section* output_section); + // Do a relocation. Return false if the caller should not issue // any warnings about this relocation. inline bool @@ -306,7 +335,8 @@ class Target_arm : public Sized_target<32, big_endian> { return (!parameters->options().shared() && gsym->is_from_dynobj() - && gsym->type() != elfcpp::STT_FUNC); + && gsym->type() != elfcpp::STT_FUNC + && gsym->type() != elfcpp::STT_ARM_TFUNC); } // Add a potential copy relocation. @@ -669,18 +699,65 @@ Target_arm<big_endian>::Scan::unsupported_reloc_local( object->name().c_str(), r_type); } +// We are about to emit a dynamic relocation of type R_TYPE. If the +// dynamic linker does not support it, issue an error. The GNU linker +// only issues a non-PIC error for an allocated read-only section. +// Here we know the section is allocated, but we don't know that it is +// read-only. But we check for all the relocation types which the +// glibc dynamic linker supports, so it seems appropriate to issue an +// error even if the section is not read-only. + +template<bool big_endian> +void +Target_arm<big_endian>::Scan::check_non_pic(Relobj* object, + unsigned int r_type) +{ + switch (r_type) + { + // These are the relocation types supported by glibc for ARM. + case elfcpp::R_ARM_RELATIVE: + case elfcpp::R_ARM_COPY: + case elfcpp::R_ARM_GLOB_DAT: + case elfcpp::R_ARM_JUMP_SLOT: + case elfcpp::R_ARM_ABS32: + case elfcpp::R_ARM_PC24: + // FIXME: The following 3 types are not supported by Android's dynamic + // linker. + case elfcpp::R_ARM_TLS_DTPMOD32: + case elfcpp::R_ARM_TLS_DTPOFF32: + case elfcpp::R_ARM_TLS_TPOFF32: + return; + + default: + // This prevents us from issuing more than one error per reloc + // section. But we can still wind up issuing more than one + // error per object file. + if (this->issued_non_pic_error_) + return; + object->error(_("requires unsupported dynamic reloc; " + "recompile with -fPIC")); + this->issued_non_pic_error_ = true; + return; + + case elfcpp::R_ARM_NONE: + gold_unreachable(); + } +} + // Scan a relocation for a local symbol. +// FIXME: This only handles a subset of relocation types used by Android +// on ARM v5te devices. template<bool big_endian> inline void Target_arm<big_endian>::Scan::local(const General_options&, - Symbol_table* /* symtab */, - Layout* /* layout */, - Target_arm* /* target */, + Symbol_table* symtab, + Layout* layout, + Target_arm* target, Sized_relobj<32, big_endian>* object, - unsigned int /* data_shndx */, - Output_section* /* output_section */, - const elfcpp::Rel<32, big_endian>& /* reloc */, + unsigned int data_shndx, + Output_section* output_section, + const elfcpp::Rel<32, big_endian>& reloc, unsigned int r_type, const elfcpp::Sym<32, big_endian>&) { @@ -690,6 +767,77 @@ Target_arm<big_endian>::Scan::local(const General_options&, case elfcpp::R_ARM_NONE: break; + case elfcpp::R_ARM_ABS32: + // If building a shared library (or a position-independent + // executable), we need to create a dynamic relocation for + // this location. The relocation applied at link time will + // apply the link-time value, so we flag the location with + // an R_ARM_RELATIVE relocation so the dynamic loader can + // relocate it easily. + if (parameters->options().output_is_position_independent()) + { + Reloc_section* rel_dyn = target->rel_dyn_section(layout); + unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info()); + // If we are to add more other reloc types than R_ARM_ABS32, + // we need to add check_non_pic(object, r_type) here. + rel_dyn->add_local_relative(object, r_sym, elfcpp::R_ARM_RELATIVE, + output_section, data_shndx, + reloc.get_r_offset()); + } + break; + + case elfcpp::R_ARM_REL32: + case elfcpp::R_ARM_THM_CALL: + case elfcpp::R_ARM_CALL: + case elfcpp::R_ARM_PREL31: + case elfcpp::R_ARM_JUMP24: + case elfcpp::R_ARM_PLT32: + break; + + case elfcpp::R_ARM_GOTOFF32: + // We need a GOT section: + target->got_section(symtab, layout); + break; + + case elfcpp::R_ARM_BASE_PREL: + // FIXME: What about this? + break; + + case elfcpp::R_ARM_GOT_BREL: + { + // The symbol requires a GOT entry. + Output_data_got<32, big_endian>* got = + target->got_section(symtab, layout); + unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info()); + if (got->add_local(object, r_sym, GOT_TYPE_STANDARD)) + { + // If we are generating a shared object, we need to add a + // dynamic RELATIVE relocation for this symbol's GOT entry. + if (parameters->options().output_is_position_independent()) + { + Reloc_section* rel_dyn = target->rel_dyn_section(layout); + unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info()); + rel_dyn->add_local_relative( + object, r_sym, elfcpp::R_ARM_RELATIVE, got, + object->local_got_offset(r_sym, GOT_TYPE_STANDARD)); + } + } + } + break; + + case elfcpp::R_ARM_TARGET1: + // This should have been mapped to another type already. + // Fall through. + case elfcpp::R_ARM_COPY: + case elfcpp::R_ARM_GLOB_DAT: + case elfcpp::R_ARM_JUMP_SLOT: + case elfcpp::R_ARM_RELATIVE: + // These are relocations which should only be seen by the + // dynamic linker, and should never be seen here. + gold_error(_("%s: unexpected reloc %u in object file"), + object->name().c_str(), r_type); + break; + default: unsupported_reloc_local(object, r_type); break; @@ -710,17 +858,19 @@ Target_arm<big_endian>::Scan::unsupported_reloc_global( } // Scan a relocation for a global symbol. +// FIXME: This only handles a subset of relocation types used by Android +// on ARM v5te devices. template<bool big_endian> inline void Target_arm<big_endian>::Scan::global(const General_options&, - Symbol_table* /* symtab */, - Layout* /* layout */, - Target_arm* /* target */, + Symbol_table* symtab, + Layout* layout, + Target_arm* target, Sized_relobj<32, big_endian>* object, - unsigned int /* data_shndx */, - Output_section* /* output_section */, - const elfcpp::Rel<32, big_endian>& /* reloc */, + unsigned int data_shndx, + Output_section* output_section, + const elfcpp::Rel<32, big_endian>& reloc, unsigned int r_type, Symbol* gsym) { @@ -730,6 +880,155 @@ Target_arm<big_endian>::Scan::global(const General_options&, case elfcpp::R_ARM_NONE: break; + case elfcpp::R_ARM_ABS32: + { + // Make a dynamic relocation if necessary. + if (gsym->needs_dynamic_reloc(Symbol::ABSOLUTE_REF)) + { + if (target->may_need_copy_reloc(gsym)) + { + target->copy_reloc(symtab, layout, object, + data_shndx, output_section, gsym, reloc); + } + else if (gsym->can_use_relative_reloc(false)) + { + // If we are to add more other reloc types than R_ARM_ABS32, + // we need to add check_non_pic(object, r_type) here. + Reloc_section* rel_dyn = target->rel_dyn_section(layout); + rel_dyn->add_global_relative(gsym, elfcpp::R_ARM_RELATIVE, + output_section, object, + data_shndx, reloc.get_r_offset()); + } + else + { + // If we are to add more other reloc types than R_ARM_ABS32, + // we need to add check_non_pic(object, r_type) here. + Reloc_section* rel_dyn = target->rel_dyn_section(layout); + rel_dyn->add_global(gsym, r_type, output_section, object, + data_shndx, reloc.get_r_offset()); + } + } + } + break; + + case elfcpp::R_ARM_REL32: + case elfcpp::R_ARM_PREL31: + { + // Make a dynamic relocation if necessary. + int flags = Symbol::NON_PIC_REF; + if (gsym->needs_dynamic_reloc(flags)) + { + if (target->may_need_copy_reloc(gsym)) + { + target->copy_reloc(symtab, layout, object, + data_shndx, output_section, gsym, reloc); + } + else + { + check_non_pic(object, r_type); + Reloc_section* rel_dyn = target->rel_dyn_section(layout); + rel_dyn->add_global(gsym, r_type, output_section, object, + data_shndx, reloc.get_r_offset()); + } + } + } + break; + + case elfcpp::R_ARM_JUMP24: + case elfcpp::R_ARM_THM_CALL: + case elfcpp::R_ARM_CALL: + { + if (Target_arm<big_endian>::Scan::symbol_needs_plt_entry(gsym)) + target->make_plt_entry(symtab, layout, gsym); + // Make a dynamic relocation if necessary. + int flags = Symbol::NON_PIC_REF; + if (gsym->type() == elfcpp::STT_FUNC + | gsym->type() == elfcpp::STT_ARM_TFUNC) + flags |= Symbol::FUNCTION_CALL; + if (gsym->needs_dynamic_reloc(flags)) + { + if (target->may_need_copy_reloc(gsym)) + { + target->copy_reloc(symtab, layout, object, + data_shndx, output_section, gsym, + reloc); + } + else + { + check_non_pic(object, r_type); + Reloc_section* rel_dyn = target->rel_dyn_section(layout); + rel_dyn->add_global(gsym, r_type, output_section, object, + data_shndx, reloc.get_r_offset()); + } + } + } + break; + + case elfcpp::R_ARM_PLT32: + // If the symbol is fully resolved, this is just a relative + // local reloc. Otherwise we need a PLT entry. + if (gsym->final_value_is_known()) + break; + // If building a shared library, we can also skip the PLT entry + // if the symbol is defined in the output file and is protected + // or hidden. + if (gsym->is_defined() + && !gsym->is_from_dynobj() + && !gsym->is_preemptible()) + break; + target->make_plt_entry(symtab, layout, gsym); + break; + + case elfcpp::R_ARM_GOTOFF32: + // We need a GOT section. + target->got_section(symtab, layout); + break; + + case elfcpp::R_ARM_BASE_PREL: + // FIXME: What about this? + break; + + case elfcpp::R_ARM_GOT_BREL: + { + // The symbol requires a GOT entry. + Output_data_got<32, big_endian>* got = + target->got_section(symtab, layout); + if (gsym->final_value_is_known()) + got->add_global(gsym, GOT_TYPE_STANDARD); + else + { + // If this symbol is not fully resolved, we need to add a + // GOT entry with a dynamic relocation. + Reloc_section* rel_dyn = target->rel_dyn_section(layout); + if (gsym->is_from_dynobj() + || gsym->is_undefined() + || gsym->is_preemptible()) + got->add_global_with_rel(gsym, GOT_TYPE_STANDARD, + rel_dyn, elfcpp::R_ARM_GLOB_DAT); + else + { + if (got->add_global(gsym, GOT_TYPE_STANDARD)) + rel_dyn->add_global_relative( + gsym, elfcpp::R_ARM_RELATIVE, got, + gsym->got_offset(GOT_TYPE_STANDARD)); + } + } + } + break; + + case elfcpp::R_ARM_TARGET1: + // This should have been mapped to another type already. + // Fall through. + case elfcpp::R_ARM_COPY: + case elfcpp::R_ARM_GLOB_DAT: + case elfcpp::R_ARM_JUMP_SLOT: + case elfcpp::R_ARM_RELATIVE: + // These are relocations which should only be seen by the + // dynamic linker, and should never be seen here. + gold_error(_("%s: unexpected reloc %u in object file"), + object->name().c_str(), r_type); + break; + default: unsupported_reloc_global(object, r_type, gsym); break; @@ -855,6 +1154,46 @@ Target_arm<big_endian>::do_finalize_sections(Layout* layout) this->copy_relocs_.emit(this->rel_dyn_section(layout)); } +// Return whether a direct absolute static relocation needs to be applied. +// In cases where Scan::local() or Scan::global() has created +// a dynamic relocation other than R_ARM_RELATIVE, the addend +// of the relocation is carried in the data, and we must not +// apply the static relocation. + +template<bool big_endian> +inline bool +Target_arm<big_endian>::Relocate::should_apply_static_reloc( + const Sized_symbol<32>* gsym, + int ref_flags, + bool is_32bit, + Output_section* output_section) +{ + // If the output section is not allocated, then we didn't call + // scan_relocs, we didn't create a dynamic reloc, and we must apply + // the reloc here. + if ((output_section->flags() & elfcpp::SHF_ALLOC) == 0) + return true; + + // For local symbols, we will have created a non-RELATIVE dynamic + // relocation only if (a) the output is position independent, + // (b) the relocation is absolute (not pc- or segment-relative), and + // (c) the relocation is not 32 bits wide. + if (gsym == NULL) + return !(parameters->options().output_is_position_independent() + && (ref_flags & Symbol::ABSOLUTE_REF) + && !is_32bit); + + // For global symbols, we use the same helper routines used in the + // scan pass. If we did not create a dynamic relocation, or if we + // created a RELATIVE dynamic relocation, we should apply the static + // relocation. + bool has_dyn = gsym->needs_dynamic_reloc(ref_flags); + bool is_rel = (ref_flags & Symbol::ABSOLUTE_REF) + && gsym->can_use_relative_reloc(ref_flags + & Symbol::FUNCTION_CALL); + return !has_dyn || is_rel; +} + // Perform a relocation. template<bool big_endian> |