diff options
Diffstat (limited to 'gold')
-rw-r--r-- | gold/ChangeLog | 30 | ||||
-rw-r--r-- | gold/arm.cc | 435 |
2 files changed, 457 insertions, 8 deletions
diff --git a/gold/ChangeLog b/gold/ChangeLog index 1e03ba4..1a5b7fa 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,3 +1,33 @@ +2009-05-29 Doug Kwan <dougkwan@google.com> + + * gold/arm.cc (Output_data_plt_arm): Forward declaration for new + template class. + (class Target_arm): Update comment. + (Target_arm::Target_arm): Initialize new data members GOT_, + PLT_, GOT_PLT_, REL_DYN_, COPY_RELOCS_ and DYNBSS_. + Declare new methods Target_arm::got_section, Target_arm::make_plt_entry + and Target_arm::rel_dyn_section. + Declare new_enum Target_arm::Got_type. + Declare new data members GOT_, PLT_, GOT_PLT_, REL_DYN_, COPY_RELOCS_ + and DYNBSS_. + Update commments for member do_dynsym_value. + (Target_arm::got_size, Target_arm::plt_section, + Target_arm::may_need_copy_reloc and Target_arm::copy_reloc): Define + new methods inside class defintion. + (Target_arm::got_section): Define new method. + (Target_arm::rel_dyn_section): Same. + (Output_data_plt_arm): New template class. + (Output_data_plt_arm::Output_data_plt_arm): Define constructor. + (Output_data_plt_arm:do_adjust_output_section): Define new method. + (Output_data_plt_arm::add_entry): Same. + (Output_data_plt_arm::first_plt_entry): Define new + static data member for PLT instruction template. + (Output_data_plt_arm::plt_entry): Same. + (Output_data_plt_arm::do_write): Define new method. + (Target_arm::make_plt_entry): Same. + (Target_arm::do_finalize_sections): Same. + (Target_arm::do_dynsym_value): Same. + 2009-05-28 Doug Kwan <dougkwan@google.com> * Makefile.am (TARGETSOURCES): Add arm.cc. diff --git a/gold/arm.cc b/gold/arm.cc index 580b3ef..e73e0ee 100644 --- a/gold/arm.cc +++ b/gold/arm.cc @@ -48,6 +48,9 @@ namespace using namespace gold; +template<bool big_endian> +class Output_data_plt_arm; + // The arm target class. // // This is a very simple port of gold for ARM-EABI. It is intended for @@ -72,7 +75,6 @@ using namespace gold; // R_ARM_PREL31 // // Coming soon (pending patches): -// - Support for dynamic symbols (GOT, PLT and etc). // - Local scanner // - Global scanner // - Relocation @@ -85,6 +87,8 @@ using namespace gold; // - Create a PT_ARM_EXIDX program header for a shared object that // might throw an exception. // - Support more relocation types as needed. +// - Make PLTs more flexible for different architecture features like +// Thumb-2 and BE8. template<bool big_endian> class Target_arm : public Sized_target<32, big_endian> @@ -94,7 +98,9 @@ class Target_arm : public Sized_target<32, big_endian> Reloc_section; Target_arm() - : Sized_target<32, big_endian>(&arm_info) + : Sized_target<32, big_endian>(&arm_info), + got_(NULL), plt_(NULL), got_plt_(NULL), rel_dyn_(NULL), + copy_relocs_(elfcpp::R_ARM_COPY), dynbss_(NULL) { } // Process the relocations to determine unreferenced sections for @@ -132,7 +138,7 @@ class Target_arm : public Sized_target<32, big_endian> void do_finalize_sections(Layout*); - // Return the value to use for a dynamic which requires special + // Return the value to use for a dynamic symbol which requires special // treatment. uint64_t do_dynsym_value(const Symbol*) const; @@ -185,6 +191,14 @@ class Target_arm : public Sized_target<32, big_endian> do_is_defined_by_abi(Symbol* sym) const { return strcmp(sym->name(), "__tls_get_addr") == 0; } + // Return the size of the GOT section. + section_size_type + got_size() + { + gold_assert(this->got_ != NULL); + return this->got_->data_size(); + } + // Map platform-specific reloc types static unsigned int get_real_reloc_type (unsigned int r_type); @@ -256,9 +270,80 @@ class Target_arm : public Sized_target<32, big_endian> get_size_for_reloc(unsigned int, Relobj*); }; + // Get the GOT section, creating it if necessary. + Output_data_got<32, big_endian>* + got_section(Symbol_table*, Layout*); + + // Get the GOT PLT section. + Output_data_space* + got_plt_section() const + { + gold_assert(this->got_plt_ != NULL); + return this->got_plt_; + } + + // Create a PLT entry for a global symbol. + void + make_plt_entry(Symbol_table*, Layout*, Symbol*); + + // Get the PLT section. + const Output_data_plt_arm<big_endian>* + plt_section() const + { + gold_assert(this->plt_ != NULL); + return this->plt_; + } + + // Get the dynamic reloc section, creating it if necessary. + Reloc_section* + rel_dyn_section(Layout*); + + // Return true if the symbol may need a COPY relocation. + // References from an executable object to non-function symbols + // defined in a dynamic object may need a COPY relocation. + bool + may_need_copy_reloc(Symbol* gsym) + { + return (!parameters->options().shared() + && gsym->is_from_dynobj() + && gsym->type() != elfcpp::STT_FUNC); + } + + // Add a potential copy relocation. + void + copy_reloc(Symbol_table* symtab, Layout* layout, + Sized_relobj<32, big_endian>* object, + unsigned int shndx, Output_section* output_section, + Symbol* sym, const elfcpp::Rel<32, big_endian>& reloc) + { + this->copy_relocs_.copy_reloc(symtab, layout, + symtab->get_sized_symbol<32>(sym), + object, shndx, output_section, reloc, + this->rel_dyn_section(layout)); + } + // Information about this specific target which we pass to the // general Target structure. static const Target::Target_info arm_info; + + // The types of GOT entries needed for this platform. + enum Got_type + { + GOT_TYPE_STANDARD = 0 // GOT entry for a regular symbol + }; + + // The GOT section. + Output_data_got<32, big_endian>* got_; + // The PLT section. + Output_data_plt_arm<big_endian>* plt_; + // The GOT PLT section. + Output_data_space* got_plt_; + // The dynamic reloc section. + Reloc_section* rel_dyn_; + // Relocs saved to avoid a COPY reloc. + Copy_relocs<elfcpp::SHT_REL, 32, big_endian> copy_relocs_; + // Space for variables copied with a COPY reloc. + Output_data_space* dynbss_; }; template<bool big_endian> @@ -278,6 +363,300 @@ const Target::Target_info Target_arm<big_endian>::arm_info = 0x1000 // common_pagesize (overridable by -z common-page-size) }; +// Get the GOT section, creating it if necessary. + +template<bool big_endian> +Output_data_got<32, big_endian>* +Target_arm<big_endian>::got_section(Symbol_table* symtab, Layout* layout) +{ + if (this->got_ == NULL) + { + gold_assert(symtab != NULL && layout != NULL); + + this->got_ = new Output_data_got<32, big_endian>(); + + Output_section* os; + os = layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, + (elfcpp::SHF_ALLOC + | elfcpp::SHF_WRITE), + this->got_); + os->set_is_relro(); + + // 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_); + os->set_is_relro(); + + // The first three entries are reserved. + this->got_plt_->set_current_data_size(3 * 4); + + // Define _GLOBAL_OFFSET_TABLE_ at the start of the PLT. + symtab->define_in_output_data("_GLOBAL_OFFSET_TABLE_", NULL, + this->got_plt_, + 0, 0, elfcpp::STT_OBJECT, + elfcpp::STB_LOCAL, + elfcpp::STV_HIDDEN, 0, + false, false); + } + return this->got_; +} + +// Get the dynamic reloc section, creating it if necessary. + +template<bool big_endian> +typename Target_arm<big_endian>::Reloc_section* +Target_arm<big_endian>::rel_dyn_section(Layout* layout) +{ + if (this->rel_dyn_ == NULL) + { + gold_assert(layout != NULL); + this->rel_dyn_ = new Reloc_section(parameters->options().combreloc()); + layout->add_output_section_data(".rel.dyn", elfcpp::SHT_REL, + elfcpp::SHF_ALLOC, this->rel_dyn_); + } + return this->rel_dyn_; +} + +// A class to handle the PLT data. + +template<bool big_endian> +class Output_data_plt_arm : public Output_section_data +{ + public: + typedef Output_data_reloc<elfcpp::SHT_REL, true, 32, big_endian> + Reloc_section; + + Output_data_plt_arm(Layout*, Output_data_space*); + + // Add an entry to the PLT. + void + add_entry(Symbol* gsym); + + // Return the .rel.plt section data. + const Reloc_section* + rel_plt() const + { return this->rel_; } + + protected: + void + do_adjust_output_section(Output_section* os); + + // Write to a map file. + void + do_print_to_mapfile(Mapfile* mapfile) const + { mapfile->print_output_data(this, _("** PLT")); } + + private: + // Template for the first PLT entry. + static const uint32_t first_plt_entry[5]; + + // Template for subsequent PLT entries. + static const uint32_t plt_entry[3]; + + // Set the final size. + void + set_final_data_size() + { + this->set_data_size(sizeof(first_plt_entry) + + this->count_ * sizeof(plt_entry)); + } + + // Write out the PLT data. + void + do_write(Output_file*); + + // The reloc section. + Reloc_section* rel_; + // The .got.plt section. + Output_data_space* got_plt_; + // The number of PLT entries. + unsigned int count_; +}; + +// Create the PLT section. The ordinary .got section is an argument, +// since we need to refer to the start. We also create our own .got +// section just for PLT entries. + +template<bool big_endian> +Output_data_plt_arm<big_endian>::Output_data_plt_arm(Layout* layout, + Output_data_space* got_plt) + : Output_section_data(4), got_plt_(got_plt), count_(0) +{ + this->rel_ = new Reloc_section(false); + layout->add_output_section_data(".rel.plt", elfcpp::SHT_REL, + elfcpp::SHF_ALLOC, this->rel_); +} + +template<bool big_endian> +void +Output_data_plt_arm<big_endian>::do_adjust_output_section(Output_section* os) +{ + os->set_entsize(0); +} + +// Add an entry to the PLT. + +template<bool big_endian> +void +Output_data_plt_arm<big_endian>::add_entry(Symbol* gsym) +{ + gold_assert(!gsym->has_plt_offset()); + + // Note that when setting the PLT offset we skip the initial + // reserved PLT entry. + gsym->set_plt_offset((this->count_) * sizeof(plt_entry) + + sizeof(first_plt_entry)); + + ++this->count_; + + section_offset_type got_offset = this->got_plt_->current_data_size(); + + // Every PLT entry needs a GOT entry which points back to the PLT + // entry (this will be changed by the dynamic linker, normally + // lazily when the function is called). + this->got_plt_->set_current_data_size(got_offset + 4); + + // Every PLT entry needs a reloc. + gsym->set_needs_dynsym_entry(); + this->rel_->add_global(gsym, elfcpp::R_ARM_JUMP_SLOT, this->got_plt_, + got_offset); + + // Note that we don't need to save the symbol. The contents of the + // PLT are independent of which symbols are used. The symbols only + // appear in the relocations. +} + +// ARM PLTs. +// FIXME: This is not very flexible. Right now this has only been tested +// on armv5te. If we are to support additional architecture features like +// Thumb-2 or BE8, we need to make this more flexible like GNU ld. + +// The first entry in the PLT. +template<bool big_endian> +const uint32_t Output_data_plt_arm<big_endian>::first_plt_entry[5] = +{ + 0xe52de004, // str lr, [sp, #-4]! + 0xe59fe004, // ldr lr, [pc, #4] + 0xe08fe00e, // add lr, pc, lr + 0xe5bef008, // ldr pc, [lr, #8]! + 0x00000000, // &GOT[0] - . +}; + +// Subsequent entries in the PLT. + +template<bool big_endian> +const uint32_t Output_data_plt_arm<big_endian>::plt_entry[3] = +{ + 0xe28fc600, // add ip, pc, #0xNN00000 + 0xe28cca00, // add ip, ip, #0xNN000 + 0xe5bcf000, // ldr pc, [ip, #0xNNN]! +}; + +// Write out the PLT. This uses the hand-coded instructions above, +// and adjusts them as needed. This is all specified by the arm ELF +// Processor Supplement. + +template<bool big_endian> +void +Output_data_plt_arm<big_endian>::do_write(Output_file* of) +{ + const off_t offset = this->offset(); + const section_size_type oview_size = + convert_to_section_size_type(this->data_size()); + unsigned char* const oview = of->get_output_view(offset, oview_size); + + const off_t got_file_offset = this->got_plt_->offset(); + const section_size_type got_size = + convert_to_section_size_type(this->got_plt_->data_size()); + unsigned char* const got_view = of->get_output_view(got_file_offset, + got_size); + unsigned char* pov = oview; + + elfcpp::Elf_types<32>::Elf_Addr plt_address = this->address(); + elfcpp::Elf_types<32>::Elf_Addr got_address = this->got_plt_->address(); + + // Write first PLT entry. All but the last word are constants. + const size_t num_first_plt_words = (sizeof(first_plt_entry) + / sizeof(plt_entry[0])); + for (size_t i = 0; i < num_first_plt_words - 1; i++) + elfcpp::Swap<32, big_endian>::writeval(pov + i * 4, first_plt_entry[i]); + // Last word in first PLT entry is &GOT[0] - . + elfcpp::Swap<32, big_endian>::writeval(pov + 16, + got_address - (plt_address + 16)); + pov += sizeof(first_plt_entry); + + unsigned char* got_pov = got_view; + + memset(got_pov, 0, 12); + got_pov += 12; + + const int rel_size = elfcpp::Elf_sizes<32>::rel_size; + unsigned int plt_offset = sizeof(first_plt_entry); + unsigned int plt_rel_offset = 0; + unsigned int got_offset = 12; + const unsigned int count = this->count_; + for (unsigned int i = 0; + i < count; + ++i, + pov += sizeof(plt_entry), + got_pov += 4, + plt_offset += sizeof(plt_entry), + plt_rel_offset += rel_size, + got_offset += 4) + { + // Set and adjust the PLT entry itself. + int32_t offset = ((got_address + got_offset) + - (plt_address + plt_offset + 8)); + + gold_assert(offset >= 0 && offset < 0x0fffffff); + uint32_t plt_insn0 = plt_entry[0] | ((offset >> 20) & 0xff); + elfcpp::Swap<32, big_endian>::writeval(pov, plt_insn0); + uint32_t plt_insn1 = plt_entry[1] | ((offset >> 12) & 0xff); + elfcpp::Swap<32, big_endian>::writeval(pov + 4, plt_insn1); + uint32_t plt_insn2 = plt_entry[2] | (offset & 0xfff); + elfcpp::Swap<32, big_endian>::writeval(pov + 8, plt_insn2); + + // Set the entry in the GOT. + elfcpp::Swap<32, big_endian>::writeval(got_pov, plt_address); + } + + gold_assert(static_cast<section_size_type>(pov - oview) == oview_size); + gold_assert(static_cast<section_size_type>(got_pov - got_view) == got_size); + + of->write_output_view(offset, oview_size, oview); + of->write_output_view(got_file_offset, got_size, got_view); +} + +// Create a PLT entry for a global symbol. + +template<bool big_endian> +void +Target_arm<big_endian>::make_plt_entry(Symbol_table* symtab, Layout* layout, + Symbol* gsym) +{ + if (gsym->has_plt_offset()) + return; + + if (this->plt_ == NULL) + { + // Create the GOT sections first. + this->got_section(symtab, layout); + + this->plt_ = new Output_data_plt_arm<big_endian>(layout, this->got_plt_); + layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS, + (elfcpp::SHF_ALLOC + | elfcpp::SHF_EXECINSTR), + this->plt_); + } + this->plt_->add_entry(gsym); +} + // Report an unsupported relocation against a local symbol. template<bool big_endian> @@ -436,9 +815,44 @@ Target_arm<big_endian>::scan_relocs(const General_options& options, template<bool big_endian> void -Target_arm<big_endian>::do_finalize_sections(Layout* /* layout */) +Target_arm<big_endian>::do_finalize_sections(Layout* layout) { - gold_unreachable (); + // Fill in some more dynamic tags. + Output_data_dynamic* const odyn = layout->dynamic_data(); + if (odyn != NULL) + { + if (this->got_plt_ != NULL) + odyn->add_section_address(elfcpp::DT_PLTGOT, this->got_plt_); + + if (this->plt_ != NULL) + { + const Output_data* od = this->plt_->rel_plt(); + odyn->add_section_size(elfcpp::DT_PLTRELSZ, od); + odyn->add_section_address(elfcpp::DT_JMPREL, od); + odyn->add_constant(elfcpp::DT_PLTREL, elfcpp::DT_REL); + } + + if (this->rel_dyn_ != NULL) + { + const Output_data* od = this->rel_dyn_; + odyn->add_section_address(elfcpp::DT_REL, od); + odyn->add_section_size(elfcpp::DT_RELSZ, od); + odyn->add_constant(elfcpp::DT_RELENT, + elfcpp::Elf_sizes<32>::rel_size); + } + + if (!parameters->options().shared()) + { + // The value of the DT_DEBUG tag is filled in by the dynamic + // linker at run time, and used by the debugger. + odyn->add_constant(elfcpp::DT_DEBUG, 0); + } + } + + // Emit any relocs we saved in an attempt to avoid generating COPY + // relocs. + if (this->copy_relocs_.any_saved_relocs()) + this->copy_relocs_.emit(this->rel_dyn_section(layout)); } // Perform a relocation. @@ -621,12 +1035,17 @@ Target_arm<big_endian>::relocate_for_relocatable( reloc_view_size); } +// Return the value to use for a dynamic symbol which requires special +// treatment. This is how we support equality comparisons of function +// pointers across shared library boundaries, as described in the +// processor specific ABI supplement. + template<bool big_endian> uint64_t -Target_arm<big_endian>::do_dynsym_value(const Symbol* /*gsym*/) const +Target_arm<big_endian>::do_dynsym_value(const Symbol* gsym) const { - gold_unreachable (); - return 0; + gold_assert(gsym->is_from_dynobj() && gsym->has_plt_offset()); + return this->plt_section()->address() + gsym->plt_offset(); } // Map platform-specific relocs to real relocs |